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