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