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