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