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