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