]> BookStack Code Mirror - bookstack/commitdiff
ESLINT: Started inital pass at addressing issues
authorDan Brown <redacted>
Wed, 19 Apr 2023 09:46:13 +0000 (10:46 +0100)
committerDan Brown <redacted>
Wed, 19 Apr 2023 09:46:13 +0000 (10:46 +0100)
18 files changed:
package.json
resources/js/app.js
resources/js/markdown/codemirror.js
resources/js/services/clipboard.js
resources/js/services/http.js
resources/js/services/util.js
resources/js/wysiwyg/config.js
resources/js/wysiwyg/drop-paste-handling.js
resources/js/wysiwyg/plugin-codeeditor.js
resources/js/wysiwyg/plugin-drawio.js
resources/js/wysiwyg/plugins-about.js
resources/js/wysiwyg/plugins-customhr.js
resources/js/wysiwyg/plugins-details.js
resources/js/wysiwyg/plugins-imagemanager.js
resources/js/wysiwyg/plugins-tasklist.js
resources/js/wysiwyg/scrolling.js
resources/js/wysiwyg/shortcuts.js
resources/js/wysiwyg/toolbars.js

index 20f96ff48d05cd3ed8c73fbb39f49b8964cf8eb1..264180f811f7cda25c1499bf1e181dfcda4fb4bf 100644 (file)
@@ -58,6 +58,7 @@
       "es2021": true
     },
     "extends": "airbnb-base",
+    "ignorePatterns": ["resources/**/*-stub.js"],
     "overrides": [
     ],
     "parserOptions": {
         "anonymous": "never",
         "named": "never",
         "asyncArrow": "always"
+      }],
+      "import/prefer-default-export": "off",
+      "no-plusplus": ["error", {
+        "allowForLoopAfterthoughts": true
+      }],
+      "arrow-body-style": "off",
+      "no-restricted-syntax": "off",
+      "no-continue": "off",
+      "no-console": ["warn", {
+        "allow": ["error"]
+      }],
+      "max-len": ["error", {
+        "code": 110,
+        "tabWidth": 4,
+        "ignoreUrls": true,
+        "ignoreComments": false,
+        "ignoreRegExpLiterals": true,
+        "ignoreStrings": true,
+        "ignoreTemplateLiterals": true
+      }],
+      "no-param-reassign": ["error", {
+        "props": false
       }]
     }
   }
index ccf54b33d1a7f609b7dfe3928b8a462cbf8252fa..86c8d080248d5e8cb4f22a3a32c7407ce6f68da1 100644 (file)
@@ -1,19 +1,20 @@
 import events from './services/events';
-import httpInstance from './services/http';
+import * as httpInstance from './services/http';
 import Translations from './services/translations';
 
 import * as components from './services/components';
 import * as componentMap from './components';
 
 // Url retrieval function
