| 1 |
(() => { |
| 2 |
'use strict'; |
| 3 |
const html = document.querySelector( 'html' ); |
| 4 |
const body = document.querySelector( 'body' ); |
| 5 |
let errors = []; |
| 6 |
if ( wpa.lang ) { |
| 7 |
let lang = html.getAttribute( 'lang' ); |
| 8 |
if ( ! lang ) { |
| 9 |
html.setAttribute( 'lang', wpa.lang ); |
| 10 |
if ( wpa.errors || wpa.tracking ) { |
| 11 |
errors.push( 'html-lang' ); |
| 12 |
console.log( 'HTML language set by WP Accessibility' ); |
| 13 |
} |
| 14 |
} |
| 15 |
} |
| 16 |
|
| 17 |
if ( wpa.dir ) { |
| 18 |
let dir = html.getAttribute( 'dir' ); |
| 19 |
if ( ! dir && wpa.dir !== 'ltr' ) { |
| 20 |
html.setAttributeattr( 'dir', wpa.dir ); |
| 21 |
if ( wpa.errors || wpa.tracking ) { |
| 22 |
errors.push( 'html-lang-direction' ); |
| 23 |
console.log( 'HTML language direction set by WP Accessibility' ); |
| 24 |
} |
| 25 |
} |
| 26 |
} |
| 27 |
|
| 28 |
if ( wpa.continue ) { |
| 29 |
let readMore = document.querySelectorAll( '.wp-block-post-excerpt__more-link' ); |
| 30 |
if ( readMore.length !== 0 ) { |
| 31 |
readMore.forEach( (el) => { |
| 32 |
if ( ! el.hasAttribute( 'aria-describedby' ) ) { |
| 33 |
let post = el.closest( '.wp-block-post' ); |
| 34 |
let readMoreId = post.getAttribute( 'class' ); |
| 35 |
readMoreId = readMoreId.replaceAll( ' ', '-' ); |
| 36 |
let heading = post.querySelector( '.wp-block-post-title' ); |
| 37 |
if ( heading ) { |
| 38 |
if ( ! heading.hasAttribute( 'id' ) ) { |
| 39 |
heading.setAttribute( 'id', readMoreId ); |
| 40 |
} else { |
| 41 |
readMoreId = heading.getAttribute( 'id' ); |
| 42 |
} |
| 43 |
el.setAttribute( 'aria-describedby', readMoreId ); |
| 44 |
if ( wpa.errors || wpa.tracking ) { |
| 45 |
console.log( 'Continue Reading link description set by WP Accessibility' ); |
| 46 |
} |
| 47 |
} |
| 48 |
} |
| 49 |
}); |
| 50 |
} |
| 51 |
} |
| 52 |
|
| 53 |
const viewport = document.querySelector( 'meta[name="viewport"]' ); |
| 54 |
if ( viewport && wpa.viewport ) { |
| 55 |
let conditionsBefore = viewport.getAttribute( 'content' ); |
| 56 |
let conditionsAfter = viewport.getAttribute( 'content' ); |
| 57 |
if ( conditionsBefore.search(/user-scalable=no/g) ) { |
| 58 |
conditionsAfter = conditionsBefore.replace( 'user-scalable=no', 'user-scalable=yes' ); |
| 59 |
viewport.setAttribute( 'content', conditionsAfter ); |
| 60 |
if ( ( wpa.errors || wpa.tracking ) && conditionsAfter != conditionsBefore ) { |
| 61 |
errors.push( 'viewport-scalable' ); |
| 62 |
console.log( 'Viewport made scalable by WP Accessibility' ); |
| 63 |
} |
| 64 |
} |
| 65 |
if ( conditionsBefore.search(/maximum-scale=1/g) ) { |
| 66 |
conditionsAfter = conditionsBefore.replace( 'maximum-scale=1', 'maximum-scale=5' ); |
| 67 |
conditionsAfter = conditionsAfter.replace( 'maximum-scale=0', 'maximum-scale=5' ); |
| 68 |
viewport.setAttribute( 'content', conditionsAfter ); |
| 69 |
if ( ( wpa.errors || wpa.tracking ) && conditionsAfter != conditionsBefore ) { |
| 70 |
errors.push( 'viewport-maxscale' ); |
| 71 |
console.log( 'Viewport maximum scale set by WP Accessibility' ); |
| 72 |
} |
| 73 |
} |
| 74 |
} |
| 75 |
|
| 76 |
if ( wpa.skiplinks.enabled ) { |
| 77 |
body.insertAdjacentHTML( 'afterbegin', wpa.skiplinks.output ); |
| 78 |
if ( wpa.errors || wpa.tracking ) { |
| 79 |
errors.push( 'skiplinks' ); |
| 80 |
console.log( 'Skip links added by WP Accessibility' ); |
| 81 |
} |
| 82 |
} |
| 83 |
|
| 84 |
if ( wpa.current ) { |
| 85 |
let current = document.querySelectorAll( '.current-menu-item a, .current_page_item a' ); |
| 86 |
if ( current.length !== 0 ) { |
| 87 |
current.forEach((el) => { |
| 88 |
el.setAttribute( 'aria-current', 'page' ); |
| 89 |
}); |
| 90 |
if ( wpa.errors || wpa.tracking ) { |
| 91 |
errors.push( 'aria-current' ); |
| 92 |
console.log( 'ARIA current added by WP Accessibility' ); |
| 93 |
} |
| 94 |
} |
| 95 |
} |
| 96 |
|
| 97 |
if ( wpa.labels ) { |
| 98 |
let wpa_names = [ 's', 'author', 'email', 'url', 'comment' ]; |
| 99 |
wpa_names.forEach((value) => { |
| 100 |
let fields, field_id, implicit, aria, ariaId, ariaTarget, hasAria, hasAriaId, label, labelText; |
| 101 |
if ( value == 'comment' ) { |
| 102 |
fields = document.querySelectorAll( 'textarea[name=' + value + ']' ); |
| 103 |
} else { |
| 104 |
fields = document.querySelectorAll( 'input[name=' + value + ']:not(#adminbar-search)' ); |
| 105 |
} |
| 106 |
if ( fields.length !== 0 ) { |
| 107 |
fields.forEach( (field) => { |
| 108 |
if ( 0 !== field.length ) { |
| 109 |
field_id = field.getAttribute( 'id' ); |
| 110 |
implicit = field.closest( 'label' ); |
| 111 |
aria = field.getAttribute( 'aria-label' ); |
| 112 |
ariaId = field.getAttribute( 'aria-labelledby' ); |
| 113 |
ariaTarget = {}; |
| 114 |
if ( ariaId ) { |
| 115 |
ariaTarget = document.getElementById( ariaId ); |
| 116 |
} |
| 117 |
hasAria = ( null == aria || '' == aria || 'undefined' == typeof( aria ) ) ? false : true; |
| 118 |
hasAriaId = ( null == ariaId || '' == ariaId || 'undefined' == typeof( ariaId ) ) ? false : true; |
| 119 |
// Add label if aria label empty, aria labelledby empty, or aria reference ID does not exist. |
| 120 |
if ( ( ! hasAria && ! hasAriaId ) || ( ! hasAria && ( hasAriaId && ! ariaTarget ) ) ) { |
| 121 |
if ( field_id ) { |
| 122 |
label = document.querySelector( 'label[for=' + field_id + ']' ); |
| 123 |
if ( label ) { |
| 124 |
labelText = label.innerText; |
| 125 |
if ( label && ! labelText ) { |
| 126 |
label.innerText = wpa.wpalabels[value]; |
| 127 |
if ( wpa.errors || wpa.tracking ) { |
| 128 |
errors.push( ['empty-label', wpa.wpalabels[value]] ); |
| 129 |
console.log( 'Empty label on ' + wpa.wpalabels[value] + ' added by WP Accessibility' ); |
| 130 |
} |
| 131 |
} |
| 132 |
} |
| 133 |
if ( !label && !implicit ) { |
| 134 |
field.insertAdjacentHTML( 'beforebegin', "<label for='" + field_id + "' class='wpa-screen-reader-text'>" + wpa.wpalabels[value] + "</label>" ); |
| 135 |
if ( wpa.errors || wpa.tracking ) { |
| 136 |
errors.push( ['explicit-label', wpa.wpalabels[value]] ); |
| 137 |
console.log( 'Explicit label on ' + wpa.wpalabels[value] + ' added by WP Accessibility' ); |
| 138 |
} |
| 139 |
} |
| 140 |
} else { |
| 141 |
if ( !implicit ) { |
| 142 |
field.setAttribute( 'id', 'wpa_label_' + value ); |
| 143 |
field.insertAdjacentHTML( 'beforebegin', "<label for='wpa_label_" + value + "' class='wpa-screen-reader-text'>" + wpa.wpalabels[value] + "</label>" ); |
| 144 |
if ( wpa.errors || wpa.tracking ) { |
| 145 |
errors.push( ['implicit-label', wpa.wpalabels[value]] ); |
| 146 |
console.log( 'Implicit label on ' + wpa.wpalabels[value] + ' added by WP Accessibility' ); |
| 147 |
} |
| 148 |
} |
| 149 |
} |
| 150 |
} |
| 151 |
} |
| 152 |
}); |
| 153 |
} |
| 154 |
}); |
| 155 |
} |
| 156 |
|
| 157 |
if ( wpa.titles ) { |
| 158 |
let images = 0, |
| 159 |
controls = 0, |
| 160 |
fields = 0, |
| 161 |
noremove = false; |
| 162 |
const els = document.querySelectorAll( 'img, a, input, textarea, select, button' ); |
| 163 |
if ( els.length !== 0 ) { |
| 164 |
els.forEach((el) => { |
| 165 |
let title = el.getAttribute( 'title' ); |
| 166 |
if ( el.classList.contains( 'nturl' ) ) { |
| 167 |
// Exempt title attributes from Translate WordPress - Google Language Translator, which uses them as a CSS hook. |
| 168 |
noremove = true; |
| 169 |
} |
| 170 |
if ( title && '' !== title ) { |
| 171 |
switch ( el.tagName ) { |
| 172 |
case 'IMG': |
| 173 |
// If image has alt, remove title. If not, set title as alt. |
| 174 |
let alt = el.getAttribute( 'alt' ); |
| 175 |
title = el.getAttribute( 'title' ); |
| 176 |
if ( ! alt || '' === alt ) { |
| 177 |
el.setAttribute( 'alt', title ); |
| 178 |
el.removeAttribute( 'title' ); |
| 179 |
} else { |
| 180 |
if ( title === alt ) { |
| 181 |
el.removeAttribute( 'title' ); |
| 182 |
} |
| 183 |
} |
| 184 |
images++; |
| 185 |
break; |
| 186 |
case 'A': |
| 187 |
case 'BUTTON': |
| 188 |
// If link or button has contained text or an img with alt, remove title. Otherwise, set title as aria-label unless element already has aria-label. |
| 189 |
let linkText = wpaElementText(el); |
| 190 |
if ( ! linkText || '' === linkText ) { |
| 191 |
let ariaLabel = el.getAttribute( 'aria-label' ); |
| 192 |
if ( ! ariaLabel || '' === ariaLabel ) { |
| 193 |
el.setAttribute( 'aria-label', title ); |
| 194 |
if ( ! noremove ) { |
| 195 |
el.removeAttribute( 'title' ); |
| 196 |
} |
| 197 |
} |
| 198 |
} else { |
| 199 |
el.removeAttribute( 'title' ); |
| 200 |
} |
| 201 |
controls++; |
| 202 |
break; |
| 203 |
case 'INPUT': |
| 204 |
case 'SELECT': |
| 205 |
case 'TEXTAREA': |
| 206 |
// If input field has an aria-label, aria-labelledby, associated label, or wrapping label, remove title. Else, add title as aria-label. |
| 207 |
let ariaLabel = el.getAttribute( 'aria-label' ); |
| 208 |
let ariaLabelled = el.getAttribute( 'aria-labelledby' ); |
| 209 |
let ariaLabeller = ( ariaLabelled ) ? document.getElementById( ariaLabelled ) : false; |
| 210 |
let labelId = el.getAttribute( 'id' ); |
| 211 |
let label = ( labelId ) ? document.querySelector( 'label[for="' + labelId + '"]' ) : false; |
| 212 |
let parentLabel = el.closest( 'label' ); |
| 213 |
let hasAriaLabel = ( ariaLabel && '' !== ariaLabel ) ? true : false; |
| 214 |
let hasRealLabel = ( label && '' !== wpaElementText( label ) ) ? true : false; |
| 215 |
let hasImplicitLabel = ( parentLabel && '' !== wpaElementText( parentLabel ) ) ? true : false; |
| 216 |
let hasAriaLabelled = ( ariaLabeller && '' !== wpaElementText( ariaLabeller ) ) ? true : false; |
| 217 |
if ( hasAriaLabel || hasRealLabel || hasImplicitLabel || hasAriaLabelled ) { |
| 218 |
// This has a label. |
| 219 |
el.removeAttribute( 'title' ); |
| 220 |
} else { |
| 221 |
el.setAttribute( 'aria-label', title ); |
| 222 |
el.removeAttribute( 'title' ); |
| 223 |
} |
| 224 |
fields++; |
| 225 |
break; |
| 226 |
} |
| 227 |
} |
| 228 |
}); |
| 229 |
if ( wpa.errors || wpa.tracking ) { |
| 230 |
if ( images > 0 ) { |
| 231 |
errors.push( ['images-titles', images] ); |
| 232 |
console.log( images + ' title attributes removed from images by WP Accessibility' ); |
| 233 |
} |
| 234 |
if ( controls > 0 ) { |
| 235 |
errors.push( ['control-titles', controls] ); |
| 236 |
console.log( controls + ' title attributes removed from links and buttons by WP Accessibility' ); |
| 237 |
} |
| 238 |
if ( fields > 0 ) { |
| 239 |
errors.push( ['input-titles', fields] ); |
| 240 |
console.log( fields + ' title attributes removed from input fields by WP Accessibility' ); |
| 241 |
} |
| 242 |
} |
| 243 |
} |
| 244 |
} |
| 245 |
|
| 246 |
if ( wpa.target ) { |
| 247 |
let targeted = document.querySelectorAll('a:not(.wpa-allow-target)'); |
| 248 |
let targetRemoved = 0; |
| 249 |
if ( targeted.length !== 0 ) { |
| 250 |
targeted.forEach( (el) => { |
| 251 |
let target = el.getAttribute( 'target' ); |
| 252 |
let href = el.getAttribute( 'href' ); |
| 253 |
|
| 254 |
if ( target ) { |
| 255 |
// For on-page fragments. |
| 256 |
if ( href.startsWith( '#') ) { |
| 257 |
el.removeAttribute( 'target' ); |
| 258 |
} else { |
| 259 |
try { |
| 260 |
let url = new URL( href ); |
| 261 |
let hostname = url.hostname; |
| 262 |
if ( ! hostname.includes( 'facebook' ) ) { |
| 263 |
el.removeAttribute( 'target' ); |
| 264 |
targetRemoved++; |
| 265 |
} |
| 266 |
} catch (exception) { |
| 267 |
console.log( "href value is not a URL or a fragment:", href ); |
| 268 |
// No action; the `href` attribute didn't resolve as a URL. |
| 269 |
} |
| 270 |
} |
| 271 |
} |
| 272 |
}); |
| 273 |
if ( targetRemoved > 0 && ( wpa.errors || wpa.tracking ) ) { |
| 274 |
errors.push( ['link-targets', targetRemoved] ); |
| 275 |
console.log( targetRemoved + ' target attributes removed from links by WP Accessibility' ); |
| 276 |
} |
| 277 |
} |
| 278 |
} |
| 279 |
|
| 280 |
if ( wpa.tabindex ) { |
| 281 |
// Remove tabindex from elements that should be natively focusable. |
| 282 |
let focusable = document.querySelectorAll('input:not(.wpa-ignore-tabindex),a[href]:not(.wpa-ignore-tabindex),select:not(.wpa-ignore-tabindex),textarea:not(.wpa-ignore-tabindex),button:not(.wpa-ignore-tabindex)'); |
| 283 |
let tabRemoved = 0; |
| 284 |
if ( focusable.length !== 0 ) { |
| 285 |
focusable.forEach( (el) => { |
| 286 |
let tabindex = el.getAttribute('tabindex'); |
| 287 |
let skip = false; |
| 288 |
// Check for intentionally hidden elements with tabindex of -1. |
| 289 |
if ( el.hasAttribute('tabindex') && tabindex === '-1' ) { |
| 290 |
let hasAriaHidden = el.getAttribute('aria-hidden'); |
| 291 |
let hasHidden = el.hasAttribute('hidden'); |
| 292 |
if ( hasAriaHidden === 'true' || hasHidden ) { |
| 293 |
skip = true; |
| 294 |
} |
| 295 |
} |
| 296 |
if ( tabindex && !skip ) { |
| 297 |
el.removeAttribute('tabindex'); |
| 298 |
tabRemoved++; |
| 299 |
} |
| 300 |
}); |
| 301 |
|
| 302 |
if ( tabRemoved > 0 && ( wpa.errors || wpa.tracking ) ) { |
| 303 |
errors.push( ['control-tabindex', tabRemoved] ); |
| 304 |
console.log( tabRemoved + ' tabindex attributes removed from links, buttons and inputs by WP Accessibility' ); |
| 305 |
} |
| 306 |
} |
| 307 |
|
| 308 |
// Add tabindex to elements that appear active but are not natively focusable. |
| 309 |
let fakeButtons = document.querySelectorAll( '[role="button"]:not([tabindex]):not(a):not(button)' ); |
| 310 |
let buttonLinks = document.querySelectorAll( 'a[role="button"]:not([tabindex]):not([href])'); |
| 311 |
let fakeLinks = document.querySelectorAll( '[role="link"]:not([tabindex]):not(a)'); |
| 312 |
let linkLinks = document.querySelectorAll( 'a:not([href]):not([tabindex])' ); |
| 313 |
if ( fakeButtons.length !== 0 ) { |
| 314 |
fakeButtons.forEach( (el) => { |
| 315 |
el.setAttribute( 'tabindex', '0' ); |
| 316 |
el.classList.add('wpa-focusable'); |
| 317 |
}); |
| 318 |
if ( fakeButtons.length > 0 && ( wpa.errors || wpa.tracking ) ) { |
| 319 |
errors.push( ['button-add-tabindex', fakeButtons.length] ); |
| 320 |
console.log( fakeButtons.length + ' tabindex attributes added to divs with the button role by WP Accessibility' ); |
| 321 |
} |
| 322 |
} |
| 323 |
if ( buttonLinks.length !== 0 ) { |
| 324 |
buttonLinks.forEach( (el) => { |
| 325 |
el.setAttribute( 'tabindex', '0' ); |
| 326 |
el.classList.add('wpa-focusable'); |
| 327 |
}); |
| 328 |
if ( buttonLinks.length > 0 && ( wpa.errors || wpa.tracking ) ) { |
| 329 |
errors.push( ['link-add-tabindex', buttonLinks.length] ); |
| 330 |
console.log( buttonLinks.length + ' tabindex attributes added to anchor elements with the button role and no href value by WP Accessibility' ); |
| 331 |
} |
| 332 |
} |
| 333 |
if ( fakeLinks.length !== 0 ) { |
| 334 |
fakeLinks.forEach( (el) => { |
| 335 |
el.setAttribute( 'tabindex', '0' ); |
| 336 |
el.classList.add('wpa-focusable'); |
| 337 |
}); |
| 338 |
if ( fakeLinks.length > 0 && ( wpa.errors || wpa.tracking ) ) { |
| 339 |
errors.push( ['fakelink-add-tabindex', fakeLinks.length] ); |
| 340 |
console.log( fakeLinks.length + ' tabindex attributes added to elements with the link role not using the a element by WP Accessibility' ); |
| 341 |
} |
| 342 |
} |
| 343 |
if ( linkLinks.length !== 0 ) { |
| 344 |
linkLinks.forEach( (el) => { |
| 345 |
el.setAttribute( 'tabindex', '0' ); |
| 346 |
el.classList.add('wpa-focusable'); |
| 347 |
}); |
| 348 |
if ( linkLinks.length > 0 && ( wpa.errors || wpa.tracking ) ) { |
| 349 |
errors.push( ['links-add-tabindex', linkLinks.length] ); |
| 350 |
console.log( linkLinks.length + ' tabindex attributes added to a with no href attribute by WP Accessibility' ); |
| 351 |
} |
| 352 |
} |
| 353 |
} |
| 354 |
|
| 355 |
// If stats are disabled, skip this. |
| 356 |
if ( 'disabled' !== wpa.url ) { |
| 357 |
/* |
| 358 |
* fingerprintJS 0.1 - Fast browser fingerprint library |
| 359 |
* https://github.com/Valve/fingerprintJS |
| 360 |
* Copyright (c) 2013 Valentin Vasilyev (iamvalentin@gmail.com) |
| 361 |
* Licensed under the MIT (http://www.opensource.org/licenses/mit-license.php) license. |
| 362 |
* Modified by Joe Dolson 2023. |
| 363 |
*/ |
| 364 |
class Fingerprint { |
| 365 |
constructor(hasher) { |
| 366 |
var nativeForEach = Array.prototype.forEach; |
| 367 |
var nativeMap = Array.prototype.map; |
| 368 |
this.each = function (obj, iterator, context) { |
| 369 |
if (obj == null) return; |
| 370 |
if (nativeForEach && obj.forEach === nativeForEach) { |
| 371 |
obj.forEach(iterator, context); |
| 372 |
} else if (obj.length === +obj.length) { |
| 373 |
for (var i = 0, l = obj.length; i < l; i++) { |
| 374 |
if (JSON.stringify(iterator.call(context, obj[i], i, obj)) === '{}') return; |
| 375 |
} |
| 376 |
} else { |
| 377 |
for (var key in obj) { |
| 378 |
if (obj.hasOwnProperty(key)) { |
| 379 |
if (JSON.stringify(iterator.call(context, obj[key], key, obj)) === '{}') return; |
| 380 |
} |
| 381 |
} |
| 382 |
} |
| 383 |
}; |
| 384 |
this.map = function (obj, iterator, context) { |
| 385 |
var results = []; |
| 386 |
if (obj == null) return results; |
| 387 |
if (nativeMap && obj.map === nativeMap) return obj.map(iterator, context); |
| 388 |
this.each(obj, function (value, index, list) { |
| 389 |
results[results.length] = iterator.call(context, value, index, list); |
| 390 |
}); |
| 391 |
return results; |
| 392 |
}; |
| 393 |
|
| 394 |
if (hasher) { |
| 395 |
this.hasher = hasher; |
| 396 |
} |
| 397 |
} |
| 398 |
get() { |
| 399 |
let keys = []; |
| 400 |
keys.push(navigator.userAgent); |
| 401 |
keys.push([screen.height, screen.width, screen.colorDepth].join('x')); |
| 402 |
keys.push(new Date().getTimezoneOffset()); |
| 403 |
keys.push(!!window.sessionStorage); |
| 404 |
keys.push(!!window.localStorage); |
| 405 |
var pluginsString = this.map(navigator.plugins, function (p) { |
| 406 |
var mimeTypes = this.map(p, function (mt) { |
| 407 |
return [mt.type, mt.suffixes].join('~'); |
| 408 |
}).join(','); |
| 409 |
return [p.name, p.description, mimeTypes].join('::'); |
| 410 |
}, this).join(';'); |
| 411 |
keys.push(pluginsString); |
| 412 |
if (this.hasher) { |
| 413 |
return this.hasher(keys.join('###'), 31); |
| 414 |
} else { |
| 415 |
return this.murmurhash3_32_gc(keys.join('###'), 31); |
| 416 |
} |
| 417 |
} |
| 418 |
/** |
| 419 |
* JS Implementation of MurmurHash3 (r136) (as of May 20, 2011) |
| 420 |
* |
| 421 |
* @author <a href="mailto:gary.court@gmail.com">Gary Court</a> |
| 422 |
* @see http://github.com/garycourt/murmurhash-js |
| 423 |
* @author <a href="mailto:aappleby@gmail.com">Austin Appleby</a> |
| 424 |
* @see http://sites.google.com/site/murmurhash/ |
| 425 |
* |
| 426 |
* @param {string} key ASCII only |
| 427 |
* @param {number} seed Positive integer only |
| 428 |
* @return {number} 32-bit positive integer hash |
| 429 |
*/ |
| 430 |
murmurhash3_32_gc(key, seed) { |
| 431 |
let remainder, bytes, h1, h1b, c1, c2, k1, i; |
| 432 |
|
| 433 |
remainder = key.length & 3; // key.length % 4 |
| 434 |
bytes = key.length - remainder; |
| 435 |
h1 = seed; |
| 436 |
c1 = 0xcc9e2d51; |
| 437 |
c2 = 0x1b873593; |
| 438 |
i = 0; |
| 439 |
|
| 440 |
while (i < bytes) { |
| 441 |
k1 = |
| 442 |
((key.charCodeAt(i) & 0xff)) | |
| 443 |
((key.charCodeAt(++i) & 0xff) << 8) | |
| 444 |
((key.charCodeAt(++i) & 0xff) << 16) | |
| 445 |
((key.charCodeAt(++i) & 0xff) << 24); |
| 446 |
++i; |
| 447 |
|
| 448 |
k1 = ((((k1 & 0xffff) * c1) + ((((k1 >>> 16) * c1) & 0xffff) << 16))) & 0xffffffff; |
| 449 |
k1 = (k1 << 15) | (k1 >>> 17); |
| 450 |
k1 = ((((k1 & 0xffff) * c2) + ((((k1 >>> 16) * c2) & 0xffff) << 16))) & 0xffffffff; |
| 451 |
|
| 452 |
h1 ^= k1; |
| 453 |
h1 = (h1 << 13) | (h1 >>> 19); |
| 454 |
h1b = ((((h1 & 0xffff) * 5) + ((((h1 >>> 16) * 5) & 0xffff) << 16))) & 0xffffffff; |
| 455 |
h1 = (((h1b & 0xffff) + 0x6b64) + ((((h1b >>> 16) + 0xe654) & 0xffff) << 16)); |
| 456 |
} |
| 457 |
|
| 458 |
k1 = 0; |
| 459 |
|
| 460 |
switch (remainder) { |
| 461 |
case 3: k1 ^= (key.charCodeAt(i + 2) & 0xff) << 16; |
| 462 |
case 2: k1 ^= (key.charCodeAt(i + 1) & 0xff) << 8; |
| 463 |
case 1: k1 ^= (key.charCodeAt(i) & 0xff); |
| 464 |
|
| 465 |
k1 = (((k1 & 0xffff) * c1) + ((((k1 >>> 16) * c1) & 0xffff) << 16)) & 0xffffffff; |
| 466 |
k1 = (k1 << 15) | (k1 >>> 17); |
| 467 |
k1 = (((k1 & 0xffff) * c2) + ((((k1 >>> 16) * c2) & 0xffff) << 16)) & 0xffffffff; |
| 468 |
h1 ^= k1; |
| 469 |
} |
| 470 |
|
| 471 |
h1 ^= key.length; |
| 472 |
|
| 473 |
h1 ^= h1 >>> 16; |
| 474 |
h1 = (((h1 & 0xffff) * 0x85ebca6b) + ((((h1 >>> 16) * 0x85ebca6b) & 0xffff) << 16)) & 0xffffffff; |
| 475 |
h1 ^= h1 >>> 13; |
| 476 |
h1 = ((((h1 & 0xffff) * 0xc2b2ae35) + ((((h1 >>> 16) * 0xc2b2ae35) & 0xffff) << 16))) & 0xffffffff; |
| 477 |
h1 ^= h1 >>> 16; |
| 478 |
|
| 479 |
return h1 >>> 0; |
| 480 |
} |
| 481 |
} |
| 482 |
|
| 483 |
let fingerprint = new Fingerprint().get(); |
| 484 |
|
| 485 |
function logStats(event,fingerprint,type='event') { |
| 486 |
event = JSON.stringify( event ); |
| 487 |
const data = new FormData(); |
| 488 |
data.append( 'action', wpa.action ); |
| 489 |
data.append( 'security', wpa.security ); |
| 490 |
data.append( 'stats', event ); |
| 491 |
data.append( 'post_id', wpa.post_id ); |
| 492 |
data.append( 'title', fingerprint ); |
| 493 |
data.append( 'type', type ); |
| 494 |
fetch( wpa.ajaxurl, { |
| 495 |
method: 'POST', |
| 496 |
body: data |
| 497 |
}); |
| 498 |
} |
| 499 |
let contrastButton = document.querySelector( '.toggle-contrast' ); |
| 500 |
if ( contrastButton ) { |
| 501 |
contrastButton.addEventListener('click', function () { |
| 502 |
let event; |
| 503 |
// This fires after the contrast change happens, and the ID is already changed. |
| 504 |
if ( this.getAttribute( 'data-id' ) == "is_normal_contrast") { |
| 505 |
// high contrast turned on. |
| 506 |
event = {'contrast' : 'disabled'}; |
| 507 |
} else { |
| 508 |
// high contrast turned off. |
| 509 |
event = {'contrast' : 'enabled'}; |
| 510 |
} |
| 511 |
logStats(event,fingerprint); |
| 512 |
}); |
| 513 |
} |
| 514 |
|
| 515 |
let fontsizeButton = document.querySelector( '.toggle-fontsize' ); |
| 516 |
if ( fontsizeButton ) { |
| 517 |
fontsizeButton.addEventListener('click', function () { |
| 518 |
let event; |
| 519 |
// This fires after the fontsize change happens, and the ID is already changed. |
| 520 |
if ( this.getAttribute( 'data-id' ) == "is_normal_fontsize") { |
| 521 |
// fontsizes turned on. |
| 522 |
event = {'fontsize' : 'disabled'}; |
| 523 |
} else { |
| 524 |
// fontsizes turned off. |
| 525 |
event = {'fontsize' : 'enabled'}; |
| 526 |
} |
| 527 |
logStats(event,fingerprint); |
| 528 |
}); |
| 529 |
} |
| 530 |
|
| 531 |
wpaWaitForElement('.wpa-ld button').then((el) => { |
| 532 |
el.addEventListener( 'click', function(e) { |
| 533 |
// For descriptions, we aren't concerned about state changes; just usage. |
| 534 |
let visible = ( 'true' === el.getAttribute( 'aria-expanded' ) ) ? true : false; |
| 535 |
if ( visible ) { |
| 536 |
let img = el.closest( 'div' ); |
| 537 |
let image_id = img.getAttribute( 'class' ).replace( 'wpa-ld wp-image-', '' ); |
| 538 |
let event = { 'longdesc' : image_id }; |
| 539 |
logStats(event,fingerprint); |
| 540 |
} |
| 541 |
}); |
| 542 |
}); |
| 543 |
|
| 544 |
let altButtons = document.querySelectorAll( '.wpa-alt button' ); |
| 545 |
if ( altButtons.length > 0 ) { |
| 546 |
altButtons.forEach( (el) => { |
| 547 |
el.addEventListener( 'click', function(e) { |
| 548 |
// For alt text, we aren't concerned about state changes; just usage. |
| 549 |
let visible = ( 'true' === el.getAttribute( 'aria-expanded' ) ) ? true : false; |
| 550 |
if ( visible ) { |
| 551 |
let img = el.closest( 'div' ); |
| 552 |
let image_id = img.getAttribute( 'class' ).replace( 'wpa-alt wp-image-', '' ); |
| 553 |
let event = { 'alttext' : image_id }; |
| 554 |
logStats(event,fingerprint); |
| 555 |
} |
| 556 |
}); |
| 557 |
}); |
| 558 |
} |
| 559 |
if ( wpa.tracking && errors.length >= 1 ) { |
| 560 |
logStats(errors,wpa.url,'view'); |
| 561 |
} |
| 562 |
|
| 563 |
} |
| 564 |
if ( wpa.underline.enabled ) { |
| 565 |
// Underline any link not inside a `nav` region. Using JS for this avoids problems with cascade precedence. |
| 566 |
let targetEls = document.querySelectorAll( wpa.underline.target + ':not(nav ' + wpa.underline.target + '):not(#wpadminbar a), .wpa-focusable[role=link]' ); |
| 567 |
if ( targetEls.length > 0 ) { |
| 568 |
targetEls.forEach( (el) => { |
| 569 |
let originalOutline = el.style.outlineWidth; |
| 570 |
let originalOffset = el.style.outlineOffset; |
| 571 |
let textColor = el.style.color; |
| 572 |
let originalColor = el.style.outlineColor; |
| 573 |
el.style.textDecoration = 'underline'; |
| 574 |
el.addEventListener( 'mouseenter', function() { |
| 575 |
this.style.textDecoration = 'none'; |
| 576 |
}); |
| 577 |
el.addEventListener( 'mouseleave', function() { |
| 578 |
this.style.textDecoration = 'underline'; |
| 579 |
}); |
| 580 |
el.addEventListener( 'focusin', function() { |
| 581 |
let newOutline = '2px'; |
| 582 |
if ( originalOutline == '2px' ) { |
| 583 |
newOutline = '4px'; |
| 584 |
} |
| 585 |
// Ensure there's a visible change of appearance on hover or focus. |
| 586 |
this.style.outlineWidth = newOutline; |
| 587 |
this.style.outlineColor = textColor; |
| 588 |
this.style.outlineOffset = '2px'; |
| 589 |
}); |
| 590 |
el.addEventListener( 'focusout', function() { |
| 591 |
// Reset visible appearance on exit. |
| 592 |
this.style.outlineWidth = originalOutline; |
| 593 |
this.style.outlineColor = originalColor; |
| 594 |
this.style.outlineOffset = originalOffset; |
| 595 |
}); |
| 596 |
}); |
| 597 |
} |
| 598 |
} |
| 599 |
|
| 600 |
if ( wpa.videos ) { |
| 601 |
// Add a pause/play button to autoplaying videos without controls. |
| 602 |
let motionQuery = matchMedia( '(prefers-reduced-motion)' ); |
| 603 |
let initialState = 'false'; |
| 604 |
let autoplayVideos = document.querySelectorAll( 'video[autoplay]:not([controls])' ); |
| 605 |
if ( autoplayVideos.length > 0 ) { |
| 606 |
autoplayVideos.forEach( (el) => { |
| 607 |
if ( motionQuery.matches ) { |
| 608 |
el.pause(); |
| 609 |
initialState = 'true'; |
| 610 |
} |
| 611 |
let parentEl = el.parentElement; |
| 612 |
let pauseButton = document.createElement( 'button' ); |
| 613 |
let buttonIcon = document.createElement( 'span' ); |
| 614 |
let buttonText = document.createElement( 'span' ); |
| 615 |
pauseButton.setAttribute( 'type', 'button' ); |
| 616 |
pauseButton.classList.add( 'wpa-video' ); |
| 617 |
pauseButton.setAttribute( 'aria-pressed', initialState ); |
| 618 |
buttonIcon.classList.add( 'dashicons-controls-pause', 'dashicons' ); |
| 619 |
buttonIcon.setAttribute( 'aria-hidden', 'true' ); |
| 620 |
buttonText.classList.add( 'screen-reader-text' ); |
| 621 |
buttonText.innerText = wpa.pause; |
| 622 |
pauseButton.append( buttonIcon, buttonText ); |
| 623 |
parentEl.append( pauseButton ); |
| 624 |
pauseButton.addEventListener( 'click', function() { |
| 625 |
let pressed = this.getAttribute( 'aria-pressed' ); |
| 626 |
console.log( pressed ); |
| 627 |
if ( 'true' === pressed ) { |
| 628 |
el.play(); |
| 629 |
this.setAttribute( 'aria-pressed', 'false' ); |
| 630 |
} else { |
| 631 |
el.pause(); |
| 632 |
this.setAttribute( 'aria-pressed', 'true' ); |
| 633 |
} |
| 634 |
}); |
| 635 |
}); |
| 636 |
} |
| 637 |
} |
| 638 |
|
| 639 |
if ( wpa.alt ) { |
| 640 |
let selector = ( wpa.altSelector ) ? wpa.altSelector : '.hentry img[alt]:not([alt=""]), .comment-content img[alt]:not([alt=""]), #content img[alt]:not([alt=""]),.entry-content img[alt]:not([alt=""])'; |
| 641 |
let collection = document.querySelectorAll( selector ); |
| 642 |
let wrap = (elem, wrapper) => { |
| 643 |
elem.parentElement.insertBefore(wrapper, elem); |
| 644 |
wrapper.appendChild(elem); |
| 645 |
}; |
| 646 |
collection.forEach((el) => { |
| 647 |
let img = el; |
| 648 |
let closestLink = img.closest( 'a' ); |
| 649 |
let closestButton = img.closest( 'button' ); |
| 650 |
let inLink = ( closestLink ) ? true : false; |
| 651 |
let inButton = ( closestButton ) ? true : false; |
| 652 |
let width = img.width; |
| 653 |
let height = img.height; |
| 654 |
if ( width || height ) { |
| 655 |
width = ( ! width ) ? 56 : width; // Enough width for button to be visible. |
| 656 |
height = ( ! height ) ? 56 : height; // Enough height for button & first line to be visible. |
| 657 |
let area = height * width; |
| 658 |
if ( area < ( 150 * 300 ) ) { |
| 659 |
// Small images won't get displayed alt text containers. |
| 660 |
return; |
| 661 |
} |
| 662 |
if ( el.classList.contains( 'wpa-skip' ) || el.closest( '.wpa-skip' ) ) { |
| 663 |
// Skip images that have been marked to skip. |
| 664 |
return; |
| 665 |
} |
| 666 |
} |
| 667 |
let alt = img.getAttribute('alt'); |
| 668 |
|
| 669 |
let classes = [...img.classList]; |
| 670 |
img.setAttribute('class', ''); |
| 671 |
let elContainer = document.createElement( 'div' ); |
| 672 |
elContainer.classList.add( 'wpa-alt' ); |
| 673 |
if ( inLink || inButton ) { |
| 674 |
let wrapper = ( inLink ) ? closestLink : closestButton; |
| 675 |
wrap( wrapper, elContainer ); |
| 676 |
} else { |
| 677 |
wrap( img, elContainer ); |
| 678 |
} |
| 679 |
classes.forEach(className => { |
| 680 |
elContainer.classList.add(className); |
| 681 |
}); |
| 682 |
|
| 683 |
elContainer.insertAdjacentHTML('afterbegin', '<button aria-expanded="false" type="button" class="wpa-toggle">alt</button>'); |
| 684 |
elContainer.insertAdjacentHTML('beforeend', '<div class="wpa-alt-text"></div>'); |
| 685 |
let container = elContainer.querySelector('.wpa-alt-text'); |
| 686 |
container.style.display = 'none'; |
| 687 |
container.innerText = alt; |
| 688 |
let button = elContainer.querySelector('button'); |
| 689 |
button.addEventListener( 'click', function(e) { |
| 690 |
let visible = container.checkVisibility(); |
| 691 |
if ( visible ) { |
| 692 |
this.setAttribute( 'aria-expanded', 'false' ); |
| 693 |
container.style.display = 'none'; |
| 694 |
} else { |
| 695 |
this.setAttribute( 'aria-expanded', 'true' ); |
| 696 |
container.style.display = 'block'; |
| 697 |
} |
| 698 |
}); |
| 699 |
}); |
| 700 |
} |
| 701 |
|
| 702 |
if ( wpa.ldType ) { |
| 703 |
let wrap = (elem, wrapper) => { |
| 704 |
elem.parentElement.insertBefore(wrapper, elem); |
| 705 |
wrapper.appendChild(elem); |
| 706 |
}; |
| 707 |
|
| 708 |
let longDescImgs = document.querySelectorAll( 'img[longdesc]' ); |
| 709 |
if ( 'link' === wpa.ldType ) { |
| 710 |
// Links with an actual longdesc attribute. |
| 711 |
if ( longDescImgs.length > 0 ) { |
| 712 |
longDescImgs.forEach( (el) => { |
| 713 |
let wrapper = document.createElement( 'div' ); |
| 714 |
wrapper.classList.add( 'wpa-ld' ); |
| 715 |
let longdesc = el.getAttribute('longdesc'); |
| 716 |
let alt = el.getAttribute('alt'); |
| 717 |
let classes = [...el.classList]; |
| 718 |
wrap(el,wrapper); |
| 719 |
classes.forEach(className => { |
| 720 |
wrapper.classList.add(className); |
| 721 |
}); |
| 722 |
el.setAttribute('class', ''); |
| 723 |
let newLink = document.createElement( 'a' ); |
| 724 |
newLink.setAttribute( 'href', longdesc ); |
| 725 |
newLink.classList.add( 'longdesc-link' ); |
| 726 |
let span = document.createElement( 'span' ); |
| 727 |
span.classList.add( 'screen-reader-text' ); |
| 728 |
span.textContent = ' of ' + alt; |
| 729 |
newLink.textContent = 'Description'; |
| 730 |
newLink.appendChild( span ); |
| 731 |
el.insertAdjacentElement( 'afterend', newLink ); |
| 732 |
}); |
| 733 |
} |
| 734 |
// Links with the block style. |
| 735 |
let hasStyleLongdesc = document.querySelectorAll( 'figure.is-style-longdesc' ); |
| 736 |
if ( hasStyleLongdesc.length > 0 ) { |
| 737 |
hasStyleLongdesc.forEach( (el) => { |
| 738 |
let img = el.querySelector( 'img' ); |
| 739 |
wpa_load_image_control( img ); |
| 740 |
}); |
| 741 |
} |
| 742 |
|
| 743 |
} else { |
| 744 |
// Handle longdescriptions with buttons. |
| 745 |
if ( longDescImgs.length > 0 ) { |
| 746 |
longDescImgs.forEach( (img) => { |
| 747 |
wpa_load_image_control( img ); |
| 748 |
}); |
| 749 |
} |
| 750 |
|
| 751 |
// Links with the block style. |
| 752 |
let hasStyleLongdesc = document.querySelectorAll( 'figure.is-style-longdesc' ); |
| 753 |
if ( hasStyleLongdesc.length > 0 ) { |
| 754 |
hasStyleLongdesc.forEach( (el) => { |
| 755 |
let img = el.querySelector( 'img' ); |
| 756 |
wpa_load_image_control( img ); |
| 757 |
}); |
| 758 |
} |
| 759 |
} |
| 760 |
|
| 761 |
function wpa_load_image_control( img ) { |
| 762 |
let classes = img.getAttribute( 'class' ); |
| 763 |
let idAtt = img.getAttribute( 'id' ); |
| 764 |
let id; |
| 765 |
if ( idAtt && idAtt.includes( 'longdesc' ) ) { |
| 766 |
id = idAtt.replace( 'longdesc-return-', '' ); |
| 767 |
} else { |
| 768 |
if ( null === classes || '' === classes ) { |
| 769 |
parent = img.closest( '.wpa-alt' ); |
| 770 |
classes = parent.getAttribute( 'class' ).replace( 'wpa-alt ', '' ); |
| 771 |
} |
| 772 |
|
| 773 |
id = classes.replace( 'wp-image-', '' ); |
| 774 |
} |
| 775 |
let api = wpa.restUrl + '/' + id; |
| 776 |
fetch( api ) |
| 777 |
.then( response => response.json()) |
| 778 |
.then(data => { |
| 779 |
let rawdesc = data.description.rendered; |
| 780 |
rawdesc = rawdesc.replace(/(<([^>]+)>)/gi, '').trim(); |
| 781 |
if ( '' !== rawdesc ) { |
| 782 |
let url = new URL( wpa.ldHome ); |
| 783 |
url.searchParams.set( 'longdesc', id ); |
| 784 |
url.searchParams.set( 'p', id ); |
| 785 |
url.toString(); |
| 786 |
wpa_draw_longdesc( img, id, url, rawdesc ); |
| 787 |
} |
| 788 |
}) |
| 789 |
.catch( error => { |
| 790 |
console.log( error ); |
| 791 |
}); |
| 792 |
} |
| 793 |
|
| 794 |
function wpa_draw_longdesc( img, image_id, longdesc, rawdesc ) { |
| 795 |
let wrapper = document.createElement( 'div' ); |
| 796 |
wrapper.classList.add( 'wpa-ld' ); |
| 797 |
let alt = img.getAttribute('alt'); |
| 798 |
if ( '' === alt ) { |
| 799 |
alt = '<code>' + img.getAttribute('src').replace( wpa.ldHome, '' ) + '</code>'; |
| 800 |
} |
| 801 |
let post_classes = document.body.className.split(/\s+/); |
| 802 |
let post_id = ''; |
| 803 |
|
| 804 |
post_classes.forEach( (value) => { |
| 805 |
if ( value.match( /postid-/gi ) ) { |
| 806 |
post_id = value.replace( 'postid-', '', value ); |
| 807 |
} |
| 808 |
if ( value.match( /page-id-/gi ) ) { |
| 809 |
post_id = value.replace( 'page-id-', '', value ); |
| 810 |
} |
| 811 |
}); |
| 812 |
let url = new URL(longdesc); |
| 813 |
url.searchParams.set( 'referrer', post_id ); |
| 814 |
url.toString(); |
| 815 |
let classes = [...img.classList]; |
| 816 |
classes.forEach( (c) => { |
| 817 |
wrapper.classList.add(c); |
| 818 |
}); |
| 819 |
wrap( img, wrapper ); |
| 820 |
|
| 821 |
img.setAttribute('alt', ''); |
| 822 |
img.setAttribute('class', ''); |
| 823 |
let newLink = document.createElement( 'a' ); |
| 824 |
newLink.setAttribute( 'href', url ); |
| 825 |
newLink.classList.add( 'longdesc-link' ); |
| 826 |
let span = document.createElement( 'span' ); |
| 827 |
span.classList.add( 'screen-reader-text' ); |
| 828 |
span.textContent = ' of ' + alt; |
| 829 |
newLink.textContent = 'Description'; |
| 830 |
newLink.appendChild( span ); |
| 831 |
if ( 'link' === wpa.ldType ) { |
| 832 |
wrapper.insertAdjacentElement( 'beforeend', newLink ); |
| 833 |
} else { |
| 834 |
wrapper.insertAdjacentHTML( 'beforeend', '<button aria-expanded="false" class="wpa-toggle">' + wpa.ldText + '</button>'); |
| 835 |
wrapper.insertAdjacentHTML( 'beforeend', '<div class="longdesc"></div>'); |
| 836 |
let container = wrapper.querySelector('.longdesc'); |
| 837 |
container.style.display = 'none'; |
| 838 |
|
| 839 |
fetch( longdesc ) |
| 840 |
.then( response => { |
| 841 |
if ( response.ok ) { |
| 842 |
return response.text(); |
| 843 |
} else { |
| 844 |
container.insertAdjacentHTML( 'beforeend', rawdesc ); |
| 845 |
} |
| 846 |
}).then (html => { |
| 847 |
const parser = new DOMParser(); |
| 848 |
const doc = parser.parseFromString( html, "text/html" ); |
| 849 |
container.insertAdjacentElement( 'beforeend', doc.querySelector( '#desc_' + image_id ) ); |
| 850 |
}); |
| 851 |
|
| 852 |
wrapper.querySelector('button').addEventListener( 'click', function(e) { |
| 853 |
let visible = container.checkVisibility(); |
| 854 |
if ( visible ) { |
| 855 |
this.setAttribute( 'aria-expanded', 'false' ); |
| 856 |
container.style.display = 'none'; |
| 857 |
} else { |
| 858 |
this.setAttribute( 'aria-expanded', 'true' ); |
| 859 |
container.style.display = 'block'; |
| 860 |
} |
| 861 |
}); |
| 862 |
} |
| 863 |
} |
| 864 |
} |
| 865 |
})(); |
| 866 |
|
| 867 |
/** |
| 868 |
* Check whether an element contains text, including inspecting contained content for image alt attributes or aria-label attributes. |
| 869 |
* |
| 870 |
* @arg el DOM element to check. |
| 871 |
* |
| 872 |
* Based on work by Roger Johansson https://www.456bereastreet.com/archive/201105/get_element_text_including_alt_text_for_images_with_javascript/ |
| 873 |
*/ |
| 874 |
function wpaElementText(el) { |
| 875 |
let text = ''; |
| 876 |
// Text node (3) or CDATA node (4) - return its text |
| 877 |
if ( (el.nodeType === 3) || (el.nodeType === 4) ) { |
| 878 |
text = el.nodeValue; |
| 879 |
// If node is an element (1) and an img, input[type=image], or area element, return its alt text |
| 880 |
} else if ( (el.nodeType === 1) && ( |
| 881 |
(el.tagName.toLowerCase() == 'img') || |
| 882 |
(el.tagName.toLowerCase() == 'area') || |
| 883 |
((el.tagName.toLowerCase() == 'input') && el.getAttribute('type') && (el.getAttribute('type').toLowerCase() == 'image')) |
| 884 |
) ) { |
| 885 |
text = el.getAttribute('alt') || ''; |
| 886 |
// Traverse children unless this is a script or style element |
| 887 |
} else if ( (el.nodeType === 1) && !el.tagName.match(/^(script|style)$/i) ) { |
| 888 |
let children = el.childNodes; |
| 889 |
for (let i = 0, l = children.length; i < l; i++) { |
| 890 |
// If an element has an aria-label, that will override any other contained text. |
| 891 |
let ariaLabel = el.getAttribute( 'aria-label' ); |
| 892 |
text += ( ariaLabel ) ? ariaLabel : wpaElementText( children[i] ) + ' '; |
| 893 |
} |
| 894 |
} |
| 895 |
|
| 896 |
return text; |
| 897 |
}; |
| 898 |
|
| 899 |
/** |
| 900 |
* Wait to see whether an element becomes available. |
| 901 |
* |
| 902 |
* @param {string} selector |
| 903 |
* @returns |
| 904 |
*/ |
| 905 |
function wpaWaitForElement(selector) { |
| 906 |
return new Promise(resolve => { |
| 907 |
if (document.querySelector(selector)) { |
| 908 |
return resolve(document.querySelector(selector)); |
| 909 |
} |
| 910 |
|
| 911 |
const observer = new MutationObserver(mutations => { |
| 912 |
if (document.querySelector(selector)) { |
| 913 |
observer.disconnect(); |
| 914 |
resolve(document.querySelector(selector)); |
| 915 |
} |
| 916 |
}); |
| 917 |
|
| 918 |
observer.observe(document.body, { |
| 919 |
childList: true, |
| 920 |
subtree: true |
| 921 |
}); |
| 922 |
}); |
| 923 |
} |