1( function () { 2 'use strict'; 3 4 var nextFrame; 5 var headerImage; 6 var brandingWrapper; 7 8 // Cool header image scroll 9 function handleScrollFrame() { 10 if ( document.documentElement.clientWidth > 800 ) { 11 var scrollY = window.scrollY || window.pageYOffset || 0; 12 headerImage.style.transform = 'translateY(' + scrollY / 3 + 'px)'; 13 14 var headerHeightPx = getComputedStyle( headerImage ).height || '0'; 15 var headerHeight = parseInt( headerHeightPx.replace( 'px', '' ), 10 ); 16 var opacity = headerHeight ? 1 - scrollY / headerHeight : 1; 17 brandingWrapper.style.opacity = opacity > 0 ? opacity : 0; 18 } else { 19 headerImage.style.transform = 'translateY(0)'; 20 brandingWrapper.style.opacity = 1; 21 } 22 nextFrame = null; 23 } 24 25 function onDocumentReady() { 26 headerImage = document.querySelector( '.site-header-image' ); 27 brandingWrapper = document.querySelector( '.site-branding-wrapper' ); 28 29 if ( headerImage && brandingWrapper ) { 30 window.addEventListener( 'scroll', function () { 31 // Schedule a new frame, if one hasn't been scheduled yet. 32 nextFrame = nextFrame || requestAnimationFrame( handleScrollFrame ); 33 } ); 34 } 35 36 // Smooth scroll to header 37 var toTheTop = document.querySelector( '.tothetop' ); 38 if ( toTheTop ) { 39 toTheTop.addEventListener( 'click', function ( e ) { 40 e.preventDefault(); 41 if ( 42 window.CSS && 43 window.CSS.supports && 44 window.CSS.supports( 'scroll-behavior', 'smooth' ) 45 ) { 46 window.scroll( { top: 0, behavior: 'smooth' } ); 47 } else { 48 window.scroll( 0, 0 ); 49 } 50 } ); 51 } 52 } 53 54 function onPageLoad() { 55 // Trigger resize event to make sure video widgets in the footer maintain the correct aspect ratio 56 setTimeout( function () { 57 if ( typeof Event === 'function' ) { 58 window.dispatchEvent( new Event( 'resize' ) ); 59 } else { 60 var event = window.document.createEvent( 'UIEvents' ); 61 event.initUIEvent( 'resize', true, false, window, 0 ); 62 window.dispatchEvent( event ); 63 } 64 } ); 65 } 66 67 if ( document.readyState !== 'loading' ) { 68 onDocumentReady(); 69 } else { 70 document.addEventListener( 'DOMContentLoaded', onDocumentReady ); 71 } 72 73 if ( document.readyState === 'complete' ) { 74 onPageLoad(); 75 } else { 76 window.addEventListener( 'load', onPageLoad ); 77 } 78} )(); 79; 80/** 81 * navigation.js 82 * 83 * Handles toggling the navigation menu and search menu for small screens. 84 */ 85( function() { 86 var container, button, menu, links, i, len; 87 88 container = document.getElementById( 'site-navigation' ); 89 if ( ! container ) { 90 return; 91 } 92 93 button = container.getElementsByTagName( 'button' )[0]; 94 if ( 'undefined' === typeof button ) { 95 return; 96 } 97 98 menu = container.getElementsByTagName( 'ul' )[0]; 99 100 // Hide menu toggle button if menu is empty and return early. 101 if ( 'undefined' === typeof menu ) { 102 button.style.display = 'none'; 103 return; 104 } 105 106 menu.setAttribute( 'aria-expanded', 'false' ); 107 if ( -1 === menu.className.indexOf( 'nav-menu' ) ) { 108 menu.className += ' nav-menu'; 109 } 110 111 button.onclick = function() { 112 if ( -1 !== container.className.indexOf( 'toggled' ) ) { 113 container.className = container.className.replace( ' toggled', '' ); 114 button.setAttribute( 'aria-expanded', 'false' ); 115 menu.setAttribute( 'aria-expanded', 'false' ); 116 } else { 117 container.className += ' toggled'; 118 button.setAttribute( 'aria-expanded', 'true' ); 119 menu.setAttribute( 'aria-expanded', 'true' ); 120 } 121 }; 122 123 // Get all the link elements within the menu. 124 links = menu.getElementsByTagName( 'a' ); 125 126 // Each time a menu link is focused or blurred, toggle focus. 127 for ( i = 0, len = links.length; i < len; i++ ) { 128 links[i].addEventListener( 'focus', toggleFocus, true ); 129 links[i].addEventListener( 'blur', toggleFocus, true ); 130 } 131 132 /** 133 * Sets or removes .focus class on an element. 134 */ 135 function toggleFocus() { 136 var self = this; 137 138 // Move up through the ancestors of the current link until we hit .nav-menu. 139 while ( -1 === self.className.indexOf( 'nav-menu' ) ) { 140 141 // On li elements toggle the class .focus. 142 if ( 'li' === self.tagName.toLowerCase() ) { 143 if ( -1 !== self.className.indexOf( 'focus' ) ) { 144 self.className = self.className.replace( ' focus', '' ); 145 } else { 146 self.className += ' focus'; 147 } 148 } 149 150 self = self.parentElement; 151 } 152 } 153
154 /** 155 * Toggles `focus` class to allow submenu access on tablets. 156 */ 157 ( function( container ) { 158 var touchStartFn, i, 159 parentLink = container.querySelectorAll( '.menu-item-has-children > a, .page_item_has_children > a' ); 160 161 if ( 'ontouchstart' in window ) { 162 touchStartFn = function( e ) { 163 var menuItem = this.parentNode, i; 164 165 if ( ! menuItem.classList.contains( 'focus' ) ) { 166 e.preventDefault(); 167 for ( i = 0; i < menuItem.parentNode.children.length; ++i ) { 168 if ( menuItem === menuItem.parentNode.children[i] ) { 169 continue; 170 } 171 menuItem.parentNode.children[i].classList.remove( 'focus' ); 172 } 173 menuItem.classList.add( 'focus' ); 174 } else { 175 menuItem.classList.remove( 'focus' ); 176 } 177 }; 178 179 for ( i = 0; i < parentLink.length; ++i ) { 180 parentLink[i].addEventListener( 'touchstart', touchStartFn, false ); 181 } 182 } 183 }( container ) ); 184 185 //Search form 186 var searchcontainer, searchbutton, searchmenu; 187 188 searchcontainer = document.getElementById( 'header-search' ); 189 if ( ! searchcontainer ) 190 return; 191 192 searchbutton = document.getElementById( 'header-search-toggle' ); 193 if ( 'undefined' === typeof searchbutton ) 194 return; 195 196 searchbutton.onclick = function() { 197 if ( -1 !== searchcontainer.className.indexOf( 'toggled' ) ) { 198 searchcontainer.className = searchcontainer.className.replace( 'toggled', '' ); 199 } 200 else { 201 searchcontainer.className += 'toggled'; 202 container.className = container.className.replace( ' toggled', '' ); 203 } 204 }; 205 206} )(); 207; 208( function() { 209 var is_webkit = navigator.userAgent.toLowerCase().indexOf( 'webkit' ) > -1, 210 is_opera = navigator.userAgent.toLowerCase().indexOf( 'opera' ) > -1, 211 is_ie = navigator.userAgent.toLowerCase().indexOf( 'msie' ) > -1; 212 213 if ( ( is_webkit || is_opera || is_ie ) && 'undefined' !== typeof( document.getElementById ) ) { 214 var eventMethod = ( window.addEventListener ) ? 'addEventListener' : 'attachEvent'; 215 window[ eventMethod ]( 'hashchange', function() { 216 var element = document.getElementById( location.hash.substring( 1 ) ); 217 218 if ( element ) { 219 if ( ! /^(?:a|select|input|button|textarea)$/i.test( element.tagName ) ) 220 element.tabIndex = -1; 221 222 element.focus(); 223 } 224 }, false ); 225 } 226})(); 227; 228/** 229 * Observe how the user enters content into the comment form in order to determine whether it's a bot or not. 230 * 231 * Note that no actual input is being saved here, only counts and timings between events. 232 */ 233 234( function() { 235 // Passive event listeners are guaranteed to never call e.preventDefault(), 236 // but they're not supported in all browsers. Use this feature detection 237 // to determine whether they're available for use. 238 var supportsPassive = false; 239 240 try { 241 var opts = Object.defineProperty( {}, 'passive', { 242 get : function() { 243 supportsPassive = true; 244 } 245 } ); 246 247 window.addEventListener( 'testPassive', null, opts ); 248 window.removeEventListener( 'testPassive', null, opts ); 249 } catch ( e ) {} 250 251 function init() { 252 var input_begin = ''; 253 254 var keydowns = {}; 255 var lastKeyup = null; 256 var lastKeydown = null; 257 var keypresses = []; 258 259 var modifierKeys = []; 260 var correctionKeys = []; 261 262 var lastMouseup = null; 263 var lastMousedown = null; 264 var mouseclicks = []; 265 var mouseclickCoordinates = []; 266 267 var mousemoveTimer = null; 268 var lastMousemoveX = null; 269 var lastMousemoveY = null; 270 var lastMousemoveTime = null; 271 var mousemoveStart = null; 272 var mousemoves = []; 273 var intervalsBetweenMousemovesAndClicks = []; 274 275 var touchmoveCountTimer = null; 276 var touchmoveCount = 0; 277 278 var lastTouchEnd = null; 279 var lastTouchStart = null; 280 var touchEvents = []; 281 282 var scrollCountTimer = null; 283 var scrollCount = 0; 284 285 var correctionKeyCodes = [ 'Backspace', 'Delete', 'ArrowUp', 'ArrowDown', 'ArrowLeft', 'ArrowRight', 'Home', 'End', 'PageUp', 'PageDown' ]; 286 var modifierKeyCodes = [ 'Shift', 'CapsLock' ]; 287 288 var forms = document.querySelectorAll( 'form[method=post]' ); 289 290 for ( var i = 0; i < forms.length; i++ ) { 291 var form = forms[i]; 292 293 var formAction = form.getAttribute( 'action' ); 294 295 // Ignore forms that POST directly to other domains; these could be things like payment forms. 296 if ( formAction ) { 297 // Check that the form is posting to an external URL, not a path. 298 if ( formAction.indexOf( 'http://' ) == 0 || formAction.indexOf( 'https://' ) == 0 ) { 299 if ( formAction.indexOf( 'http://' + window.location.hostname + '/' ) != 0 && formAction.indexOf( 'https://' + window.location.hostname + '/' ) != 0 ) { 300 continue; 301 } 302 } 303 } 304 305 form.addEventListener( 'submit', function () { 306 var ak_bkp = prepare_array_for_request( keypresses ); 307 var ak_bmc = prepare_array_for_request( mouseclicks ); 308 var ak_bte = prepare_array_for_request( touchEvents ); 309 var ak_bmm = prepare_array_for_request( mousemoves ); 310 var ak_bcc = prepare_array_for_request( mouseclickCoordinates ); 311 var ak_bibmac = intervalsBetweenMousemovesAndClicks.slice( 0, 100 ).join( ';' ); 312 313 var input_fields = { 314 // When did the user begin entering any input? 315 'bib': input_begin, 316 317 // When was the form submitted? 318 'bfs': Date.now(), 319 320 // How many keypresses did they make? 321 'bkpc': keypresses.length, 322
323 // How quickly did they press a sample of keys, and how long between them? 324 'bkp': ak_bkp, 325 326 // How quickly did they click the mouse, and how long between clicks? 327 'bmc': ak_bmc, 328 329 // How many mouseclicks did they make? 330 'bmcc': mouseclicks.length, 331 332 // When did they press modifier keys (like Shift or Capslock)? 333 'bmk': modifierKeys.join( ';' ), 334 335 // When did they correct themselves? e.g., press Backspace, or use the arrow keys to move the cursor back 336 'bck': correctionKeys.join( ';' ), 337 338 // How many times did they move the mouse? 339 'bmmc': mousemoves.length, 340 341 // How many times did they move around using a touchscreen? 342 'btmc': touchmoveCount, 343 344 // How many times did they scroll? 345 'bsc': scrollCount, 346 347 // How quickly did they perform touch events, and how long between them? 348 'bte': ak_bte, 349 350 // How many touch events were there? 351 'btec' : touchEvents.length, 352 353 // How quickly did they move the mouse, and how long between moves? 354 'bmm' : ak_bmm, 355 356 // Click coordinates 357 'bcc' : ak_bcc, 358 359 // Milliseconds between last mouse movement and each click 360 'bibmac' : ak_bibmac 361 }; 362 363 var akismet_field_prefix = 'ak_'; 364 365 if ( this.getElementsByClassName ) { 366 // Check to see if we've used an alternate field name prefix. We store this as an attribute of the container around some of the Akismet fields. 367 var possible_akismet_containers = this.getElementsByClassName( 'akismet-fields-container' ); 368 369 for ( var containerIndex = 0; containerIndex < possible_akismet_containers.length; containerIndex++ ) { 370 var container = possible_akismet_containers.item( containerIndex ); 371 372 if ( container.getAttribute( 'data-prefix' ) ) { 373 akismet_field_prefix = container.getAttribute( 'data-prefix' ); 374 break; 375 } 376 } 377 } 378 379 for ( var field_name in input_fields ) { 380 var field = document.createElement( 'input' ); 381 field.setAttribute( 'type', 'hidden' ); 382 field.setAttribute( 'name', akismet_field_prefix + field_name ); 383 field.setAttribute( 'value', input_fields[ field_name ] ); 384 this.appendChild( field ); 385 } 386 }, supportsPassive ? { passive: true } : false ); 387 388 form.addEventListener( 'keydown', function ( e ) { 389 // If you hold a key down, some browsers send multiple keydown events in a row. 390 // Ignore any keydown events for a key that hasn't come back up yet. 391 if ( e.key in keydowns ) { 392 return; 393 } 394 395 var keydownTime = ( new Date() ).getTime(); 396 keydowns[ e.key ] = [ keydownTime ]; 397 398 if ( ! input_begin ) { 399 input_begin = keydownTime; 400 } 401 402 // In some situations, we don't want to record an interval since the last keypress -- for example, 403 // on the first keypress, or on a keypress after focus has changed to another element. Normally, 404 // we want to record the time between the last keyup and this keydown. But if they press a 405 // key while already pressing a key, we want to record the time between the two keydowns. 406 407 var lastKeyEvent = Math.max( lastKeydown, lastKeyup ); 408 409 if ( lastKeyEvent ) { 410 keydowns[ e.key ].push( keydownTime - lastKeyEvent ); 411 } 412 413 lastKeydown = keydownTime; 414 }, supportsPassive ? { passive: true } : false ); 415 416 form.addEventListener( 'keyup', function ( e ) { 417 if ( ! ( e.key in keydowns ) ) { 418 // This key was pressed before this script was loaded, or a mouseclick happened during the keypress, or... 419 return; 420 } 421 422 var keyupTime = ( new Date() ).getTime(); 423 424 if ( 'TEXTAREA' === e.target.nodeName || 'INPUT' === e.target.nodeName ) { 425 if ( -1 !== modifierKeyCodes.indexOf( e.key ) ) { 426 modifierKeys.push( keypresses.length - 1 ); 427 } else if ( -1 !== correctionKeyCodes.indexOf( e.key ) ) { 428 correctionKeys.push( keypresses.length - 1 ); 429 } else { 430 // ^ Don't record timings for keys like Shift or backspace, since they 431 // typically get held down for longer than regular typing. 432 433 var keydownTime = keydowns[ e.key ][0]; 434 435 var keypress = []; 436 437 // Keypress duration. 438 keypress.push( keyupTime - keydownTime ); 439 440 // Amount of time between this keypress and the previous keypress. 441 if ( keydowns[ e.key ].length > 1 ) { 442 keypress.push( keydowns[ e.key ][1] ); 443 } 444 445 keypresses.push( keypress ); 446 } 447 } 448
449 delete keydowns[ e.key ]; 450 451 lastKeyup = keyupTime; 452 }, supportsPassive ? { passive: true } : false ); 453 454 form.addEventListener( "focusin", function ( e ) { 455 lastKeydown = null; 456 lastKeyup = null; 457 keydowns = {}; 458 }, supportsPassive ? { passive: true } : false ); 459 460 form.addEventListener( "focusout", function ( e ) { 461 lastKeydown = null; 462 lastKeyup = null; 463 keydowns = {}; 464 }, supportsPassive ? { passive: true } : false ); 465 } 466 467 document.addEventListener( 'mousedown', function ( e ) { 468 lastMousedown = ( new Date() ).getTime(); 469 470 if ( lastMousemoveTime ) { 471 intervalsBetweenMousemovesAndClicks.push( lastMousedown - lastMousemoveTime ); 472 } 473 474 var mouseclickCoordinate = []; 475 476 var rect = e.target.getBoundingClientRect(); 477 var relativeX = e.clientX - rect.left; 478 var relativeY = e.clientY - rect.top; 479 480 // Pixel offset of the click within the target element. 481 mouseclickCoordinate.push( Math.round( relativeX ) ); 482 mouseclickCoordinate.push( Math.round( relativeY ) ); 483 484 // Percentage offset of the click within the target element. 485 mouseclickCoordinate.push( rect.width > 0 ? Math.round( relativeX / rect.width * 100 ) : 0 ); 486 mouseclickCoordinate.push( rect.height > 0 ? Math.round( relativeY / rect.height * 100 ) : 0 ); 487 488 mouseclickCoordinates.push( mouseclickCoordinate ); 489 }, supportsPassive ? { passive: true } : false ); 490 491 document.addEventListener( 'mouseup', function ( e ) { 492 if ( ! lastMousedown ) { 493 // If the mousedown happened before this script was loaded, but the mouseup happened after... 494 return; 495 } 496 497 var now = ( new Date() ).getTime(); 498 499 var mouseclick = []; 500 mouseclick.push( now - lastMousedown ); 501 502 if ( lastMouseup ) { 503 mouseclick.push( lastMousedown - lastMouseup ); 504 } 505 506 mouseclicks.push( mouseclick ); 507 508 lastMouseup = now; 509 510 // If the mouse has been clicked, don't record this time as an interval between keypresses. 511 lastKeydown = null; 512 lastKeyup = null; 513 keydowns = {}; 514 }, supportsPassive ? { passive: true } : false ); 515 516 document.addEventListener( 'mousemove', function ( e ) { 517 lastMousemoveTime = ( new Date() ).getTime(); 518 519 if ( mousemoveTimer ) { 520 clearTimeout( mousemoveTimer ); 521 mousemoveTimer = null; 522 } 523 else { 524 mousemoveStart = lastMousemoveTime; 525 lastMousemoveX = e.offsetX; 526 lastMousemoveY = e.offsetY; 527 } 528 529 mousemoveTimer = setTimeout( function ( theEvent, originalMousemoveStart ) { 530 var now = ( new Date() ).getTime() - 500; // To account for the timer delay. 531 532 var mousemove = []; 533 mousemove.push( now - originalMousemoveStart ); 534 mousemove.push( 535 Math.round( 536 Math.sqrt( 537 Math.pow( theEvent.offsetX - lastMousemoveX, 2 ) + 538 Math.pow( theEvent.offsetY - lastMousemoveY, 2 ) 539 ) 540 ) 541 ); 542 543 if ( mousemove[1] > 0 ) { 544 // If there was no measurable distance, then it wasn't really a move. 545 mousemoves.push( mousemove ); 546 } 547 548 mousemoveStart = null; 549 mousemoveTimer = null; 550 }, 500, e, mousemoveStart ); 551 }, supportsPassive ? { passive: true } : false ); 552 553 document.addEventListener( 'touchmove', function ( e ) { 554 if ( touchmoveCountTimer ) { 555 clearTimeout( touchmoveCountTimer ); 556 } 557 558 touchmoveCountTimer = setTimeout( function () { 559 touchmoveCount++; 560 }, 500 ); 561 }, supportsPassive ? { passive: true } : false ); 562 563 document.addEventListener( 'touchstart', function ( e ) { 564 lastTouchStart = ( new Date() ).getTime(); 565 }, supportsPassive ? { passive: true } : false ); 566 567 document.addEventListener( 'touchend', function ( e ) { 568 if ( ! lastTouchStart ) { 569 // If the touchstart happened before this script was loaded, but the touchend happened after... 570 return; 571 } 572 573 var now = ( new Date() ).getTime(); 574 575 var touchEvent = []; 576 touchEvent.push( now - lastTouchStart ); 577 578 if ( lastTouchEnd ) { 579 touchEvent.push( lastTouchStart - lastTouchEnd ); 580 } 581 582 touchEvents.push( touchEvent ); 583 584 lastTouchEnd = now; 585 586 // Don't record this time as an interval between keypresses. 587 lastKeydown = null; 588 lastKeyup = null; 589 keydowns = {}; 590 }, supportsPassive ? { passive: true } : false ); 591 592 document.addEventListener( 'scroll', function ( e ) {
593 if ( scrollCountTimer ) { 594 clearTimeout( scrollCountTimer ); 595 } 596 597 scrollCountTimer = setTimeout( function () { 598 scrollCount++; 599 }, 500 ); 600 }, supportsPassive ? { passive: true } : false ); 601 } 602 603 /** 604 * For the timing/coordinate data that is collected, don't send more than `limit` data points in the request. 605 * Choose a random slice and send those, with each batch separated by semicolons and the items in each batch 606 * separated by commas. 607 */ 608 function prepare_array_for_request( a, limit ) { 609 if ( ! limit ) { 610 limit = 100; 611 } 612 613 var rv = ''; 614 615 if ( a.length > 0 ) { 616 var random_starting_point = Math.max( 0, Math.floor( Math.random() * a.length - limit ) ); 617 618 for ( var i = 0; i < limit && i < a.length; i++ ) { 619 var entry = a[ random_starting_point + i ]; 620 rv += entry.join( ',' ) + ';'; 621 } 622 } 623 624 return rv; 625 } 626 627 if ( document.readyState !== 'loading' ) { 628 init(); 629 } else { 630 document.addEventListener( 'DOMContentLoaded', init ); 631 } 632})();;
Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.