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