]> BookStack Code Mirror - bookstack/blobdiff - resources/js/components/page-display.js
Opensearch: Fixed XML declaration when php short tags enabled
[bookstack] / resources / js / components / page-display.js
index cc55fe35e1e38caae9de9f9cca013e0c7b1d14f6..d3ac78a4ad19347779583d1331cb470d6532959a 100644 (file)
@@ -1,22 +1,46 @@
-import Clipboard from "clipboard/dist/clipboard.min";
-import Code from "../services/code";
-import * as DOM from "../services/dom";
-import {scrollAndHighlightElement} from "../services/util";
+import * as DOM from '../services/dom.ts';
+import {scrollAndHighlightElement} from '../services/util.ts';
+import {Component} from './component';
+
+function toggleAnchorHighlighting(elementId, shouldHighlight) {
+    DOM.forEach(`#page-navigation 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);
+    }
+}
 
-class PageDisplay {
+export class PageDisplay extends Component {
 
-    constructor(elem) {
-        this.elem = elem;
-        this.pageId = elem.getAttribute('page-display');
+    setup() {
+        this.container = this.$el;
+        this.pageId = this.$opts.pageId;
 
-        Code.highlight();
-        this.setupPointer();
+        window.importVersioned('code').then(Code => Code.highlight());
         this.setupNavHighlighting();
-        this.setupDetailsCodeBlockRefresh();
 
         // Check the hash on load
         if (window.location.hash) {
-            let text = window.location.hash.replace(/\%20/g, ' ').substr(1);
+            const text = window.location.hash.replace(/%20/g, ' ').substring(1);
             this.goToText(text);
         }
 
@@ -25,10 +49,10 @@ class PageDisplay {
         if (sidebarPageNav) {
             DOM.onChildEvent(sidebarPageNav, 'a', 'click', (event, child) => {
                 event.preventDefault();
-                window.components['tri-layout'][0].showContent();
+                window.$components.first('tri-layout').showContent();
                 const contentId = child.getAttribute('href').substr(1);
                 this.goToText(contentId);
-                window.history.pushState(null, null, '#' + contentId);
+                window.history.pushState(null, null, `#${contentId}`);
             });
         }
     }
@@ -51,162 +75,15 @@ class PageDisplay {
         }
     }
 
