1function ReadtheDokus() 2{ 3 4 this._currentPage; 5 this._currentPageIndex; 6 this._pages; 7 this._toc = document.getElementById("dw__toc"); 8 this._header = document.querySelector("header"); 9 this._sidebar = document.querySelector("#dokuwiki__aside"); 10 this._delimiter = ( window.location.search.indexOf(":") > -1 ? ":" : "/"); 11 this._id = ( this._delimiter == ":" ? JSINFO["id"] : JSINFO["id"].split(":").join("/") ); 12 this._startPage = ""; 13 14} 15 16ReadtheDokus.prototype.run = function() 17{ 18 19 // Enum sidebar items to 20 // - embed toc in the corresponding sidebar item 21 // - collect all page links 22 var isFound = false; 23 this._pages = []; 24 if (JSINFO["ACT"] == "show") 25 { 26 this._enumSidebarLinks(function(elem) { 27 // Embed toc 28 if (!isFound) 29 { 30 if (elem.href.indexOf(this._id) > -1) 31 { 32 this._embedToc(elem, this._toc); 33 isFound = true; 34 } 35 } 36 37 // Collect page links 38 this._pages.push(elem.href); 39 }.bind(this)); 40 } 41 42 // Start page 43 if (this._pages.length > 0) 44 { 45 this._startPage = this._getStartPage(this._pages[0], this._delimiter); 46 this._pages.unshift(this._startPage); 47 var list = document.querySelectorAll("#sidebarheader > div.home > a, #pageheader .breadcrumbs > .home > a"); 48 var nodes = Array.prototype.slice.call(list, 0); 49 nodes.forEach(function(elem) { 50 elem.href = this._startPage; 51 }.bind(this)); 52 } 53 54 // Show toc on top of sidebar if item was not found in sidebar 55 if (!isFound && this._toc) 56 { 57 this._showToc(this._toc); 58 this.showSidebar(); 59 } 60 61 this._initToc(this._toc); 62 this._initMobileHeader(); 63 this._initPageButtons(); 64 this._sidebar.querySelector("#sidebarheader #qsearch__in").setAttribute("placeholder", "Search docs"); 65 66 if (this._toc) 67 { 68 this._toc.scrollIntoView(true); 69 } 70 71}; 72 73ReadtheDokus.prototype.getMediaQuery = function(elem) 74{ 75 76 return getComputedStyle(document.querySelector("#__media_query")).getPropertyValue("--media-query").trim() || getComputedStyle(document.querySelector("#__media_query"))["-media-query"].trim(); 77 78}; 79 80ReadtheDokus.prototype.toggleTocMenu = function(elem) 81{ 82 83 var invisible = elem.parentNode.querySelector(".toc").classList.contains("invisible"); 84 if (invisible) 85 { 86 this.expandTocMenu(elem); 87 } 88 else 89 { 90 this.collapseTocMenu(elem); 91 } 92 93}; 94 95ReadtheDokus.prototype.expandTocMenu = function(elem, allChildren) 96{ 97 98 if (elem && elem.classList.contains("expandable")) 99 { 100 elem.parentNode.querySelector(".toc").classList.remove("invisible"); 101 102 var i = elem.children[0].children[0].children[0]; 103 i.classList.remove("fa-plus-square"); 104 i.classList.add("fa-minus-square"); 105 106 var img = elem.children[0].children[0].children[1]; 107 img.classList.remove("plus"); 108 img.classList.add("minus"); 109 img.src= DOKU_BASE + "lib/images/minus.gif"; 110 } 111 112}; 113 114ReadtheDokus.prototype.collapseTocMenu = function(elem, allChildren) 115{ 116 117 if (elem && elem.classList.contains("expandable")) 118 { 119 elem.parentNode.querySelector(".toc").classList.add("invisible"); 120 121 var i = elem.children[0].children[0].children[0]; 122 i.classList.remove("fa-minus-square"); 123 i.classList.add("fa-plus-square"); 124 125 var img = elem.children[0].children[0].children[1]; 126 img.classList.remove("minus"); 127 img.classList.add("plus"); 128 img.src=DOKU_BASE + "lib/images/plus.gif"; 129 } 130 131}; 132 133ReadtheDokus.prototype.toggleSidebar = function(elem) 134{ 135 136 var mq = this.getMediaQuery(); 137 if (mq == "pc" || mq == "tb") 138 { 139 document.querySelector("#dokuwiki__site").classList.toggle("showSidebar"); 140 } 141 else 142 { 143 document.querySelector("#dokuwiki__site").classList.toggle("showSidebarSP"); 144 } 145 146}; 147 148ReadtheDokus.prototype.showSidebar = function(elem) 149{ 150 151 var mq = this.getMediaQuery(); 152 if (mq == "pc" || mq == "tb") 153 { 154 document.querySelector("#dokuwiki__site").classList.add("showSidebar"); 155 } 156 else 157 { 158 document.querySelector("#dokuwiki__site").classList.add("showSidebarSP"); 159 } 160 161}; 162 163ReadtheDokus.prototype.hideSidebar = function(elem) 164{ 165 166 if (dokus.getMediaQuery() == "pc" || dokus.getMediaQuery() == "tb") 167 { 168 document.querySelector("#dokuwiki__site").classList.remove("showSidebar"); 169 } 170 else 171 { 172 document.querySelector("#dokuwiki__site").classList.remove("showSidebarSP"); 173 } 174 175}; 176 177 178ReadtheDokus.prototype._enumSidebarLinks = function(callback) 179{ 180 181 callback = ( typeof callback === "function" ? callback : function(){} ); 182 var links = this._sidebar.querySelectorAll(".aside > #sidebar > ul .level1 a"); 183 var nodes = Array.prototype.slice.call(links, 0); 184 nodes.forEach(function(elem) { 185 callback(elem); 186 }); 187 188}; 189 190ReadtheDokus.prototype._getStartPage = function(basePage, delimiter) 191{ 192 193 var result = ""; 194 195 if (basePage && delimiter) 196 { 197 var re = new RegExp("\\" + delimiter + "[^\\" + delimiter + "]*[^\\" + delimiter + "]*$"); 198 result = basePage.replace(re, "").replace(re, "") + delimiter + "start"; 199 200 } 201 202 return result; 203 204}; 205 206ReadtheDokus.prototype._embedToc = function(target, toc) 207{ 208 209 if (target && toc) 210 { 211 target.parentNode.parentNode.appendChild(toc); 212 target.parentNode.style.display = "none"; 213 } 214 215}; 216 217ReadtheDokus.prototype._showToc = function(toc) 218{ 219 220 if (toc) 221 { 222 this._toc.parentNode.style.display = "block"; 223 } 224 225}; 226 227ReadtheDokus.prototype._initToc = function(toc) 228{ 229 230 if (toc) 231 { 232 this._installTocSelectHandler(); 233 this._installTocMenuHandler(); 234 this._installTocJumpHandler(); 235 } 236 237}; 238 239// Install click handler to highlight and expand toc menu 240ReadtheDokus.prototype._installTocSelectHandler = function() 241{ 242 243 var list = this._toc.querySelectorAll(".level1 div.li"); 244 var nodes = Array.prototype.slice.call(list, 0); 245 nodes.forEach(function(elem) { 246 elem.addEventListener("click", function() { 247 // Get level2 parent 248 let p = this._getParent(elem, "level2"); 249 250 // Remove all current 251 var list2 = this._toc.querySelectorAll(".current"); 252 var nodes2 = Array.prototype.slice.call(list2, 0); 253 nodes2.forEach(function(elem) { 254 elem.classList.remove("current"); 255 }); 256 257 // Set current to this and level2 parent 258 if (p) 259 { 260 p.parentNode.classList.add("current"); 261 p.classList.add("current"); 262 elem.classList.add("current"); 263 elem.scrollIntoView(true); 264 } 265 266 // Expand 267 this.expandTocMenu(elem); 268 269 // Fold the other level2 items 270 var list3 = this._toc.querySelectorAll(".level2 > div.li.expandable"); 271 var nodes3 = Array.prototype.slice.call(list3, 0); 272 nodes3.forEach(function(item) { 273 if (item != p) 274 { 275 this.collapseTocMenu(item); 276 } 277 }.bind(this)); 278 }.bind(this)); 279 }.bind(this)); 280 281}; 282 283// Install click handler to expand/collapse toc menu 284ReadtheDokus.prototype._installTocMenuHandler = function() 285{ 286 287 // Search for toc menu items which have children 288 var list = this._toc.querySelectorAll("div.li"); 289 var nodes = Array.prototype.slice.call(list, 0); 290 nodes.forEach(function(elem) { 291 if (elem.parentNode.querySelector(".toc")) 292 { 293 elem.classList.add("expandable"); 294 295 // Insert +/- fontawesome icon and image 296 elem.children[0].insertAdjacentHTML("afterbegin", '<div class="btn-expand"><i class="far fa-minus-square"></i><img class="minus" src="' + DOKU_BASE + 'lib/images/minus.gif" alt="−"></div>'); 297 298 // Install click handler 299 elem.children[0].children[0].addEventListener("click", function(e) { 300 this.toggleTocMenu(elem); 301 302 e.stopPropagation(); 303 e.preventDefault(); 304 }.bind(this)); 305 306 // Only level1 menu items are open at start 307 if (!elem.parentNode.classList.contains("level1")) 308 { 309 this.collapseTocMenu(elem); 310 } 311 } 312 313 // Install click handler to move an clicked item to top 314 elem.addEventListener("click", function() { 315 elem.scrollIntoView(true); 316 }); 317 }.bind(this)); 318 319}; 320 321// Install click handler to jump to anchor taking fixed header into account 322ReadtheDokus.prototype._installTocJumpHandler = function() 323{ 324 325 var headerHeight = this._header.offsetHeight; 326 var list = this._toc.querySelectorAll('a[href*="#"]'); 327 var nodes = Array.prototype.slice.call(list, 0); 328 nodes.forEach(function(elem){ 329 elem.addEventListener("click", function(e) { 330 var hash = elem.getAttribute("href"); 331 var target = document.querySelector(hash); 332 if (target) 333 { 334 if (dokus.getMediaQuery() == "sp") 335 { 336 this.hideSidebar(); 337 } 338 339 var top = target.getBoundingClientRect().top; 340 window.scrollTo(0, window.pageYOffset + top - headerHeight); 341 } 342 343 e.preventDefault(); 344 return false; 345 }.bind(this)); 346 }.bind(this)); 347 348}; 349ReadtheDokus.prototype._getParent = function(elem, level) 350{ 351 352 let current = elem.parentNode; 353 354 while (current && !current.classList.contains("level1")) 355 { 356 if (current.classList.contains(level)) 357 { 358 return current.children[0]; 359 } 360 361 current = current.parentNode.parentNode; 362 } 363 364 return null; 365 366}; 367 368ReadtheDokus.prototype._initMobileHeader = function() 369{ 370 371 // Add click event handler for mobile menu 372 document.getElementById("btn-mobilemenu").addEventListener("click", function(){ 373 this.toggleSidebar(); 374 }.bind(this)); 375 376}; 377 378ReadtheDokus.prototype._initPageButtons = function() 379{ 380 381 // Get current page (remove hash) 382 this._currentPage = window.location.href.replace(/#.*$/, ""); 383 384 // Get current page index 385 this._currentPageIndex = this._pages.indexOf(this._currentPage); 386 387 // Show prev button 388 if (this._currentPageIndex > 0) 389 { 390 document.getElementById("btn-prevpage").classList.remove("invisible"); 391 document.getElementById("btn-prevpage").href = this._pages[this._currentPageIndex - 1]; 392 } 393 394 // Show next button 395 if (this._currentPageIndex > -1 && this._currentPageIndex < this._pages.length - 1) 396 { 397 document.getElementById("btn-nextpage").classList.remove("invisible"); 398 document.getElementById("btn-nextpage").href = this._pages[this._currentPageIndex + 1]; 399 } 400 401}; 402