1/**
2 *  Page scripts for Ad Hominem Info Template
3 *
4 * @author     Sascha Leib <sascha@leib.be>
5 * @license    GPL 2 (http://www.gnu.org/licenses/gpl.html)
6 */
7
8/* everything is contained in the $p namespace: */
9$p = {
10
11	/* called to initialize the entire script */
12	init:	function() {
13
14		$p.cookie_banner.init();
15		$p.linkinfo.init();
16		$p.search.init();
17		$p.togglers.init();
18		$p.langMenu.init();
19
20	},
21
22	/* link information */
23	linkinfo: {
24		init: function() {
25
26			/* find all links in the main section */
27			var main = document.getElementById("main-layout");
28			var al = main.getElementsByTagName("a");
29			Array.prototype.forEach.call(al, function (a) {
30
31				Object.entries($p.linkinfo._restURLs).forEach((c) => {
32					var cls = c[0];
33					if (a.classList.contains(cls)) {
34						a.addEventListener('mouseover', $p.linkinfo._linkHoverCallback);
35					}
36				});
37			});
38		},
39
40		/* pre-defined REST API URLs for different sites. */
41		/* variables are enclosed in %, allowed vars are: */
42		/* - basedir = this site's basedir (e.g. "/"), */
43		/* - id = the data id of the link (internal only) */
44		/* - ln = the link name (e.g. for Wikipedia links) */
45		/* types can be 'internal', 'wikimedia', or 'ahtpl' */
46		/*  for other sites using this template. */
47		_restURLs : {
48			'wikilink1'	: {
49				url: '%basedir%lib/tpl/ad-hominem/rest/pageinfo.php?id=%id%&v=preview',
50				type:'internal'
51			},
52			'iw_wp'		: {
53				url:'https://en.wikipedia.org/api/rest_v1/page/summary/%id%',
54				type:'wikimedia'
55			},
56			'iw_wpfr' 	: {
57				url:'https://fr.wikipedia.org/api/rest_v1/page/summary/%id%',
58				type:'wikimedia'
59			},
60			'iw_wpde' 	: {
61				url:'https://de.wikipedia.org/api/rest_v1/page/summary/%id%',
62				type:'wikimedia'
63			},
64			'iw_wpes' 	: {
65				url:'https://es.wikipedia.org/api/rest_v1/page/summary/%id%',
66				type:'wikimedia'
67			},
68			'iw_wppl' 	: {
69				url:'https://pl.wikipedia.org/api/rest_v1/page/summary/%id%',
70				type:'wikimedia'
71			},
72			'iw_wpja' 	: {
73				url:'https://it.wikipedia.org/api/rest_v1/page/summary/%id%',
74				type:'wikimedia'
75			},
76			'iw_wpru' 	: {
77				url:'https://ru.wikipedia.org/api/rest_v1/page/summary/%id%',
78				type:'wikimedia'
79			},
80			'iw_meta' 	: {
81				url:'https://meta.wikipedia.org/api/rest_v1/page/summary/%id%',
82				type:'wikimedia'
83			},
84			'iw_fo' 	: {
85				url:'https://fallacies.online/wiki/lib/tpl/ad-hominem/rest/pageinfo.php?id=%id%&v=preview',
86				base:'https://fallacies.online/wiki/',
87				type:'ahtpl'
88			},
89			'iw_dfo' 	: {
90				url:'https://denkfehler.online/wiki/lib/tpl/ad-hominem/rest/pageinfo.php?id=%id%&v=preview',
91				base:'https://denkfehler.online/wiki/',
92				type:'ahtpl'
93			}
94		},
95		/* note: this covers the internal links and the most common
96		   wikipedia lang versions. If you know about any other
97		   relevant sites to be added here, let the author of this
98		   template know (ad@hominem.info) */
99
100		/* TODO: mechanism to dynamically add sites by site admin */
101
102		/* callback for the onhover event of links: */
103		_linkHoverCallback: function() {
104
105			var a = jQuery(this);
106			var hi = jQuery.data(this, 'has-info');
107			var href = jQuery(this).attr('href');
108			var wid = null;
109			var url = null;
110			var type = '';
111
112			/* only if the info hasn't been set yet: */
113			if (hi == undefined || hi == '') {
114
115				// remember that we are now working on the link:
116				jQuery.data(this, 'has-info', '0');
117
118				// find the URL to query:
119				try {
120					for (var cls in $p.linkinfo._restURLs) {
121						if (a.hasClass(cls)) {
122							url = $p.linkinfo._restURLs[cls].url;
123							type = $p.linkinfo._restURLs[cls].type;
124							break;
125						}
126					};
127				} catch (e) {}
128
129				/* get the ID to request: */
130				switch(type) {
131
132					case 'internal': // internal links
133						url = url.replace('%basedir%', (typeof BASEDIR!=='undefined'?BASEDIR:'/'));
134						wid = jQuery(this).data('wiki-id');
135						break;
136					case 'wikimedia': // wikipedia sites
137						wid = href.substring(href.lastIndexOf('/')+1);
138						break;
139					case 'ahtpl': // Other sites with this template
140						wid = href.substring($p.linkinfo._restURLs[cls].base.length).replaceAll('/', ':');
141						break;
142					default: // unknown -> skip
143						return;
144				}
145
146				// URL & ID found?
147				if (url !== null && typeof wid !== 'undefined') {
148
149					/* load the page info */
150					jQuery.ajax({
151						url:		url.replace('%id%', encodeURIComponent(wid)),
152						context:	a,
153						dataType:	'json',
154						crossDomain: true,
155						error:		function(xhr, msg, e) {
156										console.error(msg);
157									},
158						success:	function(data, msg, xhr) {
159										// build the new title for the element:
160										if (typeof data.title !== 'undefined') {
161											jQuery(this).attr('title', data.title + "\n" + data.extract);
162											jQuery.data(this, 'has-info', '1');
163										}
164									},
165						complete:	function() {
166										if (jQuery.data(this, 'has-info') == '0') {
167											jQuery.removeData(this, 'has-info');
168										}
169									}
170					});
171				}
172			}
173		}
174	},
175
176	/* anything related to the search */
177	search: {
178
179		/* initializer */
180		init: function() {
181			$p.search.gui.init();
182		},
183
184		/* the search gui */
185		gui: {
186
187			_container: null,
188			_elements: { field: null, clear: null, search: null },
189
190			/* init the gui */
191			init: function() {
192
193				try {
194
195					/* find all the search elements: */
196					var form = document.getElementById('dw__search');
197
198					var div = form.getElementsByClassName('search-field')[0];
199					$p.search.gui._container = div;
200
201					var field = div.getElementsByTagName('input')[0];
202					$p.search.gui._elements.field = field;
203					field.addEventListener('focus', $p.search.gui.__elementFocus);
204					field.addEventListener('blur', $p.search.gui.__elementBlur);
205
206					var buttons = div.getElementsByTagName('button');
207					Array.prototype.forEach.call(buttons, function(b) {
208						var type = b.getAttribute('type');
209						if (type == 'reset') {
210							$p.search.gui._elements.clear = b;
211						} else if (type == 'submit') {
212							$p.search.gui._elements.search = b;
213						}
214						b.addEventListener('focus', $p.search.gui.__elementFocus);
215						b.addEventListener('blur', $p.search.gui.__elementBlur);
216					});
217
218				} catch (e) {
219					console.warn("Can’t initialize search form.");
220					console.error(e);
221				}
222			},
223
224			/* call back for fields */
225			__elementFocus: function() {
226				$p.search.gui._container.classList.add("focus");
227			},
228			__elementBlur: function() {
229				$p.search.gui._container.classList.remove("focus");
230
231			}
232		}
233	},
234
235	/* expaning sections, for menus, etc. */
236	togglers: {
237
238		/* initialize togglers */
239		init:	function() {
240
241			const togglers = document.getElementsByClassName("toggle");
242
243			Array.prototype.forEach.call(togglers, function(t) {
244
245				/* add default state  */
246				if (!(t.classList.contains('show') || (t.classList.contains('hide')))) {
247					t.classList.add('auto');
248				}
249
250				/* add a callback to the toggler buttons */
251				var btn = t.getElementsByClassName('tg_button');
252				Array.prototype.forEach.call(btn, function(b) {
253					b.addEventListener('click', $p.togglers._buttonCallback);
254					b.classList.add('active');
255				});
256
257			});
258		},
259
260		/* callback for the toggler button click */
261		_buttonCallback: function() {
262
263			var t = this.parentNode;
264
265			/* current state of the toggler: */
266			var state = 'auto';
267			if (t.classList.contains('show')) state = 'show';
268			if (t.classList.contains('hide')) state = 'hide';
269			if (t.classList.contains('alt')) state = 'alt';
270
271			/* set new state: */
272			var newState = 'alt';
273			if (state == 'show') { newState = 'hide' }
274			else if (state == 'hide') { newState = 'show' }
275			else if (state == 'alt') { newState = 'auto' }
276
277			t.classList.remove(state);
278			t.classList.add(newState);
279
280		}
281	},
282
283	/* the language menu, if present */
284	langMenu: {
285		/* initialize lang menu */
286		init:	function() {
287
288			const langMenu = document.getElementById('langButton');
289			if (langMenu) {
290				jQuery(langMenu).click($p.langMenu._btnCallback);
291			}
292		},
293
294		_btnCallback: function(e) {
295
296			const btn = e.currentTarget;
297			if (btn) {
298				const menuId = btn.getAttribute('aria-controls');
299				const expanded = (btn.getAttribute('aria-expanded') == 'true');
300				if (menuId) {
301					const menu = document.getElementById(menuId);
302					if (menu) {
303						if (expanded) {
304							jQuery(menu).hide();
305							btn.setAttribute('aria-expanded', 'false')
306						} else {
307							jQuery(menu).show();
308							btn.setAttribute('aria-expanded', 'true')
309						}
310					}
311				}
312			}
313		}
314
315	},
316	/* Cookies info banner */
317	cookie_banner: {
318
319		/* initialize Cookies info banner */
320		init: function() {
321
322			// find the cookiebanner elements:
323			var btn = jQuery('#cookiebanner button');
324
325			var cookie = jQuery.cookie('cookielaw');
326
327			if ( (cookie !== '1') && (btn.length >= 1) ) { // if found only
328
329				// assign callback:
330				jQuery(btn).click($p.cookie_banner._buttonCallback);
331
332				// show the banner
333				jQuery('#cookiebanner').show();
334
335				// set focus:
336				jQuery(btn).first().focus();
337			}
338		},
339
340		/* callback for the "OK" button */
341		_buttonCallback: function() {
342
343			const date = new Date();
344			date.setFullYear(date.getFullYear() + 1);
345
346			var path = ( typeof BASEDIR !== 'undefined' ? BASEDIR : '/');
347
348			document.cookie = 'cookielaw=1; path=' + path + '; expires=' + date.toUTCString() + '; SameSite=Lax';
349			jQuery('#cookiebanner').remove();
350		}
351	}
352};
353
354/* load the script when the DOM is ready */
355
356window.addEventListener("DOMContentLoaded", $p.init);
357