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