xref: /template/readthedokus/js/readthedokus.js (revision cec625de86a310574ac853808127db304607676f)
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.toggleSidebar = function(elem)
130{
131
132	if (dokus.getMediaQuery() == "pc" || dokus.getMediaQuery() == "tb")
133	{
134		document.querySelector("#dokuwiki__site").classList.toggle("showSidebar");
135	}
136	else
137	{
138		document.querySelector("#dokuwiki__site").classList.toggle("showSidebarSP");
139	}
140
141}
142
143ReadtheDokus.prototype.showSidebar = function(elem)
144{
145
146	if (dokus.getMediaQuery() == "pc" || dokus.getMediaQuery() == "tb")
147	{
148		document.querySelector("#dokuwiki__site").classList.add("showSidebar");
149	}
150	else
151	{
152		document.querySelector("#dokuwiki__site").classList.add("showSidebarSP");
153	}
154
155}
156
157ReadtheDokus.prototype.hideSidebar = function(elem)
158{
159
160	if (dokus.getMediaQuery() == "pc" || dokus.getMediaQuery() == "tb")
161	{
162		document.querySelector("#dokuwiki__site").classList.remove("showSidebar");
163	}
164	else
165	{
166		document.querySelector("#dokuwiki__site").classList.remove("showSidebarSP");
167	}
168
169}
170
171
172ReadtheDokus.prototype._enumSidebarLinks = function(callback)
173{
174
175	callback = ( typeof callback === "function" ? callback : function(){} );
176	var links = this._sidebar.querySelectorAll(".aside > ul .level1 a");
177	var nodes = Array.prototype.slice.call(links, 0);
178	nodes.forEach(function(elem) {
179		callback(elem);
180	});
181
182};
183
184ReadtheDokus.prototype._getStartPage = function(basePage, delimiter)
185{
186
187	var result = "";
188
189	if (basePage && delimiter)
190	{
191		var re = new RegExp("\\" + delimiter + "[^\\" + delimiter + "]*[^\\" + delimiter + "]*$");
192		result = basePage.replace(re, "").replace(re, "") + delimiter + "start";
193	}
194
195	return result;
196
197};
198
199ReadtheDokus.prototype._embedToc = function(target, toc)
200{
201
202	if (target && toc)
203	{
204		target.parentNode.parentNode.appendChild(toc);
205		target.parentNode.style.display = "none";
206	}
207
208};
209
210ReadtheDokus.prototype._showToc = function(toc)
211{
212
213	if (toc)
214	{
215		this._toc.parentNode.style.display = "block";
216	}
217
218};
219
220ReadtheDokus.prototype._initToc = function(toc)
221{
222
223	if (toc)
224	{
225		this._installTocSelectHandler();
226		this._installTocMenuHandler();
227		this._installTocJumpHandler();
228	}
229
230};
231
232// Install click handler to highlight and expand toc menu
233ReadtheDokus.prototype._installTocSelectHandler = function()
234{
235
236	var list = this._toc.querySelectorAll(".level1 div.li");
237	var nodes = Array.prototype.slice.call(list, 0);
238	nodes.forEach(function(elem) {
239		elem.addEventListener("click", function() {
240			// Get level2 parent
241			let p = this._getParent(elem, "level2");
242
243			// Remove all current
244			var list2 = this._toc.querySelectorAll(".current");
245			var nodes2 = Array.prototype.slice.call(list2, 0);
246			nodes2.forEach(function(elem) {
247				elem.classList.remove("current");
248			});
249
250			// Set current to this and level2 parent
251			if (p)
252			{
253				p.parentNode.classList.add("current");
254				p.classList.add("current");
255				elem.classList.add("current");
256				elem.scrollIntoView(true);
257			}
258
259			// Expand
260			this.expandTocMenu(elem);
261
262			// Fold the other level2 items
263			var list3 = this._toc.querySelectorAll(".level2 > div.li.expandable");
264			var nodes3 = Array.prototype.slice.call(list3, 0);
265			nodes3.forEach(function(item) {
266				if (item != p)
267				{
268					this.collapseTocMenu(item);
269				}
270			}.bind(this));
271		}.bind(this));
272	}.bind(this));
273
274};
275
276// Install click handler to expand/collapse toc menu
277ReadtheDokus.prototype._installTocMenuHandler = function()
278{
279
280	// Search for toc menu items which have children
281	var list = this._toc.querySelectorAll("div.li");
282	var nodes = Array.prototype.slice.call(list, 0);
283	nodes.forEach(function(elem) {
284		if (elem.parentNode.querySelector(".toc"))
285		{
286			elem.classList.add("expandable");
287
288			// Insert +/- fontawesome icon and image
289			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>');
290
291			// Install click handler
292			elem.children[0].children[0].addEventListener("click", function(e) {
293				this.toggleTocMenu(elem);
294
295				e.stopPropagation();
296				e.preventDefault();
297			}.bind(this));
298
299			// Only level1 menu items are open at start
300			if (!elem.parentNode.classList.contains("level1"))
301			{
302				this.collapseTocMenu(elem);
303			}
304		}
305
306		// Install click handler to move an clicked item to top
307		elem.addEventListener("click", function() {
308			elem.scrollIntoView(true);
309		});
310	}.bind(this));
311
312};
313
314// Install click handler to jump to anchor taking fixed header into account
315ReadtheDokus.prototype._installTocJumpHandler = function()
316{
317
318	var headerHight = this._header.height;
319	var list = this._toc.querySelectorAll('a[href*="#"]');
320	var nodes = Array.prototype.slice.call(list, 0);
321	nodes.forEach(function(elem){
322		elem.addEventListener("click", function(e) {
323			var hash = elem.getAttribute("href");
324			var target = document.querySelector(hash);
325			if (target)
326			{
327				var top = target.getBoundingClientRect().top;
328				window.scrollTo({top:window.pageYOffset + top - 50});
329			}
330
331			e.preventDefault();
332			return false;
333		});
334	});
335
336};
337ReadtheDokus.prototype._getParent = function(elem, level)
338{
339
340	let current = elem.parentNode;
341
342	while (current && !current.classList.contains("level1"))
343	{
344		if (current.classList.contains(level))
345		{
346			return current.children[0];
347		}
348
349		current = current.parentNode.parentNode;
350	}
351
352	return null;
353
354};
355
356ReadtheDokus.prototype._initMobileHeader = function()
357{
358
359	// Add click event handler for mobile menu
360	document.getElementById("btn-mobilemenu").addEventListener("click", function(){
361		this.toggleSidebar();
362	}.bind(this));
363
364};
365
366ReadtheDokus.prototype._initPageButtons = function()
367{
368
369	// Get current page (remove hash)
370	this._currentPage = window.location.href.replace(/#.*$/, "");
371
372	// Get current page index
373	this._currentPageIndex = this._pages.indexOf(this._currentPage);
374
375	// Show prev button
376	if (this._currentPageIndex > 0)
377	{
378		document.getElementById("btn-prevpage").classList.add("visible");
379		document.getElementById("btn-prevpage").href = this._pages[this._currentPageIndex - 1];
380	}
381
382	// Show next button
383	if (this._currentPageIndex > -1 && this._currentPageIndex < this._pages.length - 1)
384	{
385		document.getElementById("btn-nextpage").classList.add("visible");
386		document.getElementById("btn-nextpage").href = this._pages[this._currentPageIndex + 1];
387	}
388
389};
390
391
392