From da3ae3ba8b47800058f8613b2608a4084d64eb43 Mon Sep 17 00:00:00 2001 From: Dan Brown Date: Wed, 19 Apr 2023 15:20:04 +0100 Subject: [PATCH] ESLINT: Addressed remaining detected issues --- package-lock.json | 1 + package.json | 90 ++++++++++----- resources/js/app.js | 2 +- resources/js/code/index.mjs | 90 +++++++-------- resources/js/components/ajax-delete-row.js | 2 +- resources/js/components/attachments.js | 2 +- resources/js/components/auto-suggest.js | 5 +- resources/js/components/book-sort.js | 36 +++--- resources/js/components/chapter-contents.js | 6 +- resources/js/components/code-editor.js | 11 +- resources/js/components/confirm-dialog.js | 2 +- resources/js/components/dropdown.js | 10 +- resources/js/components/dropzone.js | 10 +- resources/js/components/entity-permissions.js | 2 +- resources/js/components/entity-search.js | 3 +- resources/js/components/entity-selector.js | 16 +-- resources/js/components/expand-toggle.js | 5 +- resources/js/components/image-manager.js | 10 +- resources/js/components/index.js | 108 +++++++++--------- resources/js/components/markdown-editor.js | 4 +- resources/js/components/page-comments.js | 2 +- resources/js/components/page-display.js | 54 ++++----- resources/js/components/page-editor.js | 14 ++- resources/js/components/page-picker.js | 10 +- resources/js/components/pointer.js | 4 +- resources/js/components/popup.js | 4 +- .../js/components/setting-app-color-scheme.js | 1 - resources/js/components/shelf-sort.js | 12 +- resources/js/components/shortcuts.js | 3 +- resources/js/components/template-manager.js | 4 +- resources/js/components/tri-layout.js | 2 +- resources/js/markdown/actions.js | 43 +++++-- resources/js/markdown/codemirror.js | 4 +- resources/js/markdown/settings.js | 2 +- resources/js/markdown/shortcuts.js | 38 +++--- resources/js/services/animations.js | 94 +++++++-------- resources/js/services/components.js | 82 ++++++------- resources/js/services/drawio.js | 101 ++++++++-------- resources/js/services/events.js | 65 ++++++----- resources/js/services/translations.js | 23 ++-- resources/js/wysiwyg/plugin-drawio.js | 2 +- 41 files changed, 525 insertions(+), 454 deletions(-) diff --git a/package-lock.json b/package-lock.json index e4dae4a5b..bb8b6049b 100644 --- a/package-lock.json +++ b/package-lock.json @@ -18,6 +18,7 @@ "@codemirror/state": "^6.2.0", "@codemirror/theme-one-dark": "^6.1.1", "@codemirror/view": "^6.9.4", + "@lezer/highlight": "^1.1.4", "@ssddanbrown/codemirror-lang-smarty": "^1.0.0", "@ssddanbrown/codemirror-lang-twig": "^1.0.0", "codemirror": "^6.0.1", diff --git a/package.json b/package.json index 264180f81..39f088234 100644 --- a/package.json +++ b/package.json @@ -42,6 +42,7 @@ "@codemirror/state": "^6.2.0", "@codemirror/theme-one-dark": "^6.1.1", "@codemirror/view": "^6.9.4", + "@lezer/highlight": "^1.1.4", "@ssddanbrown/codemirror-lang-smarty": "^1.0.0", "@ssddanbrown/codemirror-lang-twig": "^1.0.0", "codemirror": "^6.0.1", @@ -58,48 +59,77 @@ "es2021": true }, "extends": "airbnb-base", - "ignorePatterns": ["resources/**/*-stub.js"], - "overrides": [ + "ignorePatterns": [ + "resources/**/*-stub.js" ], + "overrides": [], "parserOptions": { "ecmaVersion": "latest", "sourceType": "module" }, "rules": { - "indent": ["error", 4], - "arrow-parens": ["error", "as-needed"], - "padded-blocks": ["error", { + "indent": [ + "error", + 4 + ], + "arrow-parens": [ + "error", + "as-needed" + ], + "padded-blocks": [ + "error", + { "blocks": "never", "classes": "always" - }], - "object-curly-spacing": ["error", "never"], - "space-before-function-paren": ["error", { - "anonymous": "never", - "named": "never", - "asyncArrow": "always" - }], + } + ], + "object-curly-spacing": [ + "error", + "never" + ], + "space-before-function-paren": [ + "error", + { + "anonymous": "never", + "named": "never", + "asyncArrow": "always" + } + ], "import/prefer-default-export": "off", - "no-plusplus": ["error", { - "allowForLoopAfterthoughts": true - }], + "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 - }] + "prefer-destructuring": "off", + "class-methods-use-this": "off", + "no-param-reassign": "off", + "no-console": [ + "warn", + { + "allow": [ + "error", + "warn" + ] + } + ], + "no-new": "off", + "max-len": [ + "error", + { + "code": 110, + "tabWidth": 4, + "ignoreUrls": true, + "ignoreComments": false, + "ignoreRegExpLiterals": true, + "ignoreStrings": true, + "ignoreTemplateLiterals": true + } + ] } } } diff --git a/resources/js/app.js b/resources/js/app.js index 86c8d0802..5b822e900 100644 --- a/resources/js/app.js +++ b/resources/js/app.js @@ -1,4 +1,4 @@ -import events from './services/events'; +import * as events from './services/events'; import * as httpInstance from './services/http'; import Translations from './services/translations'; diff --git a/resources/js/code/index.mjs b/resources/js/code/index.mjs index 450592c25..e51472dc4 100644 --- a/resources/js/code/index.mjs +++ b/resources/js/code/index.mjs @@ -6,24 +6,36 @@ import {createView} from './views'; import {SimpleEditorInterface} from './simple-editor-interface'; /** - * Highlight pre elements on a page + * Add a button to a CodeMirror instance which copies the contents to the clipboard upon click. + * @param {EditorView} editorView */ -export function highlight() { - const codeBlocks = document.querySelectorAll('.page-content pre, .comment-box .content pre'); - for (const codeBlock of codeBlocks) { - highlightElem(codeBlock); - } -} +function addCopyIcon(editorView) { + const copyIcon = ''; + const checkIcon = ''; + const copyButton = document.createElement('button'); + copyButton.setAttribute('type', 'button'); + copyButton.classList.add('cm-copy-button'); + copyButton.innerHTML = copyIcon; + editorView.dom.appendChild(copyButton); -/** - * Highlight all code blocks within the given parent element - * @param {HTMLElement} parent - */ -export function highlightWithin(parent) { - const codeBlocks = parent.querySelectorAll('pre'); - for (const codeBlock of codeBlocks) { - highlightElem(codeBlock); - } + const notifyTime = 620; + const transitionTime = 60; + copyButton.addEventListener('click', () => { + copyTextToClipboard(editorView.state.doc.toString()); + copyButton.classList.add('success'); + + setTimeout(() => { + copyButton.innerHTML = checkIcon; + }, transitionTime / 2); + + setTimeout(() => { + copyButton.classList.remove('success'); + }, notifyTime); + + setTimeout(() => { + copyButton.innerHTML = copyIcon; + }, notifyTime + (transitionTime / 2)); + }); } /** @@ -32,7 +44,7 @@ export function highlightWithin(parent) { */ function highlightElem(elem) { const innerCodeElem = elem.querySelector('code[class^=language-]'); - elem.innerHTML = elem.innerHTML.replace(//gi, '\n'); + elem.innerHTML = elem.innerHTML.replace(//gi, '\n'); const content = elem.textContent.trimEnd(); let langName = ''; @@ -57,36 +69,24 @@ function highlightElem(elem) { } /** - * Add a button to a CodeMirror instance which copies the contents to the clipboard upon click. - * @param {EditorView} editorView + * Highlight all code blocks within the given parent element + * @param {HTMLElement} parent */ -function addCopyIcon(editorView) { - const copyIcon = ''; - const checkIcon = ''; - const copyButton = document.createElement('button'); - copyButton.setAttribute('type', 'button'); - copyButton.classList.add('cm-copy-button'); - copyButton.innerHTML = copyIcon; - editorView.dom.appendChild(copyButton); +export function highlightWithin(parent) { + const codeBlocks = parent.querySelectorAll('pre'); + for (const codeBlock of codeBlocks) { + highlightElem(codeBlock); + } +} - const notifyTime = 620; - const transitionTime = 60; - copyButton.addEventListener('click', event => { - copyTextToClipboard(editorView.state.doc.toString()); - copyButton.classList.add('success'); - - setTimeout(() => { - copyButton.innerHTML = checkIcon; - }, transitionTime / 2); - - setTimeout(() => { - copyButton.classList.remove('success'); - }, notifyTime); - - setTimeout(() => { - copyButton.innerHTML = copyIcon; - }, notifyTime + (transitionTime / 2)); - }); +/** + * Highlight pre elements on a page + */ +export function highlight() { + const codeBlocks = document.querySelectorAll('.page-content pre, .comment-box .content pre'); + for (const codeBlock of codeBlocks) { + highlightElem(codeBlock); + } } /** diff --git a/resources/js/components/ajax-delete-row.js b/resources/js/components/ajax-delete-row.js index f48db7939..aa2801f19 100644 --- a/resources/js/components/ajax-delete-row.js +++ b/resources/js/components/ajax-delete-row.js @@ -20,7 +20,7 @@ export class AjaxDeleteRow extends Component { window.$events.emit('success', resp.data.message); } this.row.remove(); - }).catch(err => { + }).catch(() => { this.row.style.opacity = null; this.row.style.pointerEvents = null; }); diff --git a/resources/js/components/attachments.js b/resources/js/components/attachments.js index 0c25bd0c0..9555a59e8 100644 --- a/resources/js/components/attachments.js +++ b/resources/js/components/attachments.js @@ -27,7 +27,7 @@ export class Attachments extends Component { this.startEdit(event.detail.id); }); - this.container.addEventListener('event-emit-select-edit-back', event => { + this.container.addEventListener('event-emit-select-edit-back', () => { this.stopEdit(); }); diff --git a/resources/js/components/auto-suggest.js b/resources/js/components/auto-suggest.js index b2435961c..92a6c6af3 100644 --- a/resources/js/components/auto-suggest.js +++ b/resources/js/components/auto-suggest.js @@ -25,7 +25,7 @@ export class AutoSuggest extends Component { setupListeners() { const navHandler = new KeyboardNavigationHandler( this.list, - event => { + () => { this.input.focus(); setTimeout(() => this.hideSuggestions(), 1); }, @@ -104,7 +104,8 @@ export class AutoSuggest extends Component { */ displaySuggestions(suggestions) { if (suggestions.length === 0) { - return this.hideSuggestions(); + this.hideSuggestions(); + return; } // This used to use `; }).join(''); } diff --git a/resources/js/components/confirm-dialog.js b/resources/js/components/confirm-dialog.js index 2a3b6e776..184618fcc 100644 --- a/resources/js/components/confirm-dialog.js +++ b/resources/js/components/confirm-dialog.js @@ -25,7 +25,7 @@ export class ConfirmDialog extends Component { this.sendResult(false); }); - return new Promise((res, rej) => { + return new Promise(res => { this.res = res; }); } diff --git a/resources/js/components/dropdown.js b/resources/js/components/dropdown.js index 8444bf038..b68f332b6 100644 --- a/resources/js/components/dropdown.js +++ b/resources/js/components/dropdown.js @@ -41,7 +41,11 @@ export class Dropdown extends Component { this.menu.style.position = 'fixed'; this.menu.style.width = `${menuOriginalRect.width}px`; this.menu.style.left = `${menuOriginalRect.left}px`; - heightOffset = dropUpwards ? (window.innerHeight - menuOriginalRect.top - toggleHeight / 2) : menuOriginalRect.top; + if (dropUpwards) { + heightOffset = (window.innerHeight - menuOriginalRect.top - toggleHeight / 2); + } else { + heightOffset = menuOriginalRect.top; + } } // Adjust menu to display upwards if near the bottom of the screen @@ -55,8 +59,8 @@ export class Dropdown extends Component { // Set listener to hide on mouse leave or window click this.menu.addEventListener('mouseleave', this.hide); - window.addEventListener('click', event => { - if (!this.menu.contains(event.target)) { + window.addEventListener('click', clickEvent => { + if (!this.menu.contains(clickEvent.target)) { this.hide(); } }); diff --git a/resources/js/components/dropzone.js b/resources/js/components/dropzone.js index 957360dae..e7aae769e 100644 --- a/resources/js/components/dropzone.js +++ b/resources/js/components/dropzone.js @@ -13,7 +13,7 @@ export class Dropzone extends Component { this.uploadLimitMessage = this.$opts.uploadLimitMessage; this.timeoutMessage = this.$opts.timeoutMessage; - const _this = this; + const component = this; this.dz = new DropZoneLib(this.container, { addRemoveLinks: true, dictRemoveFile: this.removeMessage, @@ -23,9 +23,9 @@ export class Dropzone extends Component { withCredentials: true, init() { this.dz = this; - this.dz.on('sending', _this.onSending.bind(_this)); - this.dz.on('success', _this.onSuccess.bind(_this)); - this.dz.on('error', _this.onError.bind(_this)); + this.dz.on('sending', component.onSending.bind(component)); + this.dz.on('success', component.onSuccess.bind(component)); + this.dz.on('error', component.onError.bind(component)); }, }); } @@ -34,7 +34,7 @@ export class Dropzone extends Component { const token = window.document.querySelector('meta[name=token]').getAttribute('content'); data.append('_token', token); - xhr.ontimeout = e => { + xhr.ontimeout = () => { this.dz.emit('complete', file); this.dz.emit('error', file, this.timeoutMessage); }; diff --git a/resources/js/components/entity-permissions.js b/resources/js/components/entity-permissions.js index 62f8653c2..7ab99a2a7 100644 --- a/resources/js/components/entity-permissions.js +++ b/resources/js/components/entity-permissions.js @@ -34,7 +34,7 @@ export class EntityPermissions extends Component { }); // Role select change - this.roleSelect.addEventListener('change', event => { + this.roleSelect.addEventListener('change', () => { const roleId = this.roleSelect.value; if (roleId) { this.addRoleRow(roleId); diff --git a/resources/js/components/entity-search.js b/resources/js/components/entity-search.js index 0795fca98..7a5044470 100644 --- a/resources/js/components/entity-search.js +++ b/resources/js/components/entity-search.js @@ -31,7 +31,8 @@ export class EntitySearch extends Component { runSearch() { const term = this.searchInput.value.trim(); if (term.length === 0) { - return this.clearSearch(); + this.clearSearch(); + return; } this.searchView.classList.remove('hidden'); diff --git a/resources/js/components/entity-selector.js b/resources/js/components/entity-selector.js index bdc2277c2..f12108fbb 100644 --- a/resources/js/components/entity-selector.js +++ b/resources/js/components/entity-selector.js @@ -29,7 +29,7 @@ export class EntitySelector extends Component { this.elem.addEventListener('click', this.onClick.bind(this)); let lastSearch = 0; - this.searchInput.addEventListener('input', event => { + this.searchInput.addEventListener('input', () => { lastSearch = Date.now(); this.showLoading(); setTimeout(() => { @@ -43,26 +43,26 @@ export class EntitySelector extends Component { }); // Keyboard navigation - onChildEvent(this.$el, '[data-entity-type]', 'keydown', (e, el) => { - if (e.ctrlKey && e.code === 'Enter') { + onChildEvent(this.$el, '[data-entity-type]', 'keydown', event => { + if (event.ctrlKey && event.code === 'Enter') { const form = this.$el.closest('form'); if (form) { form.submit(); - e.preventDefault(); + event.preventDefault(); return; } } - if (e.code === 'ArrowDown') { + if (event.code === 'ArrowDown') { this.focusAdjacent(true); } - if (e.code === 'ArrowUp') { + if (event.code === 'ArrowUp') { this.focusAdjacent(false); } }); - this.searchInput.addEventListener('keydown', e => { - if (e.code === 'ArrowDown') { + this.searchInput.addEventListener('keydown', event => { + if (event.code === 'ArrowDown') { this.focusAdjacent(true); } }); diff --git a/resources/js/components/expand-toggle.js b/resources/js/components/expand-toggle.js index 76928623d..0d2018b9d 100644 --- a/resources/js/components/expand-toggle.js +++ b/resources/js/components/expand-toggle.js @@ -3,7 +3,7 @@ import {Component} from './component'; export class ExpandToggle extends Component { - setup(elem) { + setup() { this.targetSelector = this.$opts.targetSelector; this.isOpen = this.$opts.isOpen === 'true'; this.updateEndpoint = this.$opts.updateEndpoint; @@ -25,7 +25,8 @@ export class ExpandToggle extends Component { const matchingElems = document.querySelectorAll(this.targetSelector); for (const match of matchingElems) { - this.isOpen ? this.close(match) : this.open(match); + const action = this.isOpen ? this.close : this.open; + action(match); } this.isOpen = !this.isOpen; diff --git a/resources/js/components/image-manager.js b/resources/js/components/image-manager.js index 732bc2cb4..3cb99f0e2 100644 --- a/resources/js/components/image-manager.js +++ b/resources/js/components/image-manager.js @@ -50,20 +50,20 @@ export class ImageManager extends Component { event.preventDefault(); }); - onSelect(this.cancelSearch, event => { + onSelect(this.cancelSearch, () => { this.resetListView(); this.resetSearchView(); this.loadGallery(); this.cancelSearch.classList.remove('active'); }); - this.searchInput.addEventListener('input', event => { + this.searchInput.addEventListener('input', () => { this.cancelSearch.classList.toggle('active', this.searchInput.value.trim()); }); onChildEvent(this.listContainer, '.load-more', 'click', async event => { showLoading(event.target); - this.page++; + this.page += 1; await this.loadGallery(); event.target.remove(); }); @@ -71,7 +71,7 @@ export class ImageManager extends Component { this.listContainer.addEventListener('event-emit-select-image', this.onImageSelectEvent.bind(this)); this.listContainer.addEventListener('error', event => { - event.target.src = baseUrl('loading_error.png'); + event.target.src = window.baseUrl('loading_error.png'); }, true); onSelect(this.selectButton, () => { @@ -81,7 +81,7 @@ export class ImageManager extends Component { this.hide(); }); - onChildEvent(this.formContainer, '#image-manager-delete', 'click', event => { + onChildEvent(this.formContainer, '#image-manager-delete', 'click', () => { if (this.lastSelected) { this.loadImageEditForm(this.lastSelected.id, true); } diff --git a/resources/js/components/index.js b/resources/js/components/index.js index 40175b1e7..803714e62 100644 --- a/resources/js/components/index.js +++ b/resources/js/components/index.js @@ -1,59 +1,59 @@ -export {AddRemoveRows} from './add-remove-rows.js'; -export {AjaxDeleteRow} from './ajax-delete-row.js'; -export {AjaxForm} from './ajax-form.js'; -export {Attachments} from './attachments.js'; -export {AttachmentsList} from './attachments-list.js'; -export {AutoSuggest} from './auto-suggest.js'; -export {AutoSubmit} from './auto-submit.js'; -export {BackToTop} from './back-to-top.js'; -export {BookSort} from './book-sort.js'; -export {ChapterContents} from './chapter-contents.js'; -export {CodeEditor} from './code-editor.js'; -export {CodeHighlighter} from './code-highlighter.js'; -export {CodeTextarea} from './code-textarea.js'; -export {Collapsible} from './collapsible.js'; +export {AddRemoveRows} from './add-remove-rows'; +export {AjaxDeleteRow} from './ajax-delete-row'; +export {AjaxForm} from './ajax-form'; +export {Attachments} from './attachments'; +export {AttachmentsList} from './attachments-list'; +export {AutoSuggest} from './auto-suggest'; +export {AutoSubmit} from './auto-submit'; +export {BackToTop} from './back-to-top'; +export {BookSort} from './book-sort'; +export {ChapterContents} from './chapter-contents'; +export {CodeEditor} from './code-editor'; +export {CodeHighlighter} from './code-highlighter'; +export {CodeTextarea} from './code-textarea'; +export {Collapsible} from './collapsible'; export {ConfirmDialog} from './confirm-dialog'; -export {CustomCheckbox} from './custom-checkbox.js'; -export {DetailsHighlighter} from './details-highlighter.js'; -export {Dropdown} from './dropdown.js'; -export {DropdownSearch} from './dropdown-search.js'; -export {Dropzone} from './dropzone.js'; -export {EditorToolbox} from './editor-toolbox.js'; +export {CustomCheckbox} from './custom-checkbox'; +export {DetailsHighlighter} from './details-highlighter'; +export {Dropdown} from './dropdown'; +export {DropdownSearch} from './dropdown-search'; +export {Dropzone} from './dropzone'; +export {EditorToolbox} from './editor-toolbox'; export {EntityPermissions} from './entity-permissions'; -export {EntitySearch} from './entity-search.js'; -export {EntitySelector} from './entity-selector.js'; -export {EntitySelectorPopup} from './entity-selector-popup.js'; -export {EventEmitSelect} from './event-emit-select.js'; -export {ExpandToggle} from './expand-toggle.js'; -export {GlobalSearch} from './global-search.js'; -export {HeaderMobileToggle} from './header-mobile-toggle.js'; -export {ImageManager} from './image-manager.js'; -export {ImagePicker} from './image-picker.js'; -export {ListSortControl} from './list-sort-control.js'; -export {MarkdownEditor} from './markdown-editor.js'; -export {NewUserPassword} from './new-user-password.js'; -export {Notification} from './notification.js'; -export {OptionalInput} from './optional-input.js'; -export {PageComments} from './page-comments.js'; -export {PageDisplay} from './page-display.js'; -export {PageEditor} from './page-editor.js'; -export {PagePicker} from './page-picker.js'; -export {PermissionsTable} from './permissions-table.js'; -export {Pointer} from './pointer.js'; -export {Popup} from './popup.js'; -export {SettingAppColorScheme} from './setting-app-color-scheme.js'; -export {SettingColorPicker} from './setting-color-picker.js'; -export {SettingHomepageControl} from './setting-homepage-control.js'; -export {ShelfSort} from './shelf-sort.js'; +export {EntitySearch} from './entity-search'; +export {EntitySelector} from './entity-selector'; +export {EntitySelectorPopup} from './entity-selector-popup'; +export {EventEmitSelect} from './event-emit-select'; +export {ExpandToggle} from './expand-toggle'; +export {GlobalSearch} from './global-search'; +export {HeaderMobileToggle} from './header-mobile-toggle'; +export {ImageManager} from './image-manager'; +export {ImagePicker} from './image-picker'; +export {ListSortControl} from './list-sort-control'; +export {MarkdownEditor} from './markdown-editor'; +export {NewUserPassword} from './new-user-password'; +export {Notification} from './notification'; +export {OptionalInput} from './optional-input'; +export {PageComments} from './page-comments'; +export {PageDisplay} from './page-display'; +export {PageEditor} from './page-editor'; +export {PagePicker} from './page-picker'; +export {PermissionsTable} from './permissions-table'; +export {Pointer} from './pointer'; +export {Popup} from './popup'; +export {SettingAppColorScheme} from './setting-app-color-scheme'; +export {SettingColorPicker} from './setting-color-picker'; +export {SettingHomepageControl} from './setting-homepage-control'; +export {ShelfSort} from './shelf-sort'; export {Shortcuts} from './shortcuts'; export {ShortcutInput} from './shortcut-input'; -export {SortableList} from './sortable-list.js'; -export {SubmitOnChange} from './submit-on-change.js'; -export {Tabs} from './tabs.js'; -export {TagManager} from './tag-manager.js'; -export {TemplateManager} from './template-manager.js'; -export {ToggleSwitch} from './toggle-switch.js'; -export {TriLayout} from './tri-layout.js'; -export {UserSelect} from './user-select.js'; +export {SortableList} from './sortable-list'; +export {SubmitOnChange} from './submit-on-change'; +export {Tabs} from './tabs'; +export {TagManager} from './tag-manager'; +export {TemplateManager} from './template-manager'; +export {ToggleSwitch} from './toggle-switch'; +export {TriLayout} from './tri-layout'; +export {UserSelect} from './user-select'; export {WebhookEvents} from './webhook-events'; -export {WysiwygEditor} from './wysiwyg-editor.js'; +export {WysiwygEditor} from './wysiwyg-editor'; diff --git a/resources/js/components/markdown-editor.js b/resources/js/components/markdown-editor.js index 1eeb02318..fa06807a5 100644 --- a/resources/js/components/markdown-editor.js +++ b/resources/js/components/markdown-editor.js @@ -82,7 +82,7 @@ export class MarkdownEditor extends Component { } handleDividerDrag() { - this.divider.addEventListener('pointerdown', event => { + this.divider.addEventListener('pointerdown', () => { const wrapRect = this.elem.getBoundingClientRect(); const moveListener = event => { const xRel = event.pageX - wrapRect.left; @@ -90,7 +90,7 @@ export class MarkdownEditor extends Component { this.displayWrap.style.flexBasis = `${100 - xPct}%`; this.editor.settings.set('editorWidth', xPct); }; - const upListener = event => { + const upListener = () => { window.removeEventListener('pointermove', moveListener); window.removeEventListener('pointerup', upListener); this.display.style.pointerEvents = null; diff --git a/resources/js/components/page-comments.js b/resources/js/components/page-comments.js index 314ddf3e1..0ac9d0572 100644 --- a/resources/js/components/page-comments.js +++ b/resources/js/components/page-comments.js @@ -100,7 +100,7 @@ export class PageComments extends Component { deleteComment(commentElem) { const id = commentElem.getAttribute('comment'); this.showLoading(commentElem.querySelector('[comment-content]')); - window.$http.delete(`/comment/${id}`).then(resp => { + window.$http.delete(`/comment/${id}`).then(() => { commentElem.parentNode.removeChild(commentElem); window.$events.success(this.deletedText); this.updateCount(); diff --git a/resources/js/components/page-display.js b/resources/js/components/page-display.js index da67a1e61..eb7df5fb6 100644 --- a/resources/js/components/page-display.js +++ b/resources/js/components/page-display.js @@ -2,6 +2,33 @@ import * as DOM from '../services/dom'; import {scrollAndHighlightElement} from '../services/util'; import {Component} from './component'; +function toggleAnchorHighlighting(elementId, shouldHighlight) { + DOM.forEach(`a[href="#${elementId}"]`, anchor => { + anchor.closest('li').classList.toggle('current-heading', shouldHighlight); + }); +} + +function headingVisibilityChange(entries) { + for (const entry of entries) { + const isVisible = (entry.intersectionRatio === 1); + toggleAnchorHighlighting(entry.target.id, isVisible); + } +} + +function addNavObserver(headings) { + // Setup the intersection observer. + const intersectOpts = { + rootMargin: '0px 0px 0px 0px', + threshold: 1.0, + }; + const pageNavObserver = new IntersectionObserver(headingVisibilityChange, intersectOpts); + + // observe each heading + for (const heading of headings) { + pageNavObserver.observe(heading); + } +} + export class PageDisplay extends Component { setup() { @@ -58,33 +85,6 @@ export class PageDisplay extends Component { if (headings.length > 0 && pageNav !== null) { addNavObserver(headings); } - - function addNavObserver(headings) { - // Setup the intersection observer. - const intersectOpts = { - rootMargin: '0px 0px 0px 0px', - threshold: 1.0, - }; - const pageNavObserver = new IntersectionObserver(headingVisibilityChange, intersectOpts); - - // observe each heading - for (const heading of headings) { - pageNavObserver.observe(heading); - } - } - - function headingVisibilityChange(entries, observer) { - for (const entry of entries) { - const isVisible = (entry.intersectionRatio === 1); - toggleAnchorHighlighting(entry.target.id, isVisible); - } - } - - function toggleAnchorHighlighting(elementId, shouldHighlight) { - DOM.forEach(`a[href="#${elementId}"]`, anchor => { - anchor.closest('li').classList.toggle('current-heading', shouldHighlight); - }); - } } setupDetailsCodeBlockRefresh() { diff --git a/resources/js/components/page-editor.js b/resources/js/components/page-editor.js index 1b7a9d52a..e7f4c0ba9 100644 --- a/resources/js/components/page-editor.js +++ b/resources/js/components/page-editor.js @@ -59,7 +59,9 @@ export class PageEditor extends Component { window.$events.listen('editor-save-page', this.savePage.bind(this)); // Listen to content changes from the editor - const onContentChange = () => this.autoSave.pendingChange = true; + const onContentChange = () => { + this.autoSave.pendingChange = true; + }; window.$events.listen('editor-html-change', onContentChange); window.$events.listen('editor-markdown-change', onContentChange); @@ -80,7 +82,8 @@ export class PageEditor extends Component { setInitialFocus() { if (this.hasDefaultTitle) { - return this.titleElem.select(); + this.titleElem.select(); + return; } window.setTimeout(() => { @@ -133,7 +136,9 @@ export class PageEditor extends Component { try { const saveKey = `draft-save-fail-${(new Date()).toISOString()}`; window.localStorage.setItem(saveKey, JSON.stringify(data)); - } catch (err) {} + } catch (lsErr) { + console.error(lsErr); + } window.$events.emit('error', this.autosaveFailText); } @@ -154,7 +159,8 @@ export class PageEditor extends Component { try { response = await window.$http.get(`/ajax/page/${this.pageId}`); } catch (e) { - return console.error(e); + console.error(e); + return; } if (this.autoSave.interval) { diff --git a/resources/js/components/page-picker.js b/resources/js/components/page-picker.js index 83bf60a17..130972fdd 100644 --- a/resources/js/components/page-picker.js +++ b/resources/js/components/page-picker.js @@ -1,5 +1,9 @@ import {Component} from './component'; +function toggleElem(elem, show) { + elem.style.display = show ? null : 'none'; +} + export class PagePicker extends Component { setup() { @@ -18,7 +22,7 @@ export class PagePicker extends Component { this.selectButton.addEventListener('click', this.showPopup.bind(this)); this.display.parentElement.addEventListener('click', this.showPopup.bind(this)); - this.resetButton.addEventListener('click', event => { + this.resetButton.addEventListener('click', () => { this.setValue('', ''); }); } @@ -55,7 +59,3 @@ export class PagePicker extends Component { } } - -function toggleElem(elem, show) { - elem.style.display = show ? null : 'none'; -} diff --git a/resources/js/components/pointer.js b/resources/js/components/pointer.js index f1208ab76..e2e2ceca7 100644 --- a/resources/js/components/pointer.js +++ b/resources/js/components/pointer.js @@ -21,7 +21,7 @@ export class Pointer extends Component { setupListeners() { // Copy on copy button click - this.button.addEventListener('click', event => { + this.button.addEventListener('click', () => { copyTextToClipboard(this.input.value); }); @@ -46,7 +46,7 @@ export class Pointer extends Component { }); // Hide pointer when clicking away - DOM.onEvents(document.body, ['click', 'focus'], event => { + DOM.onEvents(document.body, ['click', 'focus'], () => { if (!this.showing || this.isSelection) return; this.hidePointer(); }); diff --git a/resources/js/components/popup.js b/resources/js/components/popup.js index 34ab7c181..662736548 100644 --- a/resources/js/components/popup.js +++ b/resources/js/components/popup.js @@ -26,11 +26,11 @@ export class Popup extends Component { this.container.addEventListener('click', event => { if (event.target === this.container && lastMouseDownTarget === this.container) { - return this.hide(); + this.hide(); } }); - onSelect(this.hideButtons, e => this.hide()); + onSelect(this.hideButtons, () => this.hide()); } hide(onComplete = null) { diff --git a/resources/js/components/setting-app-color-scheme.js b/resources/js/components/setting-app-color-scheme.js index d8e39d465..add2d0cda 100644 --- a/resources/js/components/setting-app-color-scheme.js +++ b/resources/js/components/setting-app-color-scheme.js @@ -59,7 +59,6 @@ export class SettingAppColorScheme extends Component { const rgb = this.hexToRgb(hexVal); const rgbLightVal = `rgba(${[rgb.r, rgb.g, rgb.b, '0.15'].join(',')})`; - console.log(input.name, lightName, hexVal, rgbLightVal); const lightColorInput = this.container.querySelector(`input[name="${lightName}"][type="hidden"]`); lightColorInput.value = rgbLightVal; } diff --git a/resources/js/components/shelf-sort.js b/resources/js/components/shelf-sort.js index 303ad8df2..01ca11a33 100644 --- a/resources/js/components/shelf-sort.js +++ b/resources/js/components/shelf-sort.js @@ -5,13 +5,13 @@ import {Component} from './component'; * @type {Object} */ const itemActions = { - move_up(item, shelfBooksList, allBooksList) { + move_up(item) { const list = item.parentNode; const index = Array.from(list.children).indexOf(item); const newIndex = Math.max(index - 1, 0); list.insertBefore(item, list.children[newIndex] || null); }, - move_down(item, shelfBooksList, allBooksList) { + move_down(item) { const list = item.parentNode; const index = Array.from(list.children).indexOf(item); const newIndex = Math.min(index + 2, list.children.length); @@ -20,7 +20,7 @@ const itemActions = { remove(item, shelfBooksList, allBooksList) { allBooksList.appendChild(item); }, - add(item, shelfBooksList, allBooksList) { + add(item, shelfBooksList) { shelfBooksList.appendChild(item); }, }; @@ -62,7 +62,7 @@ export class ShelfSort extends Component { } }); - this.bookSearchInput.addEventListener('input', event => { + this.bookSearchInput.addEventListener('input', () => { this.filterBooksByName(this.bookSearchInput.value); }); @@ -121,10 +121,10 @@ export class ShelfSort extends Component { const bProp = bookB.dataset[sortProperty].toLowerCase(); if (reverse) { - return aProp < bProp ? (aProp === bProp ? 0 : 1) : -1; + return bProp.localeCompare(aProp); } - return aProp < bProp ? (aProp === bProp ? 0 : -1) : 1; + return aProp.localeCompare(bProp); }); for (const book of books) { diff --git a/resources/js/components/shortcuts.js b/resources/js/components/shortcuts.js index 8c6c25ba1..8e927e34c 100644 --- a/resources/js/components/shortcuts.js +++ b/resources/js/components/shortcuts.js @@ -33,7 +33,8 @@ export class Shortcuts extends Component { window.addEventListener('keydown', event => { if (event.key === '?') { - this.hintsShowing ? this.hideHints() : this.showHints(); + const action = this.hintsShowing ? this.hideHints : this.showHints; + action(); } }); } diff --git a/resources/js/components/template-manager.js b/resources/js/components/template-manager.js index ec143b70f..56ec876d4 100644 --- a/resources/js/components/template-manager.js +++ b/resources/js/components/template-manager.js @@ -36,10 +36,10 @@ export class TemplateManager extends Component { }); // Search submit button press - this.searchButton.addEventListener('click', event => this.performSearch()); + this.searchButton.addEventListener('click', () => this.performSearch()); // Search cancel button press - this.searchCancel.addEventListener('click', event => { + this.searchCancel.addEventListener('click', () => { this.searchInput.value = ''; this.performSearch(); }); diff --git a/resources/js/components/tri-layout.js b/resources/js/components/tri-layout.js index 6510a690e..8ccefb06c 100644 --- a/resources/js/components/tri-layout.js +++ b/resources/js/components/tri-layout.js @@ -19,7 +19,7 @@ export class TriLayout extends Component { // Watch layout changes this.updateLayout(); - window.addEventListener('resize', event => { + window.addEventListener('resize', () => { this.updateLayout(); }, {passive: true}); } diff --git a/resources/js/markdown/actions.js b/resources/js/markdown/actions.js index 3cb7b5d4f..514bff87d 100644 --- a/resources/js/markdown/actions.js +++ b/resources/js/markdown/actions.js @@ -1,4 +1,4 @@ -import DrawIO from '../services/drawio'; +import * as DrawIO from '../services/drawio'; export class Actions { @@ -140,7 +140,7 @@ export class Actions { } else { window.$events.emit('error', this.editor.config.text.imageUploadError); } - console.log(error); + console.error(error); } // Make the editor full screen @@ -165,7 +165,7 @@ export class Actions { scrollToLine = lineCount; break; } - lineCount++; + lineCount += 1; } if (scrollToLine === -1) { @@ -258,22 +258,31 @@ export class Actions { * @param {String} end */ wrapSelection(start, end) { - const selectionRange = this.#getSelectionRange(); - const selectionText = this.#getSelectionText(selectionRange); - if (!selectionText) return this.#wrapLine(start, end); + const selectRange = this.#getSelectionRange(); + const selectionText = this.#getSelectionText(selectRange); + if (!selectionText) { + this.#wrapLine(start, end); + return; + } let newSelectionText = selectionText; let newRange; if (selectionText.startsWith(start) && selectionText.endsWith(end)) { newSelectionText = selectionText.slice(start.length, selectionText.length - end.length); - newRange = selectionRange.extend(selectionRange.from, selectionRange.to - (start.length + end.length)); + newRange = selectRange.extend(selectRange.from, selectRange.to - (start.length + end.length)); } else { newSelectionText = `${start}${selectionText}${end}`; - newRange = selectionRange.extend(selectionRange.from, selectionRange.to + (start.length + end.length)); + newRange = selectRange.extend(selectRange.from, selectRange.to + (start.length + end.length)); } - this.#dispatchChange(selectionRange.from, selectionRange.to, newSelectionText, newRange.anchor, newRange.head); + this.#dispatchChange( + selectRange.from, + selectRange.to, + newSelectionText, + newRange.anchor, + newRange.head, + ); } replaceLineStartForOrderedList() { @@ -314,7 +323,13 @@ export class Actions { const newFormat = formats[newFormatIndex]; const newContent = line.text.replace(matches[0], matches[0].replace(format, newFormat)); const lineDiff = newContent.length - line.text.length; - this.#dispatchChange(line.from, line.to, newContent, selectionRange.anchor + lineDiff, selectionRange.head + lineDiff); + this.#dispatchChange( + line.from, + line.to, + newContent, + selectionRange.anchor + lineDiff, + selectionRange.head + lineDiff, + ); } } @@ -399,7 +414,7 @@ export class Actions { } catch (err) { window.$events.emit('error', this.editor.config.text.imageUploadError); this.#findAndReplaceContent(placeHolderText, ''); - console.log(err); + console.error(err); } } @@ -432,7 +447,8 @@ export class Actions { */ #replaceSelection(newContent, cursorOffset = 0, selectionRange = null) { selectionRange = selectionRange || this.editor.cm.state.selection.main; - this.#dispatchChange(selectionRange.from, selectionRange.to, newContent, selectionRange.from + cursorOffset); + const selectFrom = selectionRange.from + cursorOffset; + this.#dispatchChange(selectionRange.from, selectionRange.to, newContent, selectFrom); this.focus(); } @@ -510,6 +526,9 @@ export class Actions { if (selectFrom) { tr.selection = {anchor: selectFrom}; + if (selectTo) { + tr.selection.head = selectTo; + } } this.editor.cm.dispatch(tr); diff --git a/resources/js/markdown/codemirror.js b/resources/js/markdown/codemirror.js index b615e666b..9d54c19d7 100644 --- a/resources/js/markdown/codemirror.js +++ b/resources/js/markdown/codemirror.js @@ -21,7 +21,9 @@ export async function init(editor) { const onScrollDebounced = debounce(editor.actions.syncDisplayPosition.bind(editor.actions), 100, false); let syncActive = editor.settings.get('scrollSync'); - editor.settings.onChange('scrollSync', val => syncActive = val); + editor.settings.onChange('scrollSync', val => { + syncActive = val; + }); const domEventHandlers = { // Handle scroll to sync display view diff --git a/resources/js/markdown/settings.js b/resources/js/markdown/settings.js index 3cdceca1a..b843aaa8a 100644 --- a/resources/js/markdown/settings.js +++ b/resources/js/markdown/settings.js @@ -21,7 +21,7 @@ export class Settings { listenToInputChanges(inputs) { for (const input of inputs) { - input.addEventListener('change', event => { + input.addEventListener('change', () => { const name = input.getAttribute('name').replace('md-', ''); this.set(name, input.checked); }); diff --git a/resources/js/markdown/shortcuts.js b/resources/js/markdown/shortcuts.js index fcb66a12d..543e6dcdd 100644 --- a/resources/js/markdown/shortcuts.js +++ b/resources/js/markdown/shortcuts.js @@ -7,35 +7,35 @@ function provide(editor) { const shortcuts = {}; // Insert Image shortcut - shortcuts['Shift-Mod-i'] = cm => editor.actions.insertImage(); + shortcuts['Shift-Mod-i'] = () => editor.actions.insertImage(); // Save draft - shortcuts['Mod-s'] = cm => window.$events.emit('editor-save-draft'); + shortcuts['Mod-s'] = () => window.$events.emit('editor-save-draft'); // Save page - shortcuts['Mod-Enter'] = cm => window.$events.emit('editor-save-page'); + shortcuts['Mod-Enter'] = () => window.$events.emit('editor-save-page'); // Show link selector - shortcuts['Shift-Mod-k'] = cm => editor.actions.showLinkSelector(); + shortcuts['Shift-Mod-k'] = () => editor.actions.showLinkSelector(); // Insert Link - shortcuts['Mod-k'] = cm => editor.actions.insertLink(); + shortcuts['Mod-k'] = () => editor.actions.insertLink(); // FormatShortcuts - shortcuts['Mod-1'] = cm => editor.actions.replaceLineStart('##'); - shortcuts['Mod-2'] = cm => editor.actions.replaceLineStart('###'); - shortcuts['Mod-3'] = cm => editor.actions.replaceLineStart('####'); - shortcuts['Mod-4'] = cm => editor.actions.replaceLineStart('#####'); - shortcuts['Mod-5'] = cm => editor.actions.replaceLineStart(''); - shortcuts['Mod-d'] = cm => editor.actions.replaceLineStart(''); - shortcuts['Mod-6'] = cm => editor.actions.replaceLineStart('>'); - shortcuts['Mod-q'] = cm => editor.actions.replaceLineStart('>'); - shortcuts['Mod-7'] = cm => editor.actions.wrapSelection('\n```\n', '\n```'); - shortcuts['Mod-8'] = cm => editor.actions.wrapSelection('`', '`'); - shortcuts['Shift-Mod-e'] = cm => editor.actions.wrapSelection('`', '`'); - shortcuts['Mod-9'] = cm => editor.actions.cycleCalloutTypeAtSelection(); - shortcuts['Mod-p'] = cm => editor.actions.replaceLineStart('-'); - shortcuts['Mod-o'] = cm => editor.actions.replaceLineStartForOrderedList(); + shortcuts['Mod-1'] = () => editor.actions.replaceLineStart('##'); + shortcuts['Mod-2'] = () => editor.actions.replaceLineStart('###'); + shortcuts['Mod-3'] = () => editor.actions.replaceLineStart('####'); + shortcuts['Mod-4'] = () => editor.actions.replaceLineStart('#####'); + shortcuts['Mod-5'] = () => editor.actions.replaceLineStart(''); + shortcuts['Mod-d'] = () => editor.actions.replaceLineStart(''); + shortcuts['Mod-6'] = () => editor.actions.replaceLineStart('>'); + shortcuts['Mod-q'] = () => editor.actions.replaceLineStart('>'); + shortcuts['Mod-7'] = () => editor.actions.wrapSelection('\n```\n', '\n```'); + shortcuts['Mod-8'] = () => editor.actions.wrapSelection('`', '`'); + shortcuts['Shift-Mod-e'] = () => editor.actions.wrapSelection('`', '`'); + shortcuts['Mod-9'] = () => editor.actions.cycleCalloutTypeAtSelection(); + shortcuts['Mod-p'] = () => editor.actions.replaceLineStart('-'); + shortcuts['Mod-o'] = () => editor.actions.replaceLineStartForOrderedList(); return shortcuts; } diff --git a/resources/js/services/animations.js b/resources/js/services/animations.js index 43bd6a0d0..bc983c807 100644 --- a/resources/js/services/animations.js +++ b/resources/js/services/animations.js @@ -5,6 +5,53 @@ */ const animateStylesCleanupMap = new WeakMap(); +/** + * Animate the css styles of an element using FLIP animation techniques. + * Styles must be an object where the keys are style properties, camelcase, and the values + * are an array of two items in the format [initialValue, finalValue] + * @param {Element} element + * @param {Object} styles + * @param {Number} animTime + * @param {Function} onComplete + */ +function animateStyles(element, styles, animTime = 400, onComplete = null) { + const styleNames = Object.keys(styles); + for (const style of styleNames) { + element.style[style] = styles[style][0]; + } + + const cleanup = () => { + for (const style of styleNames) { + element.style[style] = null; + } + element.style.transition = null; + element.removeEventListener('transitionend', cleanup); + animateStylesCleanupMap.delete(element); + if (onComplete) onComplete(); + }; + + setTimeout(() => { + element.style.transition = `all ease-in-out ${animTime}ms`; + for (const style of styleNames) { + element.style[style] = styles[style][1]; + } + + element.addEventListener('transitionend', cleanup); + animateStylesCleanupMap.set(element, cleanup); + }, 15); +} + +/** + * Run the active cleanup action for the given element. + * @param {Element} element + */ +function cleanupExistingElementAnimation(element) { + if (animateStylesCleanupMap.has(element)) { + const oldCleanup = animateStylesCleanupMap.get(element); + oldCleanup(); + } +} + /** * Fade in the given element. * @param {Element} element @@ -113,50 +160,3 @@ export function transitionHeight(element, animTime = 400) { animateStyles(element, animStyles, animTime); }; } - -/** - * Animate the css styles of an element using FLIP animation techniques. - * Styles must be an object where the keys are style properties, camelcase, and the values - * are an array of two items in the format [initialValue, finalValue] - * @param {Element} element - * @param {Object} styles - * @param {Number} animTime - * @param {Function} onComplete - */ -function animateStyles(element, styles, animTime = 400, onComplete = null) { - const styleNames = Object.keys(styles); - for (const style of styleNames) { - element.style[style] = styles[style][0]; - } - - const cleanup = () => { - for (const style of styleNames) { - element.style[style] = null; - } - element.style.transition = null; - element.removeEventListener('transitionend', cleanup); - animateStylesCleanupMap.delete(element); - if (onComplete) onComplete(); - }; - - setTimeout(() => { - element.style.transition = `all ease-in-out ${animTime}ms`; - for (const style of styleNames) { - element.style[style] = styles[style][1]; - } - - element.addEventListener('transitionend', cleanup); - animateStylesCleanupMap.set(element, cleanup); - }, 15); -} - -/** - * Run the active cleanup action for the given element. - * @param {Element} element - */ -function cleanupExistingElementAnimation(element) { - if (animateStylesCleanupMap.has(element)) { - const oldCleanup = animateStylesCleanupMap.get(element); - oldCleanup(); - } -} diff --git a/resources/js/services/components.js b/resources/js/services/components.js index 102b24d45..beb0ce92f 100644 --- a/resources/js/services/components.js +++ b/resources/js/services/components.js @@ -19,44 +19,6 @@ const componentModelMap = {}; */ const elementComponentMap = new WeakMap(); -/** - * Initialize a component instance on the given dom element. - * @param {String} name - * @param {Element} element - */ -function initComponent(name, element) { - /** @type {Function|undefined} * */ - const componentModel = componentModelMap[name]; - if (componentModel === undefined) return; - - // Create our component instance - /** @type {Component} * */ - let instance; - try { - instance = new componentModel(); - instance.$name = name; - instance.$el = element; - const allRefs = parseRefs(name, element); - instance.$refs = allRefs.refs; - instance.$manyRefs = allRefs.manyRefs; - instance.$opts = parseOpts(name, element); - instance.setup(); - } catch (e) { - console.error('Failed to create component', e, name, element); - } - - // Add to global listing - if (typeof components[name] === 'undefined') { - components[name] = []; - } - components[name].push(instance); - - // Add to element mapping - const elComponents = elementComponentMap.get(element) || {}; - elComponents[name] = instance; - elementComponentMap.set(element, elComponents); -} - /** * Parse out the element references within the given element * for the given component name. @@ -93,13 +55,13 @@ function parseRefs(name, element) { /** * Parse out the element component options. - * @param {String} name + * @param {String} componentName * @param {Element} element * @return {Object} */ -function parseOpts(name, element) { +function parseOpts(componentName, element) { const opts = {}; - const prefix = `option:${name}:`; + const prefix = `option:${componentName}:`; for (const {name, value} of element.attributes) { if (name.startsWith(prefix)) { const optName = name.replace(prefix, ''); @@ -109,6 +71,44 @@ function parseOpts(name, element) { return opts; } +/** + * Initialize a component instance on the given dom element. + * @param {String} name + * @param {Element} element + */ +function initComponent(name, element) { + /** @type {Function|undefined} * */ + const ComponentModel = componentModelMap[name]; + if (ComponentModel === undefined) return; + + // Create our component instance + /** @type {Component} * */ + let instance; + try { + instance = new ComponentModel(); + instance.$name = name; + instance.$el = element; + const allRefs = parseRefs(name, element); + instance.$refs = allRefs.refs; + instance.$manyRefs = allRefs.manyRefs; + instance.$opts = parseOpts(name, element); + instance.setup(); + } catch (e) { + console.error('Failed to create component', e, name, element); + } + + // Add to global listing + if (typeof components[name] === 'undefined') { + components[name] = []; + } + components[name].push(instance); + + // Add to element mapping + const elComponents = elementComponentMap.get(element) || {}; + elComponents[name] = instance; + elementComponentMap.set(element, elComponents); +} + /** * Initialize all components found within the given element. * @param {Element|Document} parentElement diff --git a/resources/js/services/drawio.js b/resources/js/services/drawio.js index 4f9398260..c773a780d 100644 --- a/resources/js/services/drawio.js +++ b/resources/js/services/drawio.js @@ -3,50 +3,8 @@ let lastApprovedOrigin; let onInit; let onSave; -/** - * Show the draw.io editor. - * @param {String} drawioUrl - * @param {Function} onInitCallback - Must return a promise with the xml to load for the editor. - * @param {Function} onSaveCallback - Is called with the drawing data on save. - */ -function show(drawioUrl, onInitCallback, onSaveCallback) { - onInit = onInitCallback; - onSave = onSaveCallback; - - iFrame = document.createElement('iframe'); - iFrame.setAttribute('frameborder', '0'); - window.addEventListener('message', drawReceive); - iFrame.setAttribute('src', drawioUrl); - iFrame.setAttribute('class', 'fullscreen'); - iFrame.style.backgroundColor = '#FFFFFF'; - document.body.appendChild(iFrame); - lastApprovedOrigin = (new URL(drawioUrl)).origin; -} - -function close() { - drawEventClose(); -} - -/** - * Receive and handle a message event from the draw.io window. - * @param {MessageEvent} event - */ -function drawReceive(event) { - if (!event.data || event.data.length < 1) return; - if (event.origin !== lastApprovedOrigin) return; - - const message = JSON.parse(event.data); - if (message.event === 'init') { - drawEventInit(); - } else if (message.event === 'exit') { - drawEventClose(); - } else if (message.event === 'save') { - drawEventSave(message); - } else if (message.event === 'export') { - drawEventExport(message); - } else if (message.event === 'configure') { - drawEventConfigure(); - } +function drawPostMessage(data) { + iFrame.contentWindow.postMessage(JSON.stringify(data), lastApprovedOrigin); } function drawEventExport(message) { @@ -75,15 +33,54 @@ function drawEventConfigure() { } function drawEventClose() { + // eslint-disable-next-line no-use-before-define window.removeEventListener('message', drawReceive); if (iFrame) document.body.removeChild(iFrame); } -function drawPostMessage(data) { - iFrame.contentWindow.postMessage(JSON.stringify(data), lastApprovedOrigin); +/** + * Receive and handle a message event from the draw.io window. + * @param {MessageEvent} event + */ +function drawReceive(event) { + if (!event.data || event.data.length < 1) return; + if (event.origin !== lastApprovedOrigin) return; + + const message = JSON.parse(event.data); + if (message.event === 'init') { + drawEventInit(); + } else if (message.event === 'exit') { + drawEventClose(); + } else if (message.event === 'save') { + drawEventSave(message); + } else if (message.event === 'export') { + drawEventExport(message); + } else if (message.event === 'configure') { + drawEventConfigure(); + } } -async function upload(imageData, pageUploadedToId) { +/** + * Show the draw.io editor. + * @param {String} drawioUrl + * @param {Function} onInitCallback - Must return a promise with the xml to load for the editor. + * @param {Function} onSaveCallback - Is called with the drawing data on save. + */ +export function show(drawioUrl, onInitCallback, onSaveCallback) { + onInit = onInitCallback; + onSave = onSaveCallback; + + iFrame = document.createElement('iframe'); + iFrame.setAttribute('frameborder', '0'); + window.addEventListener('message', drawReceive); + iFrame.setAttribute('src', drawioUrl); + iFrame.setAttribute('class', 'fullscreen'); + iFrame.style.backgroundColor = '#FFFFFF'; + document.body.appendChild(iFrame); + lastApprovedOrigin = (new URL(drawioUrl)).origin; +} + +export async function upload(imageData, pageUploadedToId) { const data = { image: imageData, uploaded_to: pageUploadedToId, @@ -92,12 +89,16 @@ async function upload(imageData, pageUploadedToId) { return resp.data; } +export function close() { + drawEventClose(); +} + /** * Load an existing image, by fetching it as Base64 from the system. * @param drawingId * @returns {Promise} */ -async function load(drawingId) { +export async function load(drawingId) { try { const resp = await window.$http.get(window.baseUrl(`/images/drawio/base64/${drawingId}`)); return `data:image/png;base64,${resp.data.content}`; @@ -109,7 +110,3 @@ async function load(drawingId) { throw error; } } - -export default { - show, close, upload, load, -}; diff --git a/resources/js/services/events.js b/resources/js/services/events.js index 0b24adfac..761305793 100644 --- a/resources/js/services/events.js +++ b/resources/js/services/events.js @@ -6,13 +6,12 @@ const stack = []; * @param {String} eventName * @param {*} eventData */ -function emit(eventName, eventData) { +export function emit(eventName, eventData) { stack.push({name: eventName, data: eventData}); - if (typeof listeners[eventName] === 'undefined') return this; - const eventsToStart = listeners[eventName]; - for (let i = 0; i < eventsToStart.length; i++) { - const event = eventsToStart[i]; - event(eventData); + + const listenersToRun = listeners[eventName] || []; + for (const listener of listenersToRun) { + listener(eventData); } } @@ -22,7 +21,7 @@ function emit(eventName, eventData) { * @param {Function} callback * @returns {Events} */ -function listen(eventName, callback) { +export function listen(eventName, callback) { if (typeof listeners[eventName] === 'undefined') listeners[eventName] = []; listeners[eventName].push(callback); } @@ -34,7 +33,7 @@ function listen(eventName, callback) { * @param {String} eventName * @param {Object} eventData */ -function emitPublic(targetElement, eventName, eventData) { +export function emitPublic(targetElement, eventName, eventData) { const event = new CustomEvent(eventName, { detail: eventData, bubbles: true, @@ -43,34 +42,40 @@ function emitPublic(targetElement, eventName, eventData) { } /** - * Notify of standard server-provided validation errors. - * @param {Object} error + * Emit a success event with the provided message. + * @param {String} message */ -function showValidationErrors(error) { - if (!error.status) return; - if (error.status === 422 && error.data) { - const message = Object.values(error.data).flat().join('\n'); - emit('error', message); +export function success(message) { + emit('success', message); +} + +/** + * Emit an error event with the provided message. + * @param {String} message + */ +export function error(message) { + emit('error', message); +} + +/** + * Notify of standard server-provided validation errors. + * @param {Object} responseErr + */ +export function showValidationErrors(responseErr) { + if (!responseErr.status) return; + if (responseErr.status === 422 && responseErr.data) { + const message = Object.values(responseErr.data).flat().join('\n'); + error(message); } } /** * Notify standard server-provided error messages. - * @param {Object} error + * @param {Object} responseErr */ -function showResponseError(error) { - if (!error.status) return; - if (error.status >= 400 && error.data && error.data.message) { - emit('error', error.data.message); +export function showResponseError(responseErr) { + if (!responseErr.status) return; + if (responseErr.status >= 400 && responseErr.data && responseErr.data.message) { + error(responseErr.data.message); } } - -export default { - emit, - emitPublic, - listen, - success: msg => emit('success', msg), - error: msg => emit('error', msg), - showValidationErrors, - showResponseError, -}; diff --git a/resources/js/services/translations.js b/resources/js/services/translations.js index eb90eeec4..e562a9152 100644 --- a/resources/js/services/translations.js +++ b/resources/js/services/translations.js @@ -5,11 +5,7 @@ */ class Translator { - /** - * Create an instance, Passing in the required translations - * @param translations - */ - constructor(translations) { + constructor() { this.store = new Map(); this.parseTranslations(); } @@ -27,7 +23,7 @@ class Translator { } /** - * Get a translation, Same format as laravel's 'trans' helper + * Get a translation, Same format as Laravel's 'trans' helper * @param key * @param replacements * @returns {*} @@ -38,8 +34,8 @@ class Translator { } /** - * Get pluralised text, Dependant on the given count. - * Same format at laravel's 'trans_choice' helper. + * Get pluralised text, Dependent on the given count. + * Same format at Laravel's 'trans_choice' helper. * @param key * @param count * @param replacements @@ -52,7 +48,7 @@ class Translator { /** * Parse the given translation and find the correct plural option - * to use. Similar format at laravel's 'trans_choice' helper. + * to use. Similar format at Laravel's 'trans_choice' helper. * @param {String} translation * @param {Number} count * @param {Object} replacements @@ -117,14 +113,17 @@ class Translator { */ performReplacements(string, replacements) { if (!replacements) return string; - const replaceMatches = string.match(/:([\S]+)/g); + const replaceMatches = string.match(/:(\S+)/g); if (replaceMatches === null) return string; + let updatedString = string; + replaceMatches.forEach(match => { const key = match.substring(1); if (typeof replacements[key] === 'undefined') return; - string = string.replace(match, replacements[key]); + updatedString = updatedString.replace(match, replacements[key]); }); - return string; + + return updatedString; } } diff --git a/resources/js/wysiwyg/plugin-drawio.js b/resources/js/wysiwyg/plugin-drawio.js index 7827efd62..7b1750786 100644 --- a/resources/js/wysiwyg/plugin-drawio.js +++ b/resources/js/wysiwyg/plugin-drawio.js @@ -1,4 +1,4 @@ -import DrawIO from '../services/drawio'; +import * as DrawIO from '../services/drawio'; let pageEditor = null; let currentNode = null;