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