]> BookStack Code Mirror - bookstack/blob - resources/js/components/page-display.js
Updated a whole load more js components
[bookstack] / resources / js / components / page-display.js
1 import * as DOM from "../services/dom";
2 import {scrollAndHighlightElement} from "../services/util";
3 import {Component} from "./component";
4
5 export class PageDisplay extends Component {
6
7     setup() {
8         this.container = this.$el;
9         this.pageId = this.$opts.pageId;
10
11         window.importVersioned('code').then(Code => Code.highlight());
12         this.setupNavHighlighting();
13         this.setupDetailsCodeBlockRefresh();
14
15         // Check the hash on load
16         if (window.location.hash) {
17             const text = window.location.hash.replace(/%20/g, ' ').substring(1);
18             this.goToText(text);
19         }
20
21         // Sidebar page nav click event
22         const sidebarPageNav = document.querySelector('.sidebar-page-nav');
23         if (sidebarPageNav) {
24             DOM.onChildEvent(sidebarPageNav, 'a', 'click', (event, child) => {
25                 event.preventDefault();
26                 window.$components.first('tri-layout').showContent();
27                 const contentId = child.getAttribute('href').substr(1);
28                 this.goToText(contentId);
29                 window.history.pushState(null, null, '#' + contentId);
30             });
31         }
32     }
33
34     goToText(text) {
35         const idElem = document.getElementById(text);
36
37         DOM.forEach('.page-content [data-highlighted]', elem => {
38             elem.removeAttribute('data-highlighted');
39             elem.style.backgroundColor = null;
40         });
41
42         if (idElem !== null) {
43             scrollAndHighlightElement(idElem);
44         } else {
45             const textElem = DOM.findText('.page-content > div > *', text);
46             if (textElem) {
47                 scrollAndHighlightElement(textElem);
48             }
49         }
50     }
51
52     setupNavHighlighting() {
53         const pageNav = document.querySelector('.sidebar-page-nav');
54
55         // fetch all the headings.
56         const headings = document.querySelector('.page-content').querySelectorAll('h1, h2, h3, h4, h5, h6');
57         // if headings are present, add observers.
58         if (headings.length > 0 && pageNav !== null) {
59             addNavObserver(headings);
60         }
61
62         function addNavObserver(headings) {
63             // Setup the intersection observer.
64             const intersectOpts = {
65                 rootMargin: '0px 0px 0px 0px',
66                 threshold: 1.0
67             };
68             const pageNavObserver = new IntersectionObserver(headingVisibilityChange, intersectOpts);
69
70             // observe each heading
71             for (const heading of headings) {
72                 pageNavObserver.observe(heading);
73             }
74         }
75
76         function headingVisibilityChange(entries, observer) {
77             for (const entry of entries) {
78                 const isVisible = (entry.intersectionRatio === 1);
79                 toggleAnchorHighlighting(entry.target.id, isVisible);
80             }
81         }
82
83         function toggleAnchorHighlighting(elementId, shouldHighlight) {
84             DOM.forEach('a[href="#' + elementId + '"]', anchor => {
85                 anchor.closest('li').classList.toggle('current-heading', shouldHighlight);
86             });
87         }
88     }
89
90     setupDetailsCodeBlockRefresh() {
91         const onToggle = event => {
92             const codeMirrors = [...event.target.querySelectorAll('.CodeMirror')];
93             codeMirrors.forEach(cm => cm.CodeMirror && cm.CodeMirror.refresh());
94         };
95
96         const details = [...this.container.querySelectorAll('details')];
97         details.forEach(detail => detail.addEventListener('toggle', onToggle));
98     }
99 }