1/**
2 * Hide Prosemirror and show the default editor
3 *
4 * @param {string} text the wiki syntax to be shown in the textarea
5 */
6function showDefaultEditor(text) {
7    window.Prosemirror.destroyProsemirror();
8    window.proseMirrorIsActive = false;
9    dw_locktimer.init(dw_locktimer.timeout/1000, dw_locktimer.draft);
10    jQuery('#wiki__text').val(text).show();
11    jQuery('#size__ctl').show();
12    jQuery('.editBox > .toolbar').show();
13}
14
15/**
16 * Hide the default editor and start a new Prosemirror Editor
17 *
18 * @param {string} json the prosemirror document json
19 */
20function showProsemirror(json) {
21    const $textArea = jQuery('#wiki__text');
22    const $prosemirrorJsonInput = jQuery('#dw__editform').find('[name=prosemirror_json]').val(json);
23    try {
24        window.Prosemirror.enableProsemirror();
25        stickyMenubar();
26        disableNativeFirefoxTableControls();
27    } catch (e) {
28        console.error(e);
29        let message = 'There was an error in the WYSIWYG editor. You will be redirected to the syntax editor in 5 seconds.';
30        if (window.SentryPlugin) {
31            SentryPlugin.logSentryException(e, {
32                tags: {
33                    plugin: 'prosemirror',
34                    'id': JSINFO.id,
35                },
36                extra: {
37                    'content': $textArea.val(),
38                    'json': $prosemirrorJsonInput.val(),
39                }
40            });
41            message += ' -- The error has been logged to Sentry.';
42        }
43        showErrorMessage(message);
44        setTimeout(function() {
45            jQuery('.plugin_prosemirror_useWYSIWYG').click();
46        }, 5000);
47    }
48    window.proseMirrorIsActive = true;
49    $textArea.hide();
50    jQuery('#size__ctl').hide();
51    jQuery('.editBox > .toolbar').hide();
52    jQuery('div.ProseMirror').focus();
53
54    if (dw_locktimer.addField) {
55        // todo remove this guard after the next stable DokuWiki release after Greebo
56        dw_locktimer.init(dw_locktimer.timeout/1000, dw_locktimer.draft, 'prosemirror__editor');
57        dw_locktimer.addField('input[name=prosemirror_json]');
58    } else {
59        console.warn('Draft saving in WYSIWYG is not available. Please upgrade your wiki to the current development snapshot.')
60    }
61}
62
63/**
64 * Disables Firefox's controls for editable tables, they are incompatible with prosemirror
65 *
66 * See https://github.com/ProseMirror/prosemirror/issues/432 and https://github.com/ProseMirror/prosemirror-tables/issues/22
67 */
68function disableNativeFirefoxTableControls() {
69    document.execCommand("enableObjectResizing", false, "false");
70    document.execCommand("enableInlineTableEditing", false, "false");
71}
72
73/**
74 * Initialize the prosemirror framework
75 *
76 * (This shouldn't do much until we actually use the editor, but we maybe shouldn't do this twice)
77 */
78function initializeProsemirror() {
79    try {
80        /* DOKUWIKI:include lib/bundle.js */
81    } catch (e) {
82        const $textArea = jQuery('#wiki__text');
83        console.error(e);
84        let message = 'There was an error initializing the WYSIWYG editor.';
85        if (window.SentryPlugin) {
86            SentryPlugin.logSentryException(e, {
87                tags: {
88                    plugin: 'prosemirror',
89                    'id': JSINFO.id,
90                },
91                extra: {
92                    'content': $textArea.val(),
93                }
94            });
95            message += ' The error has been logged to sentry.';
96        }
97
98        showErrorMessage(message);
99
100        DokuCookie.setValue('plugin_prosemirror_useWYSIWYG', '');
101    }
102}
103
104/**
105 * Add the error message above the editor
106 *
107 * @param {string} errorMsg
108 */
109function showErrorMessage(errorMsg) {
110    jQuery('.editBox').before(
111        jQuery('<div class="error"></div>').text(errorMsg)
112    );
113}
114
115/**
116 * Switch between WYSIWYG and Syntax editor
117 */
118function toggleEditor() {
119    const $textArea = jQuery('#wiki__text');
120    const $jsonField = jQuery('#dw__editform').find('[name=prosemirror_json]');
121    jQuery.post(DOKU_BASE + 'lib/exe/ajax.php', {
122        call: 'plugin_prosemirror_switch_editors',
123        data: window.proseMirrorIsActive ? $jsonField.val() : $textArea.val(),
124        getJSON: window.proseMirrorIsActive ? '0' : '1',
125    }).done(function handleSwitchEditorResponse(data) {
126        if (window.proseMirrorIsActive) {
127            showDefaultEditor(data.text);
128        } else {
129            showProsemirror(data.json);
130        }
131    }).fail(function (jqXHR, textStatus, errorThrown) {
132        console.error(jqXHR, textStatus, errorThrown); // FIXME: proper error handling
133        if (jqXHR.responseJSON && jqXHR.responseJSON.error) {
134            showErrorMessage(jqXHR.responseJSON.error);
135        } else {
136            let message = 'The request failed with an unexpected error.';
137            if (window.SentryPlugin) {
138                SentryPlugin.logSentryException(new Error(textStatus), {
139                    tags: {
140                        plugin: 'prosemirror',
141                        'id': JSINFO.id,
142                        status: jqXHR.status
143                    },
144                    extra: {
145                        'content': $textArea.val(),
146                        'responseText': jqXHR.responseText,
147                    }
148                });
149                message += ' -- The error has been logged to Sentry.';
150            }
151            showErrorMessage(message);
152        }
153    });
154
155    const $current = DokuCookie.getValue('plugin_prosemirror_useWYSIWYG');
156    DokuCookie.setValue('plugin_prosemirror_useWYSIWYG', $current ? '' : '1');
157}
158
159/**
160 * If the cookie is set, then show the WYSIWYG editor and add the switch-editor-event to the button
161 */
162function handleEditSession() {
163    const $jsonField = jQuery('#dw__editform').find('[name=prosemirror_json]');
164    if (DokuCookie.getValue('plugin_prosemirror_useWYSIWYG')) {
165        showProsemirror($jsonField.val());
166    }
167    const $toggleEditorButton = jQuery('.plugin_prosemirror_useWYSIWYG');
168    $toggleEditorButton.on('click', toggleEditor);
169}
170
171/**
172 * when the editor switch button moves out of the view-port, the menubar gets a class
173 * @see https://codepen.io/hey-nick/pen/mLpmMV
174 * @see https://developer.mozilla.org/en-US/docs/Web/API/Intersection_Observer_API
175 */
176
177function stickyMenubar() {
178    const editorswitch = document.querySelector('button[name=prosemirror]');
179    const menubar = document.querySelector('#prosemirror__editor div.menubar');
180
181    const observer = new IntersectionObserver(
182        ([e]) => {
183            return menubar.classList.toggle('prosemirror-menubar-fixed', e.intersectionRatio !== 1);
184        },
185        {
186            root: null,
187            threshold: [0, 1]
188        }
189    );
190    if (editorswitch && menubar) {
191        observer.observe(editorswitch);
192    }
193}
194
195
196jQuery(function () {
197    initializeProsemirror();
198    window.proseMirrorIsActive = false;
199
200    if (jQuery('#dw__editform').find('[name=prosemirror_json]').length) {
201        handleEditSession();
202    }
203
204    jQuery(window).on('fastwiki:afterSwitch', function(evt, viewMode, isSectionEdit, prevViewMode) {
205        if (viewMode === 'edit' || isSectionEdit) {
206            handleEditSession();
207        }
208    });
209});
210