]> BookStack Code Mirror - bookstack/blobdiff - resources/js/wysiwyg/config.js
Added a custom link context toolbar
[bookstack] / resources / js / wysiwyg / config.js
index 11090ce4bd6f0874bbfa4d4037c8b5988c9778a1..2da1e2c989d704bb8961bc330494b66c360c47a7 100644 (file)
@@ -1,13 +1,15 @@
 import {register as registerShortcuts} from "./shortcuts";
 import {listen as listenForCommonEvents} from "./common-events";
-import {scrollToQueryString, fixScrollForMobile} from "./scrolling";
+import {scrollToQueryString} from "./scrolling";
 import {listenForDragAndPaste} from "./drop-paste-handling";
+import {getPrimaryToolbar, registerAdditionalToolbars} from "./toolbars";
 
 import {getPlugin as getCodeeditorPlugin} from "./plugin-codeeditor";
 import {getPlugin as getDrawioPlugin} from "./plugin-drawio";
 import {getPlugin as getCustomhrPlugin} from "./plugins-customhr";
 import {getPlugin as getImagemanagerPlugin} from "./plugins-imagemanager";
 import {getPlugin as getAboutPlugin} from "./plugins-about";
+import {getPlugin as getDetailsPlugin} from "./plugins-details";
 
 const style_formats = [
     {title: "Large Header", format: "h2", preview: 'color: blue;'},
@@ -16,7 +18,6 @@ const style_formats = [
     {title: "Tiny Header", format: "h5"},
     {title: "Paragraph", format: "p", exact: true, classes: ''},
     {title: "Blockquote", format: "blockquote"},
-    {title: "Inline Code", inline: "code"},
     {
         title: "Callouts", items: [
             {title: "Information", format: 'calloutinfo'},
@@ -28,7 +29,6 @@ const style_formats = [
 ];
 
 const formats = {
-    codeeditor: {selector: 'p,h1,h2,h3,h4,h5,h6,td,th,div'},
     alignleft: {selector: 'p,h1,h2,h3,h4,h5,h6,td,th,div,ul,ol,li,table,img', classes: 'align-left'},
     aligncenter: {selector: 'p,h1,h2,h3,h4,h5,h6,td,th,div,ul,ol,li,table,img', classes: 'align-center'},
     alignright: {selector: 'p,h1,h2,h3,h4,h5,h6,td,th,div,ul,ol,li,table,img', classes: 'align-right'},
@@ -59,28 +59,6 @@ function file_picker_callback(callback, value, meta) {
 
 }
 
-/**
- * @param {WysiwygConfigOptions} options
- * @return {string}
- */
-function buildToolbar(options) {
-    const textDirPlugins = options.textDirection === 'rtl' ? 'ltr rtl' : '';
-
-    const toolbar = [
-        'undo redo',
-        'styleselect',
-        'bold italic underline strikethrough superscript subscript',
-        'forecolor backcolor',
-        'alignleft aligncenter alignright alignjustify',
-        'bullist numlist outdent indent',
-        textDirPlugins,
-        'table imagemanager-insert link hr codeeditor drawio media',
-        'removeformat code about fullscreen'
-    ];
-
-    return toolbar.filter(row => Boolean(row)).join(' | ');
-}
-
 /**
  * @param {WysiwygConfigOptions} options
  * @return {string}
@@ -102,6 +80,7 @@ function gatherPlugins(options) {
         "media",
         "imagemanager",
         "about",
+        "details",
         options.textDirection === 'rtl' ? 'directionality' : '',
     ];
 
@@ -109,6 +88,7 @@ function gatherPlugins(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));
 
     if (options.drawioUrl) {
         window.tinymce.PluginManager.add('drawio', getDrawioPlugin(options));
@@ -119,16 +99,16 @@ function gatherPlugins(options) {
 }
 
 /**
- * Load custom HTML head content from the settings into the editor.
- * TODO: We should be able to get this from current parent page?
- * @param {Editor} editor
+ * Fetch custom HTML head content from the parent page head into the editor.
  */
-function loadCustomHeadContent(editor) {
-    window.$http.get(window.baseUrl('/custom-head-content')).then(resp => {
-        if (!resp.data) return;
-        let head = editor.getDoc().querySelector('head');
-        head.innerHTML += resp.data;
-    });
+function fetchCustomHeadContent() {
+    const headContentLines = document.head.innerHTML.split("\n");
+    const startLineIndex = headContentLines.findIndex(line => line.trim() === '<!-- Start: custom user content -->');
+    const endLineIndex = headContentLines.findIndex(line => line.trim() === '<!-- End: custom user content -->');
+    if (startLineIndex === -1 || endLineIndex === -1) {
+        return ''
+    }
+    return headContentLines.slice(startLineIndex + 1, endLineIndex).join('\n');
 }
 
 /**
@@ -145,7 +125,6 @@ function getSetupCallback(options) {
         editor.on('init', () => {
             editorChange();
             scrollToQueryString(editor);
-            fixScrollForMobile(editor);
             window.editor = editor;
         });
 
@@ -157,20 +136,39 @@ function getSetupCallback(options) {
             window.$events.emit('editor-html-change', content);
         }
 
-        // TODO - Update to standardise across both editors
-        // Use events within listenForBookStackEditorEvents instead (Different event signature)
-        window.$events.listen('editor-html-update', html => {
-            editor.setContent(html);
-            editor.selection.select(editor.getBody(), true);
-            editor.selection.collapse(false);
-            editorChange(html);
-        });
-
         // Custom handler hook
         window.$events.emitPublic(options.containerElement, 'editor-tinymce::setup', {editor});
+
+        // Inline code format button
+        editor.ui.registry.addButton('inlinecode', {
+            tooltip: 'Inline code',
+            icon: 'sourcecode',
+            onAction() {
+                editor.execCommand('mceToggleFormat', false, 'code');
+            }
+        })
     }
 }
 
+/**
+ * @param {WysiwygConfigOptions} options
+ */
+function getContentStyle(options) {
+    return `
+html, body, html.dark-mode {
+    background: ${options.darkMode ? '#222' : '#fff'};
+} 
+body {
+    padding-left: 15px !important;
+    padding-right: 15px !important; 
+    height: initial !important;
+    margin:0!important; 
+    margin-left: auto! important;
+    margin-right: auto !important;
+    overflow-y: hidden !important;
+}`.trim().replace('\n', '');
+}
+
 /**
  * @param {WysiwygConfigOptions} options
  * @return {Object}
@@ -201,14 +199,22 @@ export function build(options) {
         statusbar: false,
         menubar: false,
         paste_data_images: false,
-        extended_valid_elements: 'pre[*],svg[*],div[drawio-diagram]',
+        extended_valid_elements: 'pre[*],svg[*],div[drawio-diagram],details[*],summary[*],div[*]',
         automatic_uploads: false,
-        valid_children: "-div[p|h1|h2|h3|h4|h5|h6|blockquote],+div[pre],+div[img]",
+        custom_elements: 'doc-root,code-block',
+        valid_children: [
+            "-div[p|h1|h2|h3|h4|h5|h6|blockquote|code-block]",
+            "+div[pre|img]",
+            "-doc-root[doc-root|#text]",
+            "-li[details]",
+            "+code-block[pre]",
+            "+doc-root[code-block]"
+        ].join(','),
         plugins: gatherPlugins(options),
         imagetools_toolbar: 'imageoptions',
         contextmenu: false,
-        toolbar: buildToolbar(options),
-        content_style: `html, body, html.dark-mode {background: ${options.darkMode ? '#222' : '#fff'};} body {padding-left: 15px !important; padding-right: 15px !important; margin:0!important; margin-left:auto!important;margin-right:auto!important;}`,
+        toolbar: getPrimaryToolbar(options),
+        content_style: getContentStyle(options),
         style_formats,
         style_formats_merge: false,
         media_alt_source: false,
@@ -217,15 +223,19 @@ export function build(options) {
         file_picker_types: 'file image',
         file_picker_callback,
         paste_preprocess(plugin, args) {
-            let content = args.content;
+            const content = args.content;
             if (content.indexOf('<img src="file://') !== -1) {
                 args.content = '';
             }
         },
         init_instance_callback(editor) {
-            loadCustomHeadContent(editor);
+            const head = editor.getDoc().querySelector('head');
+            head.innerHTML += fetchCustomHeadContent();
+        },
+        setup(editor) {
+            registerAdditionalToolbars(editor, options);
+            getSetupCallback(options)(editor);
         },
-        setup: getSetupCallback(options),
     };
 }