-window.baseUrl = function(path) {
+window.baseUrl = function baseUrl(path) {
+    let targetPath = path;
     let basePath = document.querySelector('meta[name="base-url"]').getAttribute('content');
     if (basePath[basePath.length - 1] === '/') basePath = basePath.slice(0, basePath.length - 1);
-    if (path[0] === '/') path = path.slice(1);
-    return `${basePath}/${path}`;
+    if (targetPath[0] === '/') targetPath = targetPath.slice(1);
+    return `${basePath}/${targetPath}`;
 };
 
-window.importVersioned = function(moduleName) {
+window.importVersioned = function importVersioned(moduleName) {
     const version = document.querySelector('link[href*="/dist/styles.css?version="]').href.split('?version=').pop();
     const importPath = window.baseUrl(`dist/${moduleName}.js?version=${version}`);
     return import(importPath);
index 67b7c68ec869d19d1cddfdf705244fba7bdd8454..b615e666bd7a1dc2ba6b39e6f1c4a33b5fa37a48 100644 (file)
@@ -1,6 +1,6 @@
 import {provideKeyBindings} from './shortcuts';
 import {debounce} from '../services/util';
-import Clipboard from '../services/clipboard';
+import {Clipboard} from '../services/clipboard';
 
 /**
  * Initiate the codemirror instance for the markdown editor.
index ecdbecf53825bce6c65df94b0d14671e5a9a781d..02db29be00aa98869c9bf1eb17df8a590a8a1370 100644 (file)
@@ -66,5 +66,3 @@ export async function copyTextToClipboard(text) {
     document.execCommand('copy');
     document.body.removeChild(tempInput);
 }
-
-export default Clipboard;
index 9f1b5deac0f84306d147078d49c2045276f961eb..d0d33e317df9230c2989bbcfefcd0bb2333032cf 100644 (file)
@@ -1,89 +1,48 @@
 /**
- * Perform a HTTP GET request.
- * Can easily pass query parameters as the second parameter.
- * @param {String} url
- * @param {Object} params
- * @returns {Promise<{headers: Headers, original: Response, data: (Object|String), redirected: boolean, statusText: string, url: string, status: number}>}
+ * @typedef FormattedResponse
+ * @property {Headers} headers
+ * @property {Response} original
+ * @property {Object|String} data
+ * @property {Boolean} redirected
+ * @property {Number} status
+ * @property {string} statusText
+ * @property {string} url
  */
-async function get(url, params = {}) {
-    return request(url, {
-        method: 'GET',
-        params,
-    });
-}
 
 /**
- * Perform a HTTP POST request.
- * @param {String} url
- * @param {Object} data
- * @returns {Promise<{headers: Headers, original: Response, data: (Object|String), redirected: boolean, statusText: string, url: string, status: number}>}
+ * Get the content from a fetch response.
+ * Checks the content-type header to determine the format.
+ * @param {Response} response
+ * @returns {Promise<Object|String>}
  */
-async function post(url, data = null) {
-    return dataRequest('POST', url, data);
-}
+async function getResponseContent(response) {
+    if (response.status === 204) {
+        return null;
+    }
 
-/**
- * Perform a HTTP PUT request.
- * @param {String} url
- * @param {Object} data
- * @returns {Promise<{headers: Headers, original: Response, data: (Object|String), redirected: boolean, statusText: string, url: string, status: number}>}
- */
-async function put(url, data = null) {
-    return dataRequest('PUT', url, data);
-}
+    const responseContentType = response.headers.get('Content-Type') || '';
+    const subType = responseContentType.split(';')[0].split('/').pop();
 
-/**
- * Perform a HTTP PATCH request.
- * @param {String} url
- * @param {Object} data
- * @returns {Promise<{headers: Headers, original: Response, data: (Object|String), redirected: boolean, statusText: string, url: string, status: number}>}
- */
-async function patch(url, data = null) {
-    return dataRequest('PATCH', url, data);
-}
+    if (subType === 'javascript' || subType === 'json') {
+        return response.json();
+    }
 
-/**
- * Perform a HTTP DELETE request.
- * @param {String} url
- * @param {Object} data
- * @returns {Promise<{headers: Headers, original: Response, data: (Object|String), redirected: boolean, statusText: string, url: string, status: number}>}
- */
-async function performDelete(url, data = null) {
-    return dataRequest('DELETE', url, data);
+    return response.text();
 }
 
-/**
- * Perform a HTTP request to the back-end that includes data in the body.
- * Parses the body to JSON if an object, setting the correct headers.
- * @param {String} method
- * @param {String} url
- * @param {Object} data
- * @returns {Promise<{headers: Headers, original: Response, data: (Object|String), redirected: boolean, statusText: string, url: string, status: number}>}
- */
-async function dataRequest(method, url, data = null) {
-    const options = {
-        method,
-        body: data,
-    };
+export class HttpError extends Error {
 
-    // Send data as JSON if a plain object
-    if (typeof data === 'object' && !(data instanceof FormData)) {
-        options.headers = {
-            'Content-Type': 'application/json',
-            'X-Requested-With': 'XMLHttpRequest',
-        };
-        options.body = JSON.stringify(data);
-    }
-
-    // Ensure FormData instances are sent over POST
-    // Since Laravel does not read multipart/form-data from other types
-    // of request. Hence the addition of the magic _method value.
-    if (data instanceof FormData && method !== 'post') {
-        data.append('_method', method);
-        options.method = 'post';
+    constructor(response, content) {
+        super(response.statusText);
+        this.data = content;
+        this.headers = response.headers;
+        this.redirected = response.redirected;
+        this.status = response.status;
+        this.statusText = response.statusText;
+        this.url = response.url;
+        this.original = response;
     }
 
-    return request(url, options);
 }
 
 /**
@@ -91,33 +50,35 @@ async function dataRequest(method, url, data = null) {
  * to communicate with the back-end. Parses & formats the response.
  * @param {String} url
  * @param {Object} options
- * @returns {Promise<{headers: Headers, original: Response, data: (Object|String), redirected: boolean, statusText: string, url: string, status: number}>}
+ * @returns {Promise<FormattedResponse>}
  */
 async function request(url, options = {}) {
-    if (!url.startsWith('http')) {
-        url = window.baseUrl(url);
+    let requestUrl = url;
+
+    if (!requestUrl.startsWith('http')) {
+        requestUrl = window.baseUrl(requestUrl);
     }
 
     if (options.params) {
-        const urlObj = new URL(url);
+        const urlObj = new URL(requestUrl);
         for (const paramName of Object.keys(options.params)) {
             const value = options.params[paramName];
             if (typeof value !== 'undefined' && value !== null) {
                 urlObj.searchParams.set(paramName, value);
             }
         }
-        url = urlObj.toString();
+        requestUrl = urlObj.toString();
     }
 
     const csrfToken = document.querySelector('meta[name=token]').getAttribute('content');
-    options = {...options, credentials: 'same-origin'};
-    options.headers = {
-        ...options.headers || {},
+    const requestOptions = {...options, credentials: 'same-origin'};
+    requestOptions.headers = {
+        ...requestOptions.headers || {},
         baseURL: window.baseUrl(''),
         'X-CSRF-TOKEN': csrfToken,
     };
 
-    const response = await fetch(url, options);
+    const response = await fetch(requestUrl, requestOptions);
     const content = await getResponseContent(response);
     const returnData = {
         data: content,
@@ -137,46 +98,91 @@ async function request(url, options = {}) {
 }
 
 /**
- * Get the content from a fetch response.
- * Checks the content-type header to determine the format.
- * @param {Response} response
- * @returns {Promise<Object|String>}
+ * Perform a HTTP request to the back-end that includes data in the body.
+ * Parses the body to JSON if an object, setting the correct headers.
+ * @param {String} method
+ * @param {String} url
+ * @param {Object} data
+ * @returns {Promise<FormattedResponse>}
  */
-async function getResponseContent(response) {
-    if (response.status === 204) {
-        return null;
-    }
+async function dataRequest(method, url, data = null) {
+    const options = {
+        method,
+        body: data,
+    };
 
-    const responseContentType = response.headers.get('Content-Type') || '';
-    const subType = responseContentType.split(';')[0].split('/').pop();
+    // Send data as JSON if a plain object
+    if (typeof data === 'object' && !(data instanceof FormData)) {
+        options.headers = {
+            'Content-Type': 'application/json',
+            'X-Requested-With': 'XMLHttpRequest',
+        };
+        options.body = JSON.stringify(data);
+    }
 
-    if (subType === 'javascript' || subType === 'json') {
-        return await response.json();
+    // Ensure FormData instances are sent over POST
+    // Since Laravel does not read multipart/form-data from other types
+    // of request. Hence the addition of the magic _method value.
+    if (data instanceof FormData && method !== 'post') {
+        data.append('_method', method);
+        options.method = 'post';
     }
 
-    return await response.text();
+    return request(url, options);
+}
+
+/**
+ * Perform a HTTP GET request.
+ * Can easily pass query parameters as the second parameter.
+ * @param {String} url
+ * @param {Object} params
+ * @returns {Promise<FormattedResponse>}
+ */
+export async function get(url, params = {}) {
+    return request(url, {
+        method: 'GET',
+        params,
+    });
+}
+
+/**
+ * Perform a HTTP POST request.
+ * @param {String} url
+ * @param {Object} data
+ * @returns {Promise<FormattedResponse>}
+ */
+export async function post(url, data = null) {
+    return dataRequest('POST', url, data);
 }
 
-class HttpError extends Error {
+/**
+ * Perform a HTTP PUT request.
+ * @param {String} url
+ * @param {Object} data
+ * @returns {Promise<FormattedResponse>}
+ */
+export async function put(url, data = null) {
+    return dataRequest('PUT', url, data);
+}
 
-    constructor(response, content) {
-        super(response.statusText);
-        this.data = content;
-        this.headers = response.headers;
-        this.redirected = response.redirected;
-        this.status = response.status;
-        this.statusText = response.statusText;
-        this.url = response.url;
-        this.original = response;
-    }
+/**
+ * Perform a HTTP PATCH request.
+ * @param {String} url
+ * @param {Object} data
+ * @returns {Promise<FormattedResponse>}
+ */
+export async function patch(url, data = null) {
+    return dataRequest('PATCH', url, data);
+}
 
+/**
+ * Perform a HTTP DELETE request.
+ * @param {String} url
+ * @param {Object} data
+ * @returns {Promise<FormattedResponse>}
+ */
+async function performDelete(url, data = null) {
+    return dataRequest('DELETE', url, data);
 }
 
-export default {
-    get,
-    post,
-    put,
-    patch,
-    delete: performDelete,
-    HttpError,
-};
+export {performDelete as delete};
index df2b31336080f437119536f17ee738d8baf1c076..dd97d81aaf47113b301660794eda9b881d54e1e8 100644 (file)
  */
 export function debounce(func, wait, immediate) {
     let timeout;
-    return function() {
-        const context = this; const
-            args = arguments;
-        const later = function() {
+    return function debouncedWrapper(...args) {
+        const context = this;
+        const later = function debouncedTimeout() {
             timeout = null;
             if (!immediate) func.apply(context, args);
         };
@@ -67,6 +66,7 @@ export function escapeHtml(unsafe) {
  * @returns {string}
  */
 export function uniqueId() {
+    // eslint-disable-next-line no-bitwise
     const S4 = () => (((1 + Math.random()) * 0x10000) | 0).toString(16).substring(1);
     return (`${S4() + S4()}-${S4()}-${S4()}-${S4()}-${S4()}${S4()}${S4()}`);
 }
index 89efdb8a080d0d1b283735de3bf376a49629e0e4..a0e7156ee6866bdbecdbe9dd43f057aa26b228ff 100644 (file)
@@ -13,7 +13,7 @@ import {getPlugin as getAboutPlugin} from './plugins-about';
 import {getPlugin as getDetailsPlugin} from './plugins-details';
 import {getPlugin as getTasklistPlugin} from './plugins-tasklist';
 
-const style_formats = [
+const styleFormats = [
     {title: 'Large Header', format: 'h2', preview: 'color: blue;'},
     {title: 'Medium Header', format: 'h3'},
     {title: 'Small Header', format: 'h4'},
@@ -43,7 +43,7 @@ const formats = {
     calloutdanger: {block: 'p', exact: true, attributes: {class: 'callout danger'}},
 };
 
-const color_map = [
+const colorMap = [
     '#BFEDD2', '',
     '#FBEEB8', '',
     '#F8CAC6', '',
@@ -72,7 +72,7 @@ const color_map = [
     '#ffffff', '',
 ];
 
-function file_picker_callback(callback, value, meta) {
+function filePickerCallback(callback, value, meta) {
     // field_name, url, type, win
     if (meta.filetype === 'file') {
         /** @type {EntitySelectorPopup} * */
@@ -119,12 +119,12 @@ function gatherPlugins(options) {
         options.textDirection === 'rtl' ? 'directionality' : '',
     ];
 
-    window.tinymce.PluginManager.add('codeeditor', getCodeeditorPlugin(options));
-    window.tinymce.PluginManager.add('customhr', getCustomhrPlugin(options));
-    window.tinymce.PluginManager.add('imagemanager', getImagemanagerPlugin(options));
-    window.tinymce.PluginManager.add('about', getAboutPlugin(options));
-    window.tinymce.PluginManager.add('details', getDetailsPlugin(options));
-    window.tinymce.PluginManager.add('tasklist', getTasklistPlugin(options));
+    window.tinymce.PluginManager.add('codeeditor', getCodeeditorPlugin());
+    window.tinymce.PluginManager.add('customhr', getCustomhrPlugin());
+    window.tinymce.PluginManager.add('imagemanager', getImagemanagerPlugin());
+    window.tinymce.PluginManager.add('about', getAboutPlugin());
+    window.tinymce.PluginManager.add('details', getDetailsPlugin());
+    window.tinymce.PluginManager.add('tasklist', getTasklistPlugin());
 
     if (options.drawioUrl) {
         window.tinymce.PluginManager.add('drawio', getDrawioPlugin(options));
@@ -156,7 +156,7 @@ function setupBrFilter(editor) {
     editor.serializer.addNodeFilter('br', nodes => {
         for (const node of nodes) {
             if (node.parent && node.parent.name === 'code') {
-                const newline = tinymce.html.Node.create('#text');
+                const newline = window.tinymce.html.Node.create('#text');
                 newline.value = '\n';
                 node.replace(newline);
             }
@@ -169,7 +169,14 @@ function setupBrFilter(editor) {
  * @return {function(Editor)}
  */
 function getSetupCallback(options) {
-    return function(editor) {
+    return function setupCallback(editor) {
+        function editorChange() {
+            if (options.darkMode) {
+                editor.contentDocument.documentElement.classList.add('dark-mode');
+            }
+            window.$events.emit('editor-html-change', '');
+        }
+
         editor.on('ExecCommand change input NodeChange ObjectResized', editorChange);
         listenForCommonEvents(editor);
         listenForDragAndPaste(editor, options);
@@ -185,13 +192,6 @@ function getSetupCallback(options) {
             setupBrFilter(editor);
         });
 
-        function editorChange() {
-            if (options.darkMode) {
-                editor.contentDocument.documentElement.classList.add('dark-mode');
-            }
-            window.$events.emit('editor-html-change', '');
-        }
-
         // Custom handler hook
         window.$events.emitPublic(options.containerElement, 'editor-tinymce::setup', {editor});
 
@@ -274,7 +274,7 @@ export function build(options) {
         contextmenu: false,
         toolbar: getPrimaryToolbar(options),
         content_style: getContentStyle(options),
-        style_formats,
+        style_formats: styleFormats,
         style_formats_merge: false,
         media_alt_source: false,
         media_poster: false,
@@ -282,8 +282,8 @@ export function build(options) {
         table_style_by_css: true,
         table_use_colgroups: true,
         file_picker_types: 'file image',
-        color_map,
-        file_picker_callback,
+        color_map: colorMap,
+        file_picker_callback: filePickerCallback,
         paste_preprocess(plugin, args) {
             const {content} = args;
             if (content.indexOf('<img src="file://') !== -1) {
@@ -296,7 +296,7 @@ export function build(options) {
         },
         setup(editor) {
             registerCustomIcons(editor);
-            registerAdditionalToolbars(editor, options);
+            registerAdditionalToolbars(editor);
             getSetupCallback(options)(editor);
         },
     };
index 48c6c625daf26871f525d31378820a67b3c5321f..33078cd1d5687b74fb877ef8dd2719dd4e0f59ff 100644 (file)
@@ -1,4 +1,4 @@
-import Clipboard from '../services/clipboard';
+import {Clipboard} from '../services/clipboard';
 
 let wrap;
 let draggedContentEditable;
@@ -7,6 +7,25 @@ function hasTextContent(node) {
     return node && !!(node.textContent || node.innerText);
 }
 
+/**
+ * Upload an image file to the server
+ * @param {File} file
+ * @param {int} pageId
+ */
+async function uploadImageFile(file, pageId) {
+    if (file === null || file.type.indexOf('image') !== 0) {
+        throw new Error('Not an image file');
+    }
+
+    const remoteFilename = file.name || `image-${Date.now()}.png`;
+    const formData = new FormData();
+    formData.append('file', file, remoteFilename);
+    formData.append('uploaded_to', pageId);
+
+    const resp = await window.$http.post(window.baseUrl('/images/gallery'), formData);
+    return resp.data;
+}
+
 /**
  * Handle pasting images from clipboard.
  * @param {Editor} editor
@@ -43,36 +62,16 @@ function paste(editor, options, event) {
             }).catch(err => {
                 editor.dom.remove(id);
                 window.$events.emit('error', options.translations.imageUploadErrorText);
-                console.log(err);
+                console.error(err);
             });
         }, 10);
     }
 }
 
-/**
- * Upload an image file to the server
- * @param {File} file
- * @param {int} pageId
- */
-async function uploadImageFile(file, pageId) {
-    if (file === null || file.type.indexOf('image') !== 0) {
-        throw new Error('Not an image file');
-    }
-
-    const remoteFilename = file.name || `image-${Date.now()}.png`;
-    const formData = new FormData();
-    formData.append('file', file, remoteFilename);
-    formData.append('uploaded_to', pageId);
-
-    const resp = await window.$http.post(window.baseUrl('/images/gallery'), formData);
-    return resp.data;
-}
-
 /**
  * @param {Editor} editor
- * @param {WysiwygConfigOptions} options
  */
-function dragStart(editor, options) {
+function dragStart(editor) {
     const node = editor.selection.getNode();
 
     if (node.nodeName === 'IMG') {
@@ -96,7 +95,11 @@ function dragStart(editor, options) {
  */
 function drop(editor, options, event) {
     const {dom} = editor;
-    const rng = tinymce.dom.RangeUtils.getCaretRangeFromPoint(event.clientX, event.clientY, editor.getDoc());
+    const rng = window.tinymce.dom.RangeUtils.getCaretRangeFromPoint(
+        event.clientX,
+        event.clientY,
+        editor.getDoc(),
+    );
 
     // Template insertion
     const templateId = event.dataTransfer && event.dataTransfer.getData('bookstack/template');
@@ -151,7 +154,7 @@ function drop(editor, options, event) {
  * @param {WysiwygConfigOptions} options
  */
 export function listenForDragAndPaste(editor, options) {
-    editor.on('dragstart', () => dragStart(editor, options));
+    editor.on('dragstart', () => dragStart(editor));
     editor.on('drop', event => drop(editor, options, event));
     editor.on('paste', event => paste(editor, options, event));
 }
index 7446e6870320b238f9403eee2e95f66ac7c91dd0..fa3804ea8368b8a8afd52631937de2023fe95606 100644 (file)
@@ -116,7 +116,9 @@ function defineCodeBlockCustomElement(editor) {
             const container = this.shadowRoot.querySelector('.CodeMirrorContainer');
             const renderEditor = Code => {
                 this.editor = Code.wysiwygView(container, this.shadowRoot, content, this.getLanguage());
-                setTimeout(() => this.style.height = null, 12);
+                setTimeout(() => {
+                    this.style.height = null;
+                }, 12);
             };
 
             window.importVersioned('code').then(Code => {
@@ -143,9 +145,8 @@ function defineCodeBlockCustomElement(editor) {
 
 /**
  * @param {Editor} editor
- * @param {String} url
  */
-function register(editor, url) {
+function register(editor) {
     editor.ui.registry.addIcon('codeblock', '<svg width="24" height="24"><path d="M4 3h16c.6 0 1 .4 1 1v16c0 .6-.4 1-1 1H4a1 1 0 0 1-1-1V4c0-.6.4-1 1-1Zm1 2v14h14V5Z"/><path d="M11.103 15.423c.277.277.277.738 0 .922a.692.692 0 0 1-1.106 0l-4.057-3.78a.738.738 0 0 1 0-1.107l4.057-3.872c.276-.277.83-.277 1.106 0a.724.724 0 0 1 0 1.014L7.6 12.012ZM12.897 8.577c-.245-.312-.2-.675.08-.955.28-.281.727-.27 1.027.033l4.057 3.78a.738.738 0 0 1 0 1.107l-4.057 3.872c-.277.277-.83.277-1.107 0a.724.724 0 0 1 0-1.014l3.504-3.412z"/></svg>');
 
     editor.ui.registry.addButton('codeeditor', {
@@ -183,7 +184,7 @@ function register(editor, url) {
         }
     });
 
-    editor.on('dblclick', event => {
+    editor.on('dblclick', () => {
         const selectedNode = editor.selection.getNode();
         if (elemIsCodeBlock(selectedNode)) {
             showPopupForCodeBlock(editor, selectedNode);
@@ -193,7 +194,7 @@ function register(editor, url) {
     editor.on('PreInit', () => {
         editor.parser.addNodeFilter('pre', elms => {
             for (const el of elms) {
-                const wrapper = tinymce.html.Node.create('code-block', {
+                const wrapper = window.tinymce.html.Node.create('code-block', {
                     contenteditable: 'false',
                 });
 
@@ -234,9 +235,8 @@ function register(editor, url) {
 }
 
 /**
- * @param {WysiwygConfigOptions} options
  * @return {register}
  */
-export function getPlugin(options) {
+export function getPlugin() {
     return register;
 }
index 3d1125841807529323e99f467e39e88e418ee0c7..7827efd6238206260ee0973ab839318f5c38b1f7 100644 (file)
@@ -32,12 +32,6 @@ function showDrawingManager(mceEditor, selectedNode = null) {
     }, 'drawio');
 }
 
-function showDrawingEditor(mceEditor, selectedNode = null) {
-    pageEditor = mceEditor;
-    currentNode = selectedNode;
-    DrawIO.show(options.drawioUrl, drawingInit, updateContent);
-}
-
 async function updateContent(pngData) {
     const id = `image-${Math.random().toString(16).slice(2)}`;
     const loadingImage = window.baseUrl('/loading.gif');
@@ -48,7 +42,7 @@ async function updateContent(pngData) {
         } else {
             window.$events.emit('error', options.translations.imageUploadErrorText);
         }
-        console.log(error);
+        console.error(error);
     };
 
     // Handle updating an existing image
@@ -92,6 +86,66 @@ function drawingInit() {
     return DrawIO.load(drawingId);
 }
 
+function showDrawingEditor(mceEditor, selectedNode = null) {
+    pageEditor = mceEditor;
+    currentNode = selectedNode;
+    DrawIO.show(options.drawioUrl, drawingInit, updateContent);
+}
+
+/**
+ * @param {Editor} editor
+ */
+function register(editor) {
+    editor.addCommand('drawio', () => {
+        const selectedNode = editor.selection.getNode();
+        showDrawingEditor(editor, isDrawing(selectedNode) ? selectedNode : null);
+    });
+
+    editor.ui.registry.addIcon('diagram', `<svg width="24" height="24" fill="${options.darkMode ? '#BBB' : '#000000'}" xmlns="http://www.w3.org/2000/svg"><path d="M20.716 7.639V2.845h-4.794v1.598h-7.99V2.845H3.138v4.794h1.598v7.99H3.138v4.794h4.794v-1.598h7.99v1.598h4.794v-4.794h-1.598v-7.99zM4.736 4.443h1.598V6.04H4.736zm1.598 14.382H4.736v-1.598h1.598zm9.588-1.598h-7.99v-1.598H6.334v-7.99h1.598V6.04h7.99v1.598h1.598v7.99h-1.598zm3.196 1.598H17.52v-1.598h1.598zM17.52 6.04V4.443h1.598V6.04zm-4.21 7.19h-2.79l-.582 1.599H8.643l2.717-7.191h1.119l2.724 7.19h-1.302zm-2.43-1.006h2.086l-1.039-3.06z"/></svg>`);
+
+    editor.ui.registry.addSplitButton('drawio', {
+        tooltip: 'Insert/edit drawing',
+        icon: 'diagram',
+        onAction() {
+            editor.execCommand('drawio');
+            // Hack to de-focus the tinymce editor toolbar
+            window.document.body.dispatchEvent(new Event('mousedown', {bubbles: true}));
+        },
+        fetch(callback) {
+            callback([
+                {
+                    type: 'choiceitem',
+                    text: 'Drawing manager',
+                    value: 'drawing-manager',
+                },
+            ]);
+        },
+        onItemAction(api, value) {
+            if (value === 'drawing-manager') {
+                const selectedNode = editor.selection.getNode();
+                showDrawingManager(editor, isDrawing(selectedNode) ? selectedNode : null);
+            }
+        },
+    });
+
+    editor.on('dblclick', () => {
+        const selectedNode = editor.selection.getNode();
+        if (!isDrawing(selectedNode)) return;
+        showDrawingEditor(editor, selectedNode);
+    });
+
+    editor.on('SetContent', () => {
+        const drawings = editor.dom.select('body > div[drawio-diagram]');
+        if (!drawings.length) return;
+
+        editor.undoManager.transact(() => {
+            for (const drawing of drawings) {
+                drawing.setAttribute('contenteditable', 'false');
+            }
+        });
+    });
+}
+
 /**
  *
  * @param {WysiwygConfigOptions} providedOptions
@@ -99,54 +153,5 @@ function drawingInit() {
  */
 export function getPlugin(providedOptions) {
     options = providedOptions;
-    return function(editor, url) {
-        editor.addCommand('drawio', () => {
-            const selectedNode = editor.selection.getNode();
-            showDrawingEditor(editor, isDrawing(selectedNode) ? selectedNode : null);
-        });
-
-        editor.ui.registry.addIcon('diagram', `<svg width="24" height="24" fill="${options.darkMode ? '#BBB' : '#000000'}" xmlns="http://www.w3.org/2000/svg"><path d="M20.716 7.639V2.845h-4.794v1.598h-7.99V2.845H3.138v4.794h1.598v7.99H3.138v4.794h4.794v-1.598h7.99v1.598h4.794v-4.794h-1.598v-7.99zM4.736 4.443h1.598V6.04H4.736zm1.598 14.382H4.736v-1.598h1.598zm9.588-1.598h-7.99v-1.598H6.334v-7.99h1.598V6.04h7.99v1.598h1.598v7.99h-1.598zm3.196 1.598H17.52v-1.598h1.598zM17.52 6.04V4.443h1.598V6.04zm-4.21 7.19h-2.79l-.582 1.599H8.643l2.717-7.191h1.119l2.724 7.19h-1.302zm-2.43-1.006h2.086l-1.039-3.06z"/></svg>`);
-
-        editor.ui.registry.addSplitButton('drawio', {
-            tooltip: 'Insert/edit drawing',
-            icon: 'diagram',
-            onAction() {
-                editor.execCommand('drawio');
-                // Hack to de-focus the tinymce editor toolbar
-                window.document.body.dispatchEvent(new Event('mousedown', {bubbles: true}));
-            },
-            fetch(callback) {
-                callback([
-                    {
-                        type: 'choiceitem',
-                        text: 'Drawing manager',
-                        value: 'drawing-manager',
-                    },
-                ]);
-            },
-            onItemAction(api, value) {
-                if (value === 'drawing-manager') {
-                    const selectedNode = editor.selection.getNode();
-                    showDrawingManager(editor, isDrawing(selectedNode) ? selectedNode : null);
-                }
-            },
-        });
-
-        editor.on('dblclick', event => {
-            const selectedNode = editor.selection.getNode();
-            if (!isDrawing(selectedNode)) return;
-            showDrawingEditor(editor, selectedNode);
-        });
-
-        editor.on('SetContent', () => {
-            const drawings = editor.dom.select('body > div[drawio-diagram]');
-            if (!drawings.length) return;
-
-            editor.undoManager.transact(() => {
-                for (const drawing of drawings) {
-                    drawing.setAttribute('contenteditable', 'false');
-                }
-            });
-        });
-    };
+    return register;
 }
index a9c0a9e64c5610b5d06819d91092120279e0f3c3..096b4f96805a98ba8676ed7c7f95789b096bc014 100644 (file)
@@ -1,8 +1,7 @@
 /**
  * @param {Editor} editor
- * @param {String} url
  */
-function register(editor, url) {
+function register(editor) {
     const aboutDialog = {
         title: 'About the WYSIWYG Editor',
         url: window.baseUrl('/help/wysiwyg'),
@@ -12,15 +11,14 @@ function register(editor, url) {
         icon: 'help',
         tooltip: 'About the editor',
         onAction() {
-            tinymce.activeEditor.windowManager.openUrl(aboutDialog);
+            window.tinymce.activeEditor.windowManager.openUrl(aboutDialog);
         },
     });
 }
 
 /**
- * @param {WysiwygConfigOptions} options
  * @return {register}
  */
-export function getPlugin(options) {
+export function getPlugin() {
     return register;
 }
index 6aa1620da4d6d39b43b2b3cc80542f0ad6ec5fdf..f5da947f21c151a16cba5922b9586956dd63eb67 100644 (file)
@@ -1,8 +1,7 @@
 /**
  * @param {Editor} editor
- * @param {String} url
  */
-function register(editor, url) {
+function register(editor) {
     editor.addCommand('InsertHorizontalRule', () => {
         const hrElem = document.createElement('hr');
         const cNode = editor.selection.getNode();
@@ -20,9 +19,8 @@ function register(editor, url) {
 }
 
 /**
- * @param {WysiwygConfigOptions} options
  * @return {register}
  */
-export function getPlugin(options) {
+export function getPlugin() {
     return register;
 }
index 5da6680ed81b4d3e1029f93427181b5b07951716..c4a6d927d2b53646144d0a7650ed12e978505bf0 100644 (file)
@@ -1,102 +1,5 @@
-/**
- * @param {Editor} editor
- * @param {String} url
- */
 import {blockElementTypes} from './util';
 
-function register(editor, url) {
-    editor.ui.registry.addIcon('details', '<svg width="24" height="24"><path d="M8.2 9a.5.5 0 0 0-.4.8l4 5.6a.5.5 0 0 0 .8 0l4-5.6a.5.5 0 0 0-.4-.8ZM20.122 18.151h-16c-.964 0-.934 2.7 0 2.7h16c1.139 0 1.173-2.7 0-2.7zM20.122 3.042h-16c-.964 0-.934 2.7 0 2.7h16c1.139 0 1.173-2.7 0-2.7z"/></svg>');
-    editor.ui.registry.addIcon('togglefold', '<svg height="24"  width="24"><path d="M8.12 19.3c.39.39 1.02.39 1.41 0L12 16.83l2.47 2.47c.39.39 1.02.39 1.41 0 .39-.39.39-1.02 0-1.41l-3.17-3.17c-.39-.39-1.02-.39-1.41 0l-3.17 3.17c-.4.38-.4 1.02-.01 1.41zm7.76-14.6c-.39-.39-1.02-.39-1.41 0L12 7.17 9.53 4.7c-.39-.39-1.02-.39-1.41 0-.39.39-.39 1.03 0 1.42l3.17 3.17c.39.39 1.02.39 1.41 0l3.17-3.17c.4-.39.4-1.03.01-1.42z"/></svg>');
-    editor.ui.registry.addIcon('togglelabel', '<svg height="18" width="18" viewBox="0 0 24 24"><path d="M21.41,11.41l-8.83-8.83C12.21,2.21,11.7,2,11.17,2H4C2.9,2,2,2.9,2,4v7.17c0,0.53,0.21,1.04,0.59,1.41l8.83,8.83 c0.78,0.78,2.05,0.78,2.83,0l7.17-7.17C22.2,13.46,22.2,12.2,21.41,11.41z M6.5,8C5.67,8,5,7.33,5,6.5S5.67,5,6.5,5S8,5.67,8,6.5 S7.33,8,6.5,8z"/></svg>');
-
-    editor.ui.registry.addButton('details', {
-        icon: 'details',
-        tooltip: 'Insert collapsible block',
-        onAction() {
-            editor.execCommand('InsertDetailsBlock');
-        },
-    });
-
-    editor.ui.registry.addButton('removedetails', {
-        icon: 'table-delete-table',
-        tooltip: 'Unwrap',
-        onAction() {
-            unwrapDetailsInSelection(editor);
-        },
-    });
-
-    editor.ui.registry.addButton('editdetials', {
-        icon: 'togglelabel',
-        tooltip: 'Edit label',
-        onAction() {
-            showDetailLabelEditWindow(editor);
-        },
-    });
-
-    editor.on('dblclick', event => {
-        if (!getSelectedDetailsBlock(editor) || event.target.closest('doc-root')) return;
-        showDetailLabelEditWindow(editor);
-    });
-
-    editor.ui.registry.addButton('toggledetails', {
-        icon: 'togglefold',
-        tooltip: 'Toggle open/closed',
-        onAction() {
-            const details = getSelectedDetailsBlock(editor);
-            details.toggleAttribute('open');
-            editor.focus();
-        },
-    });
-
-    editor.addCommand('InsertDetailsBlock', () => {
-        let content = editor.selection.getContent({format: 'html'});
-        const details = document.createElement('details');
-        const summary = document.createElement('summary');
-        const id = `details-${Date.now()}`;
-        details.setAttribute('data-id', id);
-        details.appendChild(summary);
-
-        if (!content) {
-            content = '<p><br></p>';
-        }
-
-        details.innerHTML += content;
-        editor.insertContent(details.outerHTML);
-        editor.focus();
-
-        const domDetails = editor.dom.select(`[data-id="${id}"]`)[0] || null;
-        if (domDetails) {
-            const firstChild = domDetails.querySelector('doc-root > *');
-            if (firstChild) {
-                firstChild.focus();
-            }
-            domDetails.removeAttribute('data-id');
-        }
-    });
-
-    editor.ui.registry.addContextToolbar('details', {
-        predicate(node) {
-            return node.nodeName.toLowerCase() === 'details';
-        },
-        items: 'editdetials toggledetails removedetails',
-        position: 'node',
-        scope: 'node',
-    });
-
-    editor.on('PreInit', () => {
-        setupElementFilters(editor);
-    });
-}
-
-/**
- * @param {Editor} editor
- */
-function showDetailLabelEditWindow(editor) {
-    const details = getSelectedDetailsBlock(editor);
-    const dialog = editor.windowManager.open(detailsDialog(editor));
-    dialog.setData({summary: getSummaryTextFromDetails(details)});
-}
-
 /**
  * @param {Editor} editor
  */
@@ -104,15 +7,18 @@ function getSelectedDetailsBlock(editor) {
     return editor.selection.getNode().closest('details');
 }
 
-/**
- * @param {Element} element
- */
-function getSummaryTextFromDetails(element) {
-    const summary = element.querySelector('summary');
-    if (!summary) {
-        return '';
-    }
-    return summary.textContent;
+function setSummary(editor, summaryContent) {
+    const details = getSelectedDetailsBlock(editor);
+    if (!details) return;
+
+    editor.undoManager.transact(() => {
+        let summary = details.querySelector('summary');
+        if (!summary) {
+            summary = document.createElement('summary');
+            details.prepend(summary);
+        }
+        summary.textContent = summaryContent;
+    });
 }
 
 /**
@@ -150,18 +56,24 @@ function detailsDialog(editor) {
     };
 }
 
-function setSummary(editor, summaryContent) {
-    const details = getSelectedDetailsBlock(editor);
-    if (!details) return;
+/**
+ * @param {Element} element
+ */
+function getSummaryTextFromDetails(element) {
+    const summary = element.querySelector('summary');
+    if (!summary) {
+        return '';
+    }
+    return summary.textContent;
+}
 
-    editor.undoManager.transact(() => {
-        let summary = details.querySelector('summary');
-        if (!summary) {
-            summary = document.createElement('summary');
-            details.prepend(summary);
-        }
-        summary.textContent = summaryContent;
-    });
+/**
+ * @param {Editor} editor
+ */
+function showDetailLabelEditWindow(editor) {
+    const details = getSelectedDetailsBlock(editor);
+    const dialog = editor.windowManager.open(detailsDialog(editor));
+    dialog.setData({summary: getSummaryTextFromDetails(details)});
 }
 
 /**
@@ -187,27 +99,21 @@ function unwrapDetailsInSelection(editor) {
 }
 
 /**
- * @param {Editor} editor
+ * @param {tinymce.html.Node} detailsEl
  */
-function setupElementFilters(editor) {
-    editor.parser.addNodeFilter('details', elms => {
-        for (const el of elms) {
-            ensureDetailsWrappedInEditable(el);
-        }
-    });
-
-    editor.serializer.addNodeFilter('details', elms => {
-        for (const el of elms) {
-            unwrapDetailsEditable(el);
-            el.attr('open', null);
+function unwrapDetailsEditable(detailsEl) {
+    detailsEl.attr('contenteditable', null);
+    let madeUnwrap = false;
+    for (const child of detailsEl.children()) {
+        if (child.name === 'doc-root') {
+            child.unwrap();
+            madeUnwrap = true;
         }
-    });
+    }
 
-    editor.serializer.addNodeFilter('doc-root', elms => {
-        for (const el of elms) {
-            el.unwrap();
-        }
-    });
+    if (madeUnwrap) {
+        unwrapDetailsEditable(detailsEl);
+    }
 }
 
 /**
@@ -217,7 +123,7 @@ function ensureDetailsWrappedInEditable(detailsEl) {
     unwrapDetailsEditable(detailsEl);
 
     detailsEl.attr('contenteditable', 'false');
-    const rootWrap = tinymce.html.Node.create('doc-root', {contenteditable: 'true'});
+    const rootWrap = window.tinymce.html.Node.create('doc-root', {contenteditable: 'true'});
     let previousBlockWrap = null;
 
     for (const child of detailsEl.children()) {
@@ -226,7 +132,7 @@ function ensureDetailsWrappedInEditable(detailsEl) {
 
         if (!isBlock) {
             if (!previousBlockWrap) {
-                previousBlockWrap = tinymce.html.Node.create('p');
+                previousBlockWrap = window.tinymce.html.Node.create('p');
                 rootWrap.append(previousBlockWrap);
             }
             previousBlockWrap.append(child);
@@ -240,27 +146,119 @@ function ensureDetailsWrappedInEditable(detailsEl) {
 }
 
 /**
- * @param {tinymce.html.Node} detailsEl
+ * @param {Editor} editor
  */
-function unwrapDetailsEditable(detailsEl) {
-    detailsEl.attr('contenteditable', null);
-    let madeUnwrap = false;
-    for (const child of detailsEl.children()) {
-        if (child.name === 'doc-root') {
-            child.unwrap();
-            madeUnwrap = true;
+function setupElementFilters(editor) {
+    editor.parser.addNodeFilter('details', elms => {
+        for (const el of elms) {
+            ensureDetailsWrappedInEditable(el);
         }
-    }
+    });
 
-    if (madeUnwrap) {
-        unwrapDetailsEditable(detailsEl);
-    }
+    editor.serializer.addNodeFilter('details', elms => {
+        for (const el of elms) {
+            unwrapDetailsEditable(el);
+            el.attr('open', null);
+        }
+    });
+
+    editor.serializer.addNodeFilter('doc-root', elms => {
+        for (const el of elms) {
+            el.unwrap();
+        }
+    });
+}
+
+/**
+ * @param {Editor} editor
+ */
+function register(editor) {
+    editor.ui.registry.addIcon('details', '<svg width="24" height="24"><path d="M8.2 9a.5.5 0 0 0-.4.8l4 5.6a.5.5 0 0 0 .8 0l4-5.6a.5.5 0 0 0-.4-.8ZM20.122 18.151h-16c-.964 0-.934 2.7 0 2.7h16c1.139 0 1.173-2.7 0-2.7zM20.122 3.042h-16c-.964 0-.934 2.7 0 2.7h16c1.139 0 1.173-2.7 0-2.7z"/></svg>');
+    editor.ui.registry.addIcon('togglefold', '<svg height="24"  width="24"><path d="M8.12 19.3c.39.39 1.02.39 1.41 0L12 16.83l2.47 2.47c.39.39 1.02.39 1.41 0 .39-.39.39-1.02 0-1.41l-3.17-3.17c-.39-.39-1.02-.39-1.41 0l-3.17 3.17c-.4.38-.4 1.02-.01 1.41zm7.76-14.6c-.39-.39-1.02-.39-1.41 0L12 7.17 9.53 4.7c-.39-.39-1.02-.39-1.41 0-.39.39-.39 1.03 0 1.42l3.17 3.17c.39.39 1.02.39 1.41 0l3.17-3.17c.4-.39.4-1.03.01-1.42z"/></svg>');
+    editor.ui.registry.addIcon('togglelabel', '<svg height="18" width="18" viewBox="0 0 24 24"><path d="M21.41,11.41l-8.83-8.83C12.21,2.21,11.7,2,11.17,2H4C2.9,2,2,2.9,2,4v7.17c0,0.53,0.21,1.04,0.59,1.41l8.83,8.83 c0.78,0.78,2.05,0.78,2.83,0l7.17-7.17C22.2,13.46,22.2,12.2,21.41,11.41z M6.5,8C5.67,8,5,7.33,5,6.5S5.67,5,6.5,5S8,5.67,8,6.5 S7.33,8,6.5,8z"/></svg>');
+
+    editor.ui.registry.addButton('details', {
+        icon: 'details',
+        tooltip: 'Insert collapsible block',
+        onAction() {
+            editor.execCommand('InsertDetailsBlock');
+        },
+    });
+
+    editor.ui.registry.addButton('removedetails', {
+        icon: 'table-delete-table',
+        tooltip: 'Unwrap',
+        onAction() {
+            unwrapDetailsInSelection(editor);
+        },
+    });
+
+    editor.ui.registry.addButton('editdetials', {
+        icon: 'togglelabel',
+        tooltip: 'Edit label',
+        onAction() {
+            showDetailLabelEditWindow(editor);
+        },
+    });
+
+    editor.on('dblclick', event => {
+        if (!getSelectedDetailsBlock(editor) || event.target.closest('doc-root')) return;
+        showDetailLabelEditWindow(editor);
+    });
+
+    editor.ui.registry.addButton('toggledetails', {
+        icon: 'togglefold',
+        tooltip: 'Toggle open/closed',
+        onAction() {
+            const details = getSelectedDetailsBlock(editor);
+            details.toggleAttribute('open');
+            editor.focus();
+        },
+    });
+
+    editor.addCommand('InsertDetailsBlock', () => {
+        let content = editor.selection.getContent({format: 'html'});
+        const details = document.createElement('details');
+        const summary = document.createElement('summary');
+        const id = `details-${Date.now()}`;
+        details.setAttribute('data-id', id);
+        details.appendChild(summary);
+
+        if (!content) {
+            content = '<p><br></p>';
+        }
+
+        details.innerHTML += content;
+        editor.insertContent(details.outerHTML);
+        editor.focus();
+
+        const domDetails = editor.dom.select(`[data-id="${id}"]`)[0] || null;
+        if (domDetails) {
+            const firstChild = domDetails.querySelector('doc-root > *');
+            if (firstChild) {
+                firstChild.focus();
+            }
+            domDetails.removeAttribute('data-id');
+        }
+    });
+
+    editor.ui.registry.addContextToolbar('details', {
+        predicate(node) {
+            return node.nodeName.toLowerCase() === 'details';
+        },
+        items: 'editdetials toggledetails removedetails',
+        position: 'node',
+        scope: 'node',
+    });
+
+    editor.on('PreInit', () => {
+        setupElementFilters(editor);
+    });
 }
 
 /**
- * @param {WysiwygConfigOptions} options
  * @return {register}
  */
-export function getPlugin(options) {
+export function getPlugin() {
     return register;
 }
index e7dd126cb0f9571c57c6d8c173e5d92dd6fe4033..37b5bfafd653b7f45fc523fd7b2775cbdd3e0c7c 100644 (file)
@@ -1,8 +1,7 @@
 /**
  * @param {Editor} editor
- * @param {String} url
  */
-function register(editor, url) {
+function register(editor) {
     // Custom Image picker button
     editor.ui.registry.addButton('imagemanager-insert', {
         title: 'Insert image',
@@ -23,9 +22,8 @@ function register(editor, url) {
 }
 
 /**
- * @param {WysiwygConfigOptions} options
  * @return {register}
  */
-export function getPlugin(options) {
+export function getPlugin() {
     return register;
 }
index cf69287f1fff6eaa7815d8d9c0b9a3bcf511fbba..191f8364927817c5129858719608c7b31669cc33 100644 (file)
@@ -1,8 +1,82 @@
+/**
+ * @param {Element} element
+ * @return {boolean}
+ */
+function elementWithinTaskList(element) {
+    const listEl = element.closest('li');
+    return listEl && listEl.parentNode.nodeName === 'UL' && listEl.classList.contains('task-list-item');
+}
+
+/**
+ * @param {MouseEvent} event
+ * @param {Element} clickedEl
+ * @param {Editor} editor
+ */
+function handleTaskListItemClick(event, clickedEl, editor) {
+    const bounds = clickedEl.getBoundingClientRect();
+    const withinBounds = event.clientX <= bounds.right
+        && event.clientX >= bounds.left
+        && event.clientY >= bounds.top
+        && event.clientY <= bounds.bottom;
+
+    // Outside of the task list item bounds mean we're probably clicking the pseudo-element.
+    if (!withinBounds) {
+        editor.undoManager.transact(() => {
+            if (clickedEl.hasAttribute('checked')) {
+                clickedEl.removeAttribute('checked');
+            } else {
+                clickedEl.setAttribute('checked', 'checked');
+            }
+        });
+    }
+}
+
+/**
+ * @param {AstNode} node
+ */
+function parseTaskListNode(node) {
+    // Force task list item class
+    node.attr('class', 'task-list-item');
+
+    // Copy checkbox status and remove checkbox within editor
+    for (const child of node.children()) {
+        if (child.name === 'input') {
+            if (child.attr('checked') === 'checked') {
+                node.attr('checked', 'checked');
+            }
+            child.remove();
+        }
+    }
+}
+
+/**
+ * @param {AstNode} node
+ */
+function serializeTaskListNode(node) {
+    // Get checked status and clean it from list node
+    const isChecked = node.attr('checked') === 'checked';
+    node.attr('checked', null);
+
+    const inputAttrs = {type: 'checkbox', disabled: 'disabled'};
+    if (isChecked) {
+        inputAttrs.checked = 'checked';
+    }
+
+    // Create & insert checkbox input element
+    const checkbox = window.tinymce.html.Node.create('input', inputAttrs);
+    checkbox.shortEnded = true;
+
+    if (node.firstChild) {
+        node.insert(checkbox, node.firstChild, true);
+    } else {
+        node.append(checkbox);
+    }
+}
+
 /**
  * @param {Editor} editor
- * @param {String} url
  */
-function register(editor, url) {
+function register(editor) {
     // Tasklist UI buttons
     editor.ui.registry.addIcon('tasklist', '<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><path d="M22,8c0-0.55-0.45-1-1-1h-7c-0.55,0-1,0.45-1,1s0.45,1,1,1h7C21.55,9,22,8.55,22,8z M13,16c0,0.55,0.45,1,1,1h7 c0.55,0,1-0.45,1-1c0-0.55-0.45-1-1-1h-7C13.45,15,13,15.45,13,16z M10.47,4.63c0.39,0.39,0.39,1.02,0,1.41l-4.23,4.25 c-0.39,0.39-1.02,0.39-1.42,0L2.7,8.16c-0.39-0.39-0.39-1.02,0-1.41c0.39-0.39,1.02-0.39,1.41,0l1.42,1.42l3.54-3.54 C9.45,4.25,10.09,4.25,10.47,4.63z M10.48,12.64c0.39,0.39,0.39,1.02,0,1.41l-4.23,4.25c-0.39,0.39-1.02,0.39-1.42,0L2.7,16.16 c-0.39-0.39-0.39-1.02,0-1.41s1.02-0.39,1.41,0l1.42,1.42l3.54-3.54C9.45,12.25,10.09,12.25,10.48,12.64L10.48,12.64z"/></svg>');
     editor.ui.registry.addToggleButton('tasklist', {
@@ -33,7 +107,7 @@ function register(editor, url) {
     // Tweak existing bullet list button active state to not be active
     // when we're in a task list.
     const existingBullListButton = editor.ui.registry.getAll().buttons.bullist;
-    existingBullListButton.onSetup = function(api) {
+    existingBullListButton.onSetup = function customBullListOnSetup(api) {
         editor.on('NodeChange', event => {
             const parentList = event.parents.find(el => el.nodeName === 'LI');
             const inTaskList = parentList && parentList.classList.contains('task-list-item');
@@ -41,7 +115,7 @@ function register(editor, url) {
             api.setActive(Boolean(inUlList && !inTaskList));
         });
     };
-    existingBullListButton.onAction = function() {
+    existingBullListButton.onAction = function customBullListOnAction() {
         // Cheeky hack to prevent list toggle action treating tasklists as normal
         // unordered lists which would unwrap the list on toggle from tasklist to bullet list.
         // Instead we quickly jump through an ordered list first if we're within a tasklist.
@@ -57,7 +131,7 @@ function register(editor, url) {
     };
     // Tweak existing number list to not allow classes on child items
     const existingNumListButton = editor.ui.registry.getAll().buttons.numlist;
-    existingNumListButton.onAction = function() {
+    existingNumListButton.onAction = function customNumListButtonOnAction() {
         editor.execCommand('InsertOrderedList', null, {
             'list-item-attributes': {class: null},
         });
@@ -92,79 +166,8 @@ function register(editor, url) {
 }
 
 /**
- * @param {Element} element
- * @return {boolean}
- */
-function elementWithinTaskList(element) {
-    const listEl = element.closest('li');
-    return listEl && listEl.parentNode.nodeName === 'UL' && listEl.classList.contains('task-list-item');
-}
-
-/**
- * @param {MouseEvent} event
- * @param {Element} clickedEl
- * @param {Editor} editor
- */
-function handleTaskListItemClick(event, clickedEl, editor) {
-    const bounds = clickedEl.getBoundingClientRect();
-    const withinBounds = event.clientX <= bounds.right
-                        && event.clientX >= bounds.left
-                        && event.clientY >= bounds.top
-                        && event.clientY <= bounds.bottom;
-
-    // Outside of the task list item bounds mean we're probably clicking the pseudo-element.
-    if (!withinBounds) {
-        editor.undoManager.transact(() => {
-            if (clickedEl.hasAttribute('checked')) {
-                clickedEl.removeAttribute('checked');
-            } else {
-                clickedEl.setAttribute('checked', 'checked');
-            }
-        });
-    }
-}
-
-/**
- * @param {AstNode} node
- */
-function parseTaskListNode(node) {
-    // Force task list item class
-    node.attr('class', 'task-list-item');
-
-    // Copy checkbox status and remove checkbox within editor
-    for (const child of node.children()) {
-        if (child.name === 'input') {
-            if (child.attr('checked') === 'checked') {
-                node.attr('checked', 'checked');
-            }
-            child.remove();
-        }
-    }
-}
-
-/**
- * @param {AstNode} node
- */
-function serializeTaskListNode(node) {
-    // Get checked status and clean it from list node
-    const isChecked = node.attr('checked') === 'checked';
-    node.attr('checked', null);
-
-    const inputAttrs = {type: 'checkbox', disabled: 'disabled'};
-    if (isChecked) {
-        inputAttrs.checked = 'checked';
-    }
-
-    // Create & insert checkbox input element
-    const checkbox = tinymce.html.Node.create('input', inputAttrs);
-    checkbox.shortEnded = true;
-    node.firstChild ? node.insert(checkbox, node.firstChild, true) : node.append(checkbox);
-}
-
-/**
- * @param {WysiwygConfigOptions} options
  * @return {register}
  */
-export function getPlugin(options) {
+export function getPlugin() {
     return register;
 }
index faeb837a4ba67e0947fe74365b52642c03455dfe..92f8f158323ca5b1c3a0ea5b828fb937dae1964b 100644 (file)
@@ -1,16 +1,3 @@
-/**
- * Scroll to a section dictated by the current URL query string, if present.
- * Used when directly editing a specific section of the page.
- * @param {Editor} editor
- */
-export function scrollToQueryString(editor) {
-    const queryParams = (new URL(window.location)).searchParams;
-    const scrollId = queryParams.get('content-id');
-    if (scrollId) {
-        scrollToText(editor, scrollId);
-    }
-}
-
 /**
  * @param {Editor} editor
  * @param {String} scrollId
@@ -27,3 +14,16 @@ function scrollToText(editor, scrollId) {
     editor.selection.collapse(false);
     editor.focus();
 }
+
+/**
+ * Scroll to a section dictated by the current URL query string, if present.
+ * Used when directly editing a specific section of the page.
+ * @param {Editor} editor
+ */
+export function scrollToQueryString(editor) {
+    const queryParams = (new URL(window.location)).searchParams;
+    const scrollId = queryParams.get('content-id');
+    if (scrollId) {
+        scrollToText(editor, scrollId);
+    }
+}
index b624b23a24b25e663dc737d29ada2eaebdcb9665..1c20df9c5516c8df6a640151b0b5be4c1b99fa11 100644 (file)
@@ -35,7 +35,9 @@ export function register(editor) {
         const callout = selectedNode ? selectedNode.closest('.callout') : null;
 
         const formats = ['info', 'success', 'warning', 'danger'];
-        const currentFormatIndex = formats.findIndex(format => callout && callout.classList.contains(format));
+        const currentFormatIndex = formats.findIndex(format => {
+            return callout && callout.classList.contains(format);
+        });
         const newFormatIndex = (currentFormatIndex + 1) % formats.length;
         const newFormat = formats[newFormatIndex];
 
index 9acb24c1dc9ba3fe3239bf66d4d27464033ae2a8..4663ad132e13f05457c4d6db1697145f7024222d 100644 (file)
@@ -70,9 +70,8 @@ function registerImageContextToolbar(editor) {
 
 /**
  * @param {Editor} editor
- * @param {WysiwygConfigOptions} options
  */
-export function registerAdditionalToolbars(editor, options) {
+export function registerAdditionalToolbars(editor) {
     registerPrimaryToolbarGroups(editor);
     registerLinkContextToolbar(editor);
     registerImageContextToolbar(editor);