-    setupPointer() {
-        let pointer = document.getElementById('pointer');
-        if (!pointer) {
-            return;
-        }
-
-        // Set up pointer
-        pointer = pointer.parentNode.removeChild(pointer);
-        const pointerInner = pointer.querySelector('div.pointer');
-
-        // Instance variables
-        let pointerShowing = false;
-        let isSelection = false;
-        let pointerModeLink = true;
-        let pointerSectionId = '';
-
-        // Select all contents on input click
-        DOM.onChildEvent(pointer, 'input', 'click', (event, input) => {
-            input.select();
-            event.stopPropagation();
-        });
-
-        // Prevent closing pointer when clicked or focused
-        DOM.onEvents(pointer, ['click', 'focus'], event => {
-            event.stopPropagation();
-        });
-
-        // Pointer mode toggle
-        DOM.onChildEvent(pointer, 'span.icon', 'click', (event, icon) => {
-            event.stopPropagation();
-            pointerModeLink = !pointerModeLink;
-            icon.querySelector('[data-icon="include"]').style.display = (!pointerModeLink) ? 'inline' : 'none';
-            icon.querySelector('[data-icon="link"]').style.display = (pointerModeLink) ? 'inline' : 'none';
-            updatePointerContent();
-        });
-
-        // Set up clipboard
-        new Clipboard(pointer.querySelector('button'));
-
-        // Hide pointer when clicking away
-        DOM.onEvents(document.body, ['click', 'focus'], event => {
-            if (!pointerShowing || isSelection) return;
-            pointer = pointer.parentElement.removeChild(pointer);
-            pointerShowing = false;
-        });
-
-        let updatePointerContent = (element) => {
-            let inputText = pointerModeLink ? window.baseUrl(`/link/${this.pageId}#${pointerSectionId}`) : `{{@${this.pageId}#${pointerSectionId}}}`;
-            if (pointerModeLink && !inputText.startsWith('http')) {
-                inputText = window.location.protocol + "//" + window.location.host + inputText;
-            }
-
-            pointer.querySelector('input').value = inputText;
-
-            // Update anchor if present
-            const editAnchor = pointer.querySelector('#pointer-edit');
-            if (editAnchor && element) {
-                const editHref = editAnchor.dataset.editHref;
-                const elementId = element.id;
-
-                // get the first 50 characters.
-                const queryContent = element.textContent && element.textContent.substring(0, 50);
-                editAnchor.href = `${editHref}?content-id=${elementId}&content-text=${encodeURIComponent(queryContent)}`;
-            }
-        };
-
-        // Show pointer when selecting a single block of tagged content
-        DOM.forEach('.page-content [id^="bkmrk"]', bookMarkElem => {
-            DOM.onEvents(bookMarkElem, ['mouseup', 'keyup'], event => {
-                event.stopPropagation();
-                let selection = window.getSelection();
-                if (selection.toString().length === 0) return;
-
-                // Show pointer and set link
-                pointerSectionId = bookMarkElem.id;
-                updatePointerContent(bookMarkElem);
-
-                bookMarkElem.parentNode.insertBefore(pointer, bookMarkElem);
-                pointer.style.display = 'block';
-                pointerShowing = true;
-                isSelection = true;
-
-                // Set pointer to sit near mouse-up position
-                requestAnimationFrame(() => {
-                    const bookMarkBounds = bookMarkElem.getBoundingClientRect();
-                    let pointerLeftOffset = (event.pageX - bookMarkBounds.left - 164);
-                    if (pointerLeftOffset < 0) {
-                        pointerLeftOffset = 0
-                    }
-                    const pointerLeftOffsetPercent = (pointerLeftOffset / bookMarkBounds.width) * 100;
-
-                    pointerInner.style.left = pointerLeftOffsetPercent + '%';
-
-                    setTimeout(() => {
-                        isSelection = false;
-                    }, 100);
-                });
-
-            });
-        });
-    }
-
     setupNavHighlighting() {
-        // Check if support is present for IntersectionObserver
-        if (!('IntersectionObserver' in window) ||
-            !('IntersectionObserverEntry' in window) ||
-            !('intersectionRatio' in window.IntersectionObserverEntry.prototype)) {
-            return;
-        }
-
-        let pageNav = document.querySelector('.sidebar-page-nav');
+        const pageNav = document.querySelector('.sidebar-page-nav');
 
         // fetch all the headings.
-        let headings = document.querySelector('.page-content').querySelectorAll('h1, h2, h3, h4, h5, h6');
+        const headings = document.querySelector('.page-content').querySelectorAll('h1, h2, h3, h4, h5, h6');
         // if headings are present, add observers.
         if (headings.length > 0 && pageNav !== null) {
             addNavObserver(headings);
         }
-
-        function addNavObserver(headings) {
-            // Setup the intersection observer.
-            let intersectOpts = {
-                rootMargin: '0px 0px 0px 0px',
-                threshold: 1.0
-            };
-            let pageNavObserver = new IntersectionObserver(headingVisibilityChange, intersectOpts);
-
-            // observe each heading
-            for (let heading of headings) {
-                pageNavObserver.observe(heading);
-            }
-        }
-
-        function headingVisibilityChange(entries, observer) {
-            for (let entry of entries) {
-                let 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() {
-        const onToggle = event => {
-            const codeMirrors = [...event.target.querySelectorAll('.CodeMirror')];
-            codeMirrors.forEach(cm => cm.CodeMirror && cm.CodeMirror.refresh());
-        };
-
-        const details = [...this.elem.querySelectorAll('details')];
-        details.forEach(detail => detail.addEventListener('toggle', onToggle));
-    }
 }
-
-export default PageDisplay;