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        id: JSINFO.id
126    }).done(function handleSwitchEditorResponse(data) {
127        if (window.proseMirrorIsActive) {
128            showDefaultEditor(data.text);
129        } else {
130            showProsemirror(data.json);
131        }
132    }).fail(function (jqXHR, textStatus, errorThrown) {
133        console.error(jqXHR, textStatus, errorThrown); // FIXME: proper error handling
134        if (jqXHR.responseJSON && jqXHR.responseJSON.error) {
135            showErrorMessage(jqXHR.responseJSON.error);
136        } else {
137            let message = 'The request failed with an unexpected error.';
138            if (window.SentryPlugin) {
139                SentryPlugin.logSentryException(new Error(textStatus), {
140                    tags: {
141                        plugin: 'prosemirror',
142                        'id': JSINFO.id,
143                        status: jqXHR.status
144                    },
145                    extra: {
146                        'content': $textArea.val(),
147                        'responseText': jqXHR.responseText,
148                    }
149                });
150                message += ' -- The error has been logged to Sentry.';
151            }
152            showErrorMessage(message);
153        }
154    });
155
156    const $current = DokuCookie.getValue('plugin_prosemirror_useWYSIWYG');
157    DokuCookie.setValue('plugin_prosemirror_useWYSIWYG', $current ? '' : '1');
158}
159
160/**
161 * If the cookie is set, then show the WYSIWYG editor and add the switch-editor-event to the button
162 */
163function handleEditSession() {
164    const $jsonField = jQuery('#dw__editform').find('[name=prosemirror_json]');
165    if (DokuCookie.getValue('plugin_prosemirror_useWYSIWYG')) {
166        showProsemirror($jsonField.val());
167    }
168    const $toggleEditorButton = jQuery('.plugin_prosemirror_useWYSIWYG');
169    $toggleEditorButton.on('click', toggleEditor);
170}
171
172/**
173 * when the editor switch button moves out of the view-port, the menubar gets a class
174 * @see https://codepen.io/hey-nick/pen/mLpmMV
175 * @see https://developer.mozilla.org/en-US/docs/Web/API/Intersection_Observer_API
176 */
177
178function stickyMenubar() {
179    const editorswitch = document.querySelector('button[name=prosemirror]');
180    const menubar = document.querySelector('#prosemirror__editor div.menubar');
181
182    const observer = new IntersectionObserver(
183        ([e]) => {
184            return menubar.classList.toggle('prosemirror-menubar-fixed', e.intersectionRatio !== 1);
185        },
186        {
187            root: null,
188            threshold: [0, 1]
189        }
190    );
191    if (editorswitch && menubar) {
192        observer.observe(editorswitch);
193    }
194}
195
196
197jQuery(function () {
198    initializeProsemirror();
199    window.proseMirrorIsActive = false;
200
201    if (jQuery('#dw__editform').find('[name=prosemirror_json]').length) {
202        handleEditSession();
203    }
204
205    jQuery(window).on('fastwiki:afterSwitch', function(evt, viewMode, isSectionEdit, prevViewMode) {
206        if (viewMode === 'edit' || isSectionEdit) {
207            handleEditSession();
208        }
209    });
210});
211