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