xref: /template/mikio/assets/mikio.js (revision 1293ce10f0629d2b03bd589124200ed1fca8794d)
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
14    ready: function () {
15        var self = this;
16
17        this.addToggleClick('mikio-sidebar-toggle', 'mikio-sidebar-collapse');
18        this.addToggleClick('mikio-navbar-toggle', 'mikio-navbar-collapse');
19        this.addDropdownClick('mikio-nav-dropdown', 'mikio-dropdown');
20        this.indexmenuPatch();
21
22        window.onresize = function () {
23            if (!this.queueResize) {
24                this.queueResize = true;
25                window.setTimeout(function () {
26                    this.queueResize = false;
27                    Array.from(document.getElementsByClassName('mikio-dropdown')).forEach(function (elem) {
28                        if (!elem.classList.contains('closed')) {
29                            elem.classList.add('closed');
30                        }
31                    });
32                }, 100);
33            }
34        };
35
36        // Mikio-Dropdown - Click
37        Array.from(document.getElementsByClassName('mikio-dropdown')).forEach(function (elem) {
38            elem.addEventListener('click', function (event) {
39                event.stopPropagation();
40            });
41        });
42
43        // Mikio-Dropdown - Close when clicked outside dropdown
44        Array.from(document.getElementsByTagName('body')).forEach(function (elem) {
45            elem.addEventListener('click', function (event) {
46                Array.from(document.getElementsByClassName('mikio-dropdown')).forEach(function (elem) {
47                    if (!elem.classList.contains('closed')) {
48                        elem.classList.add('closed');
49                    }
50                });
51            });
52        });
53
54        // Mikio-Navbar-Toggle - Fix
55        Array.from(document.getElementsByClassName('mikio-navbar-toggle')).forEach(function (elem) {
56            elem.classList.add('closed');
57        });
58
59        // Mikio-Dropdown - Fix
60        Array.from(document.getElementsByClassName('mikio-dropdown')).forEach(function (elem) {
61            elem.classList.add('closed');
62        });
63
64        // Input File - Cleanup
65        Array.from(document.querySelectorAll('input[type=file]')).forEach(function (elem) {
66            var style = window.getComputedStyle(elem);
67
68            if (style.display != 'none') {
69                var parentElem = elem.parentElement;
70                var fileRect = elem.getBoundingClientRect();
71                var parentRect = parentElem.getBoundingClientRect();
72                var spanElem = document.createElement('span');
73
74                elem.style.opacity = 0;
75                parentElem.style.position = 'relative';
76                spanElem.innerHTML = 'Choose file...';
77                spanElem.classList.add('mikio-input-file');
78                spanElem.style.left = Math.floor(fileRect.left - parentRect.left) + 'px';
79                spanElem.style.width = Math.floor(fileRect.right - fileRect.left) + 'px';
80                mikio.insertAfter(spanElem, elem);
81
82                spanElem.addEventListener('click', function (event) {
83                    if (event.target.parentElement.tagName.toLowerCase() != 'label') {
84                        let sibling = mikio.getPrevSibling(event.target, 'input');
85                        if (typeof sibling !== 'undefined') {
86                            sibling.click();
87                        }
88                    }
89                });
90
91                elem.addEventListener('change', function () {
92                    if (this.files.length > 0) {
93                        let mikioInput = mikio.getNextSibling(this, '.mikio-input-file');
94                        if (typeof mikioInput !== 'undefined') {
95                            mikioInput.innerHTML = this.files[0].name;
96                        }
97                    }
98                });
99            }
100        });
101
102        // Input - Span (Placeholder) clear when typing
103        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) {
104            if (elem.value.length != 0) {
105                var sibling = mikio.getPrevSibling(elem, 'span');
106                if (sibling) {
107                    sibling.style.display = 'none';
108                }
109            }
110
111            elem.addEventListener('keydown', function (event) {
112                var sibling = mikio.getPrevSibling(event.target, 'span');
113
114                setTimeout(function () {
115                    if (sibling) {
116                        if (event.target.value != '') {
117                            sibling.style.display = 'none';
118                        } else {
119                            sibling.style.display = 'block';
120                        }
121                    }
122                }, 50);
123            });
124        });
125
126        // Admin - Exit button
127        Array.from(document.querySelectorAll('a[rel="exit-admin"]')).forEach(function (elem) {
128            elem.addEventListener('click', function (event) {
129                event.preventDefault();
130
131                var href = window.location.protocol + "//" + window.location.host + "/" + window.location.pathname;
132
133                var params = window.location.search;
134                if (params !== '') {
135                    params = params.substr(1).split('&');
136                    if (params.length > 1) {
137                        href += '?';
138                        params.forEach(function (p) {
139                            if (p.substring(0, 3) == 'id=') {
140                                href += p;
141                            }
142                        });
143                    }
144                }
145
146                window.location = href;
147            });
148        });
149
150        // Admin - Back button
151        Array.from(document.querySelectorAll('a[rel="exit-page"]')).forEach(function (elem) {
152            elem.addEventListener('click', function (event) {
153                event.preventDefault();
154
155                var href = window.location.protocol + "//" + window.location.host + "/" + window.location.pathname;
156
157                var params = window.location.search;
158                if (params != '') {
159                    params = params.substr(1).split('&');
160                    if (params.length > 1) {
161                        href += '?';
162                        params.forEach(function (p) {
163                            if (p.substring(0, 5) != 'page=') {
164                                href += p + '&';
165                            }
166                        });
167                    }
168                }
169
170                window.location = href;
171            });
172        });
173
174        // Admin - Resize large text blocks in tasks
175        Array.from(document.querySelectorAll('.admin_tasks span.prompt')).forEach(function (elem) {
176            if (elem.offsetHeight > 48) {
177                elem.style.fontSize = '80%';
178            }
179        });
180
181        // Media Manager - ui-resizable is always auto
182        var mediaChangedObserver = new MutationObserver(function (mutationsList) {
183            for (let mutation of mutationsList) {
184                if (mutation.type === 'childList') {
185                    if (mutation.addedNodes) {
186                        mutation.addedNodes.forEach(function (node) {
187                            if (node.nodeName == 'LI') {
188
189                            }
190                        });
191                    }
192                }
193
194                if (mutation.type === 'attributes' && mutation.attributeName == 'style' && mutation.target && mutation.target.style.height) {
195                    mutation.target.style.height = '';
196                }
197            }
198        });
199
200        var target = document.getElementById('mediamanager__page');
201        if (target) {
202            mediaChangedObserver.observe(target, { attributes: true, childList: true, subtree: true });
203        }
204
205        // Media Manager - file click
206        Array.from(document.querySelectorAll('#mediamanager__page .filelist')).forEach(function (elem) {
207            elem.addEventListener('click', function (event) {
208                var liElem = event.target.closest('li');
209                if (liElem && event.target.closest('ul.thumbs')) {
210                    var aElem = liElem.querySelector('dd.name a');
211                    if (aElem) aElem.click();
212                }
213            });
214        });
215
216        // Popup Media Manager - clean file info
217        var mediaPopupFileInfoClean = function (elem) {
218            var file = { resolution: '', date: '', time: '', size: '' };
219
220            var infoElem = elem.querySelector('span.info');
221            if (infoElem) {
222                var infoText = infoElem.innerText.replace(/(<[^>]*>|[\(\)])/g, '');
223                var detail = infoText.split(' ');
224                while (detail.length < 4) {
225                    detail.unshift('');
226                }
227
228                infoElem.innerHTML = detail[0] + '<br>' + detail[1] + ' ' + detail[2] + '<br>' + detail[3];
229            }
230
231            Array.from(elem.querySelectorAll('img')).forEach(function (elem) {
232                elem.removeAttribute('width');
233                elem.removeAttribute('height');
234            });
235        }
236
237        var mediaPopupObserver = new MutationObserver(function (mutationsList) {
238            for (let mutation of mutationsList) {
239                if (mutation.type === 'childList') {
240                    if (mutation.addedNodes) {
241                        mutation.addedNodes.forEach(function (node) {
242                            if (node.nodeName == 'DIV') {
243                                mediaPopupFileInfoClean(node);
244                            }
245                        });
246                    }
247                }
248            }
249        });
250
251        var target = document.getElementById('media__content');
252        if (target) {
253            Array.from(target.querySelectorAll('div.odd, div.even')).forEach(function (elem) {
254                mediaPopupFileInfoClean(elem);
255            });
256
257            mediaPopupObserver.observe(target, { attributes: false, childList: true });
258        }
259
260        if (typeof mikioFooterRun === "function") mikioFooterRun();
261
262        // TESTING
263
264        var mediaChangedObserver = new MutationObserver(function (mutationsList) {
265            for (let mutation of mutationsList) {
266                if (mutation.type === 'attributes' && mutation.attributeName == 'href') {
267                    if (self.mikioCSS != false) {
268                        var elem = self.mikioCSS;
269                        var prev = elem.href;
270
271                        setTimeout(function () {
272                            var url = new URL(prev);
273                            var params = url.searchParams;
274                            params.set('seed', new Date().getTime());
275                            url.search = params.toString();
276                            elem.href = url.toString();
277                        }, 500);
278                    }
279                }
280            }
281        });
282
283        var linkElements = document.getElementsByTagName('link');
284        for (let element of linkElements) {
285            if (element.rel == 'stylesheet' && element.href) {
286                if (element.href.includes('/lib/exe/css.php')) {
287                    mediaChangedObserver.observe(element, { attributes: true, childList: true, subtree: true });
288                } else if (element.href.includes('/lib/tpl/mikio/css.php')) {
289                    this.mikioCSS = element;
290                }
291            }
292        }
293    },
294
295    insertAfter: function (newNode, existingNode) {
296        existingNode.parentNode.insertBefore(newNode, existingNode.nextSibling);
297    },
298
299    addToggleClick: function (elemToggle, elemCollapse) {
300        this.addEventListenerByClassName(elemToggle, 'click', function (event) {
301            event.preventDefault();
302            event.stopPropagation();
303            let nextSibling = mikio.getNextSibling(this, '.' + elemCollapse);
304
305            if (typeof nextSibling !== 'undefined') {
306                mikio.toggleCollapse(this, nextSibling);
307            }
308        });
309    },
310
311    addDropdownClick: function (elemToggle, elemCollapse) {
312        this.addEventListenerByClassName(elemToggle, 'click', function (event) {
313            event.preventDefault();
314            event.stopPropagation();
315
316            var dropdown = this.querySelector('.' + elemCollapse);
317            if (dropdown) {
318                mikio.toggleDropdown(dropdown);
319            }
320        });
321    },
322
323    addEventListenerByClassName: function (className, eventType, callback) {
324        Array.from(document.getElementsByClassName(className)).forEach(function (elem) {
325            elem.addEventListener(eventType, callback);
326        });
327    },
328
329    getNextSibling: function (elem, selector) {
330        var sibling = elem.nextElementSibling;
331
332        while (sibling) {
333            if (sibling.matches(selector)) return sibling;
334            sibling = sibling.nextElementSibling;
335        }
336    },
337
338    getPrevSibling: function (elem, selector) {
339        var sibling = elem.previousElementSibling;
340
341        while (sibling) {
342            if (sibling.matches(selector)) return sibling;
343            sibling = sibling.previousElementSibling;
344        }
345    },
346
347    toggleCollapse: function (objToggle, objCollapse) {
348        if (objToggle.classList.contains('closed')) {
349            objToggle.classList.remove('closed');
350            objToggle.classList.add('open');
351            var height = objCollapse.offsetHeight;
352            objCollapse.style.overflow = 'hidden';
353            objCollapse.style.height = 0;
354            objCollapse.style.paddingTop = 0;
355            objCollapse.style.paddingBottom = 0;
356            objCollapse.style.marginTop = 0;
357            objCollapse.style.marginBottom = 0;
358            objCollapse.offsetHeight;
359            objCollapse.style.boxSizing = 'border-box';
360            objCollapse.style.transitionProperty = "height, margin, padding";
361            objCollapse.style.transitionDuration = '500ms';
362            objCollapse.style.height = height + 'px';
363            objCollapse.style.removeProperty('padding-top');
364            objCollapse.style.removeProperty('padding-bottom');
365            objCollapse.style.removeProperty('margin-top');
366            objCollapse.style.removeProperty('margin-bottom');
367            window.setTimeout(function () {
368                objCollapse.style.removeProperty('height');
369                objCollapse.style.removeProperty('overflow');
370                objCollapse.style.removeProperty('transition-duration');
371                objCollapse.style.removeProperty('transition-property');
372                objCollapse.style.removeProperty('box-sizing');
373            }, 500);
374        } else {
375            objCollapse.style.transitionProperty = 'height, margin, padding';
376            objCollapse.style.transitionDuration = '500ms';
377            objCollapse.style.boxSizing = 'border-box';
378            objCollapse.style.height = objCollapse.offsetHeight + 'px';
379            objCollapse.offsetHeight;
380            objCollapse.style.overflow = 'hidden';
381            objCollapse.style.height = 0;
382            objCollapse.style.paddingTop = 0;
383            objCollapse.style.paddingBottom = 0;
384            objCollapse.style.marginTop = 0;
385            objCollapse.style.marginBottom = 0;
386            window.setTimeout(function () {
387                objToggle.classList.add('closed');
388                objToggle.classList.remove('open');
389                objCollapse.style.removeProperty('height');
390                objCollapse.style.removeProperty('padding-top');
391                objCollapse.style.removeProperty('padding-bottom');
392                objCollapse.style.removeProperty('margin-top');
393                objCollapse.style.removeProperty('margin-bottom');
394                objCollapse.style.removeProperty('overflow');
395                objCollapse.style.removeProperty('transition-duration');
396                objCollapse.style.removeProperty('transition-property');
397                objCollapse.style.removeProperty('box-sizing');
398            }, 500);
399        }
400    },
401
402
403    toggleDropdown: function (objToggle) {
404        if (objToggle.classList.contains('closed')) {
405            objToggle.classList.remove('closed');
406        } else {
407            objToggle.classList.add('closed');
408        }
409
410        Array.from(document.getElementsByClassName('mikio-dropdown')).forEach(function (elem) {
411            if (!elem.classList.contains('closed') && elem != objToggle) {
412                elem.classList.add('closed');
413            }
414        });
415    },
416
417    setHeroSubTitle: function (str) {
418        Array.from(document.getElementsByClassName('mikio-hero-subtitle')).forEach(function (elem) {
419            elem.innerHTML = str;
420        });
421    },
422
423    setHeroImage: function (str) {
424        var heroImages = document.getElementsByClassName('mikio-hero-image');
425
426        if (heroImages.length > 0) {
427            Array.from(document.getElementsByClassName('mikio-hero-image')).forEach(function (elem) {
428                elem.style.backgroundImage = 'url(\'' + str + '\')';
429                elem.classList.add('mikio-hero-image-resize');
430            });
431        } else {
432            Array.from(document.getElementsByClassName('mikio-hero-text')).forEach(function (elem) {
433                elem.insertAdjacentHTML('afterend', '<div class="mikio-hero-image mikio-hero-image-resize" style="background-image:url(\'' + str + '\');"></div>');
434            });
435        }
436    },
437
438    setHeroColor: function (str) {
439        var colors = str.trim().replace(/ +(?= )/g, '').split(/(?!\(.*)\s(?![^(]*?\))/g);
440        if (colors.length > 0 && colors[0] != '') {
441            Array.from(document.getElementsByClassName('mikio-hero')).forEach(function (elem) {
442                elem.style.backgroundColor = colors[0];
443            });
444
445            if (colors.length > 1) {
446                Array.from(document.getElementsByClassName('mikio-hero-title')).forEach(function (elem) {
447                    elem.style.color = colors[1];
448                });
449            }
450
451            if (colors.length > 2) {
452                Array.from(document.getElementsByClassName('mikio-hero-subtitle')).forEach(function (elem) {
453                    elem.style.color = colors[2];
454                });
455            }
456
457            if (colors.length > 3) {
458                Array.from(document.getElementsByClassName('mikio-hero')).forEach(function (parentElem) {
459                    Array.from(parentElem.querySelectorAll('.mikio-breadcrumbs ul li a')).forEach(function (elem) {
460                        elem.style.color = colors[3];
461                    });
462
463                    Array.from(parentElem.querySelectorAll('.mikio-breadcrumbs ul li, .mikio-breadcrumbs ul li a')).forEach(function (elem) {
464                        elem.style.color = colors[3];
465                        elem.onmouseover = function () { this.style.color = (colors.length > 4 ? colors[4] : 'initial'); };
466                        elem.onmouseout = function () { this.style.color = colors[3]; };
467                    });
468                });
469            }
470        }
471    },
472
473    setTags: function (str) {
474        Array.from(document.getElementsByClassName('mikio-tags')).forEach(function (elem) {
475            elem.innerHTML = str;
476        });
477    },
478
479    hidePart: function (part) {
480        var selectorArray = {
481            topheader: '.mikio-page-topheader',
482            header: '.mikio-page-header',
483            contentheader: '.mikio-page-contentheader',
484            contentfooter: '.mikio-page-contentfooter',
485            sidebarheader: '.mikio-sidebar-left .mikio-sidebar-header',
486            sidebarfooter: '.mikio-sidebar-left .mikio-sidebar-footer',
487            rightsidebarheader: '.mikio-sidebar-right .mikio-sidebar-header',
488            rightsidebarfooter: '.mikio-sidebar-right .mikio-sidebar-footer',
489            footer: '.mikio-footer',
490            bottomfooter: '.mikio-page-bottomfooter',
491            navbar: '.mikio-navbar',
492            hero: '.mikio-hero'
493        };
494
495        if (selectorArray.hasOwnProperty(part)) {
496            Array.from(document.querySelectorAll(selectorArray[part])).forEach(function (elem) {
497                elem.style.display = 'none';
498            });
499        }
500    },
501
502    indexmenuPatch: function () {
503        window.setTimeout(function () {
504            Array.from(document.querySelectorAll('a.navSel')).forEach(function (elem) {
505                let prev = mikio.getPrevSibling(elem, 'img');
506                if (prev) {
507                    prev.style.opacity = 1;
508                }
509            });
510        }, 50);
511
512
513        document.addEventListener('mouseover', function (event) {
514            const indexmenuClasses = ['nodeUrl', 'nodeSel', 'node'];
515            if ([...event.target.classList].some(className => indexmenuClasses.indexOf(className) !== -1)) {
516                let prev = mikio.getPrevSibling(event.target, 'img');
517                if (prev) {
518                    prev.style.opacity = 1;
519                }
520            }
521        });
522
523        document.addEventListener('mouseout', function (event) {
524            const indexmenuClasses = ['nodeUrl', 'nodeSel', 'node'];
525            if ([...event.target.classList].some(className => indexmenuClasses.indexOf(className) !== -1)) {
526                let prev = mikio.getPrevSibling(event.target, 'img');
527                if (prev) {
528                    prev.style.opacity = '';
529                }
530            }
531        });
532    },
533};
534
535
536if (document.readyState != 'loading') {
537    mikio.ready();
538} else {
539    document.addEventListener('DOMContentLoaded', function () { mikio.ready() });
540}
541