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