1/**
2 * DokuWiki Mikio Template Javascript
3 *
4 * @link    http://dokuwiki.org/template:mikio
5 * @author  James Collins <james.collins@outlook.com.au>
6 * @license GPLv2 (http://www.gnu.org/licenses/gpl-2.0.html)
7 */
8"use strict";
9
10var mikio = {
11    queueResize: false,
12    mikioCSS: false,
13    stickyItems: [],
14    stickyOffset: 0,
15    stickyIndex: 2010,
16    darkMode: 'light',
17
18    ready: function () {
19        var self = this;
20
21        this.initDarkMode();
22        this.addToggleClick('mikio-sidebar-toggle', 'mikio-sidebar-collapse');
23        this.addToggleClick('mikio-navbar-toggle', 'mikio-navbar-collapse');
24        this.addDropdownClick('mikio-nav-dropdown', 'mikio-dropdown');
25        this.indexmenuPatch();
26        this.typeahead();
27
28        var updateStickyItems = function () {
29            window.removeEventListener('scroll', updateStickyScroll);
30
31            var stickyElements = document.getElementsByClassName('mikio-sticky');
32            self.stickyItems = [];
33            if (stickyElements && stickyElements.length > 0) {
34                var stickyOffset = stickyElements[0].offsetTop;
35                var stickyHeightCount = stickyOffset;
36
37                [].forEach.call(stickyElements, (item) => {
38                    var top = stickyOffset;
39                    if (item.offsetTop - stickyHeightCount > stickyHeightCount) {
40                        top = stickyHeightCount;
41                    }
42
43                    self.stickyItems.push({ element: item, offsetYTop: top, debugItemTop: item.offsetTop, debugOffset: stickyOffset, debugHeight: stickyHeightCount });
44                    stickyHeightCount += item.offsetHeight;
45                });
46
47                window.addEventListener('scroll', updateStickyScroll);
48                updateStickyScroll();
49            }
50        };
51
52        var updateStickyScroll = function () {
53            self.stickyItems.forEach((item) => {
54                if (window.pageYOffset > item.offsetYTop) {
55                    if (item.element.style.position != 'fixed') {
56                        var site = document.getElementById('dokuwiki__site');
57                        site.style.paddingTop = ((parseInt(site.style.paddingTop) || 0) + item.element.offsetHeight) + 'px';
58
59                        item.element.style.position = 'fixed';
60                        item.element.style.top = self.stickyOffset + 'px';
61                        item.element.style.zIndex = self.stickyIndex;
62
63                        self.stickyOffset += item.element.offsetHeight;
64                        self.stickyIndex--;
65                    }
66                } else {
67                    if (item.element.style.position == 'fixed') {
68                        var site = document.getElementById('dokuwiki__site');
69                        site.style.paddingTop = ((parseInt(site.style.paddingTop) || 0) - item.element.offsetHeight) + 'px';
70                        self.stickyOffset -= item.element.offsetHeight;
71                        self.stickyIndex++;
72
73                        item.element.style.position = 'relative';
74                        item.element.style.top = null;
75                        item.element.style.zIndex = null;
76                    }
77                }
78            });
79        };
80
81        updateStickyItems();
82
83        window.onresize = function () {
84            if (!this.queueResize) {
85                this.queueResize = true;
86                window.setTimeout(function () {
87                    this.queueResize = false;
88                    Array.from(document.getElementsByClassName('mikio-dropdown')).forEach(function (elem) {
89                        if (!elem.classList.contains('closed')) {
90                            elem.classList.add('closed');
91                        }
92                    });
93
94                    updateStickyItems();
95                }, 100);
96            }
97        };
98
99        // Mikio-Dropdown - Click
100        Array.from(document.getElementsByClassName('mikio-dropdown')).forEach(function (elem) {
101            elem.addEventListener('click', function (event) {
102                event.stopPropagation();
103            });
104        });
105
106        // Mikio-Dropdown - Close when clicked outside dropdown
107        Array.from(document.getElementsByTagName('body')).forEach(function (elem) {
108            elem.addEventListener('click', function (event) {
109                Array.from(document.getElementsByClassName('mikio-dropdown')).forEach(function (elem) {
110                    if (!elem.classList.contains('closed')) {
111                        elem.classList.add('closed');
112                    }
113                });
114            });
115        });
116
117        // Mikio-Navbar-Toggle - Fix
118        Array.from(document.getElementsByClassName('mikio-navbar-toggle')).forEach(function (elem) {
119            elem.classList.add('closed');
120        });
121
122        // Mikio-Dropdown - Fix
123        Array.from(document.getElementsByClassName('mikio-dropdown')).forEach(function (elem) {
124            elem.classList.add('closed');
125        });
126
127        // Input File - Cleanup
128        Array.from(document.querySelectorAll('input[type=file]')).forEach(function (elem) {
129            var style = window.getComputedStyle(elem);
130
131            if (style.display != 'none') {
132                var parentElem = elem.parentElement;
133                var fileRect = elem.getBoundingClientRect();
134                var parentRect = parentElem.getBoundingClientRect();
135                var spanElem = document.createElement('span');
136
137                elem.style.opacity = 0;
138                parentElem.style.position = 'relative';
139                spanElem.innerHTML = 'Choose file...';
140                spanElem.classList.add('mikio-input-file');
141                spanElem.style.left = Math.floor(fileRect.left - parentRect.left) + 'px';
142                spanElem.style.width = Math.floor(fileRect.right - fileRect.left) + 'px';
143                mikio.insertAfter(spanElem, elem);
144
145                spanElem.addEventListener('click', function (event) {
146                    if (event.target.parentElement.tagName.toLowerCase() != 'label') {
147                        let sibling = mikio.getPrevSibling(event.target, 'input');
148                        if (typeof sibling !== 'undefined') {
149                            sibling.click();
150                        }
151                    }
152                });
153
154                elem.addEventListener('change', function () {
155                    if (this.files.length > 0) {
156                        let mikioInput = mikio.getNextSibling(this, '.mikio-input-file');
157                        if (typeof mikioInput !== 'undefined') {
158                            mikioInput.innerHTML = this.files[0].name;
159                        }
160                    }
161                });
162            }
163        });
164
165        // Input - Span (Placeholder) clear when typing
166        Array.from(document.querySelectorAll('.mikio.dokuwiki .mode_login fieldset label.block input.edit, .mikio.dokuwiki .mode_denied fieldset label.block input.edit')).forEach(function (elem) {
167            if (elem.value.length != 0) {
168                var sibling = mikio.getPrevSibling(elem, 'span');
169                if (sibling) {
170                    sibling.style.display = 'none';
171                }
172            }
173
174            elem.addEventListener('keydown', function (event) {
175                var sibling = mikio.getPrevSibling(event.target, 'span');
176
177                setTimeout(function () {
178                    if (sibling) {
179                        if (event.target.value != '') {
180                            sibling.style.display = 'none';
181                        } else {
182                            sibling.style.display = 'block';
183                        }
184                    }
185                }, 50);
186            });
187        });
188
189        // Admin - Exit button
190        Array.from(document.querySelectorAll('a[rel="exit-admin"]')).forEach(function (elem) {
191            elem.addEventListener('click', function (event) {
192                event.preventDefault();
193
194                var href = window.location.protocol + "//" + window.location.host + "/" + window.location.pathname;
195
196                var params = window.location.search;
197                if (params !== '') {
198                    params = params.substr(1).split('&');
199                    if (params.length > 1) {
200                        href += '?';
201                        params.forEach(function (p) {
202                            if (p.substring(0, 3) == 'id=') {
203                                href += p;
204                            }
205                        });
206                    }
207                }
208
209                window.location = href;
210            });
211        });
212
213        // Admin - Back button
214        Array.from(document.querySelectorAll('a[rel="exit-page"]')).forEach(function (elem) {
215            elem.addEventListener('click', function (event) {
216                event.preventDefault();
217
218                var href = window.location.protocol + "//" + window.location.host + "/" + window.location.pathname;
219
220                var params = window.location.search;
221                if (params != '') {
222                    params = params.substr(1).split('&');
223                    if (params.length > 1) {
224                        href += '?';
225                        params.forEach(function (p) {
226                            if (p.substring(0, 5) != 'page=') {
227                                href += p + '&';
228                            }
229                        });
230                    }
231                }
232
233                window.location = href;
234            });
235        });
236
237        // Admin - Resize large text blocks in tasks
238        Array.from(document.querySelectorAll('.admin_tasks span.prompt')).forEach(function (elem) {
239            if (elem.offsetHeight > 48) {
240                elem.style.fontSize = '80%';
241            }
242        });
243
244        // Media Manager - ui-resizable is always auto
245        var mediaChangedObserver = new MutationObserver(function (mutationsList) {
246            for (let mutation of mutationsList) {
247                if (mutation.type === 'childList') {
248                    if (mutation.addedNodes) {
249                        mutation.addedNodes.forEach(function (node) {
250                            if (node.nodeName == 'LI') {
251
252                            }
253                        });
254                    }
255                }
256
257                if (mutation.type === 'attributes' && mutation.attributeName == 'style' && mutation.target && mutation.target.style.height) {
258                    mutation.target.style.height = '';
259                }
260            }
261        });
262
263        var target = document.getElementById('mediamanager__page');
264        if (target) {
265            mediaChangedObserver.observe(target, { attributes: true, childList: true, subtree: true });
266        }
267
268        // Media Manager - file click
269        Array.from(document.querySelectorAll('#mediamanager__page .filelist')).forEach(function (elem) {
270            elem.addEventListener('click', function (event) {
271                var liElem = event.target.closest('li');
272                if (liElem && event.target.closest('ul.thumbs')) {
273                    var aElem = liElem.querySelector('dd.name a');
274                    if (aElem) aElem.click();
275                }
276            });
277        });
278
279        // Popup Media Manager - clean file info
280        var mediaPopupFileInfoClean = function (elem) {
281            var file = { resolution: '', date: '', time: '', size: '' };
282
283            var infoElem = elem.querySelector('span.info');
284            if (infoElem) {
285                var infoText = infoElem.innerText.replace(/(<[^>]*>|[\(\)])/g, '');
286                var detail = infoText.split(' ');
287                while (detail.length < 4) {
288                    detail.unshift('');
289                }
290
291                infoElem.innerHTML = detail[0] + '<br>' + detail[1] + ' ' + detail[2] + '<br>' + detail[3];
292            }
293
294            Array.from(elem.querySelectorAll('img')).forEach(function (elem) {
295                elem.removeAttribute('width');
296                elem.removeAttribute('height');
297            });
298        }
299
300        var mediaPopupObserver = new MutationObserver(function (mutationsList) {
301            for (let mutation of mutationsList) {
302                if (mutation.type === 'childList') {
303                    if (mutation.addedNodes) {
304                        mutation.addedNodes.forEach(function (node) {
305                            if (node.nodeName == 'DIV') {
306                                mediaPopupFileInfoClean(node);
307                            }
308                        });
309                    }
310                }
311            }
312        });
313
314        var target = document.getElementById('media__content');
315        if (target) {
316            Array.from(target.querySelectorAll('div.odd, div.even')).forEach(function (elem) {
317                mediaPopupFileInfoClean(elem);
318            });
319
320            mediaPopupObserver.observe(target, { attributes: false, childList: true });
321        }
322
323        if (typeof mikioFooterRun === "function") mikioFooterRun();
324
325        var mediaChangedObserver = new MutationObserver(function (mutationsList) {
326            for (let mutation of mutationsList) {
327                if (mutation.type === 'attributes' && mutation.attributeName == 'href') {
328                    if (self.mikioCSS != false) {
329                        var elem = self.mikioCSS;
330                        var prev = elem.href;
331
332                        setTimeout(function () {
333                            var url = new URL(prev);
334                            var params = url.searchParams;
335                            params.set('seed', new Date().getTime());
336                            url.search = params.toString();
337                            elem.href = url.toString();
338                        }, 500);
339                    }
340                }
341            }
342        });
343
344        var linkElements = document.getElementsByTagName('link');
345        for (let element of linkElements) {
346            if (element.rel == 'stylesheet' && element.href) {
347                if (element.href.includes('/lib/exe/css.php')) {
348                    mediaChangedObserver.observe(element, { attributes: true, childList: true, subtree: true });
349                } else if (element.href.includes('/lib/tpl/mikio/css.php')) {
350                    this.mikioCSS = element;
351                }
352            }
353        }
354
355        // Update color text field when selector changes
356        let colorSelectorInputs = document.querySelectorAll('div.mikio-color-picker input[type="color"]');
357        colorSelectorInputs.forEach(input => {
358            input.addEventListener('input', () => {
359                let colorTextInput = document.querySelector(`div.mikio-color-picker input[for="${input.id}"]`);
360                if (colorTextInput) {
361                    colorTextInput.value = input.value;
362                }
363            });
364        });
365
366        let colorTextInputs = document.querySelectorAll('div.mikio-color-picker input[type="text"]');
367        colorTextInputs.forEach(input => {
368            input.addEventListener('blur', () => {
369                const id = input.getAttribute('for');
370                let colorSelectorInput = document.querySelector(`div.mikio-color-picker input[id="${id}"]`);
371                if (colorSelectorInput) {
372                    let s = this.colorToHex(input.value);
373                    colorSelectorInput.value = s;
374                }
375            });
376        });
377    },
378
379    initDarkMode: function () {
380        const showLightDark = document.querySelector('.mikio-darklight-button') != null;
381        if (showLightDark == true) {
382            let setting = this.getCookie('lightDarkToggle');
383            if (setting == 'dark' || setting == 'light' || setting == 'auto') {
384                this.darkMode = setting;
385            }
386
387            if (document.querySelector('.mikio-auto-darklight') == null && setting == 'auto') {
388                this.darkMode = 'light';
389            }
390        } else {
391            if (document.querySelector('.mikio-auto-darklight') != null) {
392                this.darkMode = 'auto';
393            }
394        }
395
396        var self = this;
397
398        if (window.matchMedia) {
399            window.matchMedia('(prefers-color-scheme: dark)').addEventListener('change', (event) => {
400                self.updateDarkMode();
401            });
402        }
403
404        this.addEventListenerByClassName('mikio-darklight-button', 'click', function (event) {
405            event.preventDefault();
406            event.stopPropagation();
407
408            const autoAllowed = (document.querySelector('.mikio-iicon.mikio-darklight-auto') != null);
409
410            if (self.darkMode == 'light') {
411                self.darkMode = 'dark';
412            } else if (self.darkMode == 'dark') {
413                if (autoAllowed == true) {
414                    self.darkMode = 'auto';
415                } else {
416                    self.darkMode = 'light';
417                }
418            } else if (self.darkMode == 'auto') {
419                self.darkMode = 'light';
420            } else {
421                self.darkMode = 'dark';
422            }
423
424            self.updateDarkMode();
425        });
426
427        this.updateDarkMode();
428    },
429
430    updateDarkMode: function () {
431        const html = document.querySelector('html');
432        let themeMode = this.darkMode;
433
434        if (this.darkMode == 'auto') {
435            html.dataset.themeAuto = true;
436
437            themeMode = 'light';
438            if (window.matchMedia) {
439                let prefersColorSchemeQuery = window.matchMedia('(prefers-color-scheme: dark)');
440                themeMode = prefersColorSchemeQuery.matches ? 'dark' : 'light';
441            }
442        } else {
443            delete html.dataset.themeAuto;
444        }
445
446        html.dataset.theme = `theme-${themeMode}`;
447        this.setCookie('lightDarkToggle', this.darkMode);
448    },
449
450    insertAfter: function (newNode, existingNode) {
451        existingNode.parentNode.insertBefore(newNode, existingNode.nextSibling);
452    },
453
454    addToggleClick: function (elemToggle, elemCollapse) {
455        this.addEventListenerByClassName(elemToggle, 'click', function (event) {
456            event.preventDefault();
457            event.stopPropagation();
458            let nextSibling = mikio.getNextSibling(this, '.' + elemCollapse);
459
460            if (typeof nextSibling !== 'undefined') {
461                mikio.toggleCollapse(this, nextSibling);
462            }
463        });
464    },
465
466    addDropdownClick: function (elemToggle, elemCollapse) {
467        this.addEventListenerByClassName(elemToggle, 'click', function (event) {
468            event.preventDefault();
469            event.stopPropagation();
470
471            var dropdown = this.querySelector('.' + elemCollapse);
472            if (dropdown) {
473                mikio.toggleDropdown(dropdown);
474            }
475        });
476    },
477
478    addEventListenerByClassName: function (className, eventType, callback) {
479        Array.from(document.getElementsByClassName(className)).forEach(function (elem) {
480            elem.addEventListener(eventType, callback);
481        });
482    },
483
484    getNextSibling: function (elem, selector) {
485        var sibling = elem.nextElementSibling;
486
487        while (sibling) {
488            if (sibling.matches(selector)) return sibling;
489            sibling = sibling.nextElementSibling;
490        }
491    },
492
493    getPrevSibling: function (elem, selector) {
494        var sibling = elem.previousElementSibling;
495
496        while (sibling) {
497            if (sibling.matches(selector)) return sibling;
498            sibling = sibling.previousElementSibling;
499        }
500    },
501
502    toggleCollapse: function (objToggle, objCollapse) {
503        if (objToggle.classList.contains('closed')) {
504            objToggle.classList.remove('closed');
505            objToggle.classList.add('open');
506            var height = objCollapse.offsetHeight;
507            objCollapse.style.overflow = 'hidden';
508            objCollapse.style.height = 0;
509            objCollapse.style.paddingTop = 0;
510            objCollapse.style.paddingBottom = 0;
511            objCollapse.style.marginTop = 0;
512            objCollapse.style.marginBottom = 0;
513            objCollapse.offsetHeight;
514            objCollapse.style.boxSizing = 'border-box';
515            objCollapse.style.transitionProperty = "height, margin, padding";
516            objCollapse.style.transitionDuration = '500ms';
517            objCollapse.style.height = height + 'px';
518            objCollapse.style.removeProperty('padding-top');
519            objCollapse.style.removeProperty('padding-bottom');
520            objCollapse.style.removeProperty('margin-top');
521            objCollapse.style.removeProperty('margin-bottom');
522            window.setTimeout(function () {
523                objCollapse.style.removeProperty('height');
524                objCollapse.style.removeProperty('overflow');
525                objCollapse.style.removeProperty('transition-duration');
526                objCollapse.style.removeProperty('transition-property');
527                objCollapse.style.removeProperty('box-sizing');
528            }, 500);
529        } else {
530            objCollapse.style.transitionProperty = 'height, margin, padding';
531            objCollapse.style.transitionDuration = '500ms';
532            objCollapse.style.boxSizing = 'border-box';
533            objCollapse.style.height = objCollapse.offsetHeight + 'px';
534            objCollapse.offsetHeight;
535            objCollapse.style.overflow = 'hidden';
536            objCollapse.style.height = 0;
537            objCollapse.style.paddingTop = 0;
538            objCollapse.style.paddingBottom = 0;
539            objCollapse.style.marginTop = 0;
540            objCollapse.style.marginBottom = 0;
541            window.setTimeout(function () {
542                objToggle.classList.add('closed');
543                objToggle.classList.remove('open');
544                objCollapse.style.removeProperty('height');
545                objCollapse.style.removeProperty('padding-top');
546                objCollapse.style.removeProperty('padding-bottom');
547                objCollapse.style.removeProperty('margin-top');
548                objCollapse.style.removeProperty('margin-bottom');
549                objCollapse.style.removeProperty('overflow');
550                objCollapse.style.removeProperty('transition-duration');
551                objCollapse.style.removeProperty('transition-property');
552                objCollapse.style.removeProperty('box-sizing');
553            }, 500);
554        }
555    },
556
557
558    toggleDropdown: function (objToggle) {
559        if (objToggle.classList.contains('closed')) {
560            objToggle.classList.remove('closed');
561        } else {
562            objToggle.classList.add('closed');
563        }
564
565        Array.from(document.getElementsByClassName('mikio-dropdown')).forEach(function (elem) {
566            if (!elem.classList.contains('closed') && elem != objToggle) {
567                elem.classList.add('closed');
568            }
569        });
570    },
571
572    setHeroSubTitle: function (str) {
573        Array.from(document.getElementsByClassName('mikio-hero-subtitle')).forEach(function (elem) {
574            elem.innerHTML = str;
575        });
576    },
577
578    setHeroImage: function (str) {
579        var heroImages = document.getElementsByClassName('mikio-hero-image');
580
581        if (heroImages.length > 0) {
582            Array.from(document.getElementsByClassName('mikio-hero-image')).forEach(function (elem) {
583                elem.style.backgroundImage = 'url(\'' + str + '\')';
584                elem.classList.add('mikio-hero-image-resize');
585            });
586        } else {
587            Array.from(document.getElementsByClassName('mikio-hero-text')).forEach(function (elem) {
588                elem.insertAdjacentHTML('afterend', '<div class="mikio-hero-image mikio-hero-image-resize" style="background-image:url(\'' + str + '\');"></div>');
589            });
590        }
591    },
592
593    setHeroColor: function (str) {
594        var colors = str.trim().replace(/ +(?= )/g, '').split(/(?!\(.*)\s(?![^(]*?\))/g);
595        if (colors.length > 0 && colors[0] != '') {
596            Array.from(document.getElementsByClassName('mikio-hero')).forEach(function (elem) {
597                elem.style.backgroundColor = colors[0];
598            });
599
600            if (colors.length > 1) {
601                Array.from(document.getElementsByClassName('mikio-hero-title')).forEach(function (elem) {
602                    elem.style.color = colors[1];
603                });
604            }
605
606            if (colors.length > 2) {
607                Array.from(document.getElementsByClassName('mikio-hero-subtitle')).forEach(function (elem) {
608                    elem.style.color = colors[2];
609                });
610            }
611
612            if (colors.length > 3) {
613                Array.from(document.getElementsByClassName('mikio-hero')).forEach(function (parentElem) {
614                    Array.from(parentElem.querySelectorAll('.mikio-breadcrumbs ul li a')).forEach(function (elem) {
615                        elem.style.color = colors[3];
616                    });
617
618                    Array.from(parentElem.querySelectorAll('.mikio-breadcrumbs ul li, .mikio-breadcrumbs ul li a')).forEach(function (elem) {
619                        elem.style.color = colors[3];
620                        elem.onmouseover = function () { this.style.color = (colors.length > 4 ? colors[4] : 'initial'); };
621                        elem.onmouseout = function () { this.style.color = colors[3]; };
622                    });
623                });
624            }
625        }
626    },
627
628    setTags: function (str) {
629        Array.from(document.getElementsByClassName('mikio-tags')).forEach(function (elem) {
630            elem.innerHTML = str;
631        });
632    },
633
634    hidePart: function (part) {
635        var selectorArray = {
636            topheader: '.mikio-page-topheader',
637            header: '.mikio-page-header',
638            contentheader: '.mikio-page-contentheader',
639            contentfooter: '.mikio-page-contentfooter',
640            sidebarheader: '.mikio-sidebar-left .mikio-sidebar-header',
641            sidebarfooter: '.mikio-sidebar-left .mikio-sidebar-footer',
642            rightsidebarheader: '.mikio-sidebar-right .mikio-sidebar-header',
643            rightsidebarfooter: '.mikio-sidebar-right .mikio-sidebar-footer',
644            footer: '.mikio-footer',
645            bottomfooter: '.mikio-page-bottomfooter',
646            navbar: '.mikio-navbar',
647            hero: '.mikio-hero'
648        };
649
650        if (selectorArray.hasOwnProperty(part)) {
651            Array.from(document.querySelectorAll(selectorArray[part])).forEach(function (elem) {
652                elem.style.display = 'none';
653            });
654        }
655    },
656
657    indexmenuPatch: function () {
658        window.setTimeout(function () {
659            Array.from(document.querySelectorAll('a.navSel')).forEach(function (elem) {
660                let prev = mikio.getPrevSibling(elem, 'img');
661                if (prev) {
662                    prev.style.opacity = 1;
663                }
664            });
665        }, 50);
666
667
668        document.addEventListener('mouseover', function (event) {
669            const indexmenuClasses = ['nodeUrl', 'nodeSel', 'node'];
670            if ([...event.target.classList].some(className => indexmenuClasses.indexOf(className) !== -1)) {
671                let prev = mikio.getPrevSibling(event.target, 'img');
672                if (prev) {
673                    prev.style.opacity = 1;
674                }
675            }
676        });
677
678        document.addEventListener('mouseout', function (event) {
679            const indexmenuClasses = ['nodeUrl', 'nodeSel', 'node'];
680            if ([...event.target.classList].some(className => indexmenuClasses.indexOf(className) !== -1)) {
681                let prev = mikio.getPrevSibling(event.target, 'img');
682                if (prev) {
683                    prev.style.opacity = '';
684                }
685            }
686        });
687    },
688
689    // Add typeahead support for quick seach. Taken from bootstrap3 theme.
690    typeahead: function () {
691
692        jQuery(".search_typeahead").typeahead({
693
694            source: function (query, process) {
695
696                return jQuery.post(DOKU_BASE + 'lib/exe/ajax.php',
697                    {
698                        call: 'qsearch',
699                        q: encodeURI(query)
700                    },
701                    function (data) {
702
703                        var results = [];
704
705                        jQuery(data).find('a').each(function () {
706
707                            var page = jQuery(this);
708
709                            results.push({
710                                name: page.text(),
711                                href: page.attr('href'),
712                                title: page.attr('title'),
713                                category: page.attr('title').replace(/:/g, ' : '),
714                            });
715
716                        });
717
718                        return process(results);
719
720                    });
721            },
722
723            itemLink: function (item) {
724                return item.href;
725            },
726
727            itemTitle: function (item) {
728                return item.title;
729            },
730
731            followLinkOnSelect: true,
732            autoSelect: false,
733            items: 10,
734            fitToElement: true,
735            delay: 500,
736            theme: 'bootstrap4',
737
738        });
739    },
740
741    getCookie: function (cname) {
742        let name = cname + "=";
743        let decodedCookie = decodeURIComponent(document.cookie);
744        let ca = decodedCookie.split(';');
745        for (let i = 0; i < ca.length; i++) {
746            let c = ca[i];
747            while (c.charAt(0) == ' ') {
748                c = c.substring(1);
749            }
750            if (c.indexOf(name) == 0) {
751                return c.substring(name.length, c.length);
752            }
753        }
754        return "";
755    },
756
757    setCookie: function (cname, cvalue, exdays) {
758        const d = new Date();
759        d.setTime(d.getTime() + (exdays * 24 * 60 * 60 * 1000));
760        let expires = "expires=" + d.toUTCString();
761        document.cookie = cname + "=" + cvalue + ";" + expires + ";path=/;SameSite=Lax";
762    },
763
764    clearCookie: function (cname) {
765        document.cookie = cname + "=;expires=Thu, 01 Jan 1970 00:00:00 GMT;path=/;SameSite=Lax";
766    },
767
768    colorToHex: function (color) {
769        // Create a canvas element
770        let canvas = document.createElement('canvas');
771        canvas.height = 1;
772        canvas.width = 1;
773        let ctx = canvas.getContext('2d');
774
775        // Set the fillStyle to the color input
776        ctx.fillStyle = color;
777        ctx.fillRect(0, 0, 1, 1);
778
779        // Get the pixel data from the canvas
780        let data = ctx.getImageData(0, 0, 1, 1).data;
781
782        // Convert the RGB values to HEX
783        let hex = '#' + ((1 << 24) + (data[0] << 16) + (data[1] << 8) + data[2]).toString(16).slice(1).toUpperCase();
784
785        return hex;
786    }
787};
788
789if (document.readyState != 'loading') {
790    mikio.ready();
791} else {
792    document.addEventListener('DOMContentLoaded', function () { mikio.ready() });
793}
794