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