xref: /template/readthedokus/js/readthedokus.js (revision 6039c3e824cf2f6eda0e4ef2ae8e15a7b363bd31)
1function ReadtheDokus()
2{
3
4	this._currentPage;
5	this._currentPageIndex;
6	this._pages;
7	this._toc = document.getElementById("dw__toc");
8	this._sidebar =document.querySelector("#dokuwiki__aside");
9	this._delimiter = ( window.location.search.indexOf(":") > -1 ? ":" : "/");
10	this._id = ( this._delimiter == ":" ? JSINFO["id"] : JSINFO["id"].replaceAll(":", "/"));
11	this._startPage = "";
12
13}
14
15ReadtheDokus.prototype.run = function()
16{
17
18	// Enum sidebar items to
19	//   - embed toc in the corresponding sidebar item
20	//   - collect all page links
21	var isFound = false;
22	this._pages = [];
23	if (JSINFO["ACT"] == "show")
24	{
25		this._enumSidebarLinks(function(elem) {
26			// Embed toc
27			if (elem.href.indexOf(this._id) > -1)
28			{
29				this._embedToc(elem, this._toc);
30				isFound = true;
31			}
32
33			// Collect page links
34			this._pages.push(elem.href);
35		}.bind(this));
36	}
37
38	// Start page
39	if (this._pages.length > 0)
40	{
41		this._startPage = this._getStartPage(this._pages[0], this._delimiter);
42		this._pages.unshift(this._startPage);
43		document.querySelectorAll("#sidebarheader > div.home > a, #pageheader .breadcrumbs > .home > a").forEach(function(elem) {
44			elem.href = this._startPage;
45		}.bind(this));
46	}
47
48	// Show toc on top of sidebar if item was not found in sidebar
49	if (!isFound)
50	{
51		this._showToc(this._toc);
52	}
53
54	this._initToc(this._toc);
55	this._initMobileHeader();
56	this._initPageButtons();
57	this._sidebar.querySelector("#sidebarheader #qsearch__in").setAttribute("placeholder", "Search docs");
58
59	if (this._toc)
60	{
61		this._toc.scrollIntoView(true);
62	}
63
64};
65
66ReadtheDokus.prototype.toggleTocMenu = function(elem)
67{
68
69	var invisible = elem.parentNode.querySelector(".toc").classList.contains("invisible");
70	if (invisible)
71	{
72		this.expandTocMenu(elem);
73	}
74	else
75	{
76		this.collapseTocMenu(elem);
77	}
78
79};
80
81ReadtheDokus.prototype.expandTocMenu = function(elem, allChildren)
82{
83
84	if (elem && elem.classList.contains("expandable"))
85	{
86		elem.parentNode.querySelector(".toc").classList.remove("invisible");
87
88		var i = elem.children[0].children[0].children[0];
89		i.classList.remove("fa-plus-square");
90		i.classList.add("fa-minus-square");
91
92		var img = elem.children[0].children[0].children[1];
93		img.classList.remove("plus");
94		img.classList.add("minus");
95		img.src= DOKU_BASE + "lib/images/minus.gif";
96	}
97
98};
99
100ReadtheDokus.prototype.collapseTocMenu = function(elem, allChildren)
101{
102
103	if (elem && elem.classList.contains("expandable"))
104	{
105		elem.parentNode.querySelector(".toc").classList.add("invisible");
106
107		var i = elem.children[0].children[0].children[0];
108		i.classList.remove("fa-minus-square");
109		i.classList.add("fa-plus-square");
110
111		var img = elem.children[0].children[0].children[1];
112		img.classList.remove("minus");
113		img.classList.add("plus");
114		img.src=DOKU_BASE + "lib/images/plus.gif";
115	}
116
117};
118
119ReadtheDokus.prototype._enumSidebarLinks = function(callback)
120{
121
122	callback = ( typeof callback === "function" ? callback : function(){} );
123	var links = this._sidebar.querySelectorAll(".aside > ul .level1 a");
124
125	links.forEach(function(elem) {
126		callback(elem);
127	});
128
129};
130
131ReadtheDokus.prototype._getStartPage = function(basePage, delimiter)
132{
133
134	var result = "";
135
136	if (basePage && delimiter)
137	{
138		var re = new RegExp("\\" + delimiter + "[^\\" + delimiter + "]*[^\\" + delimiter + "]*$");
139		result = basePage.replace(re, "").replace(re, "") + delimiter + "start";
140	}
141
142	return result;
143
144};
145
146ReadtheDokus.prototype._embedToc = function(target, toc)
147{
148
149	if (target && toc)
150	{
151		target.parentNode.parentNode.appendChild(toc);
152		target.parentNode.style.display = "none";
153	}
154
155};
156
157ReadtheDokus.prototype._showToc = function(toc)
158{
159
160	if (toc)
161	{
162		this._toc.parentNode.style.display = "block";
163	}
164
165};
166
167ReadtheDokus.prototype._initToc = function(toc)
168{
169
170	if (toc)
171	{
172		this._installTocSelectHandler();
173		this._installTocMenuHandler();
174	}
175
176};
177
178// Install click handler to highlight and expand toc menu
179ReadtheDokus.prototype._installTocSelectHandler = function()
180{
181
182	this._toc.querySelectorAll(".level1 div.li").forEach(function(elem) {
183		elem.addEventListener("click", function() {
184			// Get level2 parent
185			let p = this._getParent(elem, "level2");
186
187			// Remove all current
188			this._toc.querySelectorAll(".current").forEach(function(elem) {
189				elem.classList.remove("current");
190			});
191
192			// Set current to this and level2 parent
193			if (p)
194			{
195				p.parentNode.classList.add("current");
196				p.classList.add("current");
197				elem.classList.add("current");
198				elem.scrollIntoView(true);
199			}
200
201			// Expand
202			this.expandTocMenu(elem);
203
204			// Fold the other level2 items
205			this._toc.querySelectorAll(".level2 > div.li.expandable").forEach(function(item) {
206				if (item != p)
207				{
208					this.collapseTocMenu(item);
209				}
210			}.bind(this));
211		}.bind(this));
212	}.bind(this));
213
214};
215
216// Install click handler to expand/collapse toc menu
217ReadtheDokus.prototype._installTocMenuHandler = function()
218{
219
220	// Search for toc menu items which have children
221	this._toc.querySelectorAll("div.li").forEach(function(elem) {
222		if (elem.parentNode.querySelector(".toc"))
223		{
224			elem.classList.add("expandable");
225
226			// Insert +/- fontawesome icon and image
227			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>');
228
229			// Install click handler
230			elem.children[0].children[0].addEventListener("click", function(e) {
231				this.toggleTocMenu(elem);
232
233				e.stopPropagation();
234				e.preventDefault();
235			}.bind(this));
236
237			// Only level1 menu items are open at start
238			if (!elem.parentNode.classList.contains("level1"))
239			{
240				this.collapseTocMenu(elem);
241			}
242		}
243
244		// Install click handler to move an clicked item to top
245		elem.addEventListener("click", function() {
246			elem.scrollIntoView(true);
247		});
248	}.bind(this));
249
250};
251
252ReadtheDokus.prototype._getParent = function(elem, level)
253{
254
255	let current = elem.parentNode;
256
257	while (current && !current.classList.contains("level1"))
258	{
259		if (current.classList.contains(level))
260		{
261			return current.children[0];
262		}
263
264		current = current.parentNode.parentNode;
265	}
266
267	return null;
268
269};
270
271ReadtheDokus.prototype._initMobileHeader = function()
272{
273
274	// Add click event handler for mobile menu
275	document.getElementById("btn-mobilemenu").addEventListener("click", function(){
276		this._sidebar.classList.toggle("visible");
277		document.querySelector("#dokuwiki__content").classList.toggle("shift");
278	}.bind(this));
279
280};
281
282ReadtheDokus.prototype._initPageButtons = function()
283{
284
285	// Get current page (remove hash)
286	this._currentPage = window.location.href.replace(/#.*$/, "");
287
288	// Get current page index
289	this._currentPageIndex = this._pages.indexOf(this._currentPage);
290
291	// Show prev button
292	if (this._currentPageIndex > 0)
293	{
294		document.getElementById("btn-prevpage").classList.add("visible");
295		document.getElementById("btn-prevpage").href = this._pages[this._currentPageIndex - 1];
296	}
297
298	// Show next button
299	if (this._currentPageIndex > -1 && this._currentPageIndex < this._pages.length - 1)
300	{
301		document.getElementById("btn-nextpage").classList.add("visible");
302		document.getElementById("btn-nextpage").href = this._pages[this._currentPageIndex + 1];
303	}
304
305};
306