Skip to content
Snippets Groups Projects
Select Git revision
  • 16774189c45ac431c73de3f4d5f159a6a570a92c
  • main default protected
  • pdf-annotieren
  • pdf-annotieren-2.0
  • issue-4244
  • issues-4244-b
  • pdf-annotieren-old
  • biest-4274
  • issue-2982
  • issue-660
  • issue-3326
  • issue-3270
  • issue-3616
  • 5.1
  • 5.2
  • 5.3
  • 5.4
  • 5.5
  • issue-4255
  • issue-4261
  • issue-4262
  • v5.4.2
  • v5.3.5
  • v5.2.7
  • v5.1.8
  • v5.4.1
  • v5.3.4
  • v5.2.6
  • v5.1.7
  • v5.0.9
  • v5.4
  • v5.3.3
  • v5.2.5
  • v5.1.6
  • v5.0.8
  • v5.3.2
  • v5.2.4
  • v5.1.5
  • v5.0.7
  • v5.3.1
  • v5.2.3
41 results

wysiwyg.js

Blame
  • Forked from Stud.IP / Stud.IP
    Source project has a limited visibility.
    Code owners
    Assign users and groups as approvers for specific file changes. Learn more.
    wysiwyg.js 7.61 KiB
    /**
     * wysiwyg.js - Replace HTML textareas with WYSIWYG editor.
     */
    import parseOptions from './parse_options.js';
    import WikiLink from '../cke/wiki-link/wiki-link.js';
    
    const wysiwyg = {
        // NOTE keep this function in sync with Markup class
        htmlMarker: '<!--HTML-->',
        htmlMarkerRegExp: /^\s*<!--\s*HTML.*?-->/i,
    
        isHtml: function isHtml(text) {
            // NOTE keep this function in sync with
            // Markup::isHtml in Markup.class.php
            return this.hasHtmlMarker(text);
        },
        hasHtmlMarker: function hasHtmlMarker(text) {
            // NOTE keep this function in sync with
            // Markup::hasHtmlMarker in Markup.class.php
            return this.htmlMarkerRegExp.test(text);
        },
        markAsHtml: function markAsHtml(text) {
            // NOTE keep this function in sync with
            // Markup::markAsHtml in Markup.class.php
            if (this.hasHtmlMarker(text) || text.trim() == '') {
                return text; // marker already set, don't set twice
            }
            return this.htmlMarker + '\n' + text;
        },
    
        getEditor,
        hasEditor,
    
        replace(textarea) {
            if (!hasEditor(textarea)) {
                if (isTextareaVisible(textarea)) {
                    replaceTextarea(textarea);
                }
            } else if (isEditorHidden(textarea)) {
                destroyTextarea(textarea);
            }
        },
    };
    
    export default wysiwyg;
    
    function isTextareaVisible(textarea) {
        return $(textarea).is(':visible');
    }
    
    function isEditorHidden(textarea) {
        if (!hasEditor(textarea)) {
            return false;
        }
        const editor = getEditor(textarea);
        return editor && editor.ui && $(editor.ui.element).is(':hidden');
    }
    
    function replaceTextarea(textarea) {
        setEditor(textarea, {});
        const $textarea = textarea instanceof jQuery ? textarea : $(textarea);
    
        let options = {};
    
        if ($textarea.attr('data-editor')) {
            const parsed = parseOptions($textarea.attr('data-editor'));
    
            if (parsed.toolbar === 'small') {
                options.toolbar = {
                    removeItems: [
                        'undo',
                        'redo',
                        'findAndReplace',
                        'strikethrough',
                        'horizontalLine',
                        'insertBlockQuote',
                        'splitBlockQuote',
                        'removeBlockQuote',
                    ]
                };
            } else if (parsed.toolbar === 'minimal') {
                options.toolbar = {
                    items: [
                        'bold',
                        'italic',
                        'underline',
                        'subscript',
                        'superscript',
                        '|',
                        'removeFormat',
                        '|',
                        'bulletedList',
                        'numberedList',
                        '|',
                        'fontColor',
                        'fontBackgroundColor',
                        '|',
                        'link',
                        'math',
                        'specialCharacters',
                    ]
                };
            }
    
            if (parsed.removePlugins) {
                options.removePlugins = parsed.removePlugins.split(",")
            }
    
            if (parsed.extraPlugins) {
                const pluginMap = { WikiLink };
                options.extraPlugins = parsed.extraPlugins.split(",").reduce((memo, plugin) => {
                    if (plugin in pluginMap) {
                        memo.push(pluginMap[plugin]);
                    }
                    return memo;
                }, []);
            }
        }
    
        return STUDIP.loadChunk('wysiwyg')
            .then(loadMathJax)
            .then(createEditor)
            .then(setEditorInstance)
            .then(enhanceEditor)
            .then(emitLoadEvent);
    
        function createEditor(ClassicEditor) {
            return ClassicEditor.create(textarea, options).then(editor => {
                function getViewportOffsetTop() {
                    const topBar = document.getElementById('top-bar');
                    const responsiveContentbar = document.getElementById('responsive-contentbar');
    
                    let top = topBar.clientHeight + topBar.clientTop;
                    if (responsiveContentbar) {
                        top += responsiveContentbar?.clientHeight + responsiveContentbar.clientTop;
                    }
    
                    return top;
                }
    
                function updateOffsetTop() {
                    // This needs to be delayed since some events will fire before
                    // changing the DOM
                    setTimeout(() => {
                        editor.ui.viewportOffset = {top: getViewportOffsetTop()};
                        editor.ui.update();
                    }, 50);
                }
    
                // Set initial offset top
                updateOffsetTop();
    
                // Listen to relevant events that may require the sticky panel to be misplaced
                STUDIP.eventBus.on('toggle-compact-navigation', updateOffsetTop);
                STUDIP.eventBus.on('switch-focus-mode', updateOffsetTop);
    
                // Stop listening if editor is destroyed
                editor.on('destroy', () => {
                    STUDIP.eventBus.off('toggle-compact-navigation', updateOffsetTop);
                    STUDIP.eventBus.off('switch-focus-mode', updateOffsetTop);
                });
    
                return editor;
            });
        }
    
        function setEditorInstance(ckeditor) {
            setEditor(textarea, ckeditor);
            return ckeditor;
        }
    
        function enhanceEditor(ckeditor) {
            // make sure HTML marker is always set, in
            // case contents are cut-off by the backend
            $textarea.closest('form').submit(() => {
                ckeditor.setData(wysiwyg.markAsHtml(ckeditor.getData()));
                ckeditor.updateSourceElement();
            });
    
            // focus the editor if requested
            if ($textarea.is('[autofocus]')) {
                ckeditor.focus();
            }
    
            ckeditor.ui.focusTracker.on('change:isFocused', (evt, name, isFocused) => {
                if (!isFocused) {
                    ckeditor.updateSourceElement(wysiwyg.markAsHtml(ckeditor.getData()));
                }
            });
    
            const button = ckeditor.ui.view.toolbar.items.find( item => item.class === "ck-source-editing-button");
            if (button) {
                button.withText = false;
            }
    
            // Tell MathJax v2.7 to leave the editor alone
            ckeditor.ui.element.classList.add('tex2jax_ignore');
    
            return ckeditor;
        }
    
        function emitLoadEvent(ckeditor) {
            $textarea.trigger('load.wysiwyg');
    
            return ckeditor;
        }
    
        async function loadMathJax(ckeditor) {
            let mathjaxP;
    
            if (window.MathJax && window.MathJax.Hub) {
                mathjaxP = Promise.resolve(window.MathJax);
            } else if (window.STUDIP && window.STUDIP.loadChunk) {
                mathjaxP = window.STUDIP.loadChunk('mathjax');
            }
    
            await mathjaxP;
    
            //console.log('loading MathJaxP...', mathjaxP);
            return ckeditor;
        }
    }
    
    function destroyTextarea(textarea) {
        if (!hasEditor(textarea)) {
            throw new Error('Trying to destroy a non-existing editor instance.');
        }
    
        const editor = getEditor(textarea);
        editor.destroy(true);
        unsetEditor(textarea);
    }
    
    // create an unused id
    function createNewId(prefix) {
        var i = 0;
        while ($('#' + prefix + i).length > 0) {
            i++;
        }
        return prefix + i;
    }
    
    const instances = new Map();
    
    function getEditor(textarea) {
        return textarea?.id !== '' ? instances.get(textarea.id) : null;
    }
    
    function hasEditor(textarea) {
        return textarea.id !== '' && instances.has(textarea.id);
    }
    
    function setEditor(textarea, editor) {
        if (textarea.id === '') {
            textarea.id = createNewId('wysiwyg');
        }
        instances.set(textarea.id, editor);
    }
    
    function unsetEditor(textarea) {
        instances.delete(textarea.id);
    }