1/* global wpcom_reblog */ 2 3var jetpackLikesWidgetBatch = []; 4var jetpackLikesMasterReady = false; 5 6// Due to performance problems on pages with a large number of widget iframes that need to be loaded, 7// we are limiting the processing at any instant to unloaded widgets that are currently in viewport, 8// plus this constant that will allow processing of widgets above and bellow the current fold. 9// This aim of it is to improve the UX and hide the transition from unloaded to loaded state from users. 10var jetpackLikesLookAhead = 2000; // pixels 11 12// Keeps track of loaded comment likes widget so we can unload them when they are scrolled out of view. 13var jetpackCommentLikesLoadedWidgets = []; 14 15var jetpackLikesDocReadyPromise = new Promise( resolve => { 16 if ( document.readyState !== 'loading' ) { 17 resolve(); 18 } else { 19 window.addEventListener( 'DOMContentLoaded', () => resolve() ); 20 } 21} ); 22 23function JetpackLikesPostMessage( message, target ) { 24 if ( typeof message === 'string' ) { 25 try { 26 message = JSON.parse( message ); 27 } catch ( e ) { 28 return; 29 } 30 } 31 32 if ( target && typeof target.postMessage === 'function' ) { 33 try { 34 target.postMessage( 35 JSON.stringify( { 36 type: 'likesMessage', 37 data: message, 38 } ), 39 '*' 40 ); 41 } catch ( e ) { 42 return; 43 } 44 } 45} 46 47function JetpackLikesBatchHandler() { 48 const requests = []; 49 document.querySelectorAll( 'div.jetpack-likes-widget-unloaded' ).forEach( widget => { 50 if ( jetpackLikesWidgetBatch.indexOf( widget.id ) > -1 ) { 51 return; 52 } 53 54 if ( ! jetpackIsScrolledIntoView( widget ) ) { 55 return; 56 } 57 58 jetpackLikesWidgetBatch.push( widget.id ); 59 60 var regex = /like-(post|comment)-wrapper-(\d+)-(\d+)-(\w+)/, 61 match = regex.exec( widget.id ), 62 info; 63 64 if ( ! match || match.length !== 5 ) { 65 return; 66 } 67 68 info = { 69 blog_id: match[ 2 ], 70 width: widget.width, 71 }; 72 73 if ( 'post' === match[ 1 ] ) { 74 info.post_id = match[ 3 ]; 75 } else if ( 'comment' === match[ 1 ] ) { 76 info.comment_id = match[ 3 ]; 77 } 78 79 info.obj_id = match[ 4 ]; 80 81 requests.push( info ); 82 } ); 83 84 if ( requests.length > 0 ) { 85 JetpackLikesPostMessage( 86 { event: 'initialBatch', requests: requests }, 87 window.frames[ 'likes-master' ] 88 ); 89 } 90} 91 92function JetpackLikesMessageListener( event ) { 93 let message = event && event.data; 94 if ( typeof message === 'string' ) { 95 try { 96 message = JSON.parse( message ); 97 } catch ( err ) { 98 return; 99 } 100 } 101 102 const type = message && message.type; 103 const data = message && message.data; 104 105 if ( type !== 'likesMessage' || typeof data.event === 'undefined' ) { 106 return; 107 } 108 109 // We only allow messages from one origin 110 const allowedOrigin = 'https://widgets.wp.com'; 111 if ( allowedOrigin !== event.origin ) { 112 return; 113 } 114 115 switch ( data.event ) { 116 case 'masterReady': 117 jetpackLikesDocReadyPromise.then( () => { 118 jetpackLikesMasterReady = true; 119 120 const stylesData = { 121 event: 'injectStyles', 122 }; 123 const sdTextColor = document.querySelector( '.sd-text-color' ); 124 const sdLinkColor = document.querySelector( '.sd-link-color' ); 125 const sdTextColorStyles = ( sdTextColor && getComputedStyle( sdTextColor ) ) || {}; 126 const sdLinkColorStyles = ( sdLinkColor && getComputedStyle( sdLinkColor ) ) || {}; 127 128 // enable reblogs if they are enabled for the page 129 if ( document.body.classList.contains( 'jetpack-reblog-enabled' ) ) { 130 JetpackLikesPostMessage( { event: 'reblogsEnabled' }, window.frames[ 'likes-master' ] ); 131 } 132 133 stylesData.textStyles = { 134 color: sdTextColorStyles[ 'color' ],
135 fontFamily: sdTextColorStyles[ 'font-family' ], 136 fontSize: sdTextColorStyles[ 'font-size' ], 137 direction: sdTextColorStyles[ 'direction' ], 138 fontWeight: sdTextColorStyles[ 'font-weight' ], 139 fontStyle: sdTextColorStyles[ 'font-style' ], 140 textDecoration: sdTextColorStyles[ 'text-decoration' ], 141 }; 142 143 stylesData.linkStyles = { 144 color: sdLinkColorStyles[ 'color' ], 145 fontFamily: sdLinkColorStyles[ 'font-family' ], 146 fontSize: sdLinkColorStyles[ 'font-size' ], 147 textDecoration: sdLinkColorStyles[ 'text-decoration' ], 148 fontWeight: sdLinkColorStyles[ 'font-weight' ], 149 fontStyle: sdLinkColorStyles[ 'font-style' ], 150 }; 151 152 JetpackLikesPostMessage( stylesData, window.frames[ 'likes-master' ] ); 153 154 JetpackLikesBatchHandler(); 155 } ); 156 157 break; 158 159 // We're keeping this for planned future follow ups. 160 // @see: https://github.com/Automattic/jetpack/pull/42361#discussion_r1995338815 161 case 'showLikeWidget': 162 break; 163 164 // We're keeping this for planned future follow ups. 165 // @see: https://github.com/Automattic/jetpack/pull/42361#discussion_r1995338815 166 case 'showCommentLikeWidget': 167 break; 168 169 case 'killCommentLikes': 170 // If kill switch for comment likes is enabled remove all widgets wrappers and `Loading...` placeholders. 171 document 172 .querySelectorAll( '.jetpack-comment-likes-widget-wrapper' ) 173 .forEach( wrapper => wrapper.remove() ); 174 break; 175 176 case 'clickReblogFlair': 177 if ( wpcom_reblog && typeof wpcom_reblog.toggle_reblog_box_flair === 'function' ) { 178 wpcom_reblog.toggle_reblog_box_flair( data.obj_id, data.post_id ); 179 } 180 break; 181 182 case 'hideOtherGravatars': { 183 hideLikersPopover(); 184 break; 185 } 186 187 case 'showOtherGravatars': { 188 const container = document.querySelector( '#likes-other-gravatars' ); 189 190 if ( ! container ) { 191 break; 192 } 193 194 const list = container.querySelector( 'ul' ); 195 196 container.style.display = 'none'; 197 list.innerHTML = ''; 198 199 container 200 .querySelectorAll( '.likes-text span' ) 201 .forEach( item => ( item.textContent = data.totalLikesLabel ) ); 202 203 ( data.likers || [] ).forEach( async ( liker, index ) => { 204 if ( liker.profile_URL.substr( 0, 4 ) !== 'http' ) { 205 // We only display gravatars with http or https schema 206 return; 207 } 208 209 const element = document.createElement( 'li' ); 210 list.append( element ); 211 212 element.innerHTML = ` 213 <a href="${ encodeURI( liker.profile_URL ) }" rel="nofollow" target="_parent" class="wpl-liker"> 214 <img src="${ encodeURI( liker.avatar_URL ) }" 215 alt="" 216 style="width: 28px; height: 28px;" /> 217 <span></span> 218 </a> 219 `; 220 221 222 // Add some extra attributes through native methods, to ensure strings are sanitized. 223 element.classList.add( liker.css_class ); 224 element.querySelector( 'img' ).alt = data.avatarAltTitle.replace( '%s', liker.name ); 225 element.querySelector( 'span' ).innerText = liker.name; 226 227 if ( index === data.likers.length - 1 ) { 228 element.addEventListener( 'keydown', ( e ) => { 229 if ( e.key === 'Tab' && ! e.shiftKey ) { 230 e.preventDefault(); 231 hideLikersPopover(); 232 233 JetpackLikesPostMessage( 234 { event: 'focusLikesCount', parent: data.parent }, 235 window.frames[ 'likes-master' ] 236 ); 237 } 238 } ); 239 } 240 } ); 241 242 const positionPopup = function() { 243 const containerStyle = getComputedStyle(container); 244 const isRtl = containerStyle.direction === 'rtl'; 245 246 const el = document.querySelector( `*[name='${ data.parent }']` ); 247 const rect = el.getBoundingClientRect(); 248 const win = el.ownerDocument.defaultView; 249 250 const offset = { 251 top: rect.top + win.pageYOffset, 252 left: rect.left + win.pageXOffset, 253 }; 254 255 // don't display yet or we get skewed window.innerWidth later 256 container.style.display = 'none'; 257 258 let containerLeft = 0; 259 container.style.top = offset.top + data.position.top - 1 + 'px'; 260 261 if ( isRtl ) { 262 const visibleAvatarsCount = data && data.likers ? Math.min( data.likers.length, 5 ) : 0; 263 // 24px is the width of the avatar + 4px is the padding between avatars 264 containerLeft = offset.left + data.position.left + 24 * visibleAvatarsCount + 4; 265 container.style.transform = 'translateX(-100%)'; 266 } else { 267 containerLeft = offset.left + data.position.left; 268 } 269 container.style.left = containerLeft + 'px'; 270 271 // Container width - padding
272 const initContainerWidth = data.width - 20; 273 const rowLength = Math.floor( initContainerWidth / 37 ); 274 // # of rows + (avatar + avatar padding) + text above + container padding 275 let height = Math.ceil( data.likers.length / rowLength ) * 37 + 17 + 22; 276 if ( height > 204 ) { 277 height = 204; 278 } 279 280 // If the popup is overflows viewport width, we should show it on the next line 281 // Push it offscreen to calculated rendered width 282 const windowWidth = win.innerWidth; 283 container.style.left = '-9999px'; 284 container.style.display = 'block'; 285 286 // If the popup exceeds the viewport width, 287 // flip the position of the popup. 288 const containerWidth = container.offsetWidth; 289 const containerRight = containerLeft + containerWidth; 290 291 if ( containerRight > windowWidth && ! isRtl) { 292 containerLeft = rect.left + rect.width - containerWidth; 293 } else if ( containerLeft - containerWidth < 0 && isRtl ) { 294 container.style.transform = 'none'; 295 containerLeft = rect.left; 296 } 297 // Set the container left 298 container.style.left = containerLeft + 'px'; 299 300 container.setAttribute( 'aria-hidden', 'false' ); 301 } 302 303 positionPopup(); 304 container.focus(); 305 306 const debounce = function( func, wait ) { 307 var timeout; 308 return function() { 309 var context = this; 310 var args = arguments; 311 clearTimeout( timeout ); 312 timeout = setTimeout( function() { 313 func.apply( context, args ); 314 }, wait ); 315 }; 316 }; 317 318 const debouncedPositionPopup = debounce( positionPopup, 100 ); 319 320 // Keep a reference of this function in the element itself 321 // so that we can destroy it later 322 container.__resizeHandler = debouncedPositionPopup; 323 324 // When window is resized, resize the popup. 325 window.addEventListener( "resize", debouncedPositionPopup ); 326 } 327 } 328} 329 330window.addEventListener( 'message', JetpackLikesMessageListener ); 331 332function hideLikersPopover() { 333 const container = document.querySelector( '#likes-other-gravatars' ); 334 335 if ( container ) { 336 container.style.display = 'none'; 337 container.setAttribute( 'aria-hidden', 'true' ); 338 339 // Remove the resize event listener and cleanup. 340 const resizeHandler = container.__resizeHandler; 341 if ( resizeHandler ) { 342 window.removeEventListener( "resize", resizeHandler ); 343 delete container.__resizeHandler; 344 } 345 } 346} 347 348document.addEventListener( 'click', hideLikersPopover ); 349 350function JetpackLikesWidgetQueueHandler() { 351 var wrapperID; 352 353 if ( ! jetpackLikesMasterReady ) { 354 setTimeout( JetpackLikesWidgetQueueHandler, 500 ); 355 return; 356 } 357 358 // Restore widgets to initial unloaded state when they are scrolled out of view. 359 jetpackUnloadScrolledOutWidgets(); 360 361 var unloadedWidgetsInView = jetpackGetUnloadedWidgetsInView(); 362 363 if ( unloadedWidgetsInView.length > 0 ) { 364 // Grab any unloaded widgets for a batch request 365 JetpackLikesBatchHandler(); 366 } 367 368 for ( var i = 0, length = unloadedWidgetsInView.length; i <= length - 1; i++ ) { 369 wrapperID = unloadedWidgetsInView[ i ].id; 370 371 if ( ! wrapperID ) { 372 continue; 373 } 374 375 jetpackLoadLikeWidgetIframe( wrapperID ); 376 } 377} 378 379function jetpackLoadLikeWidgetIframe( wrapperID ) { 380 if ( typeof wrapperID === 'undefined' ) { 381 return; 382 } 383 384 const wrapper = document.querySelector( '#' + wrapperID ); 385 wrapper.querySelectorAll( 'iframe' ).forEach( iFrame => iFrame.remove() ); 386 387 const placeholder = wrapper.querySelector( '.likes-widget-placeholder' ); 388 389 // Post like iframe 390 if ( placeholder && placeholder.classList.contains( 'post-likes-widget-placeholder' ) ) { 391 const postLikesFrame = document.createElement( 'iframe' ); 392 393 postLikesFrame.classList.add( 'post-likes-widget', 'jetpack-likes-widget' ); 394 postLikesFrame.name = wrapper.dataset.name; 395 postLikesFrame.src = wrapper.dataset.src; 396 postLikesFrame.height = '55px'; 397 postLikesFrame.width = '100%'; 398 postLikesFrame.frameBorder = '0'; 399 postLikesFrame.scrolling = 'no'; 400 postLikesFrame.title = wrapper.dataset.title; 401 402 placeholder.after( postLikesFrame ); 403 } 404 405 // Comment like iframe 406 if ( placeholder.classList.contains( 'comment-likes-widget-placeholder' ) ) { 407 const commentLikesFrame = document.createElement( 'iframe' ); 408 409 commentLikesFrame.class = 'comment-likes-widget-frame jetpack-likes-widget-frame'; 410 commentLikesFrame.name = wrapper.dataset.name; 411 commentLikesFrame.src = wrapper.dataset.src; 412 commentLikesFrame.height = '18px'; 413 commentLikesFrame.width = '100%'; 414 commentLikesFrame.frameBorder = '0'; 415 commentLikesFrame.scrolling = 'no'; 416 417 wrapper.querySelector( '.comment-like-feedback' ).after( commentLikesFrame ); 418 419 jetpackCommentLikesLoadedWidgets.push( commentLikesFrame ); 420 } 421 422 wrapper.classList.remove( 'jetpack-likes-widget-unloaded' ); 423 wrapper.classList.add( 'jetpack-likes-widget-loading' ); 424 425 wrapper.querySelector( 'iframe' ).addEventListener( 'load', e => { 426 JetpackLikesPostMessage( 427 { event: 'loadLikeWidget', name: e.target.name, width: e.target.width }, 428 window.frames[ 'likes-master' ] 429 ); 430 431 wrapper.classList.remove( 'jetpack-likes-widget-loading' ); 432 wrapper.classList.add( 'jetpack-likes-widget-loaded' ); 433 } ); 434} 435 436function jetpackGetUnloadedWidgetsInView() { 437 const unloadedWidgets = document.querySelectorAll( 'div.jetpack-likes-widget-unloaded' ); 438 439 return [ ...unloadedWidgets ].filter( item => jetpackIsScrolledIntoView( item ) ); 440} 441 442function jetpackIsScrolledIntoView( element ) { 443 const top = element.getBoundingClientRect().top; 444 const bottom = element.getBoundingClientRect().bottom; 445
446 // Allow some slack above and bellow the fold with jetpackLikesLookAhead, 447 // with the aim of hiding the transition from unloaded to loaded widget from users. 448 return top + jetpackLikesLookAhead >= 0 && bottom <= window.innerHeight + jetpackLikesLookAhead; 449} 450 451function jetpackUnloadScrolledOutWidgets() { 452 for ( let i = jetpackCommentLikesLoadedWidgets.length - 1; i >= 0; i-- ) { 453 const currentWidgetIframe = jetpackCommentLikesLoadedWidgets[ i ]; 454 455 if ( ! jetpackIsScrolledIntoView( currentWidgetIframe ) ) { 456 const widgetWrapper = 457 currentWidgetIframe && 458 currentWidgetIframe.parentElement && 459 currentWidgetIframe.parentElement.parentElement; 460 461 // Restore parent class to 'unloaded' so this widget can be picked up by queue manager again if needed. 462 widgetWrapper.classList.remove( 'jetpack-likes-widget-loaded' ); 463 widgetWrapper.classList.remove( 'jetpack-likes-widget-loading' ); 464 widgetWrapper.classList.add( 'jetpack-likes-widget-unloaded' ); 465 466 // Bring back the loading placeholder into view. 467 widgetWrapper 468 .querySelectorAll( '.comment-likes-widget-placeholder' ) 469 .forEach( item => ( item.style.display = 'block' ) ); 470 471 // Remove it from the list of loaded widgets. 472 jetpackCommentLikesLoadedWidgets.splice( i, 1 ); 473 474 // Remove comment like widget iFrame. 475 currentWidgetIframe.remove(); 476 } 477 } 478} 479 480var jetpackWidgetsDelayedExec = function ( after, fn ) { 481 var timer; 482 return function () { 483 clearTimeout( timer ); 484 timer = setTimeout( fn, after ); 485 }; 486}; 487 488var jetpackOnScrollStopped = jetpackWidgetsDelayedExec( 250, JetpackLikesWidgetQueueHandler ); 489 490// Load initial batch of widgets, prior to any scrolling events. 491JetpackLikesWidgetQueueHandler(); 492 493// Add event listener to execute queue handler after scroll. 494window.addEventListener( 'scroll', jetpackOnScrollStopped, true ); 495; 496/** 497 * Comment Likes - JavaScript 498 * 499 * This handles liking and unliking comments, as well as viewing who has 500 * liked a particular comment. 501 * 502 * @dependency Swipe (dynamically loaded when needed) 503 * 504 * @package Comment_Likes 505 * @subpackage JavaScript 506 */ 507(function () { 508 function init() { 509 let extWin; 510 let extWinCheck; 511 let commentLikeEvent; 512 513 // Only run once. 514 if (window.comment_likes_loaded) { 515 return; 516 } 517 window.comment_likes_loaded = true; 518 519 // Client-side cache of who liked a particular comment to avoid 520 // having to hit the server multiple times for the same data. 521 const commentLikeCache = {}; 522 523 let swipeLibPromise; 524 525 // Load the Swipe library, if it's not already loaded. 526 function swipeLibLoader() { 527 if (!swipeLibPromise) { 528 swipeLibPromise = new Promise((resolve, reject) => { 529 if (window.Swipe) { 530 resolve(window.Swipe); 531 } else { 532 const swipeScript = document.createElement('script'); 533 swipeScript.src = comment_like_text.swipeUrl; 534 swipeScript.async = true; 535 document.body.appendChild(swipeScript); 536 swipeScript.addEventListener('load', () => resolve(window.Swipe)); 537 swipeScript.addEventListener('error', error => reject(error)); 538 } 539 }); 540 } 541 return swipeLibPromise; 542 } 543 544 /** 545 * Parse the comment ID from a comment like link. 546 */ 547 function getCommentId(link) { 548 const commentId = 549 link && link.getAttribute('href') && link.getAttribute('href').split('like_comment='); 550 return commentId[1].split('&_wpnonce=')[0]; 551 } 552 553 /** 554 * Handle an ajax action on the comment like link. 555 */ 556 function handleLinkAction(link, action, commentId, callback) { 557 const nonce = 558 link && link.getAttribute('href') && link.getAttribute('href').split('_wpnonce=')[1]; 559 560 fetch('/wp-admin/admin-ajax.php', { 561 method: 'POST', 562 body: new URLSearchParams({ 563 action: action, 564 _wpnonce: nonce, 565 like_comment: commentId, 566 blog_id: Number(link.dataset.blog), 567 }), 568 headers: { 569 'Content-Type': 'application/x-www-form-urlencoded; charset=UTF-8', 570 'X-Requested-With': 'XMLHttpRequest', 571 Accept: 'application/json', 572 'cache-control': 'no-cache', 573 pragma: 'no-cache', 574 }, 575 }) 576 .then(response => response.json()) 577 .then(callback); 578 } 579 580 function startPolling() { 581 // Append cookie polling login iframe to this window to wait for user to finish logging in (or cancel) 582 const loginIframe = document.createElement('iframe'); 583 loginIframe.id = 'wp-login-polling-iframe'; 584 loginIframe.src = 'https://wordpress.com/public.api/connect/?iframe=true'; 585 document.body.appendChild(loginIframe); 586 loginIframe.style.display = 'none'; 587 } 588 589 function stopPolling() { 590 const iframe = document.querySelector('#wp-login-polling-iframe'); 591 if (iframe) { 592 iframe.remove(); 593 } 594 } 595 596 function hide(el) { 597 if (el && el.style) { 598 el.style.display = 'none'; 599 } 600 } 601 602 function show(el) { 603 if (el && el.style) { 604 el.style.removeProperty('display'); 605 } 606 } 607 608 // Overlay used for displaying comment like info. 609 class Overlay { 610 constructor() { 611 // Overlay element. 612 this.el = document.createElement('div'); 613 this.el.classList.add('comment-likes-overlay'); 614 document.body.appendChild(this.el); 615 hide(this.el); 616 617 this.el.addEventListener('mouseenter', () => { 618 // Don't hide the overlay if the user is mousing over it. 619 overlay.cancelHide(); 620 }); 621 622 this.el.addEventListener('mouseleave', () => overlay.requestHide()); 623 624 // Inner contents of overlay. 625 this.innerEl = null; 626 627 // Instance of the Swipe library. 628 this.swipe = null; 629 630 // Timeout used for hiding the overlay. 631 this.hideTimeout = null; 632 } 633 634 // Initialise the overlay for use, removing any old content. 635 clear() { 636 // Unload any previous instance of Swipe (to avoid leaking a global 637 // event handler). This is done before clearing the contents of the 638 // overlay because Swipe expects the slides to still be present. 639 if (this.swipe) { 640 this.swipe.kill(); 641 this.swipe = null; 642 } 643 this.el.innerHTML = ''; 644 this.innerEl = document.createElement('div'); 645 this.innerEl.classList.add('inner'); 646 this.el.appendChild(this.innerEl); 647 } 648 649 /** 650 * Construct a list (<ul>) of user (gravatar, name) details. 651 * 652 * @param data liker data returned from the server 653 * @param klass CSS class to apply to the <ul> element 654 * @param start index of user to start at 655 * @param length number of users to include in the list 656 * 657 * @return A container element with the list 658 */ 659 getUserBits(data, klass, start, length) { 660 start = start || 0; 661 let last = start + (length || data.length); 662 last = last > data.length ? data.length : last; 663 const container = document.createElement('div'); 664 container.classList.add('liker-list'); 665 let html = `<ul class="${klass || ''}">`; 666 for (let i = start; i < last; ++i) { 667 const user = data[i]; 668 html += ` 669 <li> 670 <a rel="nofollow" title="${user.display_name_esc}" href="${user.profile_url_esc}"> 671 <img src="${user.avatar_url_esc}" alt="${user.display_name_esc}" /> 672 <span class="user-name">${user.display_name_esc}</span> 673 </a> 674 </li> 675 `; 676 } 677 html += '</ul>'; 678 container.innerHTML = html; 679 return container; 680 } 681 682 /** 683 * Render the display of who has liked this comment. The type of 684 * display depends on how many people have liked the comment. 685 * If more than 10 people have liked the comment, this function 686 * renders navigation controls and sets up the Swipe library for 687 * changing between pages. 688 * 689 * @param link the element over which the user is hovering 690 * @param data the results retrieved from the server 691 */ 692 showLikes(link, data) { 693 this.clear(); 694 695 link.dataset.likeCount = data.length; 696 if (data.length === 0) { 697 // No likers after all. 698 hide(this.el); 699 return; 700 } 701 702 this.innerEl.style.padding = '12px'; 703 704 if (data.length < 6) { 705 // Only one column needed. 706 this.innerEl.style.maxWidth = '200px'; 707 this.innerEl.innerHTML = ''; 708 this.innerEl.appendChild(this.getUserBits(data, 'single')); 709 this.setPosition(link); 710 } else if (data.length < 11) { 711 // Two columns, but only one page. 712 this.innerEl.innerHTML = ''; 713 this.innerEl.appendChild(this.getUserBits(data, 'double')); 714 this.setPosition(link); 715 } else { 716 // Multiple pages. 717 this.renderLikesWithPagination(data, link); 718 } 719 } 720 721 /**
722 * Render multiple pages of likes with pagination controls. 723 * This function is intended to be called by `showLikes` above. 724 * 725 * @param data the results retrieved from the server 726 */ 727 renderLikesWithPagination(data, link) { 728 swipeLibLoader().then(() => { 729 const page_count = Math.ceil(data.length / 10); 730 // Swipe requires two nested containers. 731 const swipe = document.createElement('div'); 732 swipe.classList.add('swipe'); 733 this.innerEl.appendChild(swipe); 734 735 const wrap = document.createElement('div'); 736 wrap.classList.add('swipe-wrap'); 737 swipe.appendChild(wrap); 738 739 for (let i = 0; i < page_count; ++i) { 740 wrap.appendChild(this.getUserBits(data, 'double', i * 10, 10)); 741 } 742 743 /** 744 * Navigation controls. 745 * This is based on the Newdash controls found in 746 * reader/recommendations-templates.php 747 */ 748 const nav = document.createElement('nav'); 749 nav.classList.add('slider-nav'); 750 751 let navContents = ` 752 <a href="#" class="prev"> 753 <span class="noticon noticon-previous" title="Previous" alt="<"></span> 754 </a> 755 <span class="position"> 756 `; 757 for (let i = 0; i < page_count; ++i) { 758 navContents += `<em data-page="${i}" class="${i === 0 ? 'on' : ''}">•</em>`; 759 } 760 navContents += ` 761 </span> 762 <a href="#" class="next"> 763 <span class="noticon noticon-next" title="Next" alt=">"></span> 764 </a> 765 `; 766 this.innerEl.appendChild(nav); 767 nav.innerHTML = navContents; 768 769 /** Set up Swipe. **/ 770 // Swipe cannot be set up successfully unless its container 771 // is visible, so we show it now. 772 show(this.el); 773 this.setPosition(link); 774 775 this.swipe = new Swipe(swipe, { 776 callback: function (pos) { 777 // Update the pagination indicators. 778 // 779 // If there are exactly two pages, Swipe has a weird 780 // special case where it duplicates both pages and 781 // can return index 2 and 3 even though those aren't 782 // real pages (see swipe.js, line 47). To deal with 783 // this, we use the expression `pos % page_count`. 784 pos = pos % page_count; 785 nav.querySelectorAll('em').forEach(em => { 786 const page = Number(em.dataset.page); 787 em.setAttribute('class', pos === page ? 'on' : ''); 788 }); 789 }, 790 }); 791 792 nav.querySelectorAll('em').forEach(em => { 793 em.addEventListener('click', e => { 794 // Go to the page corresponding to the indicator clicked. 795 this.swipe.slide(Number(em.dataset.page)); 796 e.preventDefault(); 797 }); 798 }); 799 // Previous and next buttons. 800 nav.querySelector('.prev').addEventListener('click', e => { 801 this.swipe.prev(); 802 e.preventDefault(); 803 }); 804 nav.querySelector('.next').addEventListener('click', e => { 805 this.swipe.next(); 806 e.preventDefault(); 807 }); 808 }); 809 } 810 811 /** 812 * Open the overlay and show a loading message. 813 */ 814 showLoadingMessage(link) { 815 this.clear(); 816 this.innerEl.textContent = comment_like_text.loading; 817 this.setPosition(link); 818 } 819 820 /** 821 * Position the overlay near the current comment. 822 * 823 * @param link element near which to position the overlay 824 */ 825 setPosition(link) { 826 // Prepare a down arrow icon for the bottom of the overlay. 827 const icon = document.createElement('span'); 828 this.el.appendChild(icon); 829 icon.classList.add('icon', 'noticon', 'noticon-downarrow'); 830 icon.style.textShadow = '0px 1px 1px rgb(223, 223, 223)'; 831 832 const rect = link.getBoundingClientRect(); 833 const win = document.defaultView; 834 const offset = { 835 top: rect.top + win.scrollY, 836 left: rect.left + win.scrollX, 837 }; 838 839 // Take measurements with the element fully visible. 840 show(this.el); 841 let left = offset.left - (this.el.offsetWidth - link.offsetWidth) / 2; 842 left = left < 5 ? 5 : left; 843 let top = offset.top - this.el.offsetHeight + 5; 844 hide(this.el); 845 846 const adminBar = document.querySelector('#wpadminbar'); 847 848 // Check if the overlay would appear off the screen. 849 if (top < win.scrollY + ((adminBar && adminBar.offsetHeight) || 0)) { 850 // We'll display the overlay beneath the link instead. 851 top = offset.top + link.offsetHeight; 852 // Instead of using the down arrow icon, use an up arrow. 853 icon.remove(); 854 this.el.prepend(icon); 855 icon.classList.remove('noticon-downarrow'); 856 icon.classList.add('noticon-uparrow'); 857 icon.style.textShadow = '0px -1px 1px rgb(223, 223, 223)'; 858 icon.style.verticalAlign = 'bottom'; 859 } 860 861 this.el.style.left = `${left}px`; 862 this.el.style.top = `${top}px`; 863 show(this.el); 864 865 // The height of the arrow icon differs slightly between browsers, 866 // so we compute the margin here to make sure it isn't disjointed 867 // from the overlay. 868 icon.style.marginTop = `${icon.scrollHeight - 26}px`; 869 icon.style.marginBottom = `${20 - icon.scrollHeight}px`; 870 871 // Position the arrow to be horizontally centred on the link. 872 icon.style.paddingLeft = `${ 873 offset.left - left + (link.offsetWidth - icon.scrollWidth) / 2 874 }px`; 875 } 876 877 /** 878 * Return whether the overlay is visible. 879 */ 880 isVisible() { 881 return this.el.style.getPropertyValue('display') !== 'none'; 882 } 883 884 /** 885 * Request that the overlay be hidden after a short delay. 886 */ 887 requestHide() { 888 if (this.hideTimeout !== null) { 889 return; 890 } 891 this.hideTimeout = setTimeout(() => { 892 hide(this.el); 893 this.clear(); 894 }, 300); 895 } 896 897 /** 898 * Cancel a request to hide the overlay. 899 */ 900 cancelHide() { 901 if (this.hideTimeout !== null) { 902 clearTimeout(this.hideTimeout); 903 this.hideTimeout = null; 904 } 905 } 906 } 907 908 // Overlay used for displaying comment like info.
909 const overlay = new Overlay(); 910 911 // The most recent comment for which the user has requested to see 912 // who liked it. 913 var relevantComment; 914 915 // Precache after this timeout. 916 var precacheTimeout = null; 917 918 /** 919 * Fetch the like data for a particular comment. 920 */ 921 function fetchLikeData(link, commentId) { 922 commentLikeCache[commentId] = null; 923 924 const container = link && link.parentElement && link.parentElement.parentElement; 925 const star = container.querySelector('a.comment-like-link'); 926 star && 927 handleLinkAction(star, 'view_comment_likes', commentId, data => { 928 // Populate the cache. 929 commentLikeCache[commentId] = data; 930 931 // Only show the overlay if the user is interested. 932 if (overlay.isVisible() && relevantComment === commentId) { 933 overlay.showLikes(link, data); 934 } 935 }); 936 } 937 938 function readCookie(c) { 939 const nameEQ = c + '='; 940 const cookieStrings = document.cookie.split(';'); 941 942 for (let i = 0; i < cookieStrings.length; i++) { 943 let cookieString = cookieStrings[i]; 944 while (cookieString.charAt(0) === ' ') { 945 cookieString = cookieString.substring(1, cookieString.length); 946 } 947 if (cookieString.indexOf(nameEQ) === 0) { 948 const chunk = cookieString.substring(nameEQ.length, cookieString.length); 949 const pairs = chunk.split('&'); 950 const cookieData = {}; 951 for (let num = pairs.length - 1; num >= 0; num--) { 952 const pair = pairs[num].split('='); 953 cookieData[pair[0]] = decodeURIComponent(pair[1]); 954 } 955 return cookieData; 956 } 957 } 958 return null; 959 } 960 961 function getServiceData() { 962 const data = readCookie('wpc_wpc'); 963 if (data === null || typeof data.access_token === 'undefined' || !data.access_token) { 964 return false; 965 } 966 return data; 967 } 968 969 function readMessage(msg) { 970 const event = msg.data; 971 972 if (typeof event.event === 'undefined') { 973 return; 974 } 975 976 if (event.event === 'login' && event.success) { 977 extWinCheck = setInterval(function () { 978 if (!extWin || extWin.closed) { 979 clearInterval(extWinCheck); 980 if (getServiceData()) { 981 // Load page in an iframe to get the current comment nonce 982 const nonceIframe = document.createElement('iframe'); 983 nonceIframe.id = 'wp-login-comment-nonce-iframe'; 984 nonceIframe.style.display = 'none'; 985 nonceIframe.src = commentLikeEvent + ''; 986 document.body.appendChild(nonceIframe); 987 988 const commentLikeId = (commentLikeEvent + '') 989 .split('like_comment=')[1] 990 .split('&_wpnonce=')[0]; 991 let c; 992 993 // Set a 5 second timeout to redirect to the comment page without doing the Like as a fallback 994 const commentLikeTimeout = setTimeout(() => { 995 window.location = commentLikeEvent; 996 }, 5000); 997 998 // Check for a new nonced redirect and use that if available before timing out 999 const commentLikeCheck = setInterval(() => { 1000 const iframe = document.querySelector('#wp-login-comment-nonce-iframe'); 1001 if (iframe) { 1002 c = iframe.querySelector(`#comment-like-${commentLikeId} .comment-like-link`); 1003 } 1004 if (c && typeof c.href !== 'undefined') { 1005 clearTimeout(commentLikeTimeout); 1006 clearInterval(commentLikeCheck); 1007 window.location = c.href; 1008 } 1009 }, 100); 1010 } 1011 } 1012 }, 100); 1013 1014 if (extWin) { 1015 if (!extWin.closed) { 1016 extWin.close(); 1017 } 1018 extWin = false; 1019 } 1020 1021 stopPolling(); 1022 } 1023 } 1024 1025 if (typeof window.postMessage !== 'undefined') { 1026 window.addEventListener('message', e => { 1027 let message = e && e.data; 1028 if (typeof message === 'string') { 1029 try { 1030 message = JSON.parse(message); 1031 } catch (err) { 1032 return; 1033 } 1034 } 1035 1036 const type = message && message.type; 1037 if (type === 'loginMessage') { 1038 readMessage(message); 1039 } 1040 }); 1041 } 1042 1043 document.body.addEventListener('click', e => { 1044 let target = e.target; 1045 1046 // Don't do anything when clicking on the "X people" link. 1047 if (target.matches('p.comment-likes a.view-likers')) { 1048 e.preventDefault(); 1049 return; 1050 } 1051 1052 // Retrieve the surrounding paragraph to the star, if it hasn't been liked. 1053 const notLikedPar = target.closest('p.comment-not-liked'); 1054 1055 // Return if not clicking on star or surrounding paragraph. 1056 if (!target.matches('a.comment-like-link') && !notLikedPar) { 1057 return; 1058 } 1059 1060 // When a comment hasn't been liked, make the text clickable, too. 1061 if (notLikedPar) { 1062 target = notLikedPar.querySelector('a.comment-like-link'); 1063 if (!target) { 1064 return; 1065 } 1066 } 1067 1068 if (target.classList.contains('needs-login')) { 1069 e.preventDefault(); 1070 commentLikeEvent = target; 1071 if (extWin) { 1072 if (!extWin.closed) { 1073 extWin.close(); 1074 } 1075 extWin = false;
1076 } 1077 1078 stopPolling(); 1079 1080 const url = 'https://wordpress.com/public.api/connect/?action=request&service=wordpress'; 1081 extWin = window.open( 1082 url, 1083 'likeconn', 1084 'status=0,toolbar=0,location=1,menubar=0,directories=0,resizable=1,scrollbars=1,height=560,width=500' 1085 ); 1086 1087 startPolling(); 1088 1089 return false; 1090 } 1091 1092 // Record that the user likes or does not like this comment. 1093 const commentId = getCommentId(target); 1094 target.classList.add('loading'); 1095 1096 let commentEl = document.querySelector(`p#comment-like-${commentId}`); 1097 // Determine whether to like or unlike based on whether the comment is 1098 // currently liked. 1099 const action = 1100 commentEl && commentEl.dataset.liked === 'comment-liked' 1101 ? 'unlike_comment' 1102 : 'like_comment'; 1103 handleLinkAction(target, action, commentId, data => { 1104 // Invalidate the like cache for this comment. 1105 delete commentLikeCache[commentId]; 1106 1107 const countEl = document.querySelector(`#comment-like-count-${data.context}`); 1108 if (countEl) { 1109 countEl.innerHTML = data.display; 1110 } 1111 1112 commentEl = document.querySelector(`p#comment-like-${data.context}`); 1113 if (action === 'like_comment') { 1114 commentEl.classList.remove('comment-not-liked'); 1115 commentEl.classList.add('comment-liked'); 1116 commentEl.dataset.liked = 'comment-liked'; 1117 } else { 1118 commentEl.classList.remove('comment-liked'); 1119 commentEl.classList.add('comment-not-liked'); 1120 commentEl.dataset.liked = 'comment-not-liked'; 1121 } 1122 1123 // Prefetch new data for this comment (if there are likers left). 1124 const parent = target.closest('.comment-likes'); 1125 const link = parent && parent.querySelector('a.view-likers'); 1126 if (link) { 1127 fetchLikeData(link, commentId); 1128 } 1129 1130 target.classList.remove('loading'); 1131 }); 1132 e.preventDefault(); 1133 e.stopPropagation(); 1134 }); 1135 1136 document.body.addEventListener( 1137 'mouseenter', 1138 function (e) { 1139 if (!e.target.matches('p.comment-likes a.view-likers')) { 1140 return; 1141 } 1142 // Show the user a list of who has liked this comment. 1143 1144 const link = e.target; 1145 if (Number(link.dataset.likeCount || 0) === 0) { 1146 // No one has liked this comment. 1147 return; 1148 } 1149 1150 // Don't hide the overlay. 1151 overlay.cancelHide(); 1152 1153 // Get the comment ID. 1154 const container = link.parentElement && link.parentElement.parentElement; 1155 const star = container && container.querySelector('a.comment-like-link'); 1156 const commentId = star && getCommentId(star); 1157 relevantComment = commentId; 1158 1159 // Check if the list of likes for this comment is already in 1160 // the cache. 1161 if (commentId in commentLikeCache) { 1162 const entry = commentLikeCache[commentId];
1163 // Only display the likes if the ajax request is 1164 // actually done. 1165 if (entry !== null) { 1166 overlay.showLikes(link, entry); 1167 } else { 1168 // Make sure the overlay is visible (in case 1169 // the user moved the mouse away while loading 1170 // but then came back before it finished 1171 // loading). 1172 overlay.showLoadingMessage(link); 1173 } 1174 return; 1175 } 1176 1177 // Position the "Loading..." overlay. 1178 overlay.showLoadingMessage(link); 1179 1180 // Fetch the data. 1181 fetchLikeData(link, commentId); 1182 }, 1183 true 1184 ); 1185 1186 document.body.addEventListener( 1187 'mouseleave', 1188 e => { 1189 if (!e.target.matches('p.comment-likes a.view-likers')) { 1190 return; 1191 } 1192 // User has moved cursor away - hide the overlay. 1193 overlay.requestHide(); 1194 }, 1195 true 1196 ); 1197 1198 document.body.addEventListener( 1199 'mouseenter', 1200 e => { 1201 if (!e.target.matches('.comment') || !e.target.querySelector('a.comment-like-link')) { 1202 return; 1203 } 1204 // User is moving over a comment - precache the comment like data.
1205 if (precacheTimeout !== null) { 1206 clearTimeout(precacheTimeout); 1207 precacheTimeout = null; 1208 } 1209 1210 const star = e.target.querySelector('a.comment-like-link'); 1211 const parent = star.closest('.comment-likes'); 1212 const link = parent && parent.querySelector('a.view-likers'); 1213 if (!link || Number(link.dataset.likeCount || 0) === 0) { 1214 // No likes. 1215 return; 1216 } 1217 const commentId = getCommentId(star); 1218 if (commentId in commentLikeCache) { 1219 // Already in cache. 1220 return; 1221 } 1222 1223 precacheTimeout = setTimeout(() => { 1224 precacheTimeout = null; 1225 if (commentId in commentLikeCache) { 1226 // Was cached in the interim. 1227 return; 1228 } 1229 fetchLikeData(link, commentId); 1230 }, 1000); 1231 }, 1232 true 1233 ); 1234 } 1235 1236 if (document.readyState !== 'loading') { 1237 init(); 1238 } else { 1239 document.addEventListener('DOMContentLoaded', init); 1240 } 1241})(); 1242; 1243/*! This file is auto-generated */ 1244/*! 1245 * imagesLoaded PACKAGED v5.0.0 1246 * JavaScript is all like "You images are done yet or what?" 1247 * MIT License 1248 */ 1249!function(t,e){"object"==typeof module&&module.exports?module.exports=e():t.EvEmitter=e()}("undefined"!=typeof window?window:this,(function(){function t(){}let e=t.prototype;return e.on=function(t,e){if(!t||!e)return this;let i=this._events=this._events||{},s=i[t]=i[t]||[];return s.includes(e)||s.push(e),this},e.once=function(t,e){if(!t||!e)return this;this.on(t,e);let i=this._onceEvents=this._onceEvents||{};return(i[t]=i[t]||{})[e]=!0,this},e.off=function(t,e){let i=this._events&&this._events[t];if(!i||!i.length)return this;let s=i.indexOf(e);return-1!=s&&i.splice(s,1),this},e.emitEvent=function(t,e){let i=this._events&&this._events[t];if(!i||!i.length)return this;i=i.slice(0),e=e||[];let s=this._onceEvents&&this._onceEvents[t];for(let n of i){s&&s[n]&&(this.off(t,n),delete s[n]),n.apply(this,e)}return this},e.allOff=function(){return delete this._events,delete this._onceEvents,this},t})), 1250/*! 1251 * imagesLoaded v5.0.0 1252 * JavaScript is all like "You images are done yet or what?" 1253 * MIT License 1254 */ 1255function(t,e){"object"==typeof module&&module.exports?module.exports=e(t,require("ev-emitter")):t.imagesLoaded=e(t,t.EvEmitter)}("undefined"!=typeof window?window:this,(function(t,e){let i=t.jQuery,s=t.console;
vendor: 4,185 bytes, lines 1255-1256
1255function n(t,e,o){if(!(this instanceof n))return new n(t,e,o);let r=t;var h;("string"==typeof t&&(r=document.querySelectorAll(t)),r)?(this.elements=(h=r,Array.isArray(h)?h:"object"==typeof h&&"number"==typeof h.length?[...h]:[h]),this.options={},"function"==typeof e?o=e:Object.assign(this.options,e),o&&this.on("always",o),this.getImages(),i&&(this.jqDeferred=new i.Deferred),setTimeout(this.check.bind(this))):s.error(`Bad element for imagesLoaded ${r||t}`)}n.prototype=Object.create(e.prototype),n.prototype.getImages=function(){this.images=[],this.elements.forEach(this.addElementImages,this)};const o=[1,9,11];n.prototype.addElementImages=function(t){"IMG"===t.nodeName&&this.addImage(t),!0===this.options.background&&this.addElementBackgroundImages(t);let{nodeType:e}=t;if(!e||!o.includes(e))return;let i=t.querySelectorAll("img");for(let t of i)this.addImage(t);if("string"==typeof this.options.background){let e=t.querySelectorAll(this.options.background);for(let t of e)this.addElementBackgroundImages(t)}};const r=/url\((['"])?(.*?)\1\)/gi;function h(t){this.img=t}function d(t,e){this.url=t,this.element=e,this.img=new Image}return n.prototype.addElementBackgroundImages=function(t){let e=getComputedStyle(t);if(!e)return;let i=r.exec(e.backgroundImage);for(;null!==i;){let s=i&&i[2];s&&this.addBackground(s,t),i=r.exec(e.backgroundImage)}},n.prototype.addImage=function(t){let e=new h(t);this.images.push(e)},n.prototype.addBackground=function(t,e){let i=new d(t,e);this.images.push(i)},n.prototype.check=function(){if(this.progressedCount=0,this.hasAnyBroken=!1,!this.images.length)return void this.complete();let t=(t,e,i)=>{setTimeout((()=>{this.progress(t,e,i)}))};this.images.forEach((function(e){e.once("progress",t),e.check()}))},n.prototype.progress=function(t,e,i){this.progressedCount++,this.hasAnyBroken=this.hasAnyBroken||!t.isLoaded,this.emitEvent("progress",[this,t,e]),this.jqDeferred&&this.jqDeferred.notify&&this.jqDeferred.notify(this,t),this.progressedCount===this.images.length&&this.complete(),this.options.debug&&s&&s.log(`progress: ${i}`,t,e)},n.prototype.complete=function(){let t=this.hasAnyBroken?"fail":"done";if(this.isComplete=!0,this.emitEvent(t,[this]),this.emitEvent("always",[this]),this.jqDeferred){let t=this.hasAnyBroken?"reject":"resolve";this.jqDeferred[t](this)}},h.prototype=Object.create(e.prototype),h.prototype.check=function(){this.getIsImageComplete()?this.confirm(0!==this.img.naturalWidth,"naturalWidth"):(this.proxyImage=new Image,this.img.crossOrigin&&(this.proxyImage.crossOrigin=this.img.crossOrigin),this.proxyImage.addEventListener("load",this),this.proxyImage.addEventListener("error",this),this.img.addEventListener("load",this),this.img.addEventListener("error",this),this.proxyImage.src=this.img.currentSrc||this.img.src)},h.prototype.getIsImageComplete=function(){return this.img.complete&&this.img.naturalWidth},h.prototype.confirm=function(t,e){this.isLoaded=t;let{parentNode:i}=this.img,s="PICTURE"===i.nodeName?i:this.img;this.emitEvent("progress",[this,s,e])},h.prototype.handleEvent=function(t){let e="on"+t.type;this[e]&&this[e](t)},h.prototype.onload=function(){this.confirm(!0,"onload"),this.unbindEvents()},h.prototype.onerror=function(){this.confirm(!1,"onerror"),this.unbindEvents()},h.prototype.unbindEvents=function(){this.proxyImage.removeEventListener("load",this),this.proxyImage.removeEventListener("error",this),this.img.removeEventListener("load",this),this.img.removeEventListener("error",this)},d.prototype=Object.create(h.prototype),d.prototype.check=function(){this.img.addEventListener("load",this),this.img.addEventListener("error",this),this.img.src=this.url,this.getIsImageComplete()&&(this.confirm(0!==this.img.naturalWidth,"naturalWidth"),this.unbindEvents())},d.prototype.unbindEvents=function(){this.img.removeEventListener("load",this),this.img.removeEventListener("error",this)},d.prototype.confirm=function(t,e){this.isLoaded=t,this.emitEvent("progress",[this,this.element,e])},n.makeJQueryPlugin=function(e){(e=e||t.jQuery)&&(i=e,i.fn.imagesLoaded=function(t,e){return new n(this,t,e).jqDeferred.promise(i(this))})},n.makeJQueryPlugin(),n}));; 1256/*! This file is auto-generated */
vendor: 24,103 bytes, lines 1257-1265
1257/*! 1258 * Masonry PACKAGED v4.2.2 1259 * Cascading grid layout library 1260 * https://masonry.desandro.com 1261 * MIT License 1262 * by David DeSandro 1263 */ 1264 1265!function(t,e){"function"==typeof define&&define.amd?define("jquery-bridget/jquery-bridget",["jquery"],function(i){return e(t,i)}):"object"==typeof module&&module.exports?module.exports=e(t,require("jquery")):t.jQueryBridget=e(t,t.jQuery)}(window,function(t,e){"use strict";function i(i,r,a){function h(t,e,n){var o,r="$()."+i+'("'+e+'")';return t.each(function(t,h){var u=a.data(h,i);if(!u)return void s(i+" not initialized. Cannot call methods, i.e. "+r);var d=u[e];if(!d||"_"==e.charAt(0))return void s(r+" is not a valid method");var l=d.apply(u,n);o=void 0===o?l:o}),void 0!==o?o:t}function u(t,e){t.each(function(t,n){var o=a.data(n,i);o?(o.option(e),o._init()):(o=new r(n,e),a.data(n,i,o))})}a=a||e||t.jQuery,a&&(r.prototype.option||(r.prototype.option=function(t){a.isPlainObject(t)&&(this.options=a.extend(!0,this.options,t))}),a.fn[i]=function(t){if("string"==typeof t){var e=o.call(arguments,1);return h(this,t,e)}return u(this,t),this},n(a))}function n(t){!t||t&&t.bridget||(t.bridget=i)}var o=Array.prototype.slice,r=t.console,s="undefined"==typeof r?function(){}:function(t){r.error(t)};return n(e||t.jQuery),i}),function(t,e){"function"==typeof define&&define.amd?define("ev-emitter/ev-emitter",e):"object"==typeof module&&module.exports?module.exports=e():t.EvEmitter=e()}("undefined"!=typeof window?window:this,function(){function t(){}var e=t.prototype;return e.on=function(t,e){if(t&&e){var i=this._events=this._events||{},n=i[t]=i[t]||[];return-1==n.indexOf(e)&&n.push(e),this}},e.once=function(t,e){if(t&&e){this.on(t,e);var i=this._onceEvents=this._onceEvents||{},n=i[t]=i[t]||{};return n[e]=!0,this}},e.off=function(t,e){var i=this._events&&this._events[t];if(i&&i.length){var n=i.indexOf(e);return-1!=n&&i.splice(n,1),this}},e.emitEvent=function(t,e){var i=this._events&&this._events[t];if(i&&i.length){i=i.slice(0),e=e||[];for(var n=this._onceEvents&&this._onceEvents[t],o=0;o<i.length;o++){var r=i[o],s=n&&n[r];s&&(this.off(t,r),delete n[r]),r.apply(this,e)}return this}},e.allOff=function(){delete this._events,delete this._onceEvents},t}),function(t,e){"function"==typeof define&&define.amd?define("get-size/get-size",e):"object"==typeof module&&module.exports?module.exports=e():t.getSize=e()}(window,function(){"use strict";function t(t){var e=parseFloat(t),i=-1==t.indexOf("%")&&!isNaN(e);return i&&e}function e(){}function i(){for(var t={width:0,height:0,innerWidth:0,innerHeight:0,outerWidth:0,outerHeight:0},e=0;u>e;e++){var i=h[e];t[i]=0}return t}function n(t){var e=getComputedStyle(t);return e||a("Style returned "+e+". Are you running this code in a hidden iframe on Firefox? See https://bit.ly/getsizebug1"),e}function o(){if(!d){d=!0;var e=document.createElement("div");e.style.width="200px",e.style.padding="1px 2px 3px 4px",e.style.borderStyle="solid",e.style.borderWidth="1px 2px 3px 4px",e.style.boxSizing="border-box";var i=document.body||document.documentElement;i.appendChild(e);var o=n(e);s=200==Math.round(t(o.width)),r.isBoxSizeOuter=s,i.removeChild(e)}}function r(e){if(o(),"string"==typeof e&&(e=document.querySelector(e)),e&&"object"==typeof e&&e.nodeType){var r=n(e);if("none"==r.display)return i();var a={};a.width=e.offsetWidth,a.height=e.offsetHeight;for(var d=a.isBorderBox="border-box"==r.boxSizing,l=0;u>l;l++){var c=h[l],f=r[c],m=parseFloat(f);a[c]=isNaN(m)?0:m}var p=a.paddingLeft+a.paddingRight,g=a.paddingTop+a.paddingBottom,y=a.marginLeft+a.marginRight,v=a.marginTop+a.marginBottom,_=a.borderLeftWidth+a.borderRightWidth,z=a.borderTopWidth+a.borderBottomWidth,E=d&&s,b=t(r.width);b!==!1&&(a.width=b+(E?0:p+_));var x=t(r.height);return x!==!1&&(a.height=x+(E?0:g+z)),a.innerWidth=a.width-(p+_),a.innerHeight=a.height-(g+z),a.outerWidth=a.width+y,a.outerHeight=a.height+v,a}}var s,a="undefined"==typeof console?e:function(t){console.error(t)},h=["paddingLeft","paddingRight","paddingTop","paddingBottom","marginLeft","marginRight","marginTop","marginBottom","borderLeftWidth","borderRightWidth","borderTopWidth","borderBottomWidth"],u=h.length,d=!1;return r}),function(t,e){"use strict";"function"==typeof define&&define.amd?define("desandro-matches-selector/matches-selector",e):"object"==typeof module&&module.exports?module.exports=e():t.matchesSelector=e()}(window,function(){"use strict";var t=function(){var t=window.Element.prototype;if(t.matches)return"matches";if(t.matchesSelector)return"matchesSelector";for(var e=["webkit","moz","ms","o"],i=0;i<e.length;i++){var n=e[i],o=n+"MatchesSelector";if(t[o])return o}}();return function(e,i){return e[t](i)}}),function(t,e){"function"==typeof define&&define.amd?define("fizzy-ui-utils/utils",["desandro-matches-selector/matches-selector"],function(i){return e(t,i)}):"object"==typeof module&&module.exports?module.exports=e(t,require("desandro-matches-selector")):t.fizzyUIUtils=e(t,t.matchesSelector)}(window,function(t,e){var i={};i.extend=function(t,e){for(var i in e)t[i]=e[i];return t},i.modulo=function(t,e){return(t%e+e)%e};var n=Array.prototype.slice;i.makeArray=function(t){if(Array.isArray(t))return t;if(null===t||void 0===t)return[];var e="object"==typeof t&&"number"==typeof t.length;return e?n.call(t):[t]},i.removeFrom=function(t,e){var i=t.indexOf(e);-1!=i&&t.splice(i,1)},i.getParent=function(t,i){for(;t.parentNode&&t!=document.body;)if(t=t.parentNode,e(t,i))return t},i.getQueryElement=function(t){return"string"==typeof t?document.querySelector(t):t},i.handleEvent=function(t){var e="on"+t.type;this[e]&&this[e](t)},i.filterFindElements=function(t,n){t=i.makeArray(t);var o=[];return t.forEach(function(t){if(t instanceof HTMLElement){if(!n)return void o.push(t);e(t,n)&&o.push(t);for(var i=t.querySelectorAll(n),r=0;r<i.length;r++)o.push(i[r])}}),o},i.debounceMethod=function(t,e,i){i=i||100;var n=t.prototype[e],o=e+"Timeout";t.prototype[e]=function(){var t=this[o];clearTimeout(t);var e=arguments,r=this;this[o]=setTimeout(function(){n.apply(r,e),delete r[o]},i)}},i.docReady=function(t){var e=document.readyState;"complete"==e||"interactive"==e?setTimeout(t):document.addEventListener("DOMContentLoaded",t)},i.toDashed=function(t){return t.replace(/(.)([A-Z])/g,function(t,e,i){return e+"-"+i}).toLowerCase()};var o=t.console;return i.htmlInit=function(e,n){i.docReady(function(){var r=i.toDashed(n),s="data-"+r,a=document.querySelectorAll("["+s+"]"),h=document.querySelectorAll(".js-"+r),u=i.makeArray(a).concat(i.makeArray(h)),d=s+"-options",l=t.jQuery;u.forEach(function(t){var i,r=t.getAttribute(s)||t.getAttribute(d);try{i=r&&JSON.parse(r)}catch(a){return void(o&&o.error("Error parsing "+s+" on "+t.className+": "+a))}var h=new e(t,i);l&&l.data(t,n,h)})})},i}),function(t,e){"function"==typeof define&&define.amd?define("outlayer/item",["ev-emitter/ev-emitter","get-size/get-size"],e):"object"==typeof module&&module.exports?module.exports=e(require("ev-emitter"),require("get-size")):(t.Outlayer={},t.Outlayer.Item=e(t.EvEmitter,t.getSize))}(window,function(t,e){"use strict";function i(t){for(var e in t)return!1;return e=null,!0}function n(t,e){t&&(this.element=t,this.layout=e,this.position={x:0,y:0},this._create())}function o(t){return t.replace(/([A-Z])/g,function(t){return"-"+t.toLowerCase()})}var r=document.documentElement.style,s="string"==typeof r.transition?"transition":"WebkitTransition",a="string"==typeof r.transform?"transform":"WebkitTransform",h={WebkitTransition:"webkitTransitionEnd",transition:"transitionend"}[s],u={transform:a,transition:s,transitionDuration:s+"Duration",transitionProperty:s+"Property",transitionDelay:s+"Delay"},d=n.prototype=Object.create(t.prototype);d.constructor=n,d._create=function(){this._transn={ingProperties:{},clean:{},onEnd:{}},this.css({position:"absolute"})},d.handleEvent=function(t){var e="on"+t.type;this[e]&&this[e](t)},d.getSize=function(){this.size=e(this.element)},d.css=function(t){var e=this.element.style;for(var i in t){var n=u[i]||i;e[n]=t[i]}},d.getPosition=function(){var t=getComputedStyle(this.element),e=this.layout._getOption("originLeft"),i=this.layout._getOption("originTop"),n=t[e?"left":"right"],o=t[i?"top":"bottom"],r=parseFloat(n),s=parseFloat(o),a=this.layout.size;-1!=n.indexOf("%")&&(r=r/100*a.width),-1!=o.indexOf("%")&&(s=s/100*a.height),r=isNaN(r)?0:r,s=isNaN(s)?0:s,r-=e?a.paddingLeft:a.paddingRight,s-=i?a.paddingTop:a.paddingBottom,this.position.x=r,this.position.y=s},d.layoutPosition=function(){var t=this.layout.size,e={},i=this.layout._getOption("originLeft"),n=this.layout._getOption("originTop"),o=i?"paddingLeft":"paddingRight",r=i?"left":"right",s=i?"right":"left",a=this.position.x+t[o];e[r]=this.getXValue(a),e[s]="";var h=n?"paddingTop":"paddingBottom",u=n?"top":"bottom",d=n?"bottom":"top",l=this.position.y+t[h];e[u]=this.getYValue(l),e[d]="",this.css(e),this.emitEvent("layout",[this])},d.getXValue=function(t){var e=this.layout._getOption("horizontal");return this.layout.options.percentPosition&&!e?t/this.layout.size.width*100+"%":t+"px"},d.getYValue=function(t){var e=this.layout._getOption("horizontal");return this.layout.options.percentPosition&&e?t/this.layout.size.height*100+"%":t+"px"},d._transitionTo=function(t,e){this.getPosition();var i=this.position.x,n=this.position.y,o=t==this.position.x&&e==this.position.y;if(this.setPosition(t,e),o&&!this.isTransitioning)return void this.layoutPosition();var r=t-i,s=e-n,a={};a.transform=this.getTranslate(r,s),this.transition({to:a,onTransitionEnd:{transform:this.layoutPosition},isCleaning:!0})},d.getTranslate=function(t,e){var i=this.layout._getOption("originLeft"),n=this.layout._getOption("originTop");return t=i?t:-t,e=n?e:-e,"translate3d("+t+"px, "+e+"px, 0)"},d.goTo=function(t,e){this.setPosition(t,e),this.layoutPosition()},d.moveTo=d._transitionTo,d.setPosition=function(t,e){this.position.x=parseFloat(t),this.position.y=parseFloat(e)},d._nonTransition=function(t){this.css(t.to),t.isCleaning&&this._removeStyles(t.to);for(var e in t.onTransitionEnd)t.onTransitionEnd[e].call(this)},d.transition=function(t){if(!parseFloat(this.layout.options.transitionDuration))return void this._nonTransition(t);var e=this._transn;for(var i in t.onTransitionEnd)e.onEnd[i]=t.onTransitionEnd[i];for(i in t.to)e.ingProperties[i]=!0,t.isCleaning&&(e.clean[i]=!0);if(t.from){this.css(t.from);var n=this.element.offsetHeight;n=null}this.enableTransition(t.to),this.css(t.to),this.isTransitioning=!0};var l="opacity,"+o(a);d.enableTransition=function(){if(!this.isTransitioning){var t=this.layout.options.transitionDuration;t="number"==typeof t?t+"ms":t,this.css({transitionProperty:l,transitionDuration:t,transitionDelay:this.staggerDelay||0}),this.element.addEventListener(h,this,!1)}},d.onwebkitTransitionEnd=function(t){this.ontransitionend(t)},d.onotransitionend=function(t){this.ontransitionend(t)};var c={"-webkit-transform":"transform"};d.ontransitionend=function(t){if(t.target===this.element){var e=this._transn,n=c[t.propertyName]||t.propertyName;if(delete e.ingProperties[n],i(e.ingProperties)&&this.disableTransition(),n in e.clean&&(this.element.style[t.propertyName]="",delete e.clean[n]),n in e.onEnd){var o=e.onEnd[n];o.call(this),delete e.onEnd[n]}this.emitEvent("transitionEnd",[this])}},d.disableTransition=function(){this.removeTransitionStyles(),this.element.removeEventListener(h,this,!1),this.isTransitioning=!1},d._removeStyles=function(t){var e={};for(var i in t)e[i]="";this.css(e)};var f={transitionProperty:"",transitionDuration:"",transitionDelay:""};return d.removeTransitionStyles=function(){this.css(f)},d.stagger=function(t){t=isNaN(t)?0:t,this.staggerDelay=t+"ms"},d.removeElem=function(){this.element.parentNode.removeChild(this.element),this.css({display:""}),this.emitEvent("remove",[this])},d.remove=function(){return s&&parseFloat(this.layout.options.transitionDuration)?(this.once("transitionEnd",function(){this.removeElem()}),void this.hide()):void this.removeElem()},d.reveal=function(){delete this.isHidden,this.css({display:""});var t=this.layout.options,e={},i=this.getHideRevealTransitionEndProperty("visibleStyle");e[i]=this.onRevealTransitionEnd,this.transition({from:t.hiddenStyle,to:t.visibleStyle,isCleaning:!0,onTransitionEnd:e})},d.onRevealTransitionEnd=function(){this.isHidden||this.emitEvent("reveal")},d.getHideRevealTransitionEndProperty=function(t){var e=this.layout.options[t];if(e.opacity)return"opacity";for(var i in e)return i},d.hide=function(){this.isHidden=!0,this.css({display:""});var t=this.layout.options,e={},i=this.getHideRevealTransitionEndProperty("hiddenStyle");e[i]=this.onHideTransitionEnd,this.transition({from:t.visibleStyle,to:t.hiddenStyle,isCleaning:!0,onTransitionEnd:e})},d.onHideTransitionEnd=function(){this.isHidden&&(this.css({display:"none"}),this.emitEvent("hide"))},d.destroy=function(){this.css({position:"",left:"",right:"",top:"",bottom:"",transition:"",transform:""})},n}),function(t,e){"use strict";"function"==typeof define&&define.amd?define("outlayer/outlayer",["ev-emitter/ev-emitter","get-size/get-size","fizzy-ui-utils/utils","./item"],function(i,n,o,r){return e(t,i,n,o,r)}):"object"==typeof module&&module.exports?module.exports=e(t,require("ev-emitter"),require("get-size"),require("fizzy-ui-utils"),require("./item")):t.Outlayer=e(t,t.EvEmitter,t.getSize,t.fizzyUIUtils,t.Outlayer.Item)}(window,function(t,e,i,n,o){"use strict";function r(t,e){var i=n.getQueryElement(t);if(!i)return void(h&&h.error("Bad element for "+this.constructor.namespace+": "+(i||t)));this.element=i,u&&(this.$element=u(this.element)),this.options=n.extend({},this.constructor.defaults),this.option(e);var o=++l;this.element.outlayerGUID=o,c[o]=this,this._create();var r=this._getOption("initLayout");r&&this.layout()}function s(t){function e(){t.apply(this,arguments)}return e.prototype=Object.create(t.prototype),e.prototype.constructor=e,e}function a(t){if("number"==typeof t)return t;var e=t.match(/(^\d*\.?\d*)(\w*)/),i=e&&e[1],n=e&&e[2];if(!i.length)return 0;i=parseFloat(i);var o=m[n]||1;return i*o}var h=t.console,u=t.jQuery,d=function(){},l=0,c={};r.namespace="outlayer",r.Item=o,r.defaults={containerStyle:{position:"relative"},initLayout:!0,originLeft:!0,originTop:!0,resize:!0,resizeContainer:!0,transitionDuration:"0.4s",hiddenStyle:{opacity:0,transform:"scale(0.001)"},visibleStyle:{opacity:1,transform:"scale(1)"}};var f=r.prototype;n.extend(f,e.prototype),f.option=function(t){n.extend(this.options,t)},f._getOption=function(t){var e=this.constructor.compatOptions[t];return e&&void 0!==this.options[e]?this.options[e]:this.options[t]},r.compatOptions={initLayout:"isInitLayout",horizontal:"isHorizontal",layoutInstant:"isLayoutInstant",originLeft:"isOriginLeft",originTop:"isOriginTop",resize:"isResizeBound",resizeContainer:"isResizingContainer"},f._create=function(){this.reloadItems(),this.stamps=[],this.stamp(this.options.stamp),n.extend(this.element.style,this.options.containerStyle);var t=this._getOption("resize");t&&this.bindResize()},f.reloadItems=function(){this.items=this._itemize(this.element.children)},f._itemize=function(t){for(var e=this._filterFindItemElements(t),i=this.constructor.Item,n=[],o=0;o<e.length;o++){var r=e[o],s=new i(r,this);n.push(s)}return n},f._filterFindItemElements=function(t){return n.filterFindElements(t,this.options.itemSelector)},f.getItemElements=function(){return this.items.map(function(t){return t.element})},f.layout=function(){this._resetLayout(),this._manageStamps();var t=this._getOption("layoutInstant"),e=void 0!==t?t:!this._isLayoutInited;this.layoutItems(this.items,e),this._isLayoutInited=!0},f._init=f.layout,f._resetLayout=function(){this.getSize()},f.getSize=function(){this.size=i(this.element)},f._getMeasurement=function(t,e){var n,o=this.options[t];o?("string"==typeof o?n=this.element.querySelector(o):o instanceof HTMLElement&&(n=o),this[t]=n?i(n)[e]:o):this[t]=0},f.layoutItems=function(t,e){t=this._getItemsForLayout(t),this._layoutItems(t,e),this._postLayout()},f._getItemsForLayout=function(t){return t.filter(function(t){return!t.isIgnored})},f._layoutItems=function(t,e){if(this._emitCompleteOnItems("layout",t),t&&t.length){var i=[];t.forEach(function(t){var n=this._getItemLayoutPosition(t);n.item=t,n.isInstant=e||t.isLayoutInstant,i.push(n)},this),this._processLayoutQueue(i)}},f._getItemLayoutPosition=function(){return{x:0,y:0}},f._processLayoutQueue=function(t){this.updateStagger(),t.forEach(function(t,e){this._positionItem(t.item,t.x,t.y,t.isInstant,e)},this)},f.updateStagger=function(){var t=this.options.stagger;return null===t||void 0===t?void(this.stagger=0):(this.stagger=a(t),this.stagger)},f._positionItem=function(t,e,i,n,o){n?t.goTo(e,i):(t.stagger(o*this.stagger),t.moveTo(e,i))},f._postLayout=function(){this.resizeContainer()},f.resizeContainer=function(){var t=this._getOption("resizeContainer");if(t){var e=this._getContainerSize();e&&(this._setContainerMeasure(e.width,!0),this._setContainerMeasure(e.height,!1))}},f._getContainerSize=d,f._setContainerMeasure=function(t,e){if(void 0!==t){var i=this.size;i.isBorderBox&&(t+=e?i.paddingLeft+i.paddingRight+i.borderLeftWidth+i.borderRightWidth:i.paddingBottom+i.paddingTop+i.borderTopWidth+i.borderBottomWidth),t=Math.max(t,0),this.element.style[e?"width":"height"]=t+"px"}},f._emitCompleteOnItems=function(t,e){function i(){o.dispatchEvent(t+"Complete",null,[e])}function n(){s++,s==r&&i()}var o=this,r=e.length;if(!e||!r)return void i();var s=0;e.forEach(function(e){e.once(t,n)})},f.dispatchEvent=function(t,e,i){var n=e?[e].concat(i):i;if(this.emitEvent(t,n),u)if(this.$element=this.$element||u(this.element),e){var o=u.Event(e);o.type=t,this.$element.trigger(o,i)}else this.$element.trigger(t,i)},f.ignore=function(t){var e=this.getItem(t);e&&(e.isIgnored=!0)},f.unignore=function(t){var e=this.getItem(t);e&&delete e.isIgnored},f.stamp=function(t){t=this._find(t),t&&(this.stamps=this.stamps.concat(t),t.forEach(this.ignore,this))},f.unstamp=function(t){t=this._find(t),t&&t.forEach(function(t){n.removeFrom(this.stamps,t),this.unignore(t)},this)},f._find=function(t){return t?("string"==typeof t&&(t=this.element.querySelectorAll(t)),t=n.makeArray(t)):void 0},f._manageStamps=function(){this.stamps&&this.stamps.length&&(this._getBoundingRect(),this.stamps.forEach(this._manageStamp,this))},f._getBoundingRect=function(){var t=this.element.getBoundingClientRect(),e=this.size;this._boundingRect={left:t.left+e.paddingLeft+e.borderLeftWidth,top:t.top+e.paddingTop+e.borderTopWidth,right:t.right-(e.paddingRight+e.borderRightWidth),bottom:t.bottom-(e.paddingBottom+e.borderBottomWidth)}},f._manageStamp=d,f._getElementOffset=function(t){var e=t.getBoundingClientRect(),n=this._boundingRect,o=i(t),r={left:e.left-n.left-o.marginLeft,top:e.top-n.top-o.marginTop,right:n.right-e.right-o.marginRight,bottom:n.bottom-e.bottom-o.marginBottom};return r},f.handleEvent=n.handleEvent,f.bindResize=function(){t.addEventListener("resize",this),this.isResizeBound=!0},f.unbindResize=function(){t.removeEventListener("resize",this),this.isResizeBound=!1},f.onresize=function(){this.resize()},n.debounceMethod(r,"onresize",100),f.resize=function(){this.isResizeBound&&this.needsResizeLayout()&&this.layout()},f.needsResizeLayout=function(){var t=i(this.element),e=this.size&&t;return e&&t.innerWidth!==this.size.innerWidth},f.addItems=function(t){var e=this._itemize(t);return e.length&&(this.items=this.items.concat(e)),e},f.appended=function(t){var e=this.addItems(t);e.length&&(this.layoutItems(e,!0),this.reveal(e))},f.prepended=function(t){var e=this._itemize(t);if(e.length){var i=this.items.slice(0);this.items=e.concat(i),this._resetLayout(),this._manageStamps(),this.layoutItems(e,!0),this.reveal(e),this.layoutItems(i)}},f.reveal=function(t){if(this._emitCompleteOnItems("reveal",t),t&&t.length){var e=this.updateStagger();t.forEach(function(t,i){t.stagger(i*e),t.reveal()})}},f.hide=function(t){if(this._emitCompleteOnItems("hide",t),t&&t.length){var e=this.updateStagger();t.forEach(function(t,i){t.stagger(i*e),t.hide()})}},f.revealItemElements=function(t){var e=this.getItems(t);this.reveal(e)},f.hideItemElements=function(t){var e=this.getItems(t);this.hide(e)},f.getItem=function(t){for(var e=0;e<this.items.length;e++){var i=this.items[e];if(i.element==t)return i}},f.getItems=function(t){t=n.makeArray(t);var e=[];return t.forEach(function(t){var i=this.getItem(t);i&&e.push(i)},this),e},f.remove=function(t){var e=this.getItems(t);this._emitCompleteOnItems("remove",e),e&&e.length&&e.forEach(function(t){t.remove(),n.removeFrom(this.items,t)},this)},f.destroy=function(){var t=this.element.style;t.height="",t.position="",t.width="",this.items.forEach(function(t){t.destroy()}),this.unbindResize();var e=this.element.outlayerGUID;delete c[e],delete this.element.outlayerGUID,u&&u.removeData(this.element,this.constructor.namespace)},r.data=function(t){t=n.getQueryElement(t);var e=t&&t.outlayerGUID;return e&&c[e]},r.create=function(t,e){var i=s(r);return i.defaults=n.extend({},r.defaults),n.extend(i.defaults,e),i.compatOptions=n.extend({},r.compatOptions),i.namespace=t,i.data=r.data,i.Item=s(o),n.htmlInit(i,t),u&&u.bridget&&u.bridget(t,i),i};var m={ms:1,s:1e3};return r.Item=o,r}),function(t,e){"function"==typeof define&&define.amd?define(["outlayer/outlayer","get-size/get-size"],e):"object"==typeof module&&module.exports?module.exports=e(require("outlayer"),require("get-size")):t.Masonry=e(t.Outlayer,t.getSize)}(window,function(t,e){var i=t.create("masonry");i.compatOptions.fitWidth="isFitWidth";var n=i.prototype;return n._resetLayout=function(){this.getSize(),this._getMeasurement("columnWidth","outerWidth"),this._getMeasurement("gutter","outerWidth"),this.measureColumns(),this.colYs=[];for(var t=0;t<this.cols;t++)this.colYs.push(0);this.maxY=0,this.horizontalColIndex=0},n.measureColumns=function(){if(this.getContainerWidth(),!this.columnWidth){var t=this.items[0],i=t&&t.element;this.columnWidth=i&&e(i).outerWidth||this.containerWidth}var n=this.columnWidth+=this.gutter,o=this.containerWidth+this.gutter,r=o/n,s=n-o%n,a=s&&1>s?"round":"floor";r=Math[a](r),this.cols=Math.max(r,1)},n.getContainerWidth=function(){var t=this._getOption("fitWidth"),i=t?this.element.parentNode:this.element,n=e(i);this.containerWidth=n&&n.innerWidth},n._getItemLayoutPosition=function(t){t.getSize();var e=t.size.outerWidth%this.columnWidth,i=e&&1>e?"round":"ceil",n=Math[i](t.size.outerWidth/this.columnWidth);n=Math.min(n,this.cols);for(var o=this.options.horizontalOrder?"_getHorizontalColPosition":"_getTopColPosition",r=this[o](n,t),s={x:this.columnWidth*r.col,y:r.y},a=r.y+t.size.outerHeight,h=n+r.col,u=r.col;h>u;u++)this.colYs[u]=a;return s},n._getTopColPosition=function(t){var e=this._getTopColGroup(t),i=Math.min.apply(Math,e);return{col:e.indexOf(i),y:i}},n._getTopColGroup=function(t){if(2>t)return this.colYs;for(var e=[],i=this.cols+1-t,n=0;i>n;n++)e[n]=this._getColGroupY(n,t);return e},n._getColGroupY=function(t,e){if(2>e)return this.colYs[t];var i=this.colYs.slice(t,t+e);return Math.max.apply(Math,i)},n._getHorizontalColPosition=function(t,e){var i=this.horizontalColIndex%this.cols,n=t>1&&i+t>this.cols;i=n?0:i;var o=e.size.outerWidth&&e.size.outerHeight;return this.horizontalColIndex=o?i+t:this.horizontalColIndex,{col:i,y:this._getColGroupY(i,t)}},n._manageStamp=function(t){var i=e(t),n=this._getElementOffset(t),o=this._getOption("originLeft"),r=o?n.left:n.right,s=r+i.outerWidth,a=Math.floor(r/this.columnWidth);a=Math.max(0,a);var h=Math.floor(s/this.columnWidth);h-=s%this.columnWidth?0:1,h=Math.min(this.cols-1,h);for(var u=this._getOption("originTop"),d=(u?n.top:n.bottom)+i.outerHeight,l=a;h>=l;l++)this.colYs[l]=Math.max(d,this.colYs[l])},n._getContainerSize=function(){this.maxY=Math.max.apply(Math,this.colYs);var t={height:this.maxY};return this._getOption("fitWidth")&&(t.width=this._getContainerFitWidth()),t},n._getContainerFitWidth=function(){for(var t=0,e=this.cols;--e&&0===this.colYs[e];)t++;return(this.cols-t)*this.columnWidth-this.gutter},n.needsResizeLayout=function(){var t=this.containerWidth;return this.getContainerWidth(),t!=this.containerWidth},i});
1265; 1266/*! 1267 * imagesLoaded PACKAGED v3.1.8 1268 * JavaScript is all like "You images are done yet or what?" 1269 * MIT License 1270 */ 1271 1272 1273/*! 1274 * EventEmitter v4.2.6 - git.io/ee 1275 * Oliver Caldwell 1276 * MIT license 1277 * @preserve 1278 */ 1279 1280(function () { 1281 1282 1283 /** 1284 * Class for managing events. 1285 * Can be extended to provide event functionality in other classes. 1286 * 1287 * @class EventEmitter Manages event registering and emitting. 1288 */ 1289 function EventEmitter() {} 1290 1291 // Shortcuts to improve speed and size 1292 var proto = EventEmitter.prototype; 1293 var exports = this; 1294 var originalGlobalValue = exports.EventEmitter; 1295 1296 /** 1297 * Finds the index of the listener for the event in it's storage array. 1298 * 1299 * @param {Function[]} listeners Array of listeners to search through. 1300 * @param {Function} listener Method to look for. 1301 * @return {Number} Index of the specified listener, -1 if not found 1302 * @api private 1303 */ 1304 function indexOfListener(listeners, listener) { 1305 var i = listeners.length; 1306 while (i--) { 1307 if (listeners[i].listener === listener) { 1308 return i; 1309 } 1310 } 1311 1312 return -1; 1313 } 1314 1315 /** 1316 * Alias a method while keeping the context correct, to allow for overwriting of target method. 1317 * 1318 * @param {String} name The name of the target method. 1319 * @return {Function} The aliased method 1320 * @api private 1321 */ 1322 function alias(name) { 1323 return function aliasClosure() { 1324 return this[name].apply(this, arguments); 1325 }; 1326 } 1327 1328 /** 1329 * Returns the listener array for the specified event. 1330 * Will initialise the event object and listener arrays if required. 1331 * Will return an object if you use a regex search. The object contains keys for each matched event. So /ba[rz]/ might return an object c
1331ontaining bar and baz. But only if you have either defined them with defineEvent or added some listeners to them. 1332 * Each property in the object response is an array of listener functions. 1333 * 1334 * @param {String|RegExp} evt Name of the event to return the listeners from. 1335 * @return {Function[]|Object} All listener functions for the event. 1336 */ 1337 proto.getListeners = function getListeners(evt) { 1338 var events = this._getEvents(); 1339 var response; 1340 var key; 1341 1342 // Return a concatenated array of all matching events if 1343 // the selector is a regular expression. 1344 if (typeof evt === 'object') { 1345 response = {}; 1346 for (key in events) { 1347 if (events.hasOwnProperty(key) && evt.test(key)) { 1348 response[key] = events[key]; 1349 } 1350 } 1351 } 1352 else { 1353 response = events[evt] || (events[evt] = []); 1354 } 1355 1356 return response; 1357 }; 1358 1359 /** 1360 * Takes a list of listener objects and flattens it into a list of listener functions. 1361 * 1362 * @param {Object[]} listeners Raw listener objects. 1363 * @return {Function[]} Just the listener functions. 1364 */ 1365 proto.flattenListeners = function flattenListeners(listeners) { 1366 var flatListeners = []; 1367 var i; 1368 1369 for (i = 0; i < listeners.length; i += 1) { 1370 flatListeners.push(listeners[i].listener); 1371 } 1372 1373 return flatListeners; 1374 }; 1375 1376 /** 1377 * Fetches the requested listeners via getListeners but will always return the results inside an object. This is mainly for internal use but others may find it useful. 1378 * 1379 * @param {String|RegExp} evt Name of the event to return the listeners from. 1380 * @return {Object} All listener functions for an event in an object. 1381 */ 1382 proto.getListenersAsObject = function getListenersAsObject(evt) { 1383 var listeners = this.getListeners(evt); 1384 var response; 1385 1386 if (listeners instanceof Array) { 1387 response = {}; 1388 response[evt] = listeners; 1389 } 1390 1391 return response || listeners; 1392 }; 1393 1394 /** 1395 * Adds a listener function to the specified event. 1396 * The listener will not be added if it is a duplicate. 1397 * If the listener returns true then it will be removed after it is called. 1398 * If you pass a regular expression as the event name then the listener will be added to all events that match it. 1399 * 1400 * @param {String|RegExp} evt Name of the event to attach the listener to. 1401 * @param {Function} listener Method to be called when the event is emitted. If the function returns true then it will be removed after calling. 1402 * @return {Object} Current instance of EventEmitter for chaining. 1403 */ 1404 proto.addListener = function addListener(evt, listener) { 1405 var listeners = this.getListenersAsObject(evt); 1406 var listenerIsWrapped = typeof listener === 'object'; 1407 var key; 1408 1409 for (key in listeners) { 1410 if (listeners.hasOwnProperty(key) && indexOfListener(listeners[key], listener) === -1) { 1411 listeners[key].push(listenerIsWrapped ? listener : { 1412 listener: listener, 1413 once: false 1414 }); 1415 } 1416 } 1417 1418 return this; 1419 }; 1420 1421 /** 1422 * Alias of addListener 1423 */ 1424 proto.on = alias('addListener'); 1425 1426 /** 1427 * Semi-alias of addListener. It will add a listener that will be 1428 * automatically removed after it's first execution. 1429 * 1430 * @param {String|RegExp} evt Name of the event to attach the listener to. 1431 * @param {Function} listener Method to be called when the event is emitted. If the function returns true then it will be removed after calling. 1432 * @return {Object} Current instance of EventEmitter for chaining. 1433 */ 1434 proto.addOnceListener = function addOnceListener(evt, listener) { 1435 return this.addListener(evt, { 1436 listener: listener, 1437 once: true 1438 }); 1439 }; 1440 1441 /** 1442 * Alias of addOnceListener. 1443 */ 1444 proto.once = alias('addOnceListener'); 1445 1446 /** 1447 * Defines an event name. This is required if you want to use a regex to add a listener to multiple events at once. If you don't do this then how do you expect it to know what event to add to? Should it just add to every possible match for a regex? No. That is scary and bad. 1448 * You need to tell it what event names should be matched by a regex. 1449 * 1450 * @param {String} evt Name of the event to create. 1451 * @return {Object} Current instance of EventEmitter for chaining. 1452 */ 1453 proto.defineEvent = function defineEvent(evt) { 1454 this.getListeners(evt); 1455 return this; 1456 }; 1457 1458 /** 1459 * Uses defineEvent to define multiple events. 1460 * 1461 * @param {String[]} evts An array of event names to define. 1462 * @return {Object} Current instance of EventEmitter for chaining. 1463 */ 1464 proto.defineEvents = function defineEvents(evts) { 1465 for (var i = 0; i < evts.length; i += 1) { 1466 this.defineEvent(evts[i]); 1467 } 1468 return this; 1469 }; 1470 1471 /** 1472 * Removes a listener function from the specified event. 1473 * When passed a regular expression as the event name, it will remove the listener from all events that match it. 1474 * 1475 * @param {String|RegExp} evt Name of the event to remove the listener from. 1476 * @param {Function} listener Method to remove from the event. 1477 * @return {Object} Current instance of EventEmitter for chaining. 1478 */ 1479 proto.removeListener = function removeListener(evt, listener) { 1480 var listeners = this.getListenersAsObject(evt); 1481 var index; 1482 var key; 1483 1484 for (key in listeners) { 1485 if (listeners.hasOwnProperty(key)) { 1486 index = indexOfListener(listeners[key], listener); 1487 1488 if (index !== -1) { 1489 listeners[key].splice(index, 1); 1490 } 1491 } 1492 } 1493 1494 return this; 1495 }; 1496 1497 /** 1498 * Alias of removeListener 1499 */ 1500 proto.off = alias('removeListener'); 1501 1502 /** 1503 * Adds listeners in bulk using the manipulateListeners method. 1504 * If you pass an object as the second argument you can add to multiple events at once. The object should contain key value pairs of events and listeners or listener arrays. You can also pass it an event name and an array of listeners to be added. 1505 * You can also pass it a regular expression to add the array of listeners to all events that match it. 1506 * Yeah, this function does quite a bit. That's probably a bad thing. 1507 * 1508 * @param {String|Object|RegExp} evt An event name if you will pass an array of listeners next. An object if you wish to add to multiple events at once. 1509 * @param {Function[]} [listeners] An optional array of listener functions to add. 1510 * @return {Object} Current instance of EventEmitter for chaining. 1511 */ 1512 proto.addListeners = function addListeners(evt, listeners) { 1513 // Pass through to manipulateListeners 1514 return this.manipulateListeners(false, evt, listeners); 1515 }; 1516 1517 /** 1518 * Removes listeners in bulk using the manipulateListeners method. 1519 * If you pass an object as the second argument you can remove from multiple events at once. The object should contain key value pairs of events and listeners or listener arrays. 1520 * You can also pass it an event name and an array of listeners to be removed. 1521 * You can also pass it a regular expression to remove the listeners from all events that match it. 1522 * 1523 * @param {String|Object|RegExp} evt An event name if you will pass an array of listeners next. An object if you wish to remove from multiple events at once. 1524 * @param {Function[]} [listeners] An optional array of listener functions to remove. 1525 * @return {Object} Current instance of EventEmitter for chaining. 1526 */ 1527 proto.removeListeners = function removeListeners(evt, listeners) { 1528 // Pass through to manipulateListeners 1529 return this.manipulateListeners(true, evt, listeners); 1530 }; 1531 1532 /** 1533 * Edits listeners in bulk. The addListeners and removeListeners methods both use this to do their job. You should really use those instead, this is a little lower level. 1534 * The first argument will determine if the listeners are removed (true) or added (false). 1535 * If you pass an object as the second argument you can add/remove from multiple events at once. The object should contain key value pairs of events and listeners or listener arrays. 1536 * You can also pass it an event name and an array of listeners to be added/removed. 1537 * You can also pass it a regular expression to manipulate the listeners of all events that match it. 1538 * 1539 * @param {Boolean} remove True if you want to remove listeners, false if you want to add. 1540 * @param {String|Object|RegExp} evt An event name if you will pass an array of listeners next. An object if you wish to add/remove from multiple events at once. 1541 * @param {Function[]} [listeners] An optional array of listener functions to add/remove. 1542 * @return {Object} Current instance of EventEmitter for chaining. 1543 */ 1544 proto.manipulateListeners = function manipulateListeners(remove, evt, listeners) { 1545 var i; 1546 var value; 1547 var single = remove ? this.removeListener : this.addListener; 1548 var multiple = remove ? this.removeListeners : this.addListeners; 1549 1550 // If evt is an object then pass each of it's properties to this method 1551 if (typeof evt === 'object' && !(evt instanceof RegExp)) { 1552 for (i in evt) { 1553 if (evt.hasOwnProperty(i) && (value = evt[i])) { 1554 // Pass the single listener straight through to the singular method 1555 if (typeof value === 'function') { 1556 single.call(this, i, value); 1557 } 1558 else { 1559 // Otherwise pass back to the multiple function 1560 multiple.call(this, i, value); 1561 } 1562 } 1563 } 1564 } 1565 else { 1566 // So evt must be a string 1567 // And listeners must be an array of listeners 1568 // Loop over it and pass each one to the multiple method 1569 i = listeners.length; 1570 while (i--) { 1571 single.call(this, evt, listeners[i]); 1572 } 1573 } 1574 1575 return this; 1576 }; 1577 1578 /** 1579 * Removes all listeners from a specified event. 1580 * If you do not specify an event then all listeners will be removed. 1581 * That means every event will be emptied. 1582 * You can also pass a regex to remove all events that match it. 1583 * 1584 * @param {String|RegExp} [evt] Optional name of the event to remove all listeners for. Will remove from every event if not passed. 1585 * @return {Object} Current instance of EventEmitter for chaining. 1586 */ 1587 proto.removeEvent = function removeEvent(evt) { 1588 var type = typeof evt; 1589 var events = this._getEvents(); 1590 var key; 1591 1592 // Remove different things depending on the state of evt 1593 if (type === 'string') { 1594 // Remove all listeners for the specified event 1595 delete events[evt]; 1596 } 1597 else if (type === 'object') { 1598 // Remove all events matching the regex. 1599 for (key in events) { 1600 if (events.hasOwnProperty(key) && evt.test(key)) { 1601 delete events[key]; 1602 } 1603 } 1604 } 1605 else { 1606 // Remove all listeners in all events 1607 delete this._events; 1608 } 1609 1610 return this; 1611 }; 1612 1613 /** 1614 * Alias of removeEvent. 1615 * 1616 * Added to mirror the node API. 1617 */ 1618 proto.removeAllListeners = alias('removeEvent'); 1619 1620 /** 1621 * Emits an event of your choice. 1622 * When emitted, every listener attached to that event will be executed. 1623 * If you pass the optional argument array then those arguments will be passed to every listener upon execution. 1624 * Because it uses `apply`, your array of arguments will be passed as if you wrote them out separately.
1625 * So they will not arrive within the array on the other side, they will be separate. 1626 * You can also pass a regular expression to emit to all events that match it. 1627 * 1628 * @param {String|RegExp} evt Name of the event to emit and execute listeners for. 1629 * @param {Array} [args] Optional array of arguments to be passed to each listener. 1630 * @return {Object} Current instance of EventEmitter for chaining. 1631 */ 1632 proto.emitEvent = function emitEvent(evt, args) { 1633 var listeners = this.getListenersAsObject(evt); 1634 var listener; 1635 var i; 1636 var key; 1637 var response; 1638 1639 for (key in listeners) { 1640 if (listeners.hasOwnProperty(key)) { 1641 i = listeners[key].length; 1642 1643 while (i--) { 1644 // If the listener returns true then it shall be removed from the event 1645 // The function is executed either with a basic call or an apply if there is an args array 1646 listener = listeners[key][i]; 1647 1648 if (listener.once === true) { 1649 this.removeListener(evt, listener.listener); 1650 } 1651 1652 response = listener.listener.apply(this, args || []); 1653 1654 if (response === this._getOnceReturnValue()) { 1655 this.removeListener(evt, listener.listener); 1656 } 1657 } 1658 } 1659 } 1660 1661 return this; 1662 }; 1663 1664 /** 1665 * Alias of emitEvent 1666 */ 1667 proto.trigger = alias('emitEvent'); 1668 1669 /** 1670 * Subtly different from emitEvent in that it will pass its arguments on to the listeners, as opposed to taking a single array of arguments to pass on. 1671 * As with emitEvent, you can pass a regex in place of the event name to emit to all events that match it. 1672 * 1673 * @param {String|RegExp} evt Name of the event to emit and execute listeners for. 1674 * @param {...*} Optional additional arguments to be passed to each listener. 1675 * @return {Object} Current instance of EventEmitter for chaining. 1676 */ 1677 proto.emit = function emit(evt) { 1678 var args = Array.prototype.slice.call(arguments, 1); 1679 return this.emitEvent(evt, args); 1680 }; 1681 1682 /** 1683 * Sets the current value to check against when executing listeners. If a 1684 * listeners return value matches the one set here then it will be removed 1685 * after execution. This value defaults to true. 1686 * 1687 * @param {*} value The new value to check for when executing listeners. 1688 * @return {Object} Current instance of EventEmitter for chaining. 1689 */ 1690 proto.setOnceReturnValue = function setOnceReturnValue(value) { 1691 this._onceReturnValue = value; 1692 return this; 1693 }; 1694 1695 /** 1696 * Fetches the current value to check against when executing listeners. If 1697 * the listeners return value matches this one then it should be removed 1698 * automatically. It will return true by default. 1699 * 1700 * @return {*|Boolean} The current value to check for or the default, true. 1701 * @api private 1702 */ 1703 proto._getOnceReturnValue = function _getOnceReturnValue() { 1704 if (this.hasOwnProperty('_onceReturnValue')) { 1705 return this._onceReturnValue; 1706 } 1707 else { 1708 return true; 1709 } 1710 }; 1711 1712 /** 1713 * Fetches the events object and creates one if required. 1714 * 1715 * @return {Object} The events storage object. 1716 * @api private 1717 */ 1718 proto._getEvents = function _getEvents() { 1719 return this._events || (this._events = {}); 1720 }; 1721 1722 /** 1723 * Reverts the global {@link EventEmitter} to its previous value and returns a reference to this version. 1724 * 1725 * @return {Function} Non conflicting EventEmitter class. 1726 */ 1727 EventEmitter.noConflict = function noConflict() { 1728 exports.EventEmitter = originalGlobalValue; 1729 return EventEmitter; 1730 }; 1731 1732 // Expose the class either via AMD, CommonJS or the global object 1733 if (typeof define === 'function' && define.amd) { 1734 define('eventEmitter/EventEmitter',[],function () { 1735 return EventEmitter; 1736 }); 1737 } 1738 else if (typeof module === 'object' && module.exports){ 1739 module.exports = EventEmitter; 1740 } 1741 else { 1742 this.EventEmitter = EventEmitter; 1743 } 1744}.call(this)); 1745 1746/*! 1747 * eventie v1.0.4 1748 * event binding helper 1749 * eventie.bind( elem, 'click', myFn ) 1750 * eventie.unbind( elem, 'click', myFn ) 1751 */ 1752 1753/*jshint browser: true, undef: true, unused: true */ 1754/*global define: false */ 1755 1756( function( window ) { 1757 1758 1759 1760var docElem = document.documentElement; 1761 1762var bind = function() {}; 1763 1764function getIEEvent( obj ) { 1765 var event = window.event; 1766 // add event.target 1767 event.target = event.target || event.srcElement || obj; 1768 return event; 1769} 1770 1771if ( docElem.addEventListener ) { 1772 bind = function( obj, type, fn ) { 1773 obj.addEventListener( type, fn, false ); 1774 }; 1775} else if ( docElem.attachEvent ) { 1776 bind = function( obj, type, fn ) { 1777 obj[ type + fn ] = fn.handleEvent ? 1778 function() { 1779 var event = getIEEvent( obj ); 1780 fn.handleEvent.call( fn, event ); 1781 } : 1782 function() { 1783 var event = getIEEvent( obj ); 1784 fn.call( obj, event ); 1785 }; 1786 obj.attachEvent( "on" + type, obj[ type + fn ] ); 1787 }; 1788} 1789 1790var unbind = function() {}; 1791 1792if ( docElem.removeEventListener ) { 1793 unbind = function( obj, type, fn ) { 1794 obj.removeEventListener( type, fn, false ); 1795 }; 1796} else if ( docElem.detachEvent ) { 1797 unbind = function( obj, type, fn ) { 1798 obj.detachEvent( "on" + type, obj[ type + fn ] ); 1799 try { 1800 delete obj[ type + fn ]; 1801 } catch ( err ) { 1802 // can't delete window object properties 1803 obj[ type + fn ] = undefined; 1804 } 1805 }; 1806} 1807 1808var eventie = { 1809 bind: bind, 1810 unbind: unbind 1811}; 1812 1813// transport 1814if ( typeof define === 'function' && define.amd ) { 1815 // AMD 1816 define( 'eventie/eventie',eventie ); 1817} else { 1818 // browser global 1819 window.eventie = eventie; 1820} 1821 1822})( this ); 1823 1824/*! 1825 * imagesLoaded v3.1.8 1826 * JavaScript is all like "You images are done yet or what?" 1827 * MIT License 1828 */ 1829 1830( function( window, factory ) { 1831 // universal module definition 1832 1833 /*global define: false, module: false, require: false */ 1834 1835 if ( typeof define === 'function' && define.amd ) { 1836 // AMD 1837 define( [ 1838 'eventEmitter/EventEmitter', 1839 'eventie/eventie' 1840 ], function( EventEmitter, eventie ) { 1841 return factory( window, EventEmitter, eventie ); 1842 }); 1843 } else if ( typeof exports === 'object' ) { 1844 // CommonJS 1845 module.exports = factory( 1846 window, 1847 require('wolfy87-eventemitter'), 1848 require('eventie') 1849 ); 1850 } else { 1851 // browser global 1852 window.imagesLoaded = factory( 1853 window, 1854 window.EventEmitter, 1855 window.eventie 1856 ); 1857 } 1858 1859})( window, 1860 1861// -------------------------- factory -------------------------- // 1862 1863function factory( window, EventEmitter, eventie ) { 1864 1865 1866 1867var $ = window.jQuery; 1868var console = window.console; 1869var hasConsole = typeof console !== 'undefined'; 1870 1871// -------------------------- helpers -------------------------- // 1872 1873// extend objects 1874function extend( a, b ) { 1875 for ( var prop in b ) { 1876 a[ prop ] = b[ prop ]; 1877 } 1878 return a; 1879} 1880 1881var objToString = Object.prototype.toString; 1882function isArray( obj ) { 1883 return objToString.call( obj ) === '[object Array]'; 1884} 1885 1886// turn element or nodeList into an array 1887function makeArray( obj ) { 1888 var ary = []; 1889 if ( isArray( obj ) ) { 1890 // use object if already an array 1891 ary = obj; 1892 } else if ( typeof obj.length === 'number' ) { 1893 // convert nodeList to array 1894 for ( var i=0, len = obj.length; i < len; i++ ) { 1895 ary.push( obj[i] ); 1896 } 1897 } else { 1898 // array of single index 1899 ary.push( obj ); 1900 } 1901 return ary; 1902} 1903 1904 // -------------------------- imagesLoaded -------------------------- // 1905 1906 /** 1907 * @param {Array, Element, NodeList, String} elem 1908 * @param {Object or Function} options - if function, use as callback 1909 * @param {Function} onAlways - callback function 1910 */ 1911 function ImagesLoaded( elem, options, onAlways ) { 1912 // coerce ImagesLoaded() without new, to be new ImagesLoaded() 1913 if ( !( this instanceof ImagesLoaded ) ) { 1914 return new ImagesLoaded( elem, options ); 1915 } 1916 // use elem as selector string 1917 if ( typeof elem === 'string' ) { 1918 elem = document.querySelectorAll( elem ); 1919 } 1920 1921 this.elements = makeArray( elem ); 1922 this.options = extend( {}, this.options ); 1923 1924 if ( typeof options === 'function' ) { 1925 onAlways = options; 1926 } else { 1927 extend( this.options, options ); 1928 } 1929 1930 if ( onAlways ) { 1931 this.on( 'always', onAlways ); 1932 } 1933 1934 this.getImages(); 1935 1936 if ( $ ) { 1937 // add jQuery Deferred object 1938 this.jqDeferred = new $.Deferred(); 1939 } 1940 1941 // HACK check async to allow time to bind listeners 1942 var _this = this; 1943 setTimeout( function() { 1944 _this.check(); 1945 }); 1946 } 1947 1948 ImagesLoaded.prototype = new EventEmitter(); 1949 1950 ImagesLoaded.prototype.options = {}; 1951 1952 ImagesLoaded.prototype.getImages = function() { 1953 this.images = []; 1954 1955 // filter & find items if we have an item selector 1956 for ( var i=0, len = this.elements.length; i < len; i++ ) { 1957 var elem = this.elements[i]; 1958 // filter siblings 1959 if ( elem.nodeName === 'IMG' ) { 1960 this.addImage( elem ); 1961 } 1962 // find children 1963 // no non-element nodes, #143 1964 var nodeType = elem.nodeType; 1965 if ( !nodeType || !( nodeType === 1 || nodeType === 9 || nodeType === 11 ) ) { 1966 continue; 1967 } 1968 var childElems = elem.querySelectorAll('img'); 1969 // concat childElems to filterFound array 1970 for ( var j=0, jLen = childElems.length; j < jLen; j++ ) { 1971 var img = childElems[j]; 1972 this.addImage( img ); 1973 } 1974 } 1975 }; 1976 1977 /** 1978 * @param {Image} img 1979 */ 1980 ImagesLoaded.prototype.addImage = function( img ) { 1981 var loadingImage = new LoadingImage( img ); 1982 this.images.push( loadingImage ); 1983 }; 1984
1985 ImagesLoaded.prototype.check = function() { 1986 var _this = this; 1987 var checkedCount = 0; 1988 var length = this.images.length; 1989 this.hasAnyBroken = false; 1990 // complete if no images 1991 if ( !length ) { 1992 this.complete(); 1993 return; 1994 } 1995 1996 function onConfirm( image, message ) { 1997 if ( _this.options.debug && hasConsole ) { 1998 console.log( 'confirm', image, message ); 1999 } 2000 2001 _this.progress( image ); 2002 checkedCount++; 2003 if ( checkedCount === length ) { 2004 _this.complete(); 2005 } 2006 return true; // bind once 2007 } 2008 2009 for ( var i=0; i < length; i++ ) { 2010 var loadingImage = this.images[i]; 2011 loadingImage.on( 'confirm', onConfirm ); 2012 loadingImage.check(); 2013 } 2014 }; 2015 2016 ImagesLoaded.prototype.progress = function( image ) { 2017 this.hasAnyBroken = this.hasAnyBroken || !image.isLoaded; 2018 // HACK - Chrome triggers event before object properties have changed. #83 2019 var _this = this; 2020 setTimeout( function() { 2021 _this.emit( 'progress', _this, image ); 2022 if ( _this.jqDeferred && _this.jqDeferred.notify ) { 2023 _this.jqDeferred.notify( _this, image ); 2024 } 2025 }); 2026 }; 2027 2028 ImagesLoaded.prototype.complete = function() { 2029 var eventName = this.hasAnyBroken ? 'fail' : 'done'; 2030 this.isComplete = true; 2031 var _this = this; 2032 // HACK - another setTimeout so that confirm happens after progress 2033 setTimeout( function() { 2034 _this.emit( eventName, _this ); 2035 _this.emit( 'always', _this ); 2036 if ( _this.jqDeferred ) { 2037 var jqMethod = _this.hasAnyBroken ? 'reject' : 'resolve'; 2038 _this.jqDeferred[ jqMethod ]( _this ); 2039 } 2040 }); 2041 }; 2042 2043 // -------------------------- jquery -------------------------- // 2044 2045 if ( $ ) { 2046 $.fn.imagesLoaded = function( options, callback ) { 2047 var instance = new ImagesLoaded( this, options, callback ); 2048 return instance.jqDeferred.promise( $(this) ); 2049 }; 2050 } 2051 2052 2053 // -------------------------- -------------------------- // 2054 2055 function LoadingImage( img ) { 2056 this.img = img; 2057 } 2058 2059 LoadingImage.prototype = new EventEmitter(); 2060 2061 LoadingImage.prototype.check = function() { 2062 // first check cached any previous images that have same src 2063 var resource = cache[ this.img.src ] || new Resource( this.img.src ); 2064 if ( resource.isConfirmed ) { 2065 this.confirm( resource.isLoaded, 'cached was confirmed' ); 2066 return; 2067 } 2068 2069 // If complete is true and browser supports natural sizes, 2070 // try to check for image status manually. 2071 if ( this.img.complete && this.img.naturalWidth !== undefined ) { 2072 // report based on naturalWidth 2073 this.confirm( this.img.naturalWidth !== 0, 'naturalWidth' ); 2074 return; 2075 } 2076 2077 // If none of the checks above matched, simulate loading on detached element. 2078 var _this = this; 2079 resource.on( 'confirm', function( resrc, message ) { 2080 _this.confirm( resrc.isLoaded, message ); 2081 return true; 2082 }); 2083 2084 resource.check(); 2085 }; 2086 2087 LoadingImage.prototype.confirm = function( isLoaded, message ) { 2088 this.isLoaded = isLoaded; 2089 this.emit( 'confirm', this, message ); 2090 }; 2091 2092 // -------------------------- Resource -------------------------- // 2093 2094 // Resource checks each src, only once 2095 // separate class from LoadingImage to prevent memory leaks. See #115 2096 2097 var cache = {}; 2098 2099 function Resource( src ) { 2100 this.src = src; 2101 // add to cache 2102 cache[ src ] = this; 2103 } 2104 2105 Resource.prototype = new EventEmitter(); 2106 2107 Resource.prototype.check = function() { 2108 // only trigger checking once 2109 if ( this.isChecked ) { 2110 return; 2111 } 2112 // simulate loading on detached element 2113 var proxyImage = new Image(); 2114 eventie.bind( proxyImage, 'load', this ); 2115 eventie.bind( proxyImage, 'error', this ); 2116 proxyImage.src = this.src; 2117 // set flag 2118 this.isChecked = true; 2119 }; 2120 2121 // ----- events ----- // 2122 2123 // trigger specified handler for event type 2124 Resource.prototype.handleEvent = function( event ) { 2125 var method = 'on' + event.type; 2126 if ( this[ method ] ) { 2127 this[ method ]( event ); 2128 } 2129 }; 2130 2131 Resource.prototype.onload = function( event ) { 2132 this.confirm( true, 'onload' ); 2133 this.unbindProxyEvents( event ); 2134 }; 2135 2136 Resource.prototype.onerror = function( event ) { 2137 this.confirm( false, 'onerror' ); 2138 this.unbindProxyEvents( event ); 2139 }; 2140 2141 // ----- confirm ----- // 2142 2143 Resource.prototype.confirm = function( isLoaded, message ) { 2144 this.isConfirmed = true; 2145 this.isLoaded = isLoaded;
2146 this.emit( 'confirm', this, message ); 2147 }; 2148 2149 Resource.prototype.unbindProxyEvents = function( event ) { 2150 eventie.unbind( event.target, 'load', this ); 2151 eventie.unbind( event.target, 'error', this ); 2152 }; 2153 2154 // ----- ----- // 2155 2156 return ImagesLoaded; 2157 2158});; 2159/*! 2160 * hoverIntent v1.8.0 // 2014.06.29 // jQuery v1.9.1+ 2161 * http://cherne.net/brian/resources/jquery.hoverIntent.html 2162 * 2163 * You may use hoverIntent under the terms of the MIT license. Basically that 2164 * means you are free to use hoverIntent as long as this header is left intact. 2165 * Copyright 2007, 2014 Brian Cherne 2166 */ 2167 2168/* hoverIntent is similar to jQuery's built-in "hover" method except that 2169 * instead of firing the handlerIn function immediately, hoverIntent checks 2170 * to see if the user's mouse has slowed down (beneath the sensitivity 2171 * threshold) before firing the event. The handlerOut function is only 2172 * called after a matching handlerIn. 2173 * 2174 * // basic usage ... just like .hover() 2175 * .hoverIntent( handlerIn, handlerOut ) 2176 * .hoverIntent( handlerInOut ) 2177 * 2178 * // basic usage ... with event delegation! 2179 * .hoverIntent( handlerIn, handlerOut, selector ) 2180 * .hoverIntent( handlerInOut, selector ) 2181 * 2182 * // using a basic configuration object 2183 * .hoverIntent( config ) 2184 * 2185 * @param handlerIn function OR configuration object 2186 * @param handlerOut function OR selector for delegation OR undefined 2187 * @param selector selector OR undefined 2188 * @author Brian Cherne <brian(at)cherne(dot)net> 2189 */ 2190(function($) { 2191 $.fn.hoverIntent = function(handlerIn,handlerOut,selector) { 2192 2193 // default configuration values 2194 var cfg = { 2195 interval: 100, 2196 sensitivity: 6, 2197 timeout: 0 2198 }; 2199 2200 if ( typeof handlerIn === "object" ) { 2201 cfg = $.extend(cfg, handlerIn ); 2202 } else if ($.isFunction(handlerOut)) { 2203 cfg = $.extend(cfg, { over: handlerIn, out: handlerOut, selector: selector } ); 2204 } else { 2205 cfg = $.extend(cfg, { over: handlerIn, out: handlerIn, selector: handlerOut } ); 2206 } 2207 2208 // instantiate variables 2209 // cX, cY = current X and Y position of mouse, updated by mousemove event 2210 // pX, pY = previous X and Y position of mouse, set by mouseover and polling interval 2211 var cX, cY, pX, pY; 2212 2213 // A private function for getting mouse position 2214 var track = function(ev) { 2215 cX = ev.pageX; 2216 cY = ev.pageY; 2217 }; 2218 2219 // A private function for comparing current and previous mouse position 2220 var compare = function(ev,ob) { 2221 ob.hoverIntent_t = clearTimeout(ob.hoverIntent_t); 2222 // compare mouse positions to see if they've crossed the threshold 2223 if ( Math.sqrt( (pX-cX)*(pX-cX) + (pY-cY)*(pY-cY) ) < cfg.sensitivity ) { 2224 $(ob).off("mousemove.hoverIntent",track); 2225 // set hoverIntent state to true (so mouseOut can be called) 2226 ob.hoverIntent_s = true; 2227 return cfg.over.apply(ob,[ev]); 2228 } else { 2229 // set previous coordinates for next time 2230 pX = cX; pY = cY; 2231 // use self-calling timeout, guarantees intervals are spaced out properly (avoids JavaScript timer bugs) 2232 ob.hoverIntent_t = setTimeout( function(){compare(ev, ob);} , cfg.interval ); 2233 } 2234 }; 2235 2236 // A private function for delaying the mouseOut function 2237 var delay = function(ev,ob) { 2238 ob.hoverIntent_t = clearTimeout(ob.hoverIntent_t); 2239 ob.hoverIntent_s = false; 2240 return cfg.out.apply(ob,[ev]); 2241 }; 2242 2243 // A private function for handling mouse 'hovering' 2244 var handleHover = function(e) { 2245 // copy objects to be passed into t (required for event object to be passed in IE) 2246 var ev = $.extend({},e); 2247 var ob = this; 2248 2249 // cancel hoverIntent timer if it exists 2250 if (ob.hoverIntent_t) { ob.hoverIntent_t = clearTimeout(ob.hoverIntent_t); } 2251 2252 // if e.type === "mouseenter" 2253 if (e.type === "mouseenter") { 2254 // set "previous" X and Y position based on initial entry point 2255 pX = ev.pageX; pY = ev.pageY; 2256 // update "current" X and Y position based on mousemove 2257 $(ob).on("mousemove.hoverIntent",track); 2258 // start polling interval (self-calling timeout) to compare mouse coordinates over time 2259 if (!ob.hoverIntent_s) { ob.hoverIntent_t = setTimeout( function(){compare(ev,ob);} , cfg.interval );} 2260 2261 // else e.type == "mouseleave" 2262 } else { 2263 // unbind expensive mousemove event 2264 $(ob).off("mousemove.hoverIntent",track); 2265 // if hoverIntent state is true, then call the mouseOut function after the specified delay 2266 if (ob.hoverIntent_s) { ob.hoverIntent_t = setTimeout( function(){delay(ev,ob);} , cfg.timeout );} 2267 } 2268 }; 2269 2270 // listen for mouseenter and mouseleave 2271 return this.on({'mouseenter.hoverIntent':handleHover,'mouseleave.hoverIntent':handleHover}, cfg.selector); 2272 }; 2273})(jQuery);; 2274/*! VelocityJS.org (1.1.0). (C) 2014 Julian Shapiro. MIT @license: en.wikipedia.org/wiki/MIT_License */ 2275 2276/************************* 2277 Velocity jQuery Shim 2278*************************/ 2279
vendor: 9,456 bytes, lines 2280-2623
2280/*! VelocityJS.org jQuery Shim (1.0.1). (C) 2014 The jQuery Foundation. MIT @license: en.wikipedia.org/wiki/MIT_License. */ 2281 2282/* This file contains the jQuery functions that Velocity relies on, thereby removing Velocity's dependency on a full copy of jQuery, and allowing it to work in any environment. */ 2283/* These shimmed functions are only used if jQuery isn't present. If both this shim and jQuery are loaded, Velocity defaults to jQuery proper. */ 2284/* Browser support: Using this shim instead of jQuery proper removes support for IE8. */ 2285 2286;(function (window) { 2287 /*************** 2288 Setup 2289 ***************/ 2290 2291 /* If jQuery is already loaded, there's no point in loading this shim. */ 2292 if (window.jQuery) { 2293 return; 2294 } 2295 2296 /* jQuery base. */ 2297 var $ = function (selector, context) { 2298 return new $.fn.init(selector, context); 2299 }; 2300 2301 /******************** 2302 Private Methods 2303 ********************/ 2304 2305 /* jQuery */ 2306 $.isWindow = function (obj) { 2307 /* jshint eqeqeq: false */ 2308 return obj != null && obj == obj.window; 2309 }; 2310 2311 /* jQuery */ 2312 $.type = function (obj) { 2313 if (obj == null) { 2314 return obj + ""; 2315 } 2316 2317 return typeof obj === "object" || typeof obj === "function" ? 2318 class2type[toString.call(obj)] || "object" : 2319 typeof obj; 2320 }; 2321 2322 /* jQuery */ 2323 $.isArray = Array.isArray || function (obj) { 2324 return $.type(obj) === "array"; 2325 }; 2326 2327 /* jQuery */ 2328 function isArraylike (obj) { 2329 var length = obj.length, 2330 type = $.type(obj); 2331 2332 if (type === "function" || $.isWindow(obj)) { 2333 return false; 2334 } 2335 2336 if (obj.nodeType === 1 && length) { 2337 return true; 2338 } 2339 2340 return type === "array" || length === 0 || typeof length === "number" && length > 0 && (length - 1) in obj; 2341 } 2342 2343 /*************** 2344 $ Methods 2345 ***************/ 2346 2347 /* jQuery: Support removed for IE<9. */ 2348 $.isPlainObject = function (obj) { 2349 var key; 2350 2351 if (!obj || $.type(obj) !== "object" || obj.nodeType || $.isWindow(obj)) { 2352 return false; 2353 } 2354 2355 try { 2356 if (obj.constructor && 2357 !hasOwn.call(obj, "constructor") && 2358 !hasOwn.call(obj.constructor.prototype, "isPrototypeOf")) { 2359 return false; 2360 } 2361 } catch (e) { 2362 return false; 2363 } 2364 2365 for (key in obj) {} 2366 2367 return key === undefined || hasOwn.call(obj, key); 2368 }; 2369 2370 /* jQuery */ 2371 $.each = function(obj, callback, args) { 2372 var value, 2373 i = 0, 2374 length = obj.length, 2375 isArray = isArraylike(obj); 2376 2377 if (args) { 2378 if (isArray) { 2379 for (; i < length; i++) { 2380 value = callback.apply(obj[i], args); 2381 2382 if (value === false) { 2383 break; 2384 } 2385 } 2386 } else { 2387 for (i in obj) { 2388 value = callback.apply(obj[i], args); 2389 2390 if (value === false) { 2391 break; 2392 } 2393 } 2394 } 2395 2396 } else { 2397 if (isArray) { 2398 for (; i < length; i++) { 2399 value = callback.call(obj[i], i, obj[i]); 2400 2401 if (value === false) { 2402 break; 2403 } 2404 } 2405 } else { 2406 for (i in obj) { 2407 value = callback.call(obj[i], i, obj[i]); 2408 2409 if (value === false) { 2410 break; 2411 } 2412 } 2413 } 2414 } 2415 2416 return obj; 2417 }; 2418 2419 /* Custom */ 2420 $.data = function (node, key, value) { 2421 /* $.getData() */ 2422 if (value === undefined) { 2423 var id = node[$.expando], 2424 store = id && cache[id]; 2425 2426 if (key === undefined) { 2427 return store; 2428 } else if (store) { 2429 if (key in store) { 2430 return store[key]; 2431 } 2432 } 2433 /* $.setData() */ 2434 } else if (key !== undefined) { 2435 var id = node[$.expando] || (node[$.expando] = ++$.uuid); 2436 2437 cache[id] = cache[id] || {}; 2438 cache[id][key] = value; 2439 2440 return value; 2441 } 2442 }; 2443 2444 /* Custom */ 2445 $.removeData = function (node, keys) { 2446 var id = node[$.expando], 2447 store = id && cache[id]; 2448 2449 if (store) { 2450 $.each(keys, function(_, key) { 2451 delete store[key]; 2452 }); 2453 } 2454 }; 2455 2456 /* jQuery */ 2457 $.extend = function () { 2458 var src, copyIsArray, copy, name, options, clone, 2459 target = arguments[0] || {}, 2460 i = 1, 2461 length = arguments.length, 2462 deep = false; 2463 2464 if (typeof target === "boolean") { 2465 deep = target; 2466 2467 target = arguments[i] || {}; 2468 i++; 2469 } 2470 2471 if (typeof target !== "object" && $.type(target) !== "function") { 2472 target = {}; 2473 } 2474 2475 if (i === length) { 2476 target = this; 2477 i--; 2478 } 2479 2480 for (; i < length; i++) { 2481 if ((options = arguments[i]) != null) { 2482 for (name in options) { 2483 src = target[name]; 2484 copy = options[name]; 2485 2486 if (target === copy) { 2487 continue; 2488 } 2489 2490 if (deep && copy && ($.isPlainObject(copy) || (copyIsArray = $.isArray(copy)))) { 2491 if (copyIsArray) { 2492 copyIsArray = false; 2493 clone = src && $.isArray(src) ? src : []; 2494 2495 } else { 2496 clone = src && $.isPlainObject(src) ? src : {}; 2497 } 2498 2499 target[name] = $.extend(deep, clone, copy); 2500 2501 } else if (copy !== undefined) { 2502 target[name] = copy; 2503 } 2504 } 2505 } 2506 } 2507 2508 return target; 2509 }; 2510 2511 /* jQuery 1.4.3 */ 2512 $.queue = function (elem, type, data) { 2513 function $makeArray (arr, results) { 2514 var ret = results || []; 2515 2516 if (arr != null) { 2517 if (isArraylike(Object(arr))) { 2518 /* $.merge */ 2519 (function(first, second) { 2520 var len = +second.length, 2521 j = 0, 2522 i = first.length; 2523 2524 while (j < len) { 2525 first[i++] = second[j++]; 2526 } 2527 2528 if (len !== len) { 2529 while (second[j] !== undefined) { 2530 first[i++] = second[j++]; 2531 } 2532 } 2533 2534 first.length = i; 2535 2536 return first; 2537 })(ret, typeof arr === "string" ? [arr] : arr); 2538 } else { 2539 [].push.call(ret, arr); 2540 } 2541 } 2542 2543 return ret; 2544 } 2545 2546 if (!elem) { 2547 return; 2548 } 2549 2550 type = (type || "fx") + "queue"; 2551 2552 var q = $.data(elem, type); 2553 2554 if (!data) { 2555 return q || []; 2556 } 2557 2558 if (!q || $.isArray(data)) { 2559 q = $.data(elem, type, $makeArray(data)); 2560 } else { 2561 q.push(data); 2562 } 2563 2564 return q; 2565 }; 2566 2567 /* jQuery 1.4.3 */ 2568 $.dequeue = function (elems, type) { 2569 /* Custom: Embed element iteration. */ 2570 $.each(elems.nodeType ? [ elems ] : elems, function(i, elem) { 2571 type = type || "fx"; 2572 2573 var queue = $.queue(elem, type), 2574 fn = queue.shift(); 2575 2576 if (fn === "inprogress") { 2577 fn = queue.shift(); 2578 } 2579 2580 if (fn) { 2581 if (type === "fx") { 2582 queue.unshift("inprogress"); 2583 } 2584 2585 fn.call(elem, function() { 2586 $.dequeue(elem, type); 2587 }); 2588 } 2589 }); 2590 }; 2591 2592 /****************** 2593 $.fn Methods 2594 ******************/ 2595 2596 /* jQuery */ 2597 $.fn = $.prototype = { 2598 init: function (selector) { 2599 /* Just return the element wrapped inside an array; don't proceed with the actual jQuery node wrapping process. */ 2600 if (selector.nodeType) { 2601 this[0] = selector; 2602 2603 return this; 2604 } else { 2605 throw new Error("Not a DOM node."); 2606 } 2607 }, 2608 2609 offset: function () { 2610 /* jQuery altered code: Dropped disconnected DOM node checking. */ 2611 var box = this[0].getBoundingClientRect ? this[0].getBoundingClientRect() : { top: 0, left: 0 }; 2612 2613 return { 2614 top: box.top + (window.pageYOffset || document.scrollTop || 0) - (document.clientTop || 0), 2615 left: box.left + (window.pageXOffset || document.scrollLeft || 0) - (document.clientLeft || 0) 2616 }; 2617 }, 2618 2619 position: function () { 2620 /* jQuery */ 2621 function offsetParent() { 2622 var offsetParent = this.offsetParent || document; 2623
2624 while (offsetParent && (!offsetParent.nodeType.toLowerCase === "html" && offsetParent.style.position === "static")) { 2625 offsetParent = offsetParent.offsetParent; 2626 } 2627 2628 return offsetParent || document; 2629 } 2630 2631 /* Zepto */ 2632 var elem = this[0], 2633 offsetParent = offsetParent.apply(elem), 2634 offset = this.offset(), 2635 parentOffset = /^(?:body|html)$/i.test(offsetParent.nodeName) ? { top: 0, left: 0 } : $(offsetParent).offset() 2636 2637 offset.top -= parseFloat(elem.style.marginTop) || 0; 2638 offset.left -= parseFloat(elem.style.marginLeft) || 0; 2639 2640 if (offsetParent.style) { 2641 parentOffset.top += parseFloat(offsetParent.style.borderTopWidth) || 0 2642 parentOffset.left += parseFloat(offsetParent.style.borderLeftWidth) || 0 2643 } 2644 2645 return { 2646 top: offset.top - parentOffset.top, 2647 left: offset.left - parentOffset.left 2648 }; 2649 } 2650 }; 2651 2652 /********************** 2653 Private Variables 2654 **********************/ 2655 2656 /* For $.data() */ 2657 var cache = {}; 2658 $.expando = "velocity" + (new Date().getTime()); 2659 $.uuid = 0; 2660 2661 /* For $.queue() */ 2662 var class2type = {}, 2663 hasOwn = class2type.hasOwnProperty, 2664 toString = class2type.toString; 2665 2666 var types = "Boolean Number String Function Array Date RegExp Object Error".split(" "); 2667 for (var i = 0; i < types.length; i++) { 2668 class2type["[object " + types[i] + "]"] = types[i].toLowerCase(); 2669 } 2670 2671 /* Makes $(node) possible, without having to call init. */ 2672 $.fn.init.prototype = $.fn; 2673 2674 /* Globalize Velocity onto the window, and assign its Utilities property. */ 2675 window.Velocity = { Utilities: $ }; 2676})(window); 2677 2678/****************** 2679 Velocity.js 2680******************/ 2681 2682;(function (factory) { 2683 /* CommonJS module. */ 2684 if (typeof module === "object" && typeof module.exports === "object") { 2685 module.exports = factory(); 2686 /* AMD module. */ 2687 } else if (typeof define === "function" && define.amd) { 2688 define(factory); 2689 /* Browser globals. */ 2690 } else { 2691 factory(); 2692 } 2693}(function() { 2694return function (global, window, document, undefined) { 2695 2696 /*************** 2697 Summary 2698 ***************/ 2699 2700 /* 2701 - CSS: CSS stack that works independently from the rest of Velocity. 2702 - animate(): Core animation method that iterates over the targeted elements and queues the incoming call onto each element individually. 2703 - Pre-Queueing: Prepare the element for animation by instantiating its data cache and processing the call's options. 2704 - Queueing: The logic that runs once the call has reached its point of execution in the element's $.queue() stack. 2705 Most logic is placed here to avoid risking it becoming stale (if the element's properties have changed). 2706 - Pushing: Consolidation of the tween data followed by its push onto the global in-progress calls container. 2707 - tick(): The single requestAnimationFrame loop responsible for tweening all in-progress calls. 2708 - completeCall(): Handles the cleanup process for each Velocity call. 2709 */ 2710 2711 /********************* 2712 Helper Functions 2713 *********************/ 2714 2715 /* IE detection. Gist: https://gist.github.com/julianshapiro/9098609 */ 2716 var IE = (function() { 2717 if (document.documentMode) { 2718 return document.documentMode; 2719 } else { 2720 for (var i = 7; i > 4; i--) { 2721 var div = document.createElement("div"); 2722 2723 div.innerHTML = "<!--[if IE " + i + "]><span></span><![endif]-->"; 2724 2725 if (div.getElementsByTagName("span").length) { 2726 div = null; 2727 2728 return i; 2729 } 2730 } 2731 } 2732 2733 return undefined; 2734 })(); 2735 2736 /* rAF shim. Gist: https://gist.github.com/julianshapiro/9497513 */ 2737 var rAFShim = (function() { 2738 var timeLast = 0; 2739 2740 return window.webkitRequestAnimationFrame || window.mozRequestAnimationFrame || function(callback) { 2741 var timeCurrent = (new Date()).getTime(), 2742 timeDelta; 2743 2744 /* Dynamically set delay on a per-tick basis to match 60fps. */ 2745 /* Technique by Erik Moller. MIT license: https://gist.github.com/paulirish/1579671 */ 2746 timeDelta = Math.max(0, 16 - (timeCurrent - timeLast)); 2747 timeLast = timeCurrent + timeDelta; 2748 2749 return setTimeout(function() { callback(timeCurrent + timeDelta); }, timeDelta); 2750 }; 2751 })(); 2752 2753 /* Array compacting. Copyright Lo-Dash. MIT License: https://github.com/lodash/lodash/blob/master/LICENSE.txt */ 2754 function compactSparseArray (array) { 2755 var index = -1, 2756 length = array ? array.length : 0, 2757 result = []; 2758 2759 while (++index < length) { 2760 var value = array[index]; 2761 2762 if (value) { 2763 result.push(value); 2764 } 2765 } 2766 2767 return result; 2768 } 2769 2770 function sanitizeElements (elements) { 2771 /* Unwrap jQuery/Zepto objects. */ 2772 if (Type.isWrapped(elements)) { 2773 elements = [].slice.call(elements); 2774 /* Wrap a single element in an array so that $.each() can iterate with the element instead of its node's children. */ 2775 } else if (Type.isNode(elements)) { 2776 elements = [ elements ]; 2777 } 2778 2779 return elements; 2780 } 2781 2782 var Type = { 2783 isString: function (variable) { 2784 return (typeof variable === "string"); 2785 }, 2786 isArray: Array.isArray || function (variable) { 2787 return Object.prototype.toString.call(variable) === "[object Array]"; 2788 }, 2789 isFunction: function (variable) { 2790 return Object.prototype.toString.call(variable) === "[object Function]"; 2791 }, 2792 isNode: function (variable) { 2793 return variable && variable.nodeType; 2794 }, 2795 /* Copyright Martin Bohm. MIT License: https://gist.github.com/Tomalak/818a78a226a0738eaade */ 2796 isNodeList: function (variable) { 2797 return typeof variable === "object" && 2798 /^\[object (HTMLCollection|NodeList|Object)\]$/.test(Object.prototype.toString.call(variable)) && 2799 variable.length !== undefined && 2800 (variable.length === 0 || (typeof variable[0] === "object" && variable[0].nodeType > 0)); 2801 }, 2802 /* Determine if variable is a wrapped jQuery or Zepto element. */ 2803 isWrapped: function (variable) { 2804 return variable && (variable.jquery || (window.Zepto && window.Zepto.zepto.isZ(variable))); 2805 }, 2806 isSVG: function (variable) { 2807 return window.SVGElement && (variable instanceof window.SVGElement); 2808 }, 2809 isEmptyObject: function (variable) { 2810 for (var name in variable) { 2811 return false;
vendor: 7,701 bytes, lines 2812-2978
2812 } 2813 2814 return true; 2815 } 2816 }; 2817 2818 /***************** 2819 Dependencies 2820 *****************/ 2821 2822 var $, 2823 isJQuery = false; 2824 2825 if (global.fn && global.fn.jquery) { 2826 $ = global; 2827 isJQuery = true; 2828 } else { 2829 $ = window.Velocity.Utilities; 2830 } 2831 2832 if (IE <= 8 && !isJQuery) { 2833 throw new Error("Velocity: IE8 and below require jQuery to be loaded before Velocity."); 2834 } else if (IE <= 7) { 2835 /* Revert to jQuery's $.animate(), and lose Velocity's extra features. */ 2836 jQuery.fn.velocity = jQuery.fn.animate; 2837 2838 /* Now that $.fn.velocity is aliased, abort this Velocity declaration. */ 2839 return; 2840 } 2841 2842 /***************** 2843 Constants 2844 *****************/ 2845 2846 var DURATION_DEFAULT = 400, 2847 EASING_DEFAULT = "swing"; 2848 2849 /************* 2850 State 2851 *************/ 2852 2853 /* Note: The global object also doubles as a publicly-accessible data store for the purposes of unit testing. */ 2854 /* Note: Alias the lowercase and uppercase variants of "velocity" to minimize user confusion due to the lowercase nature of the $.fn extension. */ 2855 var Velocity = { 2856 /* Container for page-wide Velocity state data. */ 2857 State: { 2858 /* Detect mobile devices to determine if mobileHA should be turned on. */ 2859 isMobile: /Android|webOS|iPhone|iPad|iPod|BlackBerry|IEMobile|Opera Mini/i.test(navigator.userAgent), 2860 /* The mobileHA option's behavior changes on older Android devices (Gingerbread, versions 2.3.3-2.3.7). */ 2861 isAndroid: /Android/i.test(navigator.userAgent), 2862 isGingerbread: /Android 2\.3\.[3-7]/i.test(navigator.userAgent), 2863 isChrome: window.chrome, 2864 isFirefox: /Firefox/i.test(navigator.userAgent), 2865 /* Create a cached element for re-use when checking for CSS property prefixes. */ 2866 prefixElement: document.createElement("div"), 2867 /* Cache every prefix match to avoid repeating lookups. */ 2868 prefixMatches: {}, 2869 /* Cache the anchor used for animating window scrolling. */ 2870 scrollAnchor: null, 2871 /* Cache the property names associated with the scroll anchor. */ 2872 scrollPropertyLeft: null, 2873 scrollPropertyTop: null, 2874 /* Keep track of whether our RAF tick is running. */ 2875 isTicking: false, 2876 /* Container for every in-progress call to Velocity. */ 2877 calls: [] 2878 }, 2879 /* Velocity's custom CSS stack. Made global for unit testing. */ 2880 CSS: { /* Defined below. */ }, 2881 /* Defined by Velocity's optional jQuery shim. */ 2882 Utilities: $, 2883 /* Container for the user's custom animation redirects that are referenced by name in place of a properties map object. */ 2884 Redirects: { /* Manually registered by the user. */ }, 2885 Easings: { /* Defined below. */ }, 2886 /* Attempt to use ES6 Promises by default. Users can override this with a third-party promises library. */ 2887 Promise: window.Promise, 2888 /* Page-wide option defaults, which can be overriden by the user. */ 2889 defaults: { 2890 queue: "", 2891 duration: DURATION_DEFAULT, 2892 easing: EASING_DEFAULT, 2893 begin: undefined, 2894 complete: undefined, 2895 progress: undefined, 2896 display: undefined, 2897 visibility: undefined, 2898 loop: false, 2899 delay: false, 2900 mobileHA: true, 2901 /* Set to false to prevent property values from being cached between consecutive Velocity-initiated chain calls. */ 2902 _cacheValues: true 2903 }, 2904 /* A design goal of Velocity is to cache data wherever possible in order to avoid DOM requerying. 2905 Accordingly, each element has a data cache instantiated on it. */ 2906 init: function (element) { 2907 $.data(element, "velocity", { 2908 /* Store whether this is an SVG element, since its properties are retrieved and updated differently than standard HTML elements. */ 2909 isSVG: Type.isSVG(element), 2910 /* Keep track of whether the element is currently being animated by Velocity. 2911 This is used to ensure that property values are not transferred between non-consecutive (stale) calls. */ 2912 isAnimating: false, 2913 /* A reference to the element's live computedStyle object. Learn more here: https://developer.mozilla.org/en/docs/Web/API/window.getComputedStyle */ 2914 computedStyle: null, 2915 /* Tween data is cached for each animation on the element so that data can be passed across calls -- 2916 in particular, end values are used as subsequent start values in consecutive Velocity calls. */ 2917 tweensContainer: null, 2918 /* The full root property values of each CSS hook being animated on this element are cached so that: 2919 1) Concurrently-animating hooks sharing the same root can have their root values' merged into one while tweening. 2920 2) Post-hook-injection root values can be transferred over to consecutively chained Velocity calls as starting root values. */ 2921 rootPropertyValueCache: {}, 2922 /* A cache for transform updates, which must be manually flushed via CSS.flushTransformCache(). */ 2923 transformCache: {} 2924 }); 2925 }, 2926 /* A parallel to jQuery's $.css(), used for getting/setting Velocity's hooked CSS properties. */ 2927 hook: null, /* Defined below. */ 2928 /* Velocity-wide animation time remapping for testing purposes. */ 2929 mock: false, 2930 version: { major: 1, minor: 1, patch: 0 }, 2931 /* Set to 1 or 2 (most verbose) to output debug info to console. */ 2932 debug: false 2933 }; 2934 2935 /* Retrieve the appropriate scroll anchor and property name for the browser: https://developer.mozilla.org/en-US/docs/Web/API/Window.scrollY */ 2936 if (window.pageYOffset !== undefined) { 2937 Velocity.State.scrollAnchor = window; 2938 Velocity.State.scrollPropertyLeft = "pageXOffset"; 2939 Velocity.State.scrollPropertyTop = "pageYOffset"; 2940 } else { 2941 Velocity.State.scrollAnchor = document.documentElement || document.body.parentNode || document.body; 2942 Velocity.State.scrollPropertyLeft = "scrollLeft"; 2943 Velocity.State.scrollPropertyTop = "scrollTop"; 2944 } 2945 2946 /* Shorthand alias for jQuery's $.data() utility. */ 2947 function Data (element) { 2948 /* Hardcode a reference to the plugin name. */ 2949 var response = $.data(element, "velocity"); 2950 2951 /* jQuery <=1.4.2 returns null instead of undefined when no match is found. We normalize this behavior. */ 2952 return response === null ? undefined : response; 2953 }; 2954 2955 /************** 2956 Easing 2957 **************/ 2958 2959 /* Step easing generator. */ 2960 function generateStep (steps) { 2961 return function (p) { 2962 return Math.round(p * steps) * (1 / steps); 2963 }; 2964 } 2965 2966 /* Bezier curve function generator. Copyright Gaetan Renaudeau. MIT License: http://en.wikipedia.org/wiki/MIT_License */ 2967 function generateBezier (mX1, mY1, mX2, mY2) { 2968 var NEWTON_ITERATIONS = 4, 2969 NEWTON_MIN_SLOPE = 0.001, 2970 SUBDIVISION_PRECISION = 0.0000001, 2971 SUBDIVISION_MAX_ITERATIONS = 10, 2972 kSplineTableSize = 11, 2973 kSampleStepSize = 1.0 / (kSplineTableSize - 1.0), 2974 float32ArraySupported = "Float32Array" in window; 2975 2976 /* Must contain four arguments. */ 2977 if (arguments.length !== 4) { 2978 return false;
2979 } 2980 2981 /* Arguments must be numbers. */ 2982 for (var i = 0; i < 4; ++i) { 2983 if (typeof arguments[i] !== "number" || isNaN(arguments[i]) || !isFinite(arguments[i])) { 2984 return false; 2985 } 2986 } 2987 2988 /* X values must be in the [0, 1] range. */ 2989 mX1 = Math.min(mX1, 1); 2990 mX2 = Math.min(mX2, 1); 2991 mX1 = Math.max(mX1, 0); 2992 mX2 = Math.max(mX2, 0); 2993 2994 var mSampleValues = float32ArraySupported ? new Float32Array(kSplineTableSize) : new Array(kSplineTableSize); 2995 2996 function A (aA1, aA2) { return 1.0 - 3.0 * aA2 + 3.0 * aA1; } 2997 function B (aA1, aA2) { return 3.0 * aA2 - 6.0 * aA1; } 2998 function C (aA1) { return 3.0 * aA1; } 2999 3000 function calcBezier (aT, aA1, aA2) { 3001 return ((A(aA1, aA2)*aT + B(aA1, aA2))*aT + C(aA1))*aT; 3002 } 3003 3004 function getSlope (aT, aA1, aA2) { 3005 return 3.0 * A(aA1, aA2)*aT*aT + 2.0 * B(aA1, aA2) * aT + C(aA1); 3006 } 3007 3008 function newtonRaphsonIterate (aX, aGuessT) { 3009 for (var i = 0; i < NEWTON_ITERATIONS; ++i) { 3010 var currentSlope = getSlope(aGuessT, mX1, mX2); 3011 3012 if (currentSlope === 0.0) return aGuessT; 3013 3014 var currentX = calcBezier(aGuessT, mX1, mX2) - aX; 3015 aGuessT -= currentX / currentSlope; 3016 } 3017 3018 return aGuessT; 3019 } 3020 3021 function calcSampleValues () { 3022 for (var i = 0; i < kSplineTableSize; ++i) { 3023 mSampleValues[i] = calcBezier(i * kSampleStepSize, mX1, mX2); 3024 } 3025 } 3026 3027 function binarySubdivide (aX, aA, aB) { 3028 var currentX, currentT, i = 0; 3029 3030 do { 3031 currentT = aA + (aB - aA) / 2.0; 3032 currentX = calcBezier(currentT, mX1, mX2) - aX; 3033 if (currentX > 0.0) { 3034 aB = currentT; 3035 } else { 3036 aA = currentT; 3037 } 3038 } while (Math.abs(currentX) > SUBDIVISION_PRECISION && ++i < SUBDIVISION_MAX_ITERATIONS); 3039 3040 return currentT; 3041 } 3042 3043 function getTForX (aX) { 3044 var intervalStart = 0.0, 3045 currentSample = 1, 3046 lastSample = kSplineTableSize - 1; 3047 3048 for (; currentSample != lastSample && mSampleValues[currentSample] <= aX; ++currentSample) { 3049 intervalStart += kSampleStepSize; 3050 } 3051 3052 --currentSample; 3053 3054 var dist = (aX - mSampleValues[currentSample]) / (mSampleValues[currentSample+1] - mSampleValues[currentSample]), 3055 guessForT = intervalStart + dist * kSampleStepSize, 3056 initialSlope = getSlope(guessForT, mX1, mX2); 3057 3058 if (initialSlope >= NEWTON_MIN_SLOPE) { 3059 return newtonRaphsonIterate(aX, guessForT); 3060 } else if (initialSlope == 0.0) { 3061 return guessForT; 3062 } else { 3063 return binarySubdivide(aX, intervalStart, intervalStart + kSampleStepSize); 3064 } 3065 } 3066 3067 var _precomputed = false; 3068 3069 function precompute() { 3070 _precomputed = true; 3071 if (mX1 != mY1 || mX2 != mY2) calcSampleValues(); 3072 } 3073 3074 var f = function (aX) { 3075 if (!_precomputed) precompute(); 3076 if (mX1 === mY1 && mX2 === mY2) return aX; 3077 if (aX === 0) return 0; 3078 if (aX === 1) return 1; 3079 3080 return calcBezier(getTForX(aX), mY1, mY2); 3081 }; 3082 3083 f.getControlPoints = function() { return [{ x: mX1, y: mY1 }, { x: mX2, y: mY2 }]; }; 3084 3085 var str = "generateBezier(" + [mX1, mY1, mX2, mY2] + ")"; 3086 f.toString = function () { return str; }; 3087 3088 return f; 3089 } 3090 3091 /* Runge-Kutta spring physics function generator. Adapted from Framer.js, copyright Koen Bok. MIT License: http://en.wikipedia.org/wiki/MIT_License */ 3092 /* Given a tension, friction, and duration, a simulation at 60FPS will first run without a defined duration in order to calculate the full path. A second pass 3093 then adjusts the time delta -- using the relation between actual time and duration -- to calculate the path for the duration-constrained animation. */ 3094 var generateSpringRK4 = (function () { 3095 function springAccelerationForState (state) { 3096 return (-state.tension * state.x) - (state.friction * state.v); 3097 } 3098 3099 function springEvaluateStateWithDerivative (initialState, dt, derivative) { 3100 var state = { 3101 x: initialState.x + derivative.dx * dt, 3102 v: initialState.v + derivative.dv * dt, 3103 tension: initialState.tension, 3104 friction: initialState.friction 3105 }; 3106 3107 return { dx: state.v, dv: springAccelerationForState(state) }; 3108 } 3109 3110 function springIntegrateState (state, dt) { 3111 var a = { 3112 dx: state.v, 3113 dv: springAccelerationForState(state) 3114 }, 3115 b = springEvaluateStateWithDerivative(state, dt * 0.5, a), 3116 c = springEvaluateStateWithDerivative(state, dt * 0.5, b), 3117 d = springEvaluateStateWithDerivative(state, dt, c), 3118 dxdt = 1.0 / 6.0 * (a.dx + 2.0 * (b.dx + c.dx) + d.dx), 3119 dvdt = 1.0 / 6.0 * (a.dv + 2.0 * (b.dv + c.dv) + d.dv); 3120 3121 state.x = state.x + dxdt * dt; 3122 state.v = state.v + dvdt * dt; 3123 3124 return state; 3125 } 3126 3127 return function springRK4Factory (tension, friction, duration) { 3128 3129 var initState = { 3130 x: -1, 3131 v: 0, 3132 tension: null, 3133 friction: null 3134 }, 3135 path = [0],
vendor: 9,606 bytes, lines 3136-3324
3136 time_lapsed = 0, 3137 tolerance = 1 / 10000, 3138 DT = 16 / 1000, 3139 have_duration, dt, last_state; 3140 3141 tension = parseFloat(tension) || 500; 3142 friction = parseFloat(friction) || 20; 3143 duration = duration || null; 3144 3145 initState.tension = tension; 3146 initState.friction = friction; 3147 3148 have_duration = duration !== null; 3149 3150 /* Calculate the actual time it takes for this animation to complete with the provided conditions. */ 3151 if (have_duration) { 3152 /* Run the simulation without a duration. */ 3153 time_lapsed = springRK4Factory(tension, friction); 3154 /* Compute the adjusted time delta. */ 3155 dt = time_lapsed / duration * DT; 3156 } else { 3157 dt = DT; 3158 } 3159 3160 while (true) { 3161 /* Next/step function .*/ 3162 last_state = springIntegrateState(last_state || initState, dt); 3163 /* Store the position. */ 3164 path.push(1 + last_state.x); 3165 time_lapsed += 16; 3166 /* If the change threshold is reached, break. */ 3167 if (!(Math.abs(last_state.x) > tolerance && Math.abs(last_state.v) > tolerance)) { 3168 break; 3169 } 3170 } 3171 3172 /* If duration is not defined, return the actual time required for completing this animation. Otherwise, return a closure that holds the 3173 computed path and returns a snapshot of the position according to a given percentComplete. */ 3174 return !have_duration ? time_lapsed : function(percentComplete) { return path[ (percentComplete * (path.length - 1)) | 0 ]; }; 3175 }; 3176 }()); 3177 3178 /* jQuery easings. */ 3179 Velocity.Easings = { 3180 linear: function(p) { return p; }, 3181 swing: function(p) { return 0.5 - Math.cos( p * Math.PI ) / 2 }, 3182 /* Bonus "spring" easing, which is a less exaggerated version of easeInOutElastic. */ 3183 spring: function(p) { return 1 - (Math.cos(p * 4.5 * Math.PI) * Math.exp(-p * 6)); } 3184 }; 3185 3186 /* CSS3 and Robert Penner easings. */ 3187 $.each( 3188 [ 3189 [ "ease", [ 0.25, 0.1, 0.25, 1.0 ] ], 3190 [ "ease-in", [ 0.42, 0.0, 1.00, 1.0 ] ], 3191 [ "ease-out", [ 0.00, 0.0, 0.58, 1.0 ] ], 3192 [ "ease-in-out", [ 0.42, 0.0, 0.58, 1.0 ] ], 3193 [ "easeInSine", [ 0.47, 0, 0.745, 0.715 ] ], 3194 [ "easeOutSine", [ 0.39, 0.575, 0.565, 1 ] ], 3195 [ "easeInOutSine", [ 0.445, 0.05, 0.55, 0.95 ] ], 3196 [ "easeInQuad", [ 0.55, 0.085, 0.68, 0.53 ] ], 3197 [ "easeOutQuad", [ 0.25, 0.46, 0.45, 0.94 ] ], 3198 [ "easeInOutQuad", [ 0.455, 0.03, 0.515, 0.955 ] ], 3199 [ "easeInCubic", [ 0.55, 0.055, 0.675, 0.19 ] ], 3200 [ "easeOutCubic", [ 0.215, 0.61, 0.355, 1 ] ], 3201 [ "easeInOutCubic", [ 0.645, 0.045, 0.355, 1 ] ], 3202 [ "easeInQuart", [ 0.895, 0.03, 0.685, 0.22 ] ], 3203 [ "easeOutQuart", [ 0.165, 0.84, 0.44, 1 ] ], 3204 [ "easeInOutQuart", [ 0.77, 0, 0.175, 1 ] ], 3205 [ "easeInQuint", [ 0.755, 0.05, 0.855, 0.06 ] ], 3206 [ "easeOutQuint", [ 0.23, 1, 0.32, 1 ] ], 3207 [ "easeInOutQuint", [ 0.86, 0, 0.07, 1 ] ], 3208 [ "easeInExpo", [ 0.95, 0.05, 0.795, 0.035 ] ], 3209 [ "easeOutExpo", [ 0.19, 1, 0.22, 1 ] ], 3210 [ "easeInOutExpo", [ 1, 0, 0, 1 ] ], 3211 [ "easeInCirc", [ 0.6, 0.04, 0.98, 0.335 ] ], 3212 [ "easeOutCirc", [ 0.075, 0.82, 0.165, 1 ] ], 3213 [ "easeInOutCirc", [ 0.785, 0.135, 0.15, 0.86 ] ] 3214 ], function(i, easingArray) { 3215 Velocity.Easings[easingArray[0]] = generateBezier.apply(null, easingArray[1]); 3216 }); 3217 3218 /* Determine the appropriate easing type given an easing input. */ 3219 function getEasing(value, duration) { 3220 var easing = value; 3221 3222 /* The easing option can either be a string that references a pre-registered easing, 3223 or it can be a two-/four-item array of integers to be converted into a bezier/spring function. */ 3224 if (Type.isString(value)) { 3225 /* Ensure that the easing has been assigned to jQuery's Velocity.Easings object. */ 3226 if (!Velocity.Easings[value]) { 3227 easing = false; 3228 } 3229 } else if (Type.isArray(value) && value.length === 1) { 3230 easing = generateStep.apply(null, value); 3231 } else if (Type.isArray(value) && value.length === 2) { 3232 /* springRK4 must be passed the animation's duration. */ 3233 /* Note: If the springRK4 array contains non-numbers, generateSpringRK4() returns an easing 3234 function generated with default tension and friction values. */ 3235 easing = generateSpringRK4.apply(null, value.concat([ duration ])); 3236 } else if (Type.isArray(value) && value.length === 4) { 3237 /* Note: If the bezier array contains non-numbers, generateBezier() returns false. */ 3238 easing = generateBezier.apply(null, value); 3239 } else { 3240 easing = false; 3241 } 3242 3243 /* Revert to the Velocity-wide default easing type, or fall back to "swing" (which is also jQuery's default) 3244 if the Velocity-wide default has been incorrectly modified. */ 3245 if (easing === false) { 3246 if (Velocity.Easings[Velocity.defaults.easing]) { 3247 easing = Velocity.defaults.easing; 3248 } else { 3249 easing = EASING_DEFAULT; 3250 } 3251 } 3252 3253 return easing; 3254 } 3255 3256 /***************** 3257 CSS Stack 3258 *****************/ 3259 3260 /* The CSS object is a highly condensed and performant CSS stack that fully replaces jQuery's. 3261 It handles the validation, getting, and setting of both standard CSS properties and CSS property hooks. */ 3262 /* Note: A "CSS" shorthand is aliased so that our code is easier to read. */ 3263 var CSS = Velocity.CSS = { 3264 3265 /************* 3266 RegEx 3267 *************/ 3268 3269 RegEx: { 3270 isHex: /^#([A-f\d]{3}){1,2}$/i, 3271 /* Unwrap a property value's surrounding text, e.g. "rgba(4, 3, 2, 1)" ==> "4, 3, 2, 1" and "rect(4px 3px 2px 1px)" ==> "4px 3px 2px 1px". */ 3272 valueUnwrap: /^[A-z]+\((.*)\)$/i, 3273 wrappedValueAlreadyExtracted: /[0-9.]+ [0-9.]+ [0-9.]+( [0-9.]+)?/, 3274 /* Split a multi-value property into an array of subvalues, e.g. "rgba(4, 3, 2, 1) 4px 3px 2px 1px" ==> [ "rgba(4, 3, 2, 1)", "4px", "3px", "2px", "1px" ]. */ 3275 valueSplit: /([A-z]+\(.+\))|(([A-z0-9#-.]+?)(?=\s|$))/ig 3276 }, 3277 3278 /************ 3279 Lists 3280 ************/ 3281 3282 Lists: { 3283 colors: [ "fill", "stroke", "stopColor", "color", "backgroundColor", "borderColor", "borderTopColor", "borderRightColor", "borderBottomColor", "borderLeftColor", "outlineColor" ], 3284 transformsBase: [ "translateX", "translateY", "scale", "scaleX", "scaleY", "skewX", "skewY", "rotateZ" ], 3285 transforms3D: [ "transformPerspective", "translateZ", "scaleZ", "rotateX", "rotateY" ] 3286 }, 3287 3288 /************ 3289 Hooks 3290 ************/ 3291 3292 /* Hooks allow a subproperty (e.g. "boxShadowBlur") of a compound-value CSS property 3293 (e.g. "boxShadow: X Y Blur Spread Color") to be animated as if it were a discrete property. */ 3294 /* Note: Beyond enabling fine-grained property animation, hooking is necessary since Velocity only 3295 tweens properties with single numeric values; unlike CSS transitions, Velocity does not interpolate compound-values. */ 3296 Hooks: { 3297 /******************** 3298 Registration 3299 ********************/ 3300 3301 /* Templates are a concise way of indicating which subproperties must be individually registered for each compound-value CSS property. */ 3302 /* Each template consists of the compound-value's base name, its constituent subproperty names, and those subproperties' default values. */ 3303 templates: { 3304 "textShadow": [ "Color X Y Blur", "black 0px 0px 0px" ], 3305 /* Todo: Add support for inset boxShadows. (webkit places it last whereas IE places it first.) */ 3306 "boxShadow": [ "Color X Y Blur Spread", "black 0px 0px 0px 0px" ], 3307 "clip": [ "Top Right Bottom Left", "0px 0px 0px 0px" ], 3308 "backgroundPosition": [ "X Y", "0% 0%" ], 3309 "transformOrigin": [ "X Y Z", "50% 50% 0px" ], 3310 "perspectiveOrigin": [ "X Y", "50% 50%" ] 3311 }, 3312 3313 /* A "registered" hook is one that has been converted from its template form into a live, 3314 tweenable property. It contains data to associate it with its root property. */ 3315 registered: { 3316 /* Note: A registered hook looks like this ==> textShadowBlur: [ "textShadow", 3 ], 3317 which consists of the subproperty's name, the associated root property's name, 3318 and the subproperty's position in the root's value. */ 3319 }, 3320 /* Convert the templates into individual hooks then append them to the registered object above. */ 3321 register: function () { 3322 /* Color hooks registration: Colors are defaulted to white -- as opposed to black -- since colors that are 3323 currently set to "transparent" default to their respective template below when color-animated, 3324 and white is typically a closer match to transparent than black
3324is. An exception is made for text ("color"), 3325 which is almost always set closer to black than white. */ 3326 for (var i = 0; i < CSS.Lists.colors.length; i++) { 3327 var rgbComponents = (CSS.Lists.colors[i] === "color") ? "0 0 0 1" : "255 255 255 1"; 3328 CSS.Hooks.templates[CSS.Lists.colors[i]] = [ "Red Green Blue Alpha", rgbComponents ]; 3329 } 3330 3331 var rootProperty, 3332 hookTemplate, 3333 hookNames; 3334 3335 /* In IE, color values inside compound-value properties are positioned at the end the value instead of at the beginning. 3336 Thus, we re-arrange the templates accordingly. */ 3337 if (IE) { 3338 for (rootProperty in CSS.Hooks.templates) { 3339 hookTemplate = CSS.Hooks.templates[rootProperty]; 3340 hookNames = hookTemplate[0].split(" "); 3341 3342 var defaultValues = hookTemplate[1].match(CSS.RegEx.valueSplit); 3343 3344 if (hookNames[0] === "Color") { 3345 /* Reposition both the hook's name and its default value to the end of their respective strings. */ 3346 hookNames.push(hookNames.shift()); 3347 defaultValues.push(defaultValues.shift()); 3348 3349 /* Replace the existing template for the hook's root property. */ 3350 CSS.Hooks.templates[rootProperty] = [ hookNames.join(" "), defaultValues.join(" ") ]; 3351 } 3352 } 3353 } 3354 3355 /* Hook registration. */ 3356 for (rootProperty in CSS.Hooks.templates) { 3357 hookTemplate = CSS.Hooks.templates[rootProperty]; 3358 hookNames = hookTemplate[0].split(" "); 3359 3360 for (var i in hookNames) { 3361 var fullHookName = rootProperty + hookNames[i], 3362 hookPosition = i; 3363 3364 /* For each hook, register its full name (e.g. textShadowBlur) with its root property (e.g. textShadow) 3365 and the hook's position in its template's default value string. */ 3366 CSS.Hooks.registered[fullHookName] = [ rootProperty, hookPosition ]; 3367 } 3368 } 3369 }, 3370 3371 /***************************** 3372 Injection and Extraction 3373 *****************************/ 3374 3375 /* Look up the root property associated with the hook (e.g. return "textShadow" for "textShadowBlur"). */ 3376 /* Since a hook cannot be set directly (the browser won't recognize it), style updating for hooks is routed through the hook's root property. */ 3377 getRoot: function (property) { 3378 var hookData = CSS.Hooks.registered[property]; 3379 3380 if (hookData) { 3381 return hookData[0]; 3382 } else { 3383 /* If there was no hook match, return the property name untouched. */ 3384 return property; 3385 } 3386 }, 3387 /* Convert any rootPropertyValue, null or otherwise, into a space-delimited list of hook values so that 3388 the targeted hook can be injected or extracted at its standard position. */ 3389 cleanRootPropertyValue: function(rootProperty, rootPropertyValue) { 3390 /* If the rootPropertyValue is wrapped with "rgb()", "clip()", etc., remove the wrapping to normalize the value before manipulation. */ 3391 if (CSS.RegEx.valueUnwrap.test(rootPropertyValue)) { 3392 rootPropertyValue = rootPropertyValue.match(CSS.RegEx.valueUnwrap)[1]; 3393 } 3394 3395 /* If rootPropertyValue is a CSS null-value (from which there's inherently no hook value to extract), 3396 default to the root's default value as defined in CSS.Hooks.templates. */ 3397 /* Note: CSS null-values include "none", "auto", and "transparent". They must be converted into their
3398 zero-values (e.g. textShadow: "none" ==> textShadow: "0px 0px 0px black") for hook manipulation to proceed. */ 3399 if (CSS.Values.isCSSNullValue(rootPropertyValue)) { 3400 rootPropertyValue = CSS.Hooks.templates[rootProperty][1]; 3401 } 3402 3403 return rootPropertyValue; 3404 }, 3405 /* Extracted the hook's value from its root property's value. This is used to get the starting value of an animating hook. */ 3406 extractValue: function (fullHookName, rootPropertyValue) { 3407 var hookData = CSS.Hooks.registered[fullHookName]; 3408 3409 if (hookData) { 3410 var hookRoot = hookData[0], 3411 hookPosition = hookData[1]; 3412 3413 rootPropertyValue = CSS.Hooks.cleanRootPropertyValue(hookRoot, rootPropertyValue); 3414 3415 /* Split rootPropertyValue into its constituent hook values then grab the desired hook at its standard position. */ 3416 return rootPropertyValue.toString().match(CSS.RegEx.valueSplit)[hookPosition]; 3417 } else { 3418 /* If the provided fullHookName isn't a registered hook, return the rootPropertyValue that was passed in. */ 3419 return rootPropertyValue; 3420 } 3421 }, 3422 /* Inject the hook's value into its root property's value. This is used to piece back together the root property 3423 once Velocity has updated one of its individually hooked values through tweening. */ 3424 injectValue: function (fullHookName, hookValue, rootPropertyValue) { 3425 var hookData = CSS.Hooks.registered[fullHookName]; 3426 3427 if (hookData) { 3428 var hookRoot = hookData[0], 3429 hookPosition = hookData[1], 3430 rootPropertyValueParts, 3431 rootPropertyValueUpdated; 3432 3433 rootPropertyValue = CSS.Hooks.cleanRootPropertyValue(hookRoot, rootPropertyValue); 3434 3435 /* Split rootPropertyValue into its individual hook values, replace the targeted value with hookValue, 3436 then reconstruct the rootPropertyValue string. */ 3437 rootPropertyValueParts = rootPropertyValue.toString().match(CSS.RegEx.valueSplit); 3438 rootPropertyValueParts[hookPosition] = hookValue; 3439 rootPropertyValueUpdated = rootPropertyValueParts.join(" "); 3440 3441 return rootPropertyValueUpdated; 3442 } else { 3443 /* If the provided fullHookName isn't a registered hook, return the rootPropertyValue that was passed in. */ 3444 return rootPropertyValue; 3445 } 3446 } 3447 }, 3448 3449 /******************* 3450 Normalizations 3451 *******************/ 3452 3453 /* Normalizations standardize CSS property manipulation by pollyfilling browser-specific implementations (e.g. opacity) 3454 and reformatting special properties (e.g. clip, rgba) to look like standard ones. */ 3455 Normalizations: { 3456 /* Normalizations are passed a normalization target (either the property's name, its extracted value, or its injected value), 3457 the targeted element (which may need to be queried), and the targeted property value. */ 3458 registered: { 3459 clip: function (type, element, propertyValue) { 3460 switch (type) { 3461 case "name": 3462 return "clip"; 3463 /* Clip needs to be unwrapped and stripped of its commas during extraction. */ 3464 case "extract": 3465 var extracted; 3466 3467 /* If Velocity also extracted this value, skip extraction. */ 3468 if (CSS.RegEx.wrappedValueAlreadyExtracted.test(propertyValue)) { 3469 extracted = propertyValue; 3470 } else { 3471 /* Remove the "rect()" wrapper. */ 3472 extracted = propertyValue.toString().match(CSS.RegEx.valueUnwrap
vendor: 8,245 bytes, lines 3472-3606
3472); 3473 3474 /* Strip off commas. */ 3475 extracted = extracted ? extracted[1].replace(/,(\s+)?/g, " ") : propertyValue; 3476 } 3477 3478 return extracted; 3479 /* Clip needs to be re-wrapped during injection. */ 3480 case "inject": 3481 return "rect(" + propertyValue + ")"; 3482 } 3483 }, 3484 3485 blur: function(type, element, propertyValue) { 3486 switch (type) { 3487 case "name": 3488 return "-webkit-filter"; 3489 case "extract": 3490 var extracted = parseFloat(propertyValue); 3491 3492 /* If extracted is NaN, meaning the value isn't already extracted. */ 3493 if (!(extracted || extracted === 0)) { 3494 var blurComponent = propertyValue.toString().match(/blur\(([0-9]+[A-z]+)\)/i); 3495 3496 /* If the filter string had a blur component, return just the blur value and unit type. */ 3497 if (blurComponent) { 3498 extracted = blurComponent[1]; 3499 /* If the component doesn't exist, default blur to 0. */ 3500 } else { 3501 extracted = 0; 3502 } 3503 } 3504 3505 return extracted; 3506 /* Blur needs to be re-wrapped during injection. */ 3507 case "inject": 3508 /* For the blur effect to be fully de-applied, it needs to be set to "none" instead of 0. */ 3509 if (!parseFloat(propertyValue)) { 3510 return "none"; 3511 } else { 3512 return "blur(" + propertyValue + ")"; 3513 } 3514 } 3515 }, 3516 3517 /* <=IE8 do not support the standard opacity property. They use filter:alpha(opacity=INT) instead. */ 3518 opacity: function (type, element, propertyValue) { 3519 if (IE <= 8) { 3520 switch (type) { 3521 case "name": 3522 return "filter"; 3523 case "extract": 3524 /* <=IE8 return a "filter" value of "alpha(opacity=\d{1,3})". 3525 Extract the value and convert it to a decimal value to match the standard CSS opacity property's formatting. */ 3526 var extracted = propertyValue.toString().match(/alpha\(opacity=(.*)\)/i); 3527 3528 if (extracted) { 3529 /* Convert to decimal value. */ 3530 propertyValue = extracted[1] / 100; 3531 } else { 3532 /* When extracting opacity, default to 1 since a null value means opacity hasn't been set. */ 3533 propertyValue = 1; 3534 } 3535 3536 return propertyValue; 3537 case "inject": 3538 /* Opacified elements are required to have their zoom property set to a non-zero value. */ 3539 element.style.zoom = 1; 3540 3541 /* Setting the filter property on elements with certain font property combinations can result in a 3542 highly unappealing ultra-bolding effect. There's no way to remedy this throughout a tween, but dropping the 3543 value altogether (when opacity hits 1) at leasts ensures that the glitch is gone post-tweening. */ 3544 if (parseFloat(propertyValue) >= 1) { 3545 return ""; 3546 } else { 3547 /* As per the filter property's spec, convert the decimal value to a whole number and wrap the value. */ 3548 return "alpha(opacity=" + parseInt(parseFloat(propertyValue) * 100, 10) + ")"; 3549 } 3550 } 3551 /* With all other browsers, normalization is not required; return the same values that were passed in. */ 3552 } else { 3553 switch (type) { 3554 case "name": 3555 return "opacity"; 3556 case "extract": 3557 return propertyValue; 3558 case "inject": 3559 return propertyValue; 3560 } 3561 } 3562 } 3563 }, 3564 3565 /***************************** 3566 Batched Registrations 3567 *****************************/ 3568 3569 /* Note: Batched normalizations extend the CSS.Normalizations.registered object. */ 3570 register: function () { 3571 3572 /***************** 3573 Transforms 3574 *****************/ 3575 3576 /* Transforms are the subproperties contained by the CSS "transform" property. Transforms must undergo normalization 3577 so that they can be referenced in a properties map by their individual names. */ 3578 /* Note: When transforms are "set", they are actually assigned to a per-element transformCache. When all transform 3579 setting is complete complete, CSS.flushTransformCache() must be manually called to flush the values to the DOM. 3580 Transform setting is batched in this way to improve performance: the transform style only needs to be updated 3581 once when multiple transform subproperties are being animated simultaneously. */ 3582 /* Note: IE9 and Android Gingerbread have support for 2D -- but not 3D -- transforms. Since animating unsupported 3583 transform properties results in the browser ignoring the *entire* transform string, we prevent these 3D values 3584 from being normalized for these browsers so that tweening skips these properties altogether 3585 (since it will ignore them as being unsupported by the browser.) */ 3586 if (!(IE <= 9) && !Velocity.State.isGingerbread) { 3587 /* Note: Since the standalone CSS "perspective" property and the CSS transform "perspective" subproperty 3588 share the same name, the latter is given a unique token within Velocity: "transformPerspective". */ 3589 CSS.Lists.transformsBase = CSS.Lists.transformsBase.concat(CSS.Lists.transforms3D); 3590 } 3591 3592 for (var i = 0; i < CSS.Lists.transformsBase.length; i++) { 3593 /* Wrap the dynamically generated normalization function in a new scope so that transformName's value is 3594 paired with its respective function. (Otherwise, all functions would take the final for loop's transformName.) */ 3595 (function() { 3596 var transformName = CSS.Lists.transformsBase[i]; 3597 3598 CSS.Normalizations.registered[transformName] = function (type, element, propertyValue) { 3599 switch (type) { 3600 /* The normalized property name is the parent "transform" property -- the property that is actually set in CSS. */ 3601 case "name": 3602 return "transform"; 3603 /* Transform values are cached onto a per-element transformCache object. */ 3604 case "extract": 3605 /* If this transform has yet to be assigned a value, return its null value. */ 3606 if (Data(element) === undefined || Data(element).transformCache[transformName] === undefined) {
3607 /* Scale CSS.Lists.transformsBase default to 1 whereas all other transform properties default to 0. */ 3608 return /^scale/i.test(transformName) ? 1 : 0; 3609 /* When transform values are set, they are wrapped in parentheses as per the CSS spec. 3610 Thus, when extracting their values (for tween calculations), we strip off the parentheses. */ 3611 } else { 3612 return Data(element).transformCache[transformName].replace(/[()]/g, ""); 3613 } 3614 case "inject": 3615 var invalid = false; 3616 3617 /* If an individual transform property contains an unsupported unit type, the browser ignores the *entire* transform property. 3618 Thus, protect users from themselves by skipping setting for transform values supplied with invalid unit types. */ 3619 /* Switch on the base transform type; ignore the axis by removing the last letter from the transform's name. */ 3620 switch (transformName.substr(0, transformName.length - 1)) { 3621 /* Whitelist unit types for each transform. */ 3622 case "translate": 3623 invalid = !/(%|px|em|rem|vw|vh|\d)$/i.test(propertyValue); 3624 break; 3625 /* Since an axis-free "scale" property is supported as well, a little hack is used here to detect it by chopping off its last letter. */ 3626 case "scal": 3627 case "scale": 3628 /* Chrome on Android has a bug in which scaled elements blur if their initial scale 3629 value is below 1 (which can happen with forcefeeding). Thus, we detect a yet-unset scale property 3630 and ensure that its first value is always 1. More info: http://stackoverflow.com/questions/10417890/css3-animations-with-transform-causes-blurred-elements-on-webkit/10417962#10417962 */ 3631 if (Velocity.State.isAndroid && Data(element).transformCache[transformName] === undefined && propertyValue < 1) { 3632 propertyValue = 1; 3633 } 3634 3635 invalid = !/(\d)$/i.test(propertyValue); 3636 break; 3637 case "skew": 3638 invalid = !/(deg|\d)$/i.test(propertyValue); 3639 break; 3640 case "rotate": 3641 invalid = !/(deg|\d)$/i.test(propertyValue); 3642 break; 3643 } 3644 3645 if (!invalid) { 3646 /* As per the CSS spec, wrap the value in parentheses. */ 3647 Data(element).transformCache[transformName] = "(" + propertyValue + ")"; 3648 } 3649 3650 /* Although the value is set on the transformCache object, return the newly-updated value for the calling code to process as normal. */ 3651 return Data(element).transformCache[transformName]; 3652 } 3653 }; 3654 })(); 3655 } 3656 3657 /************* 3658 Colors 3659 *************/ 3660 3661 /* Since Velocity only animates a single numeric value per property, color animation is achieved by hooking the individual RGBA components of CSS color properties. 3662 Accordingly, color values must be normalized (e.g. "#ff0000", "red", and "rgb(255, 0, 0)" ==> "255 0 0 1") so that their components can be injected/extracted by CSS.Hooks logic. */ 3663 for (var i = 0; i < CSS.Lists.colors.length; i++) { 3664 /* Wrap the dynamically generated normalization function in a new scope so that colorName's value is paired with its respective function. 3665 (Otherwise, all functions would take the final for loop's colorName.) */ 3666 (function () { 3667 var colorName = CSS.Lists.colors[i]; 3668 3669 /* Note: In IE<=8, which support rgb but not rgba, c
3669olor properties are reverted to rgb by stripping off the alpha component. */ 3670 CSS.Normalizations.registered[colorName] = function(type, element, propertyValue) { 3671 switch (type) { 3672 case "name": 3673 return colorName; 3674 /* Convert all color values into the rgb format. (Old IE can return hex values and color names instead of rgb/rgba.) */ 3675 case "extract": 3676 var extracted; 3677 3678 /* If the color is already in its hookable form (e.g. "255 255 255 1") due to having been previously extracted, skip extraction. */ 3679 if (CSS.RegEx.wrappedValueAlreadyExtracted.test(propertyValue)) { 3680 extracted = propertyValue; 3681 } else { 3682 var converted, 3683 colorNames = { 3684 black: "rgb(0, 0, 0)", 3685 blue: "rgb(0, 0, 255)", 3686 gray: "rgb(128, 128, 128)", 3687 green: "rgb(0, 128, 0)", 3688 red: "rgb(255, 0, 0)", 3689 white: "rgb(255, 255, 255)" 3690 }; 3691 3692 /* Convert color names to rgb. */ 3693 if (/^[A-z]+$/i.test(propertyValue)) { 3694 if (colorNames[propertyValue] !== undefined) { 3695 converted = colorNames[propertyValue] 3696 } else { 3697 /* If an unmatched color name is provided, default to black. */ 3698 converted = colorNames.black; 3699 } 3700 /* Convert hex values to rgb. */ 3701 } else if (CSS.RegEx.isHex.test(propertyValue)) { 3702 converted = "rgb(" + CSS.Values.hexToRgb(propertyValue).join(" ") + ")"; 3703 /* If the provided color doesn't match any of the accepted color formats, default to black. */ 3704 } else if (!(/^rgba?\(/i.test(propertyValue))) { 3705 converted = colorNames.black; 3706 } 3707 3708 /* Remove the surrounding "rgb/rgba()" string then replace commas with spaces and strip 3709 repeated spaces (in case the value included spaces to begin with). */ 3710 extracted = (converted || propertyValue).toString().match(CSS.RegEx.valueUnwrap)[1].replace(/,(\s+)?/g, " "); 3711 } 3712 3713 /* So long as this isn't <=IE8, add a fourth (alpha) component if it's missing and default it to 1 (visible). */ 3714 if (!(IE <= 8) && extracted.split(" ").length === 3) { 3715 extracted += " 1"; 3716 } 3717 3718 return extracted; 3719 case "inject": 3720 /* If this is IE<=8 and an alpha component exists, strip it off. */ 3721 if (IE <= 8) { 3722 if (propertyValue.split(" ").length === 4) { 3723 propertyValue = propertyValue.split(/\s+/).slice(0, 3).join(" "); 3724 } 3725 /* Otherwise, add a fourth (alpha) component if it's missing and default it to 1 (visible). */ 3726 } else if (propertyValue.split(" ").length === 3) { 3727 propertyValue += " 1"; 3728 } 3729 3730 /* Re-insert the browser-appropriate wrapper("rgb/rgba()"), insert commas, and strip off decimal units 3731 on all values but the fourth (R, G, and B only accept whole numbers). */ 3732 return (IE <= 8 ? "rgb" : "rgba") + "(" + propertyValue.replace(/\s+/g, ",").replace(/\.(\d)+(?=,)/g, "") + ")"; 3733 } 3734 }; 3735 })(); 3736 } 3737 } 3738 }, 3739 3740 /************************ 3741 CSS Property Names 3742 ************************/ 3743 3744 Names: {
vendor: 5,382 bytes, lines 3745-3842
3745 /* Camelcase a property name into its JavaScript notation (e.g. "background-color" ==> "backgroundColor"). 3746 Camelcasing is used to normalize property names between and across calls. */ 3747 camelCase: function (property) { 3748 return property.replace(/-(\w)/g, function (match, subMatch) { 3749 return subMatch.toUpperCase(); 3750 }); 3751 }, 3752 3753 /* For SVG elements, some properties (namely, dimensional ones) are GET/SET via the element's HTML attributes (instead of via CSS styles). */ 3754 SVGAttribute: function (property) { 3755 var SVGAttributes = "width|height|x|y|cx|cy|r|rx|ry|x1|x2|y1|y2"; 3756 3757 /* Certain browsers require an SVG transform to be applied as an attribute. (Otherwise, application via CSS is preferable due to 3D support.) */ 3758 if (IE || (Velocity.State.isAndroid && !Velocity.State.isChrome)) { 3759 SVGAttributes += "|transform"; 3760 } 3761 3762 return new RegExp("^(" + SVGAttributes + ")$", "i").test(property); 3763 }, 3764 3765 /* Determine whether a property should be set with a vendor prefix. */ 3766 /* If a prefixed version of the property exists, return it. Otherwise, return the original property name. 3767 If the property is not at all supported by the browser, return a false flag. */ 3768 prefixCheck: function (property) { 3769 /* If this property has already been checked, return the cached value. */ 3770 if (Velocity.State.prefixMatches[property]) { 3771 return [ Velocity.State.prefixMatches[property], true ]; 3772 } else { 3773 var vendors = [ "", "Webkit", "Moz", "ms", "O" ]; 3774 3775 for (var i = 0, vendorsLength = vendors.length; i < vendorsLength; i++) { 3776 var propertyPrefixed; 3777 3778 if (i === 0) { 3779 propertyPrefixed = property; 3780 } else { 3781 /* Capitalize the first letter of the property to conform to JavaScript vendor prefix notation (e.g. webkitFilter). */ 3782 propertyPrefixed = vendors[i] + property.replace(/^\w/, function(match) { return match.toUpperCase(); }); 3783 } 3784 3785 /* Check if the browser supports this property as prefixed. */ 3786 if (Type.isString(Velocity.State.prefixElement.style[propertyPrefixed])) { 3787 /* Cache the match. */ 3788 Velocity.State.prefixMatches[property] = propertyPrefixed; 3789 3790 return [ propertyPrefixed, true ]; 3791 } 3792 } 3793 3794 /* If the browser doesn't support this property in any form, include a false flag so that the caller can decide how to proceed. */ 3795 return [ property, false ]; 3796 } 3797 } 3798 }, 3799 3800 /************************ 3801 CSS Property Values 3802 ************************/ 3803 3804 Values: { 3805 /* Hex to RGB conversion. Copyright Tim Down: http://stackoverflow.com/questions/5623838/rgb-to-hex-and-hex-to-rgb */ 3806 hexToRgb: function (hex) { 3807 var shortformRegex = /^#?([a-f\d])([a-f\d])([a-f\d])$/i, 3808 longformRegex = /^#?([a-f\d]{2})([a-f\d]{2})([a-f\d]{2})$/i, 3809 rgbParts; 3810 3811 hex = hex.replace(shortformRegex, function (m, r, g, b) { 3812 return r + r + g + g + b + b; 3813 }); 3814 3815 rgbParts = longformRegex.exec(hex); 3816 3817 return rgbParts ? [ parseInt(rgbParts[1], 16), parseInt(rgbParts[2], 16), parseInt(rgbParts[3], 16) ] : [ 0, 0, 0 ]; 3818 }, 3819 3820 isCSSNullValue: function (value) { 3821 /* The browser defaults CSS values that have not been set to either 0 or one of several possible null-value strings. 3822 Thus, we check for both falsiness and these special strings. */ 3823 /* Null-value checking is performed to default the special strings to 0 (for the sake of tweening) or their hook 3824 templates as defined as CSS.Hooks (for the sake of hook injection/extraction). */ 3825 /* Note: Chrome returns "rgba(0, 0, 0, 0)" for an undefined color whereas IE returns "transparent". */ 3826 return (value == 0 || /^(none|auto|transparent|(rgba\(0, ?0, ?0, ?0\)))$/i.test(value)); 3827 }, 3828 3829 /* Retrieve a property's default unit type. Used for assigning a unit type when one is not supplied by the user. */ 3830 getUnitType: function (property) { 3831 if (/^(rotate|skew)/i.test(property)) { 3832 return "deg"; 3833 } else if (/(^(scale|scaleX|scaleY|scaleZ|alpha|flexGrow|flexHeight|zIndex|fontWeight)$)|((opacity|red|green|blue|alpha)$)/i.test(property)) { 3834 /* The above properties are unitless. */ 3835 return ""; 3836 } else { 3837 /* Default to px for all other properties. */ 3838 return "px"; 3839 } 3840 }, 3841 3842 /* HTML elements default to an associated display type when they're not set to display:none. */
3843 /* Note: This function is used for correctly setting the non-"none" display value in certain Velocity redirects, such as fadeIn/Out. */ 3844 getDisplayType: function (element) { 3845 var tagName = element && element.tagName.toString().toLowerCase(); 3846 3847 if (/^(b|big|i|small|tt|abbr|acronym|cite|code|dfn|em|kbd|strong|samp|var|a|bdo|br|img|map|object|q|script|span|sub|sup|button|input|label|select|textarea)$/i.test(tagName)) { 3848 return "inline"; 3849 } else if (/^(li)$/i.test(tagName)) { 3850 return "list-item"; 3851 } else if (/^(tr)$/i.test(tagName)) { 3852 return "table-row"; 3853 /* Default to "block" when no match is found. */ 3854 } else { 3855 return "block"; 3856 } 3857 }, 3858 3859 /* The class add/remove functions are used to temporarily apply a "velocity-animating" class to elements while they're animating. */ 3860 addClass: function (element, className) { 3861 if (element.classList) { 3862 element.classList.add(className); 3863 } else { 3864 element.className += (element.className.length ? " " : "") + className; 3865 } 3866 }, 3867 3868 removeClass: function (element, className) { 3869 if (element.classList) { 3870 element.classList.remove(className); 3871 } else { 3872 element.className = element.className.toString().replace(new RegExp("(^|\\s)" + className.split(" ").join("|") + "(\\s|$)", "gi"), " "); 3873 } 3874 } 3875 }, 3876 3877 /**************************** 3878 Style Getting & Setting 3879 ****************************/ 3880 3881 /* The singular getPropertyValue, which routes the logic for all normalizations, hooks, and standard CSS properties. */ 3882 getPropertyValue: function (element, property, rootPropertyValue, forceStyleLookup) { 3883 /* Get an element's computed property value. */ 3884 /* Note: Retrieving the value of a CSS property cannot simply be performed by checking an element's 3885 style attribute (which only reflects user-defined values). Instead, the browser must be queried for a property's 3886 *computed* value. You can read more about getComputedStyle here: https://developer.mozilla.org/en/docs/Web/API/window.getComputedStyle */ 3887 function computePropertyValue (element, property) { 3888 /* When box-sizing isn't set to border-box, height and width style values are incorrectly computed when an 3889 element's scrollbars are visible (which expands the element's dimensions). Thus, we defer to the more accurate 3890 offsetHeight/Width property, which includes the total dimensions for interior, border, padding, and scrollbar. 3891 We subtract border and padding to get the sum of interior + scrollbar. */ 3892 var computedValue = 0; 3893 3894 /* IE<=8 doesn't support window.getComputedStyle, thus we defer to jQuery, which has an extensive array 3895 of hacks to accurately retrieve IE8 property values. Re-implementing that logic here is not worth bloating the 3896 codebase for a dying browser. The performance repercussions of using jQuery here are minimal since 3897 Velocity is optimized to rarely (and sometimes never) query the DOM. Further, the $.css() codepath isn't that slow. */ 3898 if (IE <= 8) { 3899 computedValue = $.css(element, property); /* GET */ 3900 /* All other browsers support getComputedStyle. The returned live object reference is cached onto its 3901 associated element so that it does not need to be refetched upon every GET. */ 3902 } else { 3903 /* Browsers do not return height and width values for elements that are set to display:"none". Thus, we temporarily 3904 toggle display to the element type's default value. */ 3905 var toggleDisplay = false;
vendor: 7,262 bytes, lines 3906-4011
3906 3907 if (/^(width|height)$/.test(property) && CSS.getPropertyValue(element, "display") === 0) { 3908 toggleDisplay = true; 3909 CSS.setPropertyValue(element, "display", CSS.Values.getDisplayType(element)); 3910 } 3911 3912 function revertDisplay () { 3913 if (toggleDisplay) { 3914 CSS.setPropertyValue(element, "display", "none"); 3915 } 3916 } 3917 3918 if (!forceStyleLookup) { 3919 if (property === "height" && CSS.getPropertyValue(element, "boxSizing").toString().toLowerCase() !== "border-box") { 3920 var contentBoxHeight = element.offsetHeight - (parseFloat(CSS.getPropertyValue(element, "borderTopWidth")) || 0) - (parseFloat(CSS.getPropertyValue(element, "borderBottomWidth")) || 0) - (parseFloat(CSS.getPropertyValue(element, "paddingTop")) || 0) - (parseFloat(CSS.getPropertyValue(element, "paddingBottom")) || 0); 3921 revertDisplay(); 3922 3923 return contentBoxHeight; 3924 } else if (property === "width" && CSS.getPropertyValue(element, "boxSizing").toString().toLowerCase() !== "border-box") { 3925 var contentBoxWidth = element.offsetWidth - (parseFloat(CSS.getPropertyValue(element, "borderLeftWidth")) || 0) - (parseFloat(CSS.getPropertyValue(element, "borderRightWidth")) || 0) - (parseFloat(CSS.getPropertyValue(element, "paddingLeft")) || 0) - (parseFloat(CSS.getPropertyValue(element, "paddingRight")) || 0); 3926 revertDisplay(); 3927 3928 return contentBoxWidth; 3929 } 3930 } 3931 3932 var computedStyle; 3933 3934 /* For elements that Velocity hasn't been called on directly (e.g. when Velocity queries the DOM on behalf 3935 of a parent of an element its animating), perform a direct getComputedStyle lookup since the object isn't cached. */ 3936 if (Data(element) === undefined) { 3937 computedStyle = window.getComputedStyle(element, null); /* GET */ 3938 /* If the computedStyle object has yet to be cached, do so now. */ 3939 } else if (!Data(element).computedStyle) { 3940 computedStyle = Data(element).computedStyle = window.getComputedStyle(element, null); /* GET */ 3941 /* If computedStyle is cached, use it. */ 3942 } else { 3943 computedStyle = Data(element).computedStyle; 3944 } 3945 3946 /* IE and Firefox do not return a value for the generic borderColor -- they only return individual values for each border side's color. 3947 As a polyfill for querying individual border side colors, just return the top border's color. */ 3948 if ((IE || Velocity.State.isFirefox) && property === "borderColor") { 3949 property = "borderTopColor"; 3950 } 3951 3952 /* IE9 has a bug in which the "filter" property must be accessed from computedStyle using the getPropertyValue method 3953 instead of a direct property lookup. The getPropertyValue method is slower than a direct lookup, which is why we avoid it by default. */ 3954 if (IE === 9 && property === "filter") { 3955 computedValue = computedStyle.getPropertyValue(property); /* GET */ 3956 } else { 3957 computedValue = computedStyle[property]; 3958 } 3959 3960 /* Fall back to the property's style value (if defined) when computedValue returns nothing, 3961 which can happen when the element hasn't been painted. */ 3962 if (computedValue === "" || computedValue === null) { 3963 computedValue = element.style[property]; 3964 } 3965 3966 revertDisplay(); 3967 } 3968 3969 /* For top, right, bottom, and left (TRBL) values that are set to "auto" on elements of "fixed" or "absolute" position, 3970 defer to jQuery for converting "auto" to a numeric value. (For elements with a "static" or "relative" position, "auto" has the same 3971 effect as being set to 0, so no conversion is necessary.) */ 3972 /* An example of why numeric conversion is necessary: When an element with "position:absolute" has an untouched "left" 3973 property, which reverts to "auto", left's value is 0 relative to its parent element, but is often non-zero relative 3974 to its *containing* (not parent) element, which is the nearest "position:relative" ancestor or the viewport (and always the viewport in the case of "position:fixed"). */ 3975 if (computedValue === "auto" && /^(top|right|bottom|left)$/i.test(property)) { 3976 var position = computePropertyValue(element, "position"); /* GET */ 3977 3978 /* For absolute positioning, jQuery's $.position() only returns values for top and left; 3979 right and bottom will have their "auto" value reverted to 0. */ 3980 /* Note: A jQuery object must be created here since jQuery doesn't have a low-level alias for $.position(). 3981 Not a big deal since we're currently in a GET batch anyway. */ 3982 if (position === "fixed" || (position === "absolute" && /top|left/i.test(property))) { 3983 /* Note: jQuery strips the pixel unit from its returned values; we re-add it here to conform with computePropertyValue's behavior. */ 3984 computedValue = $(element).position()[property] + "px"; /* GET */ 3985 } 3986 } 3987 3988 return computedValue; 3989 } 3990 3991 var propertyValue; 3992 3993 /* If this is a hooked property (e.g. "clipLeft" instead of the root property of "clip"), 3994 extract the hook's value from a normalized rootPropertyValue using CSS.Hooks.extractValue(). */ 3995 if (CSS.Hooks.registered[property]) { 3996 var hook = property, 3997 hookRoot = CSS.Hooks.getRoot(hook); 3998 3999 /* If a cached rootPropertyValue wasn't passed in (which Velocity always attempts to do in order to avoid requerying the DOM), 4000 query the DOM for the root property's value. */ 4001 if (rootPropertyValue === undefined) { 4002 /* Since the browser is now being directly queried, use the official post-prefixing property name for this lookup. */ 4003 rootPropertyValue = CSS.getPropertyValue(element, CSS.Names.prefixCheck(hookRoot)[0]); /* GET */ 4004 } 4005 4006 /* If this root has a normalization registered, peform the associated normalization extraction. */ 4007 if (CSS.Normalizations.registered[hookRoot]) { 4008 rootPropertyValue = CSS.Normalizations.registered[hookRoot]("extract", element, rootPropertyValue); 4009 } 4010 4011 /* Extract the hook's value. */
4012 propertyValue = CSS.Hooks.extractValue(hook, rootPropertyValue); 4013 4014 /* If this is a normalized property (e.g. "opacity" becomes "filter" in <=IE8) or "translateX" becomes "transform"), 4015 normalize the property's name and value, and handle the special case of transforms. */ 4016 /* Note: Normalizing a property is mutually exclusive from hooking a property since hook-extracted values are strictly 4017 numerical and therefore do not require normalization extraction. */ 4018 } else if (CSS.Normalizations.registered[property]) { 4019 var normalizedPropertyName, 4020 normalizedPropertyValue; 4021 4022 normalizedPropertyName = CSS.Normalizations.registered[property]("name", element); 4023 4024 /* Transform values are calculated via normalization extraction (see below), which checks against the element's transformCache. 4025 At no point do transform GETs ever actually query the DOM; initial stylesheet values are never processed. 4026 This is because parsing 3D transform matrices is not always accurate and would bloat our codebase; 4027 thus, normalization extraction defaults initial transform values to their zero-values (e.g. 1 for scaleX and 0 for translateX). */ 4028 if (normalizedPropertyName !== "transform") { 4029 normalizedPropertyValue = computePropertyValue(element, CSS.Names.prefixCheck(normalizedPropertyName)[0]); /* GET */ 4030 4031 /* If the value is a CSS null-value and this property has a hook template, use that zero-value template so that hooks can be extracted from it. */ 4032 if (CSS.Values.isCSSNullValue(normalizedPropertyValue) && CSS.Hooks.templates[property]) { 4033 normalizedPropertyValue = CSS.Hooks.templates[property][1]; 4034 } 4035 } 4036 4037 propertyValue = CSS.Normalizations.registered[property]("extract", element, normalizedPropertyValue); 4038 } 4039 4040 /* If a (numeric) value wasn't produced via hook extraction or normalization, query the DOM. */ 4041 if (!/^[\d-]/.test(propertyValue)) { 4042 /* For SVG elements, dimensional properties (which SVGAttribute() detects) are tweened via 4043 their HTML attribute values instead of their CSS style values. */ 4044 if (Data(element) && Data(element).isSVG && CSS.Names.SVGAttribute(property)) { 4045 /* Since the height/width attribute values must be set manually, they don't reflect computed values. 4046 Thus, we use use getBBox() to ensure we always get values for elements with undefined height/width attributes. */ 4047 if (/^(height|width)$/i.test(property)) { 4048 propertyValue = element.getBBox()[property]; 4049 /* Otherwise, access the attribute value directly. */ 4050 } else { 4051 propertyValue = element.getAttribute(property); 4052 } 4053 } else { 4054 propertyValue = computePropertyValue(element, CSS.Names.prefixCheck(property)[0]); /* GET */ 4055 } 4056 } 4057 4058 /* Since property lookups are for animation purposes (which entails computing the numeric delta between start and end values), 4059 convert CSS null-values to an integer of value 0. */ 4060 if (CSS.Values.isCSSNullValue(propertyValue)) { 4061 propertyValue = 0; 4062 } 4063 4064 if (Velocity.debug >= 2) console.log("Get " + property + ": " + propertyValue); 4065 4066 return propertyValue; 4067 }, 4068 4069 /* The singular setPropertyValue, which routes the logic for all normalizations, hooks, and standard CSS properties. */ 4070 setPropertyValue: function(element, property, propertyValue, rootPropertyValue, scrollData) { 4071 var propertyName = property; 4072 4073 /* In order to be subjected to call options and element queueing, scroll animation is routed through Velocity as if it were a standard CSS property. */ 4074 if (property === "scroll") { 4075 /* If a container option is present, scroll the container instead of the browser window. */ 4076 if (scrollData.container) { 4077 scrollData.container["scroll" + scrollData.direction] = propertyValue; 4078 /* Otherwise, Velocity defaults to scrolling the browser window. */ 4079 } else { 4080 if (scrollData.direction === "Left") { 4081 window.scrollTo(propertyValue, scrollData.alternateValue); 4082 } else { 4083 window.scrollTo(scrollData.alternateValue, propertyValue); 4084 } 4085 } 4086 } else { 4087 /* Transforms (translateX, rotateZ, etc.) are applied to a per-element transformCache object, which is manually flushed via flushTransformCache(). 4088 Thus, for now, we merely cache transforms being SET. */ 4089 if (CSS.Normalizations.registered[property] && CSS.Normalizations.registered[property]("name", element) === "transform") { 4090 /* Perform a normalization injection. */ 4091 /* Note: The normalization logic handles the transformCache updating. */ 4092 CSS.Normalizations.registered[property]("inject", element, propertyValue); 4093 4094 propertyName = "transform"; 4095 propertyValue = Data(element).transformCache[property]; 4096 } else { 4097 /* Inject hooks. */ 4098 if (CSS.Hooks.registered[property]) { 4099 var hookName = property, 4100 hookRoot = CSS.Hooks.getRoot(property); 4101 4102 /* If a cached rootPropertyValue was not provided, query the DOM for the hookRoot's current value. */ 4103 rootPropertyValue = rootPropertyValue || CSS.getPropertyValue(element, hookRoot); /* GET */ 4104 4105 propertyValue = CSS.Hooks.injectValue(ho
4105okName, propertyValue, rootPropertyValue); 4106 property = hookRoot; 4107 } 4108 4109 /* Normalize names and values. */ 4110 if (CSS.Normalizations.registered[property]) { 4111 propertyValue = CSS.Normalizations.registered[property]("inject", element, propertyValue); 4112 property = CSS.Normalizations.registered[property]("name", element); 4113 } 4114 4115 /* Assign the appropriate vendor prefix before performing an official style update. */ 4116 propertyName = CSS.Names.prefixCheck(property)[0]; 4117 4118 /* A try/catch is used for IE<=8, which throws an error when "invalid" CSS values are set, e.g. a negative width. 4119 Try/catch is avoided for other browsers since it incurs a performance overhead. */ 4120 if (IE <= 8) { 4121 try { 4122 element.style[propertyName] = propertyValue; 4123 } catch (error) { if (Velocity.debug) console.log("Browser does not support [" + propertyValue + "] for [" + propertyName + "]"); } 4124 /* SVG elements have their dimensional properties (width, height, x, y, cx, etc.) applied directly as attributes instead of as styles. */ 4125 /* Note: IE8 does not support SVG elements, so it's okay that we skip it for SVG animation. */ 4126 } else if (Data(element) && Data(element).isSVG && CSS.Names.SVGAttribute(property)) { 4127 /* Note: For SVG attributes, vendor-prefixed property names are never used. */ 4128 /* Note: Not all CSS properties can be animated via attributes, but the browser won't throw an error for unsupported properties. */ 4129 element.setAttribute(property, propertyValue); 4130 } else { 4131 element.style[propertyName] = propertyValue; 4132 } 4133 4134 if (Velocity.debug >= 2) console.log("Set " + property + " (" + propertyName + "): " + propertyValue); 4135 } 4136 } 4137 4138 /* Return the normalized property name and value in case the caller wants to know how these values were modified before being applied to the DOM. */ 4139 return [ propertyName, propertyValue ]; 4140 }, 4141 4142 /* To increase performance by batching transform updates into a single SET, transforms are not directly applied to an element until flushTransformCache() is called. */ 4143 /* Note: Velocity applies transform properties in the same order that they are chronogically introduced to the element's CSS styles. */ 4144 flushTransformCache: function(element) { 4145 var transformString = ""; 4146 4147 /* Certain browsers require that SVG transforms be applied as an attribute. However, the SVG transform attribute takes a modified version of CSS's transform string 4148 (units are dropped and, except for skewX/Y, subproperties are merged into their master property -- e.g. scaleX and scaleY are merged into scale(X Y). */ 4149 if ((IE || (Velocity.State.isAndroid && !Velocity.State.isChrome)) && Data(element).isSVG) { 4150 /* Since transform values are stored in their parentheses-wrapped form, we use a helper function to strip out their numeric values. 4151 Further, SVG transform properties only take unitless (representing pixels) values, so it's okay that parseFloat() strips the unit suffixed to the float value. */ 4152 function getTransformFloat (transformProperty) { 4153 return parseFloat(CSS.getPropertyValue(element, transformProperty)); 4154 } 4155 4156 /* Create an object to organize all the transforms that we'll apply to the SVG element. To keep the logic simple, 4157 we process *all* transform properties -- even those that may not be explicitly applied (since they default to their zero-values anyway). */ 4158 var SVGTransforms = { 4159 translate: [ getTransformFloat("translateX"), getTransformFloat("translateY") ], 4160 skewX: [ getTransformFloat("skewX") ], skewY: [ getTransformFloat("skewY") ],
vendor: 6,513 bytes, lines 4161-4290
4161 /* If the scale property is set (non-1), use that value for the scaleX and scaleY values 4162 (this behavior mimics the result of animating all these properties at once on HTML elements). */ 4163 scale: getTransformFloat("scale") !== 1 ? [ getTransformFloat("scale"), getTransformFloat("scale") ] : [ getTransformFloat("scaleX"), getTransformFloat("scaleY") ], 4164 /* Note: SVG's rotate transform takes three values: rotation degrees followed by the X and Y values 4165 defining the rotation's origin point. We ignore the origin values (default them to 0). */ 4166 rotate: [ getTransformFloat("rotateZ"), 0, 0 ] 4167 }; 4168 4169 /* Iterate through the transform properties in the user-defined property map order. 4170 (This mimics the behavior of non-SVG transform animation.) */ 4171 $.each(Data(element).transformCache, function(transformName) { 4172 /* Except for with skewX/Y, revert the axis-specific transform subproperties to their axis-free master 4173 properties so that they match up with SVG's accepted transform properties. */ 4174 if (/^translate/i.test(transformName)) { 4175 transformName = "translate"; 4176 } else if (/^scale/i.test(transformName)) { 4177 transformName = "scale"; 4178 } else if (/^rotate/i.test(transformName)) { 4179 transformName = "rotate"; 4180 } 4181 4182 /* Check that we haven't yet deleted the property from the SVGTransforms container. */ 4183 if (SVGTransforms[transformName]) { 4184 /* Append the transform property in the SVG-supported transform format. As per the spec, surround the space-delimited values in parentheses. */ 4185 transformString += transformName + "(" + SVGTransforms[transformName].join(" ") + ")" + " "; 4186 4187 /* After processing an SVG transform property, delete it from the SVGTransforms container so we don't 4188 re-insert the same master property if we encounter another one of its axis-specific properties. */ 4189 delete SVGTransforms[transformName]; 4190 } 4191 }); 4192 } else { 4193 var transformValue, 4194 perspective; 4195 4196 /* Transform properties are stored as members of the transformCache object. Concatenate all the members into a string. */ 4197 $.each(Data(element).transformCache, function(transformName) { 4198 transformValue = Data(element).transformCache[transformName]; 4199 4200 /* Transform's perspective subproperty must be set first in order to take effect. Store it temporarily. */ 4201 if (transformName === "transformPerspective") { 4202 perspective = transformValue; 4203 return true; 4204 } 4205 4206 /* IE9 only supports one rotation type, rotateZ, which it refers to as "rotate". */ 4207 if (IE === 9 && transformName === "rotateZ") { 4208 transformName = "rotate"; 4209 } 4210 4211 transformString += transformName + transformValue + " "; 4212 }); 4213 4214 /* If present, set the perspective subproperty first. */ 4215 if (perspective) { 4216 transformString = "perspective" + perspective + " " + transformString; 4217 } 4218 } 4219 4220 CSS.setPropertyValue(element, "transform", transformString); 4221 } 4222 }; 4223 4224 /* Register hooks and normalizations. */ 4225 CSS.Hooks.register(); 4226 CSS.Normalizations.register(); 4227 4228 /* Allow hook setting in the same fashion as jQuery's $.css(). */ 4229 Velocity.hook = function (elements, arg2, arg3) { 4230 var value = undefined; 4231 4232 elements = sanitizeElements(elements); 4233 4234 $.each(elements, function(i, element) { 4235 /* Initialize Velocity's per-element data cache if this element hasn't previously been animated. */ 4236 if (Data(element) === undefined) { 4237 Velocity.init(element); 4238 } 4239 4240 /* Get property value. If an element set was passed in, only return the value for the first element. */ 4241 if (arg3 === undefined) { 4242 if (value === undefined) { 4243 value = Velocity.CSS.getPropertyValue(element, arg2); 4244 } 4245 /* Set property value. */ 4246 } else { 4247 /* sPV returns an array of the normalized propertyName/propertyValue pair used to update the DOM. */ 4248 var adjustedSet = Velocity.CSS.setPropertyValue(element, arg2, arg3); 4249 4250 /* Transform properties don't automatically set. They have to be flushed to the DOM. */ 4251 if (adjustedSet[0] === "transform") { 4252 Velocity.CSS.flushTransformCache(element); 4253 } 4254 4255 value = adjustedSet; 4256 } 4257 }); 4258 4259 return value; 4260 }; 4261 4262 /***************** 4263 Animation 4264 *****************/ 4265 4266 var animate = function() { 4267 4268 /****************** 4269 Call Chain 4270 ******************/ 4271 4272 /* Logic for determining what to return to the call stack when exiting out of Velocity. */ 4273 function getChain () { 4274 /* If we are using the utility function, attempt to return this call's promise. If no promise library was detected, 4275 default to null instead of returning the targeted elements so that utility function's return value is standardized. */ 4276 if (isUtility) { 4277 return promiseData.promise || null; 4278 /* Otherwise, if we're using $.fn, return the jQuery-/Zepto-wrapped element set. */ 4279 } else { 4280 return elementsWrapped; 4281 } 4282 } 4283 4284 /************************* 4285 Arguments Assignment 4286 *************************/ 4287 4288 /* To allow for expressive CoffeeScript code, Velocity supports an alternative syntax in which "properties" and "options" 4289 objects are defined on a container object that's passed in as Velocity's sole argument. */ 4290 /* Note: Some browsers automatically populate arguments with a "properties" object. We detect it by checking for its default "n
4290ames" property. */ 4291 var syntacticSugar = (arguments[0] && (($.isPlainObject(arguments[0].properties) && !arguments[0].properties.names) || Type.isString(arguments[0].properties))), 4292 /* Whether Velocity was called via the utility function (as opposed to on a jQuery/Zepto object). */ 4293 isUtility, 4294 /* When Velocity is called via the utility function ($.Velocity()/Velocity()), elements are explicitly 4295 passed in as the first parameter. Thus, argument positioning varies. We normalize them here. */ 4296 elementsWrapped, 4297 argumentIndex; 4298 4299 var elements, 4300 propertiesMap, 4301 options; 4302 4303 /* Detect jQuery/Zepto elements being animated via the $.fn method. */ 4304 if (Type.isWrapped(this)) { 4305 isUtility = false; 4306 4307 argumentIndex = 0; 4308 elements = this; 4309 elementsWrapped = this; 4310 /* Otherwise, raw elements are being animated via the utility function. */ 4311 } else { 4312 isUtility = true; 4313 4314 argumentIndex = 1; 4315 elements = syntacticSugar ? arguments[0].elements : arguments[0]; 4316 } 4317 4318 elements = sanitizeElements(elements); 4319 4320 if (!elements) { 4321 return; 4322 } 4323 4324 if (syntacticSugar) { 4325 propertiesMap = arguments[0].properties; 4326 options = arguments[0].options; 4327 } else { 4328 propertiesMap = arguments[argumentIndex]; 4329 options = arguments[argumentIndex + 1]; 4330 } 4331 4332 /* The length of the element set (in the form of a nodeList or an array of elements) is defaulted to 1 in case a 4333 single raw DOM element is passed in (which doesn't contain a length property). */ 4334 var elementsLength = elements.length, 4335 elementsIndex = 0; 4336 4337 /*************************** 4338 Argument Overloading 4339 ***************************/ 4340 4341 /* Support is included for jQuery's argument overloading: $.animate(propertyMap [, duration] [, easing] [, complete]). 4342 Overloading is detected by checking for the absence of an object being passed into options. */ 4343 /* Note: The stop action does not accept animation options, and is therefore excluded from this check. */ 4344 if (propertiesMap !== "stop" && !$.isPlainObject(options)) { 4345 /* The utility function shifts all arguments one position to the right, so we adjust for that offset. */ 4346 var startingArgumentPosition = argumentIndex + 1; 4347 4348 options = {}; 4349 4350 /* Iterate through all options arguments */ 4351 for (var i = startingArgumentPosition; i < arguments.length; i++) { 4352 /* Treat a number as a duration. Parse it out. */ 4353 /* Note: The following RegEx will return true if passed an array with a number as its first item. 4354 Thus, arrays are skipped from this check. */ 4355 if (!Type.isArray(arguments[i]) && (/^(fast|normal|slow)$/i.test(arguments[i]) || /^\d/.test(arguments[i]))) { 4356 options.duration = arguments[i]; 4357 /* Treat strings and arrays as easings. */ 4358 } else if (Type.isString(arguments[i]) || Type.isArray(arguments[i])) { 4359 options.easing = arguments[i]; 4360 /* Treat a function as a complete callback. */ 4361 } else if (Type.isFunction(arguments[i])) { 4362 options.complete = arguments[i]; 4363 } 4364 } 4365 } 4366 4367 /*************** 4368 Promises 4369 ***************/ 4370 4371 var promiseData = { 4372 promise: null, 4373 resolver: null, 4374 rejecter: null 4375 }; 4376 4377 /* If this call was made via the utility function (which is the default method of invocation when jQuery/Zepto are not being used), and if 4378 promise support was detected, create a promise object for this call and store references to its resolver and rejecter methods. The resolve 4379 method is used when a call completes naturally or is prematurely stopped by the user. In both cases, completeCall() handles the associated 4380 call cleanup and promise resolving logic. The reject method is used when an invali
4380d set of arguments is passed into a Velocity call. */ 4381 /* Note: Velocity employs a call-based queueing architecture, which means that stopping an animating element actually stops the full call that 4382 triggered it -- not that one element exclusively. Similarly, there is one promise per call, and all elements targeted by a Velocity call are 4383 grouped together for the purposes of resolving and rejecting a promise. */ 4384 if (isUtility && Velocity.Promise) { 4385 promiseData.promise = new Velocity.Promise(function (resolve, reject) { 4386 promiseData.resolver = resolve; 4387 promiseData.rejecter = reject; 4388 }); 4389 } 4390 4391 /********************* 4392 Action Detection 4393 *********************/ 4394 4395 /* Velocity's behavior is categorized into "actions": Elements can either be specially scrolled into view, 4396 or they can be started, stopped, or reversed. If a literal or referenced properties map is passed in as Velocity's 4397 first argument, the associated action is "start". Alternatively, "scroll", "reverse", or "stop" can be passed in instead of a properties map. */ 4398 var action; 4399 4400 switch (propertiesMap) { 4401 case "scroll": 4402 action = "scroll"; 4403 break; 4404 4405 case "reverse": 4406 action = "reverse"; 4407 break; 4408 4409 case "stop": 4410 /******************* 4411 Action: Stop 4412 *******************/ 4413 4414 /* Clear the currently-active delay on each targeted element. */ 4415 $.each(elements, function(i, element) { 4416 if (Data(element) && Data(element).delayTimer) { 4417 /* Stop the timer from triggering its cached next() function. */ 4418 clearTimeout(Data(element).delayTimer.setTimeout); 4419 4420 /* Manually call the next() function so that the subsequent queue items can progress. */ 4421 if (Data(element).delayTimer.next) { 4422 Data(element).delayTimer.next(); 4423 } 4424 4425 delete Data(element).delayTimer; 4426 } 4427 }); 4428 4429 var callsToStop = []; 4430 4431 /* When the stop action is triggered, the elements' currently active call is immediately stopped. The active call might have 4432 been applied to multiple elements, in which case all of the call's elements will be subjected to stopping. When an element 4433 is stopped, the next item in its animation queue is immediately triggered. */ 4434 /* An additional argument may be passed in to clear an element's remaining queued calls. Either true (which defaults to the "fx" queue) 4435 or a custom queue string can be passed in. */ 4436 /* Note: The stop command runs prior to Queueing since its behavior is intended to take effect *immediately*, 4437 regardless of the element's current queue state. */ 4438 4439 /* Iterate through every active call. */ 4440 $.each(Velocity.State.calls, function(i, activeCall) { 4441 /* Inactive calls are set to false by the logic inside completeCall(). Skip them. */ 4442 if (activeCall) { 4443 /* Iterate through the active call's targeted elements. */ 4444 $.each(activeCall[1], function(k, activeElement) { 4445 var queueName = Type.isString(options) ? options : ""; 4446 4447 if (options !== undefined && activeCall[2].queue !== queueName) { 4448 return true; 4449 } 4450 4451 /* Iterate through the calls targeted by the stop command. */ 4452 $.each(elements, function(l, element) { 4453 /* Check that this call was applied to the target element. */ 4454 if (element === activeElement) { 4455 /* Optionally clear the remaining queued calls. */ 4456 if (options !== undefined) { 4457 /* Iterate through the items in the element's queue. */ 4458 $.each($.queue(element, queueName), function(_, item) { 4459 /* The queue array can contain an "inprogress" string, which we skip. */ 4460 if (Type.isFunction(item)) { 4461 /* Pass the item's callback a flag indicating that we want to abort from the queue call. 4462 (Specifically, the queue will resolve the call's associated promise then abort.) */ 4463 item(null, true); 4464 } 4465 }); 4466 4467 /* Clearing the $.queue() array is achieved by resetting it to []. */ 4468 $.queue(element, queueName, []); 4469 } 4470 4471 if (Data(element) && queueName === "") { 4472 /* Since "reverse" uses cached start values (the previous call's endValues), 4473 these values must be changed to reflect the final value that the elements were actually tweened to. */ 4474 $.each(Data(element).tweensContainer, function(m, activeTween) { 4475 activeTween.endValue = activeTween.currentValue; 4476 }); 4477 } 4478 4479 callsToStop.push(i); 4480 } 4481 }); 4482 }); 4483 } 4484 }); 4485 4486 /* Prematurely call completeCall() on each matched active call, passing an additional flag to indicate 4487 that the complete callback and display:none setting should be skipped since we're completing prematurely. */ 4488 $.each(callsToStop, function(i, j) { 4489 completeCall(j, true); 4490 }); 4491 4492 if (promiseData.promise) { 4493 /* Immediately resolve the promise associated with this stop call since stop runs synchronously. */ 4494 promiseData.resolver(elements); 4495 } 4496 4497 /* Since we're stopping, and not proceeding with queueing, exit out of Velocity. */ 4498 return getChain(); 4499 4500 default: 4501 /* Treat a non-empty plain object as a literal properties map. */ 4502 if ($.isPlainObject(propertiesMap) && !Type.isEmptyObject(propertiesMap)) { 4503 action = "start"; 4504 4505 /**************** 4506 Redirects 4507 ****************/ 4508 4509 /* Check if a string matches a registered redirect (see Redirects above). */ 4510 } else if (Type.isString(propertiesMap) && Velocity.Redirects[propertiesMap]) { 4511 var opts = $.extend({}, options), 4512 durationOriginal = opts.duration, 4513 delayOriginal = opts.delay || 0; 4514 4515 /* If the backwards option was passed in, reverse the element set so that elements animate from the last to the first. */ 4516 if (opts.backwards === true) { 4517 elements = $.extend(true, [], elements).reverse(); 4518 } 4519 4520 /* Individually trigger the redirect for each element in the set to prevent users from having to handle iteration logic in their redirect. */ 4521 $.each(elements, function(elementIndex, element) { 4522 /* If the stagger option was passed in, successively delay each element by the stagger value (in ms). Retain the original delay value. */ 4523 if (parseFloat(opts.stagger)) {
vendor: 18,569 bytes, lines 4524-4830
4524 opts.delay = delayOriginal + (parseFloat(opts.stagger) * elementIndex); 4525 } else if (Type.isFunction(opts.stagger)) { 4526 opts.delay = delayOriginal + opts.stagger.call(element, elementIndex, elementsLength); 4527 } 4528 4529 /* If the drag option was passed in, successively increase/decrease (depending on the presense of opts.backwards) 4530 the duration of each element's animation, using floors to prevent producing very short durations. */ 4531 if (opts.drag) { 4532 /* Default the duration of UI pack effects (callouts and transitions) to 1000ms instead of the usual default duration of 400ms. */ 4533 opts.duration = parseFloat(durationOriginal) || (/^(callout|transition)/.test(propertiesMap) ? 1000 : DURATION_DEFAULT); 4534 4535 /* For each element, take the greater duration of: A) animation completion percentage relative to the original duration, 4536 B) 75% of the original duration, or C) a 200ms fallback (in case duration is already set to a low value). 4537 The end result is a baseline of 75% of the redirect's duration that increases/decreases as the end of the element set is approached. */ 4538 opts.duration = Math.max(opts.duration * (opts.backwards ? 1 - elementIndex/elementsLength : (elementIndex + 1) / elementsLength), opts.duration * 0.75, 200); 4539 } 4540 4541 /* Pass in the call's opts object so that the redirect can optionally extend it. It defaults to an empty object instead of null to 4542 reduce the opts checking logic required inside the redirect. */ 4543 Velocity.Redirects[propertiesMap].call(element, element, opts || {}, elementIndex, elementsLength, elements, promiseData.promise ? promiseData : undefined); 4544 }); 4545 4546 /* Since the animation logic resides within the redirect's own code, abort the remainder of this call. 4547 (The performance overhead up to this point is virtually non-existant.) */ 4548 /* Note: The jQuery call chain is kept intact by returning the complete element set. */ 4549 return getChain(); 4550 } else { 4551 var abortError = "Velocity: First argument (" + propertiesMap + ") was not a property map, a known action, or a registered redirect. Aborting."; 4552 4553 if (promiseData.promise) { 4554 promiseData.rejecter(new Error(abortError)); 4555 } else { 4556 console.log(abortError); 4557 } 4558 4559 return getChain(); 4560 } 4561 } 4562 4563 /************************** 4564 Call-Wide Variables 4565 **************************/ 4566 4567 /* A container for CSS unit conversion ratios (e.g. %, rem, and em ==> px) that is used to cache ratios across all elements 4568 being animated in a single Velocity call. Calculating unit ratios necessitates DOM querying and updating, and is therefore 4569 avoided (via caching) wherever possible. This container is call-wide instead of page-wide to avoid the risk of using stale 4570 conversion metrics across Velocity animations that are not immediately consecutively chained. */ 4571 var callUnitConversionData = { 4572 lastParent: null, 4573 lastPosition: null, 4574 lastFontSize: null, 4575 lastPercentToPxWidth: null, 4576 lastPercentToPxHeight: null, 4577 lastEmToPx: null, 4578 remToPx: null, 4579 vwToPx: null, 4580 vhToPx: null 4581 }; 4582 4583 /* A container for all the ensuing tween data and metadata associated with this call. This container gets pushed to the page-wide 4584 Velocity.State.calls array that is processed during animation ticking. */ 4585 var call = []; 4586 4587 /************************ 4588 Element Processing 4589 ************************/ 4590 4591 /* Element processing consists of three parts -- data processing that cannot go stale and data processing that *can* go stale (i.e. third-party style modifications): 4592 1) Pre-Queueing: Element-wide variables, including the element's data storage, are instantiated. Call options are prepared. If triggered, the Stop action is executed. 4593 2) Queueing: The logic that runs once this call has reached its point of execution in the element's $.queue() stack. Most logic is placed here to avoid risking it becoming stale. 4594 3) Pushing: Consolidation of the tween data followed by its push onto the global in-progress calls container. 4595 */ 4596 4597 function processElement () { 4598 4599 /************************* 4600 Part I: Pre-Queueing 4601 *************************/ 4602 4603 /*************************** 4604 Element-Wide Variables 4605 ***************************/ 4606 4607 var element = this, 4608 /* The runtime opts object is the extension of the current call's options and Velocity's page-wide option defaults. */ 4609 opts = $.extend({}, Velocity.defaults, options), 4610 /* A container for the processed data associated with each property in the propertyMap. 4611 (Each property in the map produces its own "tween".) */ 4612 tweensContainer = {}, 4613 elementUnitConversionData; 4614 4615 /****************** 4616 Element Init 4617 ******************/ 4618 4619 if (Data(element) === undefined) { 4620 Velocity.init(element); 4621 } 4622 4623 /****************** 4624 Option: Delay 4625 ******************/ 4626 4627 /* Since queue:false doesn't respect the item's existing queue, we avoid injecting its delay here (it's set later on). */ 4628 /* Note: Velocity rolls its own delay function since jQuery doesn't have a utility alias for $.fn.delay() 4629 (and thus requires jQuery element creation, which we avoid since its overhead includes DOM querying). */ 4630 if (parseFloat(opts.delay) && opts.queue !== false) { 4631 $.queue(element, opts.queue, function(next) { 4632 /* This is a flag used to indicate to the upcoming completeCall() function that this queue entry was initiated by Velocity. See completeCall() for further details. */ 4633 Velocity.velocityQueueEntryFlag = true; 4634 4635 /* The ensuing queue item (which is assigned to the "next" argument that $.queue() automatically passes in) will be triggered after a setTimeout delay. 4636 The setTimeout is stored so that it can be subjected to clearTimeout() if this animation is prematurely stopped via Velocity's "stop" command. */ 4637 Data(element).delayTimer = { 4638 setTimeout: setTimeout(next, parseFloat(opts.delay)), 4639 next: next 4640 }; 4641 }); 4642 } 4643 4644 /********************* 4645 Option: Duration 4646 *********************/ 4647 4648 /* Support for jQuery's named durations. */ 4649 switch (opts.duration.toString().toLowerCase()) { 4650 case "fast": 4651 opts.duration = 200; 4652 break; 4653 4654 case "normal": 4655 opts.duration = DURATION_DEFAULT; 4656 break; 4657 4658 case "slow": 4659 opts.duration = 600; 4660 break; 4661 4662 default: 4663 /* Remove the potential "ms" suffix and default to 1 if the user is attempting to set a duration of 0 (in order to produce an immediate style change). */ 4664 opts.duration = parseFloat(opts.duration) || 1; 4665 } 4666 4667 /************************ 4668 Global Option: Mock 4669 ************************/ 4670 4671 if (Velocity.mock !== false) { 4672 /* In mock mode, all animations are forced to 1ms so that they occur immediately upon the next rAF tick. 4673 Alternatively, a multiplier can be passed in to time remap all delays and durations. */ 4674 if (Velocity.mock === true) { 4675 opts.duration = opts.delay = 1; 4676 } else { 4677 opts.duration *= parseFloat(Velocity.mock) || 1; 4678 opts.delay *= parseFloat(Velocity.mock) || 1; 4679 } 4680 } 4681 4682 /******************* 4683 Option: Easing 4684 *******************/ 4685 4686 opts.easing = getEasing(opts.easing, opts.duration); 4687 4688 /********************** 4689 Option: Callbacks 4690 **********************/ 4691 4692 /* Callbacks must functions. Otherwise, default to null. */ 4693 if (opts.begin && !Type.isFunction(opts.begin)) { 4694 opts.begin = null; 4695 } 4696 4697 if (opts.progress && !Type.isFunction(opts.progress)) { 4698 opts.progress = null; 4699 } 4700 4701 if (opts.complete && !Type.isFunction(opts.complete)) { 4702 opts.complete = null; 4703 } 4704 4705 /********************************* 4706 Option: Display & Visibility 4707 *********************************/ 4708 4709 /* Refer to Velocity's documentation (VelocityJS.org/#displayAndVisibility) for a description of the display and visibility options' behavior. */ 4710 /* Note: We strictly check for undefined instead of falsiness because display accepts an empty string value. */ 4711 if (opts.display !== undefined && opts.display !== null) { 4712 opts.display = opts.display.toString().toLowerCase(); 4713 4714 /* Users can pass in a special "auto" value to instruct Velocity to set the element to its default display value. */ 4715 if (opts.display === "auto") { 4716 opts.display = Velocity.CSS.Values.getDisplayType(element); 4717 } 4718 } 4719 4720 if (opts.visibility !== undefined && opts.visibility !== null) { 4721 opts.visibility = opts.visibility.toString().toLowerCase(); 4722 } 4723 4724 /********************** 4725 Option: mobileHA 4726 **********************/ 4727 4728 /* When set to true, and if this is a mobile device, mobileHA automatically enables hardware acceleration (via a null transform hack) 4729 on animating elements. HA is removed from the element at the completion of its animation. */ 4730 /* Note: Android Gingerbread doesn't support HA. If a null transform hack (mobileHA) is in fact set, it will prevent other tranform subproperties from taking effect. */ 4731 /* Note: You can read more about the use of mobileHA in Velocity's documentation: VelocityJS.org/#mobileHA. */ 4732 opts.mobileHA = (opts.mobileHA && Velocity.State.isMobile && !Velocity.State.isGingerbread); 4733 4734 /*********************** 4735 Part II: Queueing 4736 ***********************/ 4737 4738 /* When a set of elements is targeted by a Velocity call, the set is broken up and each element has the current Velocity call individually queued onto it. 4739 In this way, each element's existing queue is respected; some elements may already be animating and accordingly should not have this current Velocity call triggered immediately. */ 4740 /* In each queue, tween data is processed for each animating property then pushed onto the call-wide calls array. When the last element in the set has had its tweens processed, 4741 the call array is pushed to Velocity.State.calls for live processing by the requestAnimationFrame tick. */ 4742 function buildQueue (next) { 4743 4744 /******************* 4745 Option: Begin 4746 *******************/ 4747 4748 /* The begin callback is fired once per call -- not once per elemenet -- and is passed the full raw DOM element set as both its context and its first argument. */ 4749 if (opts.begin && elementsIndex === 0) { 4750 /* We throw callbacks in a setTimeout so that thrown errors don't halt the execution of Velocity itself. */ 4751 try { 4752 opts.begin.call(elements, elements); 4753 } catch (error) { 4754 setTimeout(function() { throw error; }, 1); 4755 } 4756 } 4757 4758 /***************************************** 4759 Tween Data Construction (for Scroll) 4760 *****************************************/ 4761 4762 /* Note: In order to be subjected to chaining and animation options, scroll's tweening is routed through Velocity as if it were a standard CSS property animation. */ 4763 if (action === "scroll") { 4764 /* The scroll action uniquely takes an optional "offset" option -- specified in pixels -- that offsets the targeted scroll position. */ 4765 var scrollDirection = (/^x$/i.test(opts.axis) ? "Left" : "Top"), 4766 scrollOffset = parseFloat(opts.offset) || 0, 4767 scrollPositionCurrent, 4768 scrollPositionCurrentAlternate, 4769 scrollPositionEnd; 4770 4771 /* Scroll also uniquely takes an optional "container" option, which indicates the parent element that should be scrolled -- 4772 as opposed to the browser window itself. This is useful for scrolling toward an element that's inside an overflowing parent element. */ 4773 if (opts.container) { 4774 /* Ensure that either a jQuery object or a raw DOM element was passed in. */ 4775 if (Type.isWrapped(opts.container) || Type.isNode(opts.container)) { 4776 /* Extract the raw DOM element from the jQuery wrapper. */ 4777 opts.container = opts.container[0] || opts.container; 4778 /* Note: Unlike other properties in Velocity, the browser's scroll position is never cached since it so frequently changes 4779 (due to the user's natural interaction with the page). */ 4780 scrollPositionCurrent = opts.container["scroll" + scrollDirection]; /* GET */ 4781 4782 /* $.position() values are relative to the container's currently viewable area (without taking into account the container's true dimensions 4783 -- say, for example, if the container was not overflowing). Thus, the scroll end value is the sum of the child element's position *and* 4784 the scroll container's current scroll position. */ 4785 /* Note: jQuery does not offer a utility alias for $.position(), so we have to incur jQuery object conversion here. 4786 This syncs up with an ensuing batch of GETs, so it fortunately does not trigger layout thrashing. */ 4787 scrollPositionEnd = (scrollPositionCurrent + $(element).position()[scrollDirection.toLowerCase()]) + scrollOffset; /* GET */ 4788 /* If a value other than a jQuery object or a raw DOM element was passed in, default to null so that this option is ignored. */ 4789 } else { 4790 opts.container = null; 4791 } 4792 } else { 4793 /* If the window itself is being scrolled -- not a containing element -- perform a live scroll position lookup using 4794 the appropriate cached property names (which differ based on browser type). */ 4795 scrollPositionCurrent = Velocity.State.scrollAnchor[Velocity.State["scrollProperty" + scrollDirection]]; /* GET */ 4796 /* When scrolling the browser window, cache the alternate axis's current value since window.scrollTo() doesn't let us change only one value at a time. */ 4797 scrollPositionCurrentAlternate = Velocity.State.scrollAnchor[Velocity.State["scrollProperty" + (scrollDirection === "Left" ? "Top" : "Left")]]; /* GET */ 4798 4799 /* Unlike $.position(), $.offset() values are relative to the browser window's true dimensions -- not merely its currently viewable area -- 4800 and therefore end values do not need to be compounded onto current values. */ 4801 scrollPositionEnd = $(element).offset()[scrollDirection.toLowerCase()] + scrollOffset; /* GET */ 4802 } 4803 4804 /* Since there's only one format that scroll's associated tweensContainer can take, we create it manually. */ 4805 tweensContainer = { 4806 scroll: { 4807 rootPropertyValue: false, 4808 startValue: scrollPositionCurrent, 4809 currentValue: scrollPositionCurrent, 4810 endValue: scrollPositionEnd, 4811 unitType: "", 4812 easing: opts.easing, 4813 scrollData: { 4814 container: opts.container, 4815 direction: scrollDirection, 4816 alternateValue: scrollPositionCurrentAlternate 4817 } 4818 }, 4819 element: element 4820 }; 4821 4822 if (Velocity.debug) console.log("tweensContainer (scroll): ", tweensContainer.scroll, element); 4823 4824 /****************************************** 4825 Tween Data Construction (for Reverse) 4826 ******************************************/ 4827 4828 /* Reverse acts like a "start" action in that a property map is animated toward. The only difference is 4829 that the property map used for reverse is the inverse of the map used in the previous call. Thus, we manipulate 4830 the previous call to construct our new map: use the previous map's end values as our new map's start values. Copy over all other data. */
4831 /* Note: Reverse can be directly called via the "reverse" parameter, or it can be indirectly triggered via the loop option. (Loops are composed of multiple reverses.) */ 4832 /* Note: Reverse calls do not need to be consecutively chained onto a currently-animating element in order to operate on cached values; 4833 there is no harm to reverse being called on a potentially stale data cache since reverse's behavior is simply defined 4834 as reverting to the element's values as they were prior to the previous *Velocity* call. */ 4835 } else if (action === "reverse") { 4836 /* Abort if there is no prior animation data to reverse to. */ 4837 if (!Data(element).tweensContainer) { 4838 /* Dequeue the element so that this queue entry releases itself immediately, allowing subsequent queue entries to run. */ 4839 $.dequeue(element, opts.queue); 4840 4841 return; 4842 } else { 4843 /********************* 4844 Options Parsing 4845 *********************/ 4846 4847 /* If the element was hidden via the display option in the previous call, 4848 revert display to "auto" prior to reversal so that the element is visible again. */ 4849 if (Data(element).opts.display === "none") { 4850 Data(element).opts.display = "auto"; 4851 } 4852 4853 if (Data(element).opts.visibility === "hidden") { 4854 Data(element).opts.visibility = "visible"; 4855 } 4856 4857 /* If the loop option was set in the previous call, disable it so that "reverse" calls aren't recursively generated. 4858 Further, remove the previous call's callback options; typically, users do not want these to be refired. */ 4859 Data(element).opts.loop = false; 4860 Data(element).opts.begin = null; 4861 Data(element).opts.complete = null; 4862 4863 /* Since we're extending an opts object that has already been extended with the defaults options object, 4864 we remove non-explicitly-defined properties that are auto-assigned values. */ 4865 if (!options.easing) { 4866 delete opts.easing; 4867 } 4868 4869 if (!options.duration) { 4870 delete opts.duration; 4871 } 4872 4873 /* The opts object used for reversal is an extension of the options object optionally passed into this 4874 reverse call plus the options used in the previous Velocity call. */ 4875 opts = $.extend({}, Data(element).opts, opts); 4876 4877 /************************************* 4878 Tweens Container Reconstruction 4879 *************************************/ 4880 4881 /* Create a deepy copy (indicated via the true flag) of the previous call's tweensContainer. */ 4882 var lastTweensContainer = $.extend(true, {}, Data(element).tweensContainer); 4883 4884 /* Manipulate the previous tweensContainer by replacing its end values and currentValues with its start values. */ 4885 for (var lastTween in lastTweensContainer) { 4886 /* In addition to tween data, tweensContainers contain an element property that we ignore here. */ 4887 if (lastTween !== "element") { 4888 var lastStartValue = lastTweensContainer[lastTween].startValue; 4889 4890 lastTweensContainer[lastTween].startValue = lastTweensContainer[lastTween].currentValue = lastTweensContainer[lastTween].endValue; 4891 lastTweensContainer[lastTween].endValue = lastStartValue; 4892 4893 /* Easing is the only option that embeds into the individual tween data (since it can be defined on a per-property basis). 4894 Accordingly, every property's easing value must be updated when an options object is passed in with a reverse call. 4895 The side effect of this extensibility is that all per-property easing values are forcefully reset to the new value. */ 4896 if (!Type.isEmptyObject(options)) { 4897 lastTweensContainer[lastTween].easing = opts.easing; 4898 } 4899 4900 if (Velocity.debug) console.log("reverse tweensContainer (" + lastTween + "): " + JSON.stringify(lastTweensContainer[lastTween]), element); 4901 } 4902 } 4903 4904 tweensContainer = lastTweensContainer; 4905 } 4906 4907 /***************************************** 4908 Tween Data Construction (for Start) 4909 *****************************************/ 4910 4911 } else if (action === "start") { 4912 4913 /************************* 4914 Value Transferring 4915 *************************/ 4916 4917 /* If this queue entry follows a previous Velocity-initiated queue entry *and* if this entry was created 4918 while the element was in the process of being animated by Velocity, then this current call is safe to use 4919 the end values from the prior call as its start values. Velocity attempts to perform this value transfer 4920 process whenever possible in order to avoid requerying the DOM. */ 4921 /* If values aren't transferred from a prior call and start values were not forcefed by the user (more on this below), 4922 then the DOM is queried for the element's current values as a last resort. */ 4923 /* Note: Conversely, animation reversal (and looping) *always* perform inter-call value transfers; they never requery the DOM. */ 4924 var lastTweensContainer; 4925 4926 /* The per-element isAnimating flag is used to indicate whether it's safe (i.e. the data isn't stale) 4927 to transfer over end values to use as start values. If it's set to true and there is a previous 4928 Velocity call to pull values from, do so. */ 4929 if (Data(element).tweensContainer && Data(element).isAnimating === true) { 4930 lastTweensContainer = Data(element).tweensContainer; 4931 } 4932 4933 /*************************** 4934 Tween Data Calculation 4935 ***************************/ 4936 4937 /* This function parses property data and defaults endValue, easing, and startValue as appropriate. */ 4938 /* Property map values can either take the form of 1) a single value representing the end value, 4939 or 2) an array in the form of [ endValue, [, easing] [, startValue] ]. 4940 The optional third parameter is a forcefed startValue to be used instead of querying the DOM for 4941 the element's current value. Read Velocity's docmentation to learn more about forcefeeding: VelocityJS.org/#forcefeeding */ 4942 function parsePropertyValue (valueData, skipResolvingEasing) { 4943 var endValue = undefined, 4944 easing = undefined, 4945 startValue = undefined; 4946 4947 /* Handle the array format, which can be structured as one of three potential overloads: 4948 A) [ endValue, easing, startValue ], B) [ endValue, easing ], or C) [ endValue, startValue ] */ 4949 if (Type.isArray(valueData)) { 4950 /* endValue is always the first item in the array. Don't bother validating endValue's value now 4951 since the ensuing property cycling logic does that. */ 4952 endValue = valueData[0]; 4953 4954 /* Two-item array format: If the second item is a number, function, or hex string, treat it as a 4955 start value since easings can only be non-hex strings or arrays. */ 4956 if ((!Type.isArray(valueData[1]) && /^[\d-]/.test(valueData[1])) || Type.isFunction(valueData[1]) || CSS.RegEx.isHex.test(valueData[1])) { 4957 startValue = valueData[1]; 4958 /* Two or three-item array: If the second item is a non-hex string or an array, treat it as an easing. */ 4959 } else if ((Type.isString(valueData[1]) && !CSS.RegEx.isHex.test(valueData[1])) || Type.isArray(valueData[1])) { 4960 easing = skipResolvingEasing ? valueData[1] : getEasing(valueData[1], opts.duration); 4961 4962 /* Don't bother validating startValue's value now since the ensuing property cycling logic inherently does that. */ 4963 if (valueData[2] !== undefined) { 4964 startValue = valueData[2]; 4965 } 4966 } 4967 /* Handle the single-value format. */ 4968 } else { 4969 endValue = valueData; 4970 } 4971 4972 /* Default to the call's easing if a per-property easing type was not defined. */ 4973 if (!skipResolvingEasing) { 4974 easing = easing || opts.easing; 4975 } 4976 4977 /* If functions were passed in as values, pass the function the current element as its context, 4978 plus the element's index and the element set's size as arguments. Then, assign the returned value. */
4979 if (Type.isFunction(endValue)) { 4980 endValue = endValue.call(element, elementsIndex, elementsLength); 4981 } 4982 4983 if (Type.isFunction(startValue)) { 4984 startValue = startValue.call(element, elementsIndex, elementsLength); 4985 } 4986 4987 /* Allow startValue to be left as undefined to indicate to the ensuing code that its value was not forcefed. */ 4988 return [ endValue || 0, easing, startValue ]; 4989 } 4990 4991 /* Cycle through each property in the map, looking for shorthand color properties (e.g. "color" as opposed to "colorRed"). Inject the corresponding 4992 colorRed, colorGreen, and colorBlue RGB component tweens into the propertiesMap (which Velocity understands) and remove the shorthand property. */ 4993 $.each(propertiesMap, function(property, value) { 4994 /* Find shorthand color properties that have been passed a hex string. */ 4995 if (RegExp("^" + CSS.Lists.colors.join("$|^") + "$").test(property)) { 4996 /* Parse the value data for each shorthand. */ 4997 var valueData = parsePropertyValue(value, true), 4998 endValue = valueData[0], 4999 easing = valueData[1], 5000 startValue = valueData[2]; 5001 5002 if (CSS.RegEx.isHex.test(endValue)) { 5003 /* Convert the hex strings into their RGB component arrays. */ 5004 var colorComponents = [ "Red", "Green", "Blue" ], 5005 endValueRGB = CSS.Values.hexToRgb(endValue), 5006 startValueRGB = startValue ? CSS.Values.hexToRgb(startValue) : undefined; 5007 5008 /* Inject the RGB component tweens into propertiesMap. */ 5009 for (var i = 0; i < colorComponents.length; i++) { 5010 var dataArray = [ endValueRGB[i] ]; 5011 5012 if (easing) { 5013 dataArray.push(easing); 5014 } 5015 5016 if (startValueRGB !== undefined) { 5017 dataArray.push(startValueRGB[i]); 5018 } 5019 5020 propertiesMap[property + colorComponents[i]] = dataArray; 5021 } 5022 5023 /* Remove the intermediary shorthand property entry now that we've processed it. */ 5024 delete propertiesMap[property]; 5025 } 5026 } 5027 }); 5028 5029 /* Create a tween out of each property, and append its associated data to tweensContainer. */ 5030 for (var property in propertiesMap) { 5031 5032 /************************** 5033 Start Value Sourcing 5034 **************************/ 5035 5036 /* Parse out endValue, easing, and startValue from the property's data. */ 5037 var valueData = parsePropertyValue(propertiesMap[property]), 5038 endValue = valueData[0], 5039 easing = valueData[1], 5040 startValue = valueData[2]; 5041 5042 /* Now that the original property name's format has been used for the parsePropertyValue() lookup above, 5043 we force the property to its camelCase styling to normalize it for manipulation. */ 5044 property = CSS.Names.camelCase(property); 5045 5046 /* In case this property is a hook, there are circumstances where we will intend to work on the hook's root property and not the hooked subproperty. */ 5047 var rootProperty = CSS.Hooks.getRoot(property), 5048 rootPropertyValue = false;
5049 5050 /* Properties that are not supported by the browser (and do not have an associated normalization) will 5051 inherently produce no style changes when set, so they are skipped in order to decrease animation tick overhead. 5052 Property support is determined via prefixCheck(), which returns a false flag when no supported is detected. */ 5053 /* Note: Since SVG elements have some of their properties directly applied as HTML attributes, 5054 there is no way to check for their explicit browser support, and so we skip skip this check for them. */ 5055 if (!Data(element).isSVG && CSS.Names.prefixCheck(rootProperty)[1] === false && CSS.Normalizations.registered[rootProperty] === undefined) { 5056 if (Velocity.debug) console.log("Skipping [" + rootProperty + "] due to a lack of browser support."); 5057 5058 continue; 5059 } 5060 5061 /* If the display option is being set to a non-"none" (e.g. "block") and opacity (filter on IE<=8) is being 5062 animated to an endValue of non-zero, the user's intention is to fade in from invisible, thus we forcefeed opacity 5063 a startValue of 0 if its startValue hasn't already been sourced by value transferring or prior forcefeeding. */ 5064 if (((opts.display !== undefined && opts.display !== null && opts.display !== "none") || (opts.visibility !== undefined && opts.visibility !== "hidden")) && /opacity|filter/.test(property) && !startValue && endValue !== 0) { 5065 startValue = 0; 5066 } 5067 5068 /* If values have been transferred from the previous Velocity call, extract the endValue and rootPropertyValue 5069 for all of the current call's properties that were *also* animated in the previous call. */ 5070 /* Note: Value transferring can optionally be disabled by the user via the _cacheValues option. */ 5071 if (opts._cacheValues && lastTweensContainer && lastTweensContainer[property]) { 5072 if (startValue === undefined) { 5073 startValue = lastTweensContainer[property].endValue + lastTweensContainer[property].unitType; 5074 } 5075 5076 /* The previous call's rootPropertyValue is extracted from the element's data cache since that's the 5077 instance of rootPropertyValue that gets freshly updated by the tweening process, whereas the rootPropertyValue 5078 attached to the incoming lastTweensContainer is equal to the root property's value prior to any tweening. */ 5079 rootPropertyValue = Data(element).rootPropertyValueCache[rootProperty]; 5080 /* If values were not transferred from a previous Velocity call, query the DOM as needed. */ 5081 } else { 5082 /* Handle hooked properties. */ 5083 if (CSS.Hooks.registered[property]) { 5084 if (startValue === undefined) { 5085 rootPropertyValue = CSS.getPropertyValue(element, rootProperty); /* GET */ 5086 /* Note: The following getPropertyValue() call does not actually trigger a DOM query; 5087 getPropertyValue() will extract the hook from rootPropertyValue. */ 5088 startValue = CSS.getPropertyValue(element, property, rootPropertyValue); 5089 /* If startValue is already defined via forcefeeding, do not query the DOM for the root property's value; 5090 just grab rootProperty's zero-value template from CSS.Hooks. This overwrites the element's actual 5091 root property value (if one is set), but this is acceptable since the primary reason users forcefeed is 5092 to avoid DOM queries, and thus we likewise avoid querying the DOM for the root property's value. */ 5093 } else { 5094 /* Grab this hook's zero-value template, e.g. "0px 0px 0px black". */ 5095 rootPropertyValue = CSS.Hooks.templates[rootProperty][1]; 5096 } 5097 /* Handle non-hooked properties that haven't already been defined via forcefeeding. */ 5098 } else if (startValue === undefined) { 5099 startValue = CSS.getPropertyValue(element, property); /* GET */ 5100 } 5101 } 5102 5103 /************************** 5104 Value Data Extraction 5105 **************************/ 5106 5107 var separatedValue, 5108 endValueUnitType, 5109 startValueUnitType, 5110 operator = false;
vendor: 4,539 bytes, lines 5111-5186
5111 5112 /* Separates a property value into its numeric value and its unit type. */ 5113 function separateValue (property, value) { 5114 var unitType, 5115 numericValue; 5116 5117 numericValue = (value || "0") 5118 .toString() 5119 .toLowerCase() 5120 /* Match the unit type at the end of the value. */ 5121 .replace(/[%A-z]+$/, function(match) { 5122 /* Grab the unit type. */ 5123 unitType = match; 5124 5125 /* Strip the unit type off of value. */ 5126 return ""; 5127 }); 5128 5129 /* If no unit type was supplied, assign one that is appropriate for this property (e.g. "deg" for rotateZ or "px" for width). */ 5130 if (!unitType) { 5131 unitType = CSS.Values.getUnitType(property); 5132 } 5133 5134 return [ numericValue, unitType ]; 5135 } 5136 5137 /* Separate startValue. */ 5138 separatedValue = separateValue(property, startValue); 5139 startValue = separatedValue[0]; 5140 startValueUnitType = separatedValue[1]; 5141 5142 /* Separate endValue, and extract a value operator (e.g. "+=", "-=") if one exists. */ 5143 separatedValue = separateValue(property, endValue); 5144 endValue = separatedValue[0].replace(/^([+-\/*])=/, function(match, subMatch) { 5145 operator = subMatch; 5146 5147 /* Strip the operator off of the value. */ 5148 return ""; 5149 }); 5150 endValueUnitType = separatedValue[1]; 5151 5152 /* Parse float values from endValue and startValue. Default to 0 if NaN is returned. */ 5153 startValue = parseFloat(startValue) || 0; 5154 endValue = parseFloat(endValue) || 0; 5155 5156 /*************************************** 5157 Property-Specific Value Conversion 5158 ***************************************/ 5159 5160 /* Custom support for properties that don't actually accept the % unit type, but where pollyfilling is trivial and relatively foolproof. */ 5161 if (endValueUnitType === "%") { 5162 /* A %-value fontSize/lineHeight is relative to the parent's fontSize (as opposed to the parent's dimensions), 5163 which is identical to the em unit's behavior, so we piggyback off of that. */ 5164 if (/^(fontSize|lineHeight)$/.test(property)) { 5165 /* Convert % into an em decimal value. */ 5166 endValue = endValue / 100; 5167 endValueUnitType = "em"; 5168 /* For scaleX and scaleY, convert the value into its decimal format and strip off the unit type. */ 5169 } else if (/^scale/.test(property)) { 5170 endValue = endValue / 100; 5171 endValueUnitType = ""; 5172 /* For RGB components, take the defined percentage of 255 and strip off the unit type. */ 5173 } else if (/(Red|Green|Blue)$/i.test(property)) { 5174 endValue = (endValue / 100) * 255; 5175 endValueUnitType = ""; 5176 } 5177 } 5178 5179 /*************************** 5180 Unit Ratio Calculation 5181 ***************************/ 5182 5183 /* When queried, the browser returns (most) CSS property values in pixels. Therefore, if an endValue with a unit type of 5184 %, em, or rem is animated toward, startValue must be converted from pixels into the same unit type as endValue in order 5185 for value manipulation logic (increment/decrement) to proceed. Further, if the startValue was forcefed or transferred 5186 from a previous call, startValue may al
5186so not be in pixels. Unit conversion logic therefore consists of two steps: 5187 1) Calculating the ratio of %/em/rem/vh/vw relative to pixels 5188 2) Converting startValue into the same unit of measurement as endValue based on these ratios. */ 5189 /* Unit conversion ratios are calculated by inserting a sibling node next to the target node, copying over its position property, 5190 setting values with the target unit type then comparing the returned pixel value. */ 5191 /* Note: Even if only one of these unit types is being animated, all unit ratios are calculated at once since the overhead 5192 of batching the SETs and GETs together upfront outweights the potential overhead 5193 of layout thrashing caused by re-querying for uncalculated ratios for subsequently-processed properties. */ 5194 /* Todo: Shift this logic into the calls' first tick instance so that it's synced with RAF. */ 5195 function calculateUnitRatios () { 5196 5197 /************************ 5198 Same Ratio Checks 5199 ************************/ 5200 5201 /* The properties below are used to determine whether the element differs sufficiently from this call's 5202 previously iterated element to also differ in its unit conversion ratios. If the properties match up with those 5203 of the prior element, the prior element's conversion ratios are used. Like most optimizations in Velocity, 5204 this is done to minimize DOM querying. */ 5205 var sameRatioIndicators = { 5206 myParent: element.parentNode || document.body, /* GET */ 5207 position: CSS.getPropertyValue(element, "position"), /* GET */ 5208 fontSize: CSS.getPropertyValue(element, "fontSize") /* GET */ 5209 }, 5210 /* Determine if the same % ratio can be used. % is based on the element's position value and its parent's width and height dimensions. */ 5211 samePercentRatio = ((sameRatioIndicators.position === callUnitConversionData.lastPosition) && (sameRatioIndicators.myParent === callUnitConversionData.lastParent)), 5212 /* Determine if the same em ratio can be used. em is relative to the element's fontSize. */ 5213 sameEmRatio = (sameRatioIndicators.fontSize === callUnitConversionData.lastFontSize); 5214 5215 /* Store these ratio indicators call-wide for the next element to compare against. */ 5216 callUnitConversionData.lastParent = sameRatioIndicators.myParent; 5217 callUnitConversionData.lastPosition = sameRatioIndicators.position; 5218 callUnitConversionData.lastFontSize = sameRatioIndicators.fontSize; 5219 5220 /*************************** 5221 Element-Specific Units 5222 ***************************/ 5223 5224 /* Note: IE8 rounds to the nearest pixel when returning CSS values, thus we perform conversions using a measurement 5225 of 100 (instead of 1) to give our ratios a precision of at least 2 decimal values. */ 5226 var measurement = 100, 5227 unitRatios = {}; 5228 5229 if (!sameEmRatio || !samePercentRatio) { 5230 var dummy = Data(element).isSVG ? document.createElementNS("http://www.w3.org/2000/svg", "rect") : document.createElement("div"); 5231 5232 Velocity.init(dummy); 5233 sameRatioIndicators.myParent.appendChild(dummy); 5234 5235 /* To accurately and consistently calculate conversion ratios, the element's cascaded overflow and box-sizing are stripped. 5236 Similarly, since width/height can be artificially constrained by their min-/max- equivalents, these are controlled for as well. */ 5237 /* Note: Overflow must be also be c
5237ontrolled for per-axis since the overflow property overwrites its per-axis values. */ 5238 $.each([ "overflow", "overflowX", "overflowY" ], function(i, property) { 5239 Velocity.CSS.setPropertyValue(dummy, property, "hidden"); 5240 }); 5241 Velocity.CSS.setPropertyValue(dummy, "position", sameRatioIndicators.position); 5242 Velocity.CSS.setPropertyValue(dummy, "fontSize", sameRatioIndicators.fontSize); 5243 Velocity.CSS.setPropertyValue(dummy, "boxSizing", "content-box"); 5244 5245 /* width and height act as our proxy properties for measuring the horizontal and vertical % ratios. */ 5246 $.each([ "minWidth", "maxWidth", "width", "minHeight", "maxHeight", "height" ], function(i, property) { 5247 Velocity.CSS.setPropertyValue(dummy, property, measurement + "%"); 5248 }); 5249 /* paddingLeft arbitrarily acts as our proxy property for the em ratio. */ 5250 Velocity.CSS.setPropertyValue(dummy, "paddingLeft", measurement + "em"); 5251 5252 /* Divide the returned value by the measurement to get the ratio between 1% and 1px. Default to 1 since working with 0 can produce Infinite. */ 5253 unitRatios.percentToPxWidth = callUnitConversionData.lastPercentToPxWidth = (parseFloat(CSS.getPropertyValue(dummy, "width", null, true)) || 1) / measurement; /* GET */ 5254 unitRatios.percentToPxHeight = callUnitConversionData.lastPercentToPxHeight = (parseFloat(CSS.getPropertyValue(dummy, "height", null, true)) || 1) / measurement; /* GET */ 5255 unitRatios.emToPx = callUnitConversionData.lastEmToPx = (parseFloat(CSS.getPropertyValue(dummy, "paddingLeft")) || 1) / measurement; /* GET */ 5256 5257 sameRatioIndicators.myParent.removeChild(dummy); 5258 } else { 5259 unitRatios.emToPx = callUnitConversionData.lastEmToPx; 5260 unitRatios.percentToPxWidth = callUnitConversionData.lastPercentToPxWidth; 5261 unitRatios.percentToPxHeight = callUnitConversionData.lastPercentToPxHeight; 5262 } 5263 5264 /*************************** 5265 Element-Agnostic Units 5266 ***************************/ 5267 5268 /* Whereas % and em ratios are determined on a per-element basis, the rem unit only needs to be checked 5269 once per call since it's exclusively dependant upon document.body's fontSize. If this is the first time 5270 that calculateUnitRatios() is being run during this call, remToPx will still be set to its default value of null, 5271 so we calculate it now. */ 5272 if (callUnitConversionData.remToPx === null) { 5273 /* Default to browsers' default fontSize of 16px in the case of 0. */ 5274 callUnitConversionData.remToPx = parseFloat(CSS.getPropertyValue(document.body, "fontSize")) || 16; /* GET */ 5275 } 5276 5277 /* Similarly, viewport units are %-relative to the window's inner dimensions. */ 5278 if (callUnitConversionData.vwToPx === null) { 5279 callUnitConversionData.vwToPx = parseFloat(window.innerWidth) / 100; /* GET */ 5280 callUnitConversionData.vhToPx = parseFloat(window.innerHeight) / 100; /* GET */ 5281 } 5282 5283 unitRatios.remToPx = callUnitConversionData.remToPx; 5284 unitRatios.vwToPx = callUnitConversionData.vwToPx; 5285 unitRatios.vhToPx = callUnitConversionData.vhToPx; 5286 5287 if (Velocity.debug >= 1) console.log("Unit ratios: " + JSON.stringify(unitRatios), element); 5288 5289 return unitRatios; 5290 } 5291 5292 /******************** 5293 Unit Conversion 5294 ********************/ 5295 5296 /* The * and / operators, which are not passed in with an associated unit, inherently use startValue's unit. Skip value and unit conversion. */ 5297 if (/[\/*]/.test(operator)) { 5298 endValueUnitType = startValueUnitType; 5299 /* If startValue and endValue differ in unit type, convert startValue into the same unit type as endValue so that if endValueUnitType 5300 is a relative unit (%, em, rem), the values set during tweening will continue to be accurately relative even if the metrics they depen
5300d 5301 on are dynamically changing during the course of the animation. Conversely, if we always normalized into px and used px for setting values, the px ratio 5302 would become stale if the original unit being animated toward was relative and the underlying metrics change during the animation. */ 5303 /* Since 0 is 0 in any unit type, no conversion is necessary when startValue is 0 -- we just start at 0 with endValueUnitType. */ 5304 } else if ((startValueUnitType !== endValueUnitType) && startValue !== 0) { 5305 /* Unit conversion is also skipped when endValue is 0, but *startValueUnitType* must be used for tween values to remain accurate. */ 5306 /* Note: Skipping unit conversion here means that if endValueUnitType was originally a relative unit, the animation won't relatively 5307 match the underlying metrics if they change, but this is acceptable since we're animating toward invisibility instead of toward visibility, 5308 which remains past the point of the animation's completion. */ 5309 if (endValue === 0) { 5310 endValueUnitType = startValueUnitType; 5311 } else { 5312 /* By this point, we cannot avoid unit conversion (it's undesirable since it causes layout thrashing). 5313 If we haven't already, we trigger calculateUnitRatios(), which runs once per element per call. */ 5314 elementUnitConversionData = elementUnitConversionData || calculateUnitRatios(); 5315 5316 /* The following RegEx matches CSS properties that have their % values measured relative to the x-axis. */ 5317 /* Note: W3C spec mandates that all of margin and padding's properties (even top and bottom) are %-relative to the *width* of the parent element. */ 5318 var axis = (/margin|padding|left|right|width|text|word|letter/i.test(property) || /X$/.test(property) || property === "x") ? "x" : "y"; 5319 5320 /* In order to avoid generating n^2 bespoke conversion functions, unit conversion is a two-step process: 5321 1) Convert startValue into pixels. 2) Convert this new pixel value into endValue's unit type. */ 5322 switch (startValueUnitType) { 5323 case "%": 5324 /* Note: translateX and translateY are the only properties that are %-relative to an element's own dimensions -- not its parent's dimensions. 5325 Velocity does not include a special conversion process to account for this behavior. Therefore, animating translateX/Y from a % value 5326 to a non-% value will produce an incorrect start value. Fortunately, this sort of cross-unit conversion is rarely done by users in practice. */ 5327 startValue *= (axis === "x" ? elementUnitConversionData.percentToPxWidth : elementUnitConversionData.percentToPxHeight); 5328 break; 5329 5330 case "px": 5331 /* px acts as our midpoint in the unit conversion process; do nothing. */ 5332 break; 5333 5334 default: 5335 startValue *= elementUnitConversionData[startValueUnitType + "ToPx"]; 5336 } 5337 5338 /* Invert the px ratios to convert into to the target unit. */ 5339 switch (endValueUnitType) { 5340 case "%": 5341 startValue *= 1 / (axis === "x" ? elementUnitConversionData.percentToPxWidth : elementUnitConversionData.percentToPxHeight); 5342 break; 5343 5344 case "px": 5345 /* startValue is already in px, do nothing; we're done. */ 5346 break; 5347 5348 default: 5349 startValue *= 1 / elementUnitConversionData[endValueUnitType + "ToPx"]; 5350 } 5351 } 5352 } 5353 5354 /********************* 5355 Relative Values 5356 *********************/ 5357 5358 /* Operator logic must be performed last since it requires unit-normalized start and end values. */ 5359 /* Note: Relative *percent values* do not behave how most people think; while one would expect "+=50%" 5360 to increase the property 1.5x its current value, it in fact increases the percent units in absolute terms: 5361 50 points is added on top of the current % value. */ 5362 switch (operator) { 5363 case "+": 5364 endValue = startValue + endValue; 5365 break; 5366 5367 case "-": 5368 endValue = startValue - endValue; 5369 break; 5370 5371 case "*": 5372 endValue = startValue * endValue; 5373 break; 5374 5375 case "/": 5376 endValue = startValue / endValue; 5377 break; 5378 } 5379 5380 /************************** 5381 tweensContainer Push 5382 **************************/ 5383 5384 /* Construct the per-property tween object, and push it to the element's tweensContainer. */ 5385 tweensContainer[property] = { 5386 rootPropertyValue: rootPropertyValue, 5387 startValue: startValue, 5388 currentValue: startValue, 5389 endValue: endValue, 5390 unitType: endValueUnitType, 5391 easing: easing 5392 }; 5393 5394 if (Velocity.debug) console.log("tweensContainer (" + property + "): " + JSON.stringify(tweensContainer[property]), element); 5395 } 5396 5397 /* Along with its property data, store a reference to the element itself onto tweensContainer. */ 5398 tweensContainer.element = element; 5399 } 5400 5401 /***************** 5402 Call Push 5403 *****************/ 5404 5405 /* Note: tweensContainer can be empty if all of the properties in this call's property map were skipped due to not 5406 being supported by the browser. The element property is used for checking that the tweensContainer has been appended to. */ 5407 if (tweensContainer.element) { 5408 /* Apply the "velocity-animating" indicator class. */ 5409 CSS.Values.addClass(element, "velocity-animating"); 5410 5411 /* The call array houses the tweensContainers for each element being animated in the current call. */ 5412 call.push(tweensContainer); 5413 5414 /* Store the tweensContainer and options if we're working on the default effects queue, so that they can be used by the reverse command. */ 5415 if (opts.queue === "") { 5416 Data(element).tweensContainer = tweensContainer; 5417 Data(element).opts = opts; 5418 } 5419 5420 /* Switch on the element's animating flag. */ 5421 Data(element).isAnimating = true; 5422 5423 /* Once the final element in this call's element set has been processed, push the call array onto 5424 Velocity.State.calls for the animation tick to immediately begin processing. */ 5425 if (elementsIndex === elementsLength - 1) { 5426 /* To speed up iterating over this array, it is compacted (falsey items -- calls that have completed -- are removed) 5427 when its length has ballooned to a point that can impact tick performance. This only becomes necessary when animation 5428 has been continuous with many elements over a long period of time; whenever all active calls are completed, completeCall() clears Velocity.State.calls. */ 5429 if (Velocity.State.calls.length > 10000) { 5430 Velocity.State.calls = compactSparseArray(Velocity.State.calls); 5431 } 5432 5433 /* Add the current call plus its associated metadata (the element set and the call's options) onto the global call container. 5434 Anything on this call container is subjected to tick() processing. */ 5435 Velocity.State.calls.push([ call, elements, opts, null, promiseData.resolver ]); 5436 5437 /* If the animation tick isn't running, start it. (Velocity shuts it off when there are no active calls to process.) */ 5438 if (Velocity.State.isTicking === false) { 5439 Velocity.State.isTicking = true; 5440 5441 /* Start the tick loop. */ 5442 tick(); 5443 } 5444 } else { 5445 elementsIndex++; 5446 } 5447 } 5448 } 5449 5450 /* When the queue option is set to false, the call skips the element's queue and fires immediately. */ 5451 if (opts.queue === false) { 5452 /* Since this buildQueue call doesn't respect the element's existing queue (which is where a delay option would have been appended), 5453 we manually inject the delay property here with an explicit setTimeout. */ 5454 if (opts.delay) { 5455 setTimeout(buildQueue, opts.delay); 5456 } else { 5457 buildQueue(); 5458 } 5459 /* Otherwise, the call undergoes element queueing as normal. */ 5460 /* Note: To interoperate with jQuery, Velocity uses jQuery's own $.queue() stack for queuing logic. */ 5461 } else { 5462 $.queue(element, opts.queue, function(next, clearQueue) { 5463 /* If the clearQueue flag was passed in by the stop command, resolve this call's promise. (Promises can only be resolved once, 5464 so it's fine if this is repeatedly triggered for each element in the associated call.) */ 5465 if (clearQueue === true) { 5466 if (promiseData.promise) { 5467 promiseData.resolver(elements); 5468 } 5469 5470 /* Do not continue with animation queueing. */ 5471 return true; 5472 } 5473 5474 /* This flag indicates to the upcoming completeCall() function that this queue entry was initiated by Velocity. 5475 See completeCall() for further details. */ 5476 Velocity.velocityQueueEntryFlag = true; 5477 5478 buildQueue(next); 5479 }); 5480 } 5481 5482 /********************* 5483 Auto-Dequeuing 5484 *********************/ 5485 5486 /* As per jQuery's $.queue() behavior, to fire the first non-custom-queue entry on an element, the element 5487 must be dequeued if its queue stack consists *solely* of the current call. (This can be determined by checking 5488 for the "inprogress" item that jQuery prepends to active queue stack arrays.) Regardless, whenever the element's 5489 queue is further appended with additional items -- including $.delay()'s or even $.animate() calls, the queue's 5490 first entry is automatically fired. This behavior contrasts that of custom queues, which never auto-fire. */ 5491 /* Note: When an element set is being subjected to a non-parallel Velocity call, the animation will not begin until 5492 each one of the elements in the set has reached the end of its individually pre-existing queue chain. */ 5493 /* Note: Unfortunately, most people don't fully grasp jQuery's powerful, yet quirky, $.queue() function. 5494 Lean more here: http://stackoverflow.com/questions/1058158/can-somebody-explain-jquery-queue-to-me */ 5495 if ((opts.queue === "" || opts.queue === "fx") && $.queue(element)[0] !== "inprogress") { 5496 $.dequeue(element); 5497 } 5498 } 5499 5500 /************************** 5501 Element Set Iteration 5502 **************************/ 5503 5504 /* If the "nodeType" property exists on the elements variable, we're animating a single element. 5505 Place it in an array so that $.each() can iterate over it. */ 5506 $.each(elements, function(i, element) { 5507 /* Ensure each element in a set has a nodeType (is a real element) to avoid throwing errors. */ 5508 if (Type.isNode(element)) { 5509 processElement.call(element); 5510 } 5511 }); 5512 5513 /****************** 5514 Option: Loop 5515 ******************/ 5516 5517 /* The loop option accepts an integer indicating how many times the element should loop between the values in the 5518 current call's properties map and the element's property values prior to this call. */ 5519 /* Note: The loop option's logic is performed here -- after element processing -- because the current call needs 5520 to undergo its queue insertion prior to the loop option generating its series of constituent "reverse" calls, 5521 which chain after the current call. Two reverse calls (two "alternations") constitute one loop. */ 5522 var opts = $.extend({}, Velocity.defaults, options), 5523 reverseCallsCount; 5524 5525 opts.loop = parseInt(opts.loop); 5526 reverseCallsCount = (opts.loop * 2) - 1; 5527 5528 if (opts.loop) {
5529 /* Double the loop count to convert it into its appropriate number of "reverse" calls. 5530 Subtract 1 from the resulting value since the current call is included in the total alternation count. */ 5531 for (var x = 0; x < reverseCallsCount; x++) { 5532 /* Since the logic for the reverse action occurs inside Queueing and therefore this call's options object 5533 isn't parsed until then as well, the current call's delay option must be explicitly passed into the reverse 5534 call so that the delay logic that occurs inside *Pre-Queueing* can process it. */ 5535 var reverseOptions = { 5536 delay: opts.delay, 5537 progress: opts.progress 5538 }; 5539 5540 /* If a complete callback was passed into this call, transfer it to the loop redirect's final "reverse" call 5541 so that it's triggered when the entire redirect is complete (and not when the very first animation is complete). */ 5542 if (x === reverseCallsCount - 1) { 5543 reverseOptions.display = opts.display; 5544 reverseOptions.visibility = opts.visibility; 5545 reverseOptions.complete = opts.complete; 5546 } 5547 5548 animate(elements, "reverse", reverseOptions); 5549 } 5550 } 5551 5552 /*************** 5553 Chaining 5554 ***************/ 5555 5556 /* Return the elements back to the call chain, with wrapped elements taking precedence in case Velocity was called via the $.fn. extension. */ 5557 return getChain(); 5558 }; 5559 5560 /* Turn Velocity into the animation function, extended with the pre-existing Velocity object. */ 5561 Velocity = $.extend(animate, Velocity); 5562 /* For legacy support, also expose the literal animate method. */ 5563 Velocity.animate = animate; 5564 5565 /************** 5566 Timing 5567 **************/ 5568 5569 /* Ticker function. */ 5570 var ticker = window.requestAnimationFrame || rAFShim; 5571 5572 /* Inactive browser tabs pause rAF, which results in all active animations immediately sprinting to their completion states when the tab refocuses. 5573 To get around this, we dynamically switch rAF to setTimeout (which the browser *doesn't* pause) when the tab loses focus. We skip this for mobile 5574 devices to avoid wasting battery power on inactive tabs. */ 5575 /* Note: Tab focus detection doesn't work on older versions of IE, but that's okay since they don't support rAF to begin with. */ 5576 if (!Velocity.State.isMobile && document.hidden !== undefined) { 5577 document.addEventListener("visibilitychange", function() { 5578 /* Reassign the rAF function (which the global tick() function uses) based on the tab's focus state. */ 5579 if (document.hidden) { 5580 ticker = function(callback) { 5581 /* The tick function needs a truthy first argument in order to pass its internal timestamp check. */ 5582 return setTimeout(function() { callback(true) }, 16); 5583 }; 5584 5585 /* The rAF loop has been paused by the browser, so we manually restart the tick. */ 5586 tick(); 5587 } else { 5588 ticker = window.requestAnimationFrame || rAFShim; 5589 } 5590 }); 5591 } 5592 5593 /************ 5594 Tick 5595 ************/ 5596 5597 /* Note: All calls to Velocity are pushed to the Velocity.State.calls array, which is fully iterated through upon each tick. */ 5598 function tick (timestamp) { 5599 /* An empty timestamp argument indicates that this is the first tick occurence since ticking was turned on. 5600 We leverage this metadata to fully ignore the first tick pass since RAF's initial pass is fired whenever 5601 the browser's next tick sync time occurs, which results in the first elements subjected to Velocity 5602 calls being animated out of sync with any elements animated immediately thereafter. In short, we ignore 5603 the first RAF tick pass so that elements being immediately consecutively animated -- instead of simultaneously animated 5604 by the same Velocity call -- are properly batched into the same initial RAF tick and consequently remain in sync thereafter. */ 5605 if (timestamp) { 5606 /* We ignore RAF's high resolution timestamp since it can be significantly offset when the browser is 5607 under high stress; we opt for choppiness over allowing the browser to drop huge chunks of frames. */ 5608 var timeCurrent = (new Date).getTime(); 5609 5610 /******************** 5611 Call Iteration 5612 ********************/ 5613 5614 /* Iterate through each active call. */ 5615 for (var i = 0, callsLength = Velocity.State.calls.length; i < callsLength; i++) { 5616 /* When a Velocity call is completed, its Velocity.State.calls entry is set to false. Continue on to the next call. */ 5617 if (!Velocity.State.calls[i]) { 5618 continue; 5619 } 5620 5621 /************************ 5622 Call-Wide Variables 5623 ************************/ 5624 5625 var callContainer = Velocity.State.calls[i], 5626 call = callContainer[0], 5627 opts = callContainer[2], 5628 timeStart = callContainer[3], 5629 firstTick = !!timeStart; 5630 5631 /* If timeStart is undefined, then this is the first time that this call has been processed by tick(). 5632 We assign timeStart now so that its value is as close to the real animation start time as possible. 5633 (Conversely, had timeStart been defined when this call was added to Velocity.State.calls, the delay 5634 between that time and now would cause the first few frames of the tween to be skipped since 5635 percentComplete is calculated relative to timeStart.) */ 5636 /* Further, subtract 16ms (the approximate resolution of RAF) from the current time value so that the 5637 first tick iteration isn't wasted by animating at 0% tween completion, which would produce the 5638 same style value as the element's current value. */ 5639 if (!timeStart) { 5640 timeStart = Velocity.State.calls[i][3] = timeCurrent - 16; 5641 } 5642 5643 /* The tween's completion percentage is relative to the tween's start time, not the tween's start value 5644 (which would result in unpredictable tween durations since JavaScript's timers are not particularly accurate). 5645 Accordingly, we ensure that percentComplete does not exceed 1. */ 5646 var percentComplete = Math.min((timeCurrent - timeStart) / opts.duration, 1); 5647 5648 /********************** 5649 Element Iteration 5650 **********************/ 5651 5652 /* For every call, iterate through each of the elements in its set. */ 5653 for (var j = 0, callLength = call.length; j < callLength; j++) { 5654 var tweensContainer = call[j], 5655 element = tweensContainer.element; 5656 5657 /* Check to see if this element has been deleted midway through the animation by checking for the 5658 continued existence of its data cache. If it's gone, skip animating this element. */ 5659 if (!Data(element)) { 5660 continue; 5661 } 5662 5663 var transformPropertyExists = false;
5664 5665 /********************************** 5666 Display & Visibility Toggling 5667 **********************************/ 5668 5669 /* If the display option is set to non-"none", set it upfront so that the element can become visible before tweening begins. 5670 (Otherwise, display's "none" value is set in completeCall() once the animation has completed.) */ 5671 if (opts.display !== undefined && opts.display !== null && opts.display !== "none") { 5672 if (opts.display === "flex") { 5673 var flexValues = [ "-webkit-box", "-moz-box", "-ms-flexbox", "-webkit-flex" ]; 5674 5675 $.each(flexValues, function(i, flexValue) { 5676 CSS.setPropertyValue(element, "display", flexValue); 5677 }); 5678 } 5679 5680 CSS.setPropertyValue(element, "display", opts.display); 5681 } 5682 5683 /* Same goes with the visibility option, but its "none" equivalent is "hidden". */ 5684 if (opts.visibility !== undefined && opts.visibility !== "hidden") { 5685 CSS.setPropertyValue(element, "visibility", opts.visibility); 5686 } 5687 5688 /************************ 5689 Property Iteration 5690 ************************/ 5691 5692 /* For every element, iterate through each property. */ 5693 for (var property in tweensContainer) { 5694 /* Note: In addition to property tween data, tweensContainer contains a reference to its associated element. */ 5695 if (property !== "element") { 5696 var tween = tweensContainer[property], 5697 currentValue, 5698 /* Easing can either be a pre-genereated function or a string that references a pre-registered easing 5699 on the Velocity.Easings object. In either case, return the appropriate easing *function*. */ 5700 easing = Type.isString(tween.easing) ? Velocity.Easings[tween.easing] : tween.easing; 5701 5702 /****************************** 5703 Current Value Calculation 5704 ******************************/ 5705 5706 /* If this is the last tick pass (if we've reached 100% completion for this tween), 5707 ensure that currentValue is explicitly set to its target endValue so that it's not subjected to any rounding. */ 5708 if (percentComplete === 1) { 5709 currentValue = tween.endValue; 5710 /* Otherwise, calculate currentValue based on the current delta from startValue. */ 5711 } else { 5712 currentValue = tween.startValue + ((tween.endValue - tween.startValue) * easing(percentComplete)); 5713 5714 /* If no value change is occurring, don't proceed with DOM updating. */ 5715 if (!firstTick && (currentValue === tween.currentValue)) { 5716 continue; 5717 } 5718 } 5719 5720 tween.currentValue = currentValue; 5721 5722 /****************** 5723 Hooks: Part I 5724 ******************/ 5725 5726 /* For hooked properties, the newly-updated rootPropertyValueCache is cached onto the element so that it can be used 5727 for subsequent hooks in this call that are associated with the same root property. If we didn't cache the updated 5728 rootPropertyValue, each subsequent update to the root property in this tick pass would reset the previous hook's 5729 updates to rootPropertyValue prior to injection. A nice performance byproduct of rootPropertyValue caching is that 5730 subsequently chained animations using the same hookRoot but a different hook can use this cached rootPropertyValue. */ 5731 if (CSS.Hooks.registered[property]) { 5732 var hookRoot = CSS.Hooks.getRoot(property), 5733 rootPropertyValueCache = Data(element).rootPropertyValueCache[hookRoot]; 5734 5735 if (rootPropertyValueCache) { 5736 tween.rootPropertyValue = rootPropertyValueCache; 5737 } 5738 } 5739 5740 /***************** 5741 DOM Update 5742 *****************/ 5743 5744 /* setPropertyValue() returns an array of the property name and property value post any normalization that may have been performed. */ 5745 /* Note: To solve an IE<=8 positioning bug, the unit type is dropped when setting a property value of 0. */ 5746 var adjustedSetData = CSS.setPropertyValue(element, /* SET */ 5747 property, 5748 tween.currentValue + (parseFloat(currentValue) === 0 ? "" : tween.unitType), 5749 tween.rootPropertyValue, 5750 tween.scrollData); 5751 5752 /******************* 5753 Hooks: Part II 5754 *******************/ 5755 5756 /* Now that we have the hook's updated rootPropertyValue (the post-processed value provided by adjustedSetData), cache it onto the element. */ 5757 if (CSS.Hooks.registered[property]) { 5758 /* Since adjustedSetData contains
5758normalized data ready for DOM updating, the rootPropertyValue needs to be re-extracted from its normalized form. ?? */ 5759 if (CSS.Normalizations.registered[hookRoot]) { 5760 Data(element).rootPropertyValueCache[hookRoot] = CSS.Normalizations.registered[hookRoot]("extract", null, adjustedSetData[1]); 5761 } else { 5762 Data(element).rootPropertyValueCache[hookRoot] = adjustedSetData[1]; 5763 } 5764 } 5765 5766 /*************** 5767 Transforms 5768 ***************/ 5769 5770 /* Flag whether a transform property is being animated so that flushTransformCache() can be triggered once this tick pass is complete. */ 5771 if (adjustedSetData[0] === "transform") { 5772 transformPropertyExists = true; 5773 } 5774 } 5775 } 5776 5777 /**************** 5778 mobileHA 5779 ****************/ 5780 5781 /* If mobileHA is enabled, set the translate3d transform to null to force hardware acceleration. 5782 It's safe to override this property since Velocity doesn't actually support its animation (hooks are used in its place). */ 5783 if (opts.mobileHA) { 5784 /* Don't set the null transform hack if we've already done so. */ 5785 if (Data(element).transformCache.translate3d === undefined) { 5786 /* All entries on the transformCache object are later concatenated into a single transform string via flushTransformCache(). */ 5787 Data(element).transformCache.translate3d = "(0px, 0px, 0px)"; 5788 5789 transformPropertyExists = true; 5790 } 5791 } 5792 5793 if (transformPropertyExists) { 5794 CSS.flushTransformCache(element); 5795 } 5796 } 5797 5798 /* The non-"none" display value is only applied to an element once -- when its associated call is first ticked through. 5799 Accordingly, it's set to false so that it isn't re-processed by this call in the next tick. */ 5800 if (opts.display !== undefined && opts.display !== "none") { 5801 Velocity.State.calls[i][2].display = false; 5802 } 5803 if (opts.visibility !== undefined && opts.visibility !== "hidden") { 5804 Velocity.State.calls[i][2].visibility = false; 5805 } 5806 5807 5808 /* Pass the elements and the timing data (percentComplete, msRemaining, and timeStart) into the progress callback. */ 5809 if (opts.progress) { 5810 opts.progress.call(callContainer[1], 5811 callContainer[1], 5812 percentComplete, 5813 Math.max(0, (timeStart + opts.duration) - timeCurrent), 5814 timeStart); 5815 } 5816 5817 /* If this call has finished tweening, pass its index to completeCall() to handle call cleanup. */ 5818 if (percentComplete === 1) { 5819 completeCall(i); 5820 } 5821 } 5822 } 5823 5824 /* Note: completeCall() sets the isTicking flag to false when the last call on Velocity.State.calls has completed. */ 5825 if (Velocity.State.isTicking) { 5826 ticker(tick); 5827 } 5828 } 5829 5830 /********************** 5831 Call Completion 5832 **********************/ 5833 5834 /* Note: Unlike tick(), which processes all active calls at once, call completion is handled on a per-call basis. */ 5835 function completeCall (callIndex, isStopped) { 5836 /* Ensure the call exists. */ 5837 if (!Velocity.State.calls[callIndex]) { 5838 return false; 5839 } 5840 5841 /* Pull the metadata from the call. */ 5842 var call = Velocity.State.calls[callIndex][0], 5843 elements = Velocity.State.calls[callIndex][1], 5844 opts = Velocity.State.calls[callIndex][2], 5845 resolver = Velocity.State.calls[callIndex][4]; 5846 5847 var remainingCallsExist = false;
5848 5849 /************************* 5850 Element Finalization 5851 *************************/ 5852 5853 for (var i = 0, callLength = call.length; i < callLength; i++) { 5854 var element = call[i].element; 5855 5856 /* If the user set display to "none" (intending to hide the element), set it now that the animation has completed. */ 5857 /* Note: display:none isn't set when calls are manually stopped (via Velocity("stop"). */ 5858 /* Note: Display gets ignored with "reverse" calls and infinite loops, since this behavior would be undesirable. */ 5859 if (!isStopped && !opts.loop) { 5860 if (opts.display === "none") { 5861 CSS.setPropertyValue(element, "display", opts.display); 5862 } 5863 5864 if (opts.visibility === "hidden") { 5865 CSS.setPropertyValue(element, "visibility", opts.visibility); 5866 } 5867 } 5868 5869 /* If the element's queue is empty (if only the "inprogress" item is left at position 0) or if its queue is about to run 5870 a non-Velocity-initiated entry, turn off the isAnimating flag. A non-Velocity-initiatied queue entry's logic might alter 5871 an element's CSS values and thereby cause Velocity's cached value data to go stale. To detect if a queue entry was initiated by Velocity, 5872 we check for the existence of our special Velocity.queueEntryFlag declaration, which minifiers won't rename since the flag 5873 is assigned to jQuery's global $ object and thus exists out of Velocity's own scope. */ 5874 if (opts.loop !== true && ($.queue(element)[1] === undefined || !/\.velocityQueueEntryFlag/i.test($.queue(element)[1]))) { 5875 /* The element may have been deleted. Ensure that its data cache still exists before acting on it. */ 5876 if (Data(element)) { 5877 Data(element).isAnimating = false; 5878 /* Clear the element's rootPropertyValueCache, which will become stale. */ 5879 Data(element).rootPropertyValueCache = {}; 5880 5881 var transformHAPropertyExists = false; 5882 /* If any 3D transform subproperty is at its default value (regardless of unit type), remove it. */ 5883 $.each(CSS.Lists.transforms3D, function(i, transformName) { 5884 var defaultValue = /^scale/.test(transformName) ? 1 : 0, 5885 currentValue = Data(element).transformCache[transformName]; 5886 5887 if (Data(element).transformCache[transformName] !== undefined && new RegExp("^\\(" + defaultValue + "[^.]").test(currentValue)) { 5888 transformHAPropertyExists = true; 5889 5890 delete Data(element).transformCache[transformName]; 5891 } 5892 }); 5893 5894 /* Mobile devices have hardware acceleration removed at the end of the animation in order to avoid hogging the GPU's memory. */ 5895 if (opts.mobileHA) { 5896 transformHAPropertyExists = true; 5897 delete Data(element).transformCache.translate3d; 5898 } 5899 5900 /* Flush the subproperty removals to the DOM. */ 5901 if (transformHAPropertyExists) { 5902 CSS.flushTransformCache(element); 5903 } 5904 5905 /* Remove the "velocity-animating" indicator class. */ 5906 CSS.Values.removeClass(element, "velocity-animating"); 5907 } 5908 } 5909 5910 /********************* 5911 Option: Complete 5912 *********************/ 5913 5914 /* Complete is fired once per call (not once per element) and is passed the full raw DOM element set as both its context and its first argument. */ 5915 /* Note: Callbacks aren't fired when calls are manually stopped (via Velocity("stop"). */ 5916 if (!isStopped && opts.complete && !opts.loop && (i === callLength - 1)) { 5917 /* We throw callbacks in a setTimeout so that thrown errors don't halt the execution of Velocity itself. */ 5918 try { 5919 opts.complete.call(elements, elements); 5920 } catch (error) { 5921 setTimeout(function() { throw error; }, 1); 5922 } 5923 } 5924 5925 /********************** 5926 Promise Resolving 5927 **********************/ 5928 5929 /* Note: Infinite loops don't return promises. */ 5930 if (resolver && opts.loop !== true) { 5931 resolver(elements); 5932 } 5933 5934 /**************************** 5935 Option: Loop (Infinite) 5936 ****************************/ 5937 5938 if (opts.loop === true && !isStopped) { 5939 /* If a rotateX/Y/Z property is being animated to 360 deg with loop:true, swap tween start/end values to enable 5940 continuous iterative rotation looping. (Otherise, the element would just rotate back and forth.) */ 5941 $.each(Data(element).tweensContainer, function(propertyName, tweenContainer) { 5942 if (/^rotate/.test(propertyName) && parseFloat(tweenContainer.endValue) === 360) { 5943 tweenContainer.endValue = 0; 5944 tweenContainer.startValue = 360; 5945 } 5946 }); 5947 5948 Velocity(element, "reverse", { loop: true, delay: opts.delay }); 5949 } 5950 5951 /*************** 5952 Dequeueing 5953 ***************/ 5954 5955 /* Fire the next call in the queue so long as this call's queue wasn't set to false (to trigger a parallel animation), 5956 which would have already caused the next call to fire. Note: Even if the end of the animation queue has been reached, 5957 $.dequeue() must still be called in order to completely clear jQuery's animation queue. */ 5958 if (opts.queue !== false) { 5959 $.dequeue(element, opts.queue); 5960 } 5961 } 5962 5963 /************************ 5964 Calls Array Cleanup 5965 ************************/ 5966 5967 /* Since this call is complete, set it to false so that the rAF tick skips it. This array is later compacted via compactSparseArray(). 5968 (For performance reasons, the call is set to false instead of being deleted from the array: http://www.html5rocks.com/en/tutorials/speed/v8/) */ 5969 Velocity.State.calls[callIndex] = false;
5970 5971 /* Iterate through the calls array to determine if this was the final in-progress animation. 5972 If so, set a flag to end ticking and clear the calls array. */ 5973 for (var j = 0, callsLength = Velocity.State.calls.length; j < callsLength; j++) { 5974 if (Velocity.State.calls[j] !== false) { 5975 remainingCallsExist = true; 5976 5977 break; 5978 } 5979 } 5980 5981 if (remainingCallsExist === false) { 5982 /* tick() will detect this flag upon its next iteration and subsequently turn itself off. */ 5983 Velocity.State.isTicking = false; 5984 5985 /* Clear the calls array so that its length is reset. */ 5986 delete Velocity.State.calls; 5987 Velocity.State.calls = []; 5988 } 5989 } 5990 5991 /****************** 5992 Frameworks 5993 ******************/ 5994 5995 /* Both jQuery and Zepto allow their $.fn object to be extended to allow wrapped elements to be subjected to plugin calls. 5996 If either framework is loaded, register a "velocity" extension pointing to Velocity's core animate() method. Velocity 5997 also registers itself onto a global container (window.jQuery || window.Zepto || window) so that certain features are 5998 accessible beyond just a per-element scope. This master object contains an .animate() method, which is later assigned to $.fn 5999 (if jQuery or Zepto are present). Accordingly, Velocity can both act on wrapped DOM elements and stand alone for targeting raw DOM elements. */ 6000 global.Velocity = Velocity; 6001 6002 if (global !== window) { 6003 /* Assign the element function to Velocity's core animate() method. */ 6004 global.fn.velocity = animate; 6005 /* Assign the object function's defaults to Velocity's global defaults object. */ 6006 global.fn.velocity.defaults = Velocity.defaults; 6007 } 6008 6009 /*********************** 6010 Packaged Redirects 6011 ***********************/ 6012 6013 /* slideUp, slideDown */ 6014 $.each([ "Down", "Up" ], function(i, direction) { 6015 Velocity.Redirects["slide" + direction] = function (element, options, elementsIndex, elementsSize, elements, promiseData) { 6016 var opts = $.extend({}, options), 6017 begin = opts.begin, 6018 complete = opts.complete, 6019 computedValues = { height: "", marginTop: "", marginBottom: "", paddingTop: "", paddingBottom: "" }, 6020 inlineValues = {}; 6021 6022 if (opts.display === undefined) { 6023 /* Show the element before slideDown begins and hide the element after slideUp completes. */ 6024 /* Note: Inline elements cannot have dimensions animated, so they're reverted to inline-block. */ 6025 opts.display = (direction === "Down" ? (Velocity.CSS.Values.getDisplayType(element) === "inline" ? "inline-block" : "block") : "none"); 6026 } 6027 6028 opts.begin = function() { 6029 /* If the user passed in a begin callback, fire it now. */ 6030 begin && begin.call(elements, elements); 6031 6032 /* Cache the elements' original vertical dimensional property values so that we can animate back to them. */ 6033 for (var property in computedValues) { 6034 /* Cache all inline values, we reset to upon animation completion. */ 6035 inlineValues[property] = element.style[property]; 6036 6037 /* For slideDown, use forcefeeding to animate all vertical properties from 0. For slideUp, 6038 use forcefeeding to start from computed values and animate down to 0. */ 6039 var propertyValue = Velocity.CSS.getPropertyValue(element, property); 6040 computedValues[property] = (direction === "Down") ? [ propertyValue, 0 ] : [ 0, propertyValue ]; 6041 } 6042 6043 /* Force vertical overflow content to clip so that sliding works as expected. */ 6044 inlineValues.overflow = element.style.overflow; 6045 element.style.overflow = "hidden"; 6046 } 6047 6048 opts.complete = function() { 6049 /* Reset element to its pre-slide inline values once its slide animation is complete. */ 6050 for (var property in inlineValues) { 6051 element.style[property] = inlineValues[property]; 6052 } 6053 6054 /* If the user passed in a complete callback, fire it now. */ 6055 complete && complete.call(elements, elements); 6056 promiseData && promiseData.resolver(elements); 6057 }; 6058 6059 Velocity(element, computedValues, opts); 6060 }; 6061 }); 6062 6063 /* fadeIn, fadeOut */ 6064 $.each([ "In", "Out" ], function(i, direction) { 6065 Velocity.Redirects["fade" + direction] = function (element, options, elementsIndex, elementsSize, elements, promiseData) { 6066 var opts = $.extend({}, options), 6067 propertiesMap = { opacity: (direction === "In") ? 1 : 0 },
6068 originalComplete = opts.complete; 6069 6070 /* Since redirects are triggered individually for each element in the animated set, avoid repeatedly triggering 6071 callbacks by firing them only when the final element has been reached. */ 6072 if (elementsIndex !== elementsSize - 1) { 6073 opts.complete = opts.begin = null; 6074 } else { 6075 opts.complete = function() { 6076 if (originalComplete) { 6077 originalComplete.call(elements, elements); 6078 } 6079 6080 promiseData && promiseData.resolver(elements); 6081 } 6082 } 6083 6084 /* If a display was passed in, use it. Otherwise, default to "none" for fadeOut or the element-specific default for fadeIn. */ 6085 /* Note: We allow users to pass in "null" to skip display setting altogether. */ 6086 if (opts.display === undefined) { 6087 opts.display = (direction === "In" ? "auto" : "none"); 6088 } 6089 6090 Velocity(this, propertiesMap, opts); 6091 }; 6092 }); 6093 6094 return Velocity; 6095}((window.jQuery || window.Zepto || window), window, document); 6096})); 6097 6098/****************** 6099 Known Issues 6100******************/ 6101 6102/* The CSS spec mandates that the translateX/Y/Z transforms are %-relative to the element itself -- not its parent. 6103Velocity, however, doesn't make this distinction. Thus, converting to or from the % unit with these subproperties 6104will produce an inaccurate conversion value. The same issue exists with the cx/cy attributes of SVG circles and ellipses. */ 6105; 6106(function ($, window, undefined) { 6107 6108 // /* ====== SHARED VARS - jQuery ====== */ 6109 // These depend on jQuery 6110 var $window = $(window), 6111 windowHeight = $window.height(), 6112 windowWidth = $window.width(); 6113 6114 /** 6115 * Detect browser size and remember it in global variables 6116 */ 6117 6118 function browserSize() { 6119 wh = $window.height(); 6120 ww = $window.width(); 6121 dh = $(document).height(); 6122 ar = ww / wh; 6123 } 6124 6125 6126 /** 6127 * Detect what platform are we on (browser, mobile, etc) 6128 */ 6129 6130 function platformDetect() { 6131 $.support.touch = 'ontouchend' in document; 6132 var navUA = navigator.userAgent.toLowerCase(), 6133 navPlat = navigator.platform.toLowerCase(); 6134 6135 var isiPhone = navPlat.indexOf("iphone"), 6136 isiPod = navPlat.indexOf("ipod"), 6137 isAndroidPhone = navPlat.indexOf("android"), 6138 safari = (navUA.indexOf('safari') != -1 && navUA.indexOf('chrome') == -1) ? true : false, 6139 svgSupport = (window.SVGAngle) ? true : false, 6140 svgSupportAlt = (document.implementation.hasFeature("http://www.w3.org/TR/SVG11/feature#BasicStructure", "1.1")) ? true : false, 6141 ff3x = (/gecko/i.test(navUA) && /rv:1.9/i.test(navUA)) ? true : false; 6142 6143 ieMobile = navigator.userAgent.match(/Windows Phone/i) ? true : false; 6144 phone = (isiPhone > -1 || isiPod > -1 || isAndroidPhone > -1) ? true : false; 6145 touch = $.support.touch ? true : false; 6146 6147 if (touch) $html.addClass('touch'); 6148 6149 if (ieMobile) $html.addClass('is--winmob'); 6150 if (is_android) $html.addClass('is--ancient-android'); 6151 6152 if (safari) $body.addClass('safari'); 6153 if (phone) $body.addClass('phone'); 6154 } 6155 6156 // /* ====== SHARED VARS ====== */ 6157 // These do not depend on jQuery 6158 var phone, touch, wh, ww, dh, ar, fonts; 6159 6160 var ua = navigator.userAgent; 6161 var winLoc = window.location.toString(); 6162 6163 var is_webkit = ua.match(/webkit/i); 6164 var is_firefox = ua.match(/gecko/i); 6165 var is_newer_ie = ua.match(/msie (9|([1-9][0-9]))/i); 6166 var is_older_ie = ua.match(/msie/i) && !is_newer_ie; 6167 var is_ancient_ie = ua.match(/msie 6/i); 6168 var is_mobile = ua.match(/mobile/i);
6169 var is_OSX = (ua.match(/(iPad|iPhone|iPod|Macintosh)/g) ? true : false); 6170 6171 var nua = navigator.userAgent; 6172 var is_android = ((nua.indexOf('Mozilla/5.0') !== -1 && nua.indexOf('Android ') !== -1 && nua.indexOf('AppleWebKit') !== -1) && nua.indexOf('Chrome') === -1); 6173 6174 var useTransform = true; 6175 var use2DTransform = (ua.match(/msie 9/i) || winLoc.match(/transform\=2d/i)); 6176 var transform; 6177 6178 var $html = $('html'); 6179 var $body = $('body'); 6180 6181 // setting up transform prefixes 6182 var prefixes = { 6183 webkit: 'webkitTransform', 6184 firefox: 'MozTransform', 6185 ie: 'msTransform', 6186 w3c: 'transform' 6187 }; 6188 6189 if (useTransform) { 6190 if (is_webkit) { 6191 transform = prefixes.webkit; 6192 } else if (is_firefox) { 6193 transform = prefixes.firefox; 6194 } else if (is_newer_ie) { 6195 transform = prefixes.ie; 6196 } 6197 } 6198 // /* ====== Masonry Logic ====== */ 6199 (function () { 6200 6201 var $container = $('.archive__grid'), 6202 $blocks = $container.children().addClass('post--animated post--loaded'), 6203 slices = $blocks.first().children().length; 6204 6205 var observerConfig = { attributes: false, childList: true, characterData: true }, 6206 observer; 6207 6208 if ( "MutationObserver" in window ) { 6209 observer = new MutationObserver( function( mutations ) { 6210 $container.masonry( 'layout' ); 6211 } ); 6212 } 6213 6214 // initialize masonry after the images have loaded 6215 $container.imagesLoaded(function () { 6216 6217 // prepare hover animations 6218 if (!$html.hasClass('touch')) 6219 $blocks.addHoverAnimation(); 6220 6221 // initialize masonry 6222 $container.masonry({ 6223 isAnimated: false, 6224 itemSelector: '.grid__item', 6225 transitionDuration: 0, 6226 hiddenStyle: { 6227 opacity: 0 6228 } 6229 }); 6230 6231 /** 6232 * function used to display cards with a simple fade in transition 6233 */ 6234 6235 function showBlocks($blocks) { 6236 6237 // use different delays for each card to stagger them 6238 $blocks.each(function (i, obj) { 6239 6240 var $block = $(obj), 6241 duration = 200 + slices * 0.1; 6242 6243 $block.velocity({ 6244 translateY: 40 6245 }, { 6246 duration: 0 6247 }); 6248 setTimeout(function () { 6249 $block.velocity({ 6250 translateY: 0 6251 }, { 6252 easing: "easeOutQuad", 6253 duration: 100 6254 }); 6255 6256 $block.children().each(function (j, child) { 6257 var $child = $(child), 6258 timeout = (j + 1) * 100; 6259 6260 $child.velocity({ 6261 opacity: 0, 6262 translateY: 40 6263 }, { 6264 duration: 0 6265 }); 6266 6267 $child.velocity({ 6268 opacity: 1, 6269 translateY: 0 6270 }, { 6271 duration: 300, 6272 delay: timeout 6273 }); 6274 }); 6275 6276 setTimeout(function () { 6277 $block.toggleClass('sticky--bg', $block.hasClass('sticky')); 6278 $block.addClass('post--visible'); 6279 }, 300); 6280 6281 }, i * 200); 6282 }); 6283 } 6284 6285 // animate cards in 6286 showBlocks($blocks); 6287 6288 $blocks.each( function( i, obj ) { 6289 if ( typeof observer !== "undefined" ) { 6290 observer.observe( obj, observerConfig ); 6291 } 6292 } ); 6293 6294 // update the masonry layout on window.resize 6295 $(window).smartresize(function () { 6296 $container.masonry('layout'); 6297 }); 6298 6299 $(window).load(function () { 6300 $container.masonry('layout'); 6301 }); 6302 6303 // handle behavior for infinite scroll 6304 $(document.body).on('post-load', function () { 6305 6306 // figure out which are the new loaded posts 6307 var $newBlocks = $('.archive__grid').children().not('.post--loaded').addClass('post--loaded'); 6308 6309 // when images have loaded take care of the layout, prepare hover animations, and animate cards in 6310 $newBlocks.imagesLoaded( function() { 6311 $container.masonry( 'appended', $newBlocks, true ).masonry( 'layout' ); 6312 if ( ! $html.hasClass( 'touch' ) ) { 6313 $newBlocks.addHoverAnimation(); 6314 } 6315 showBlocks( $newBlocks ); 6316 } ); 6317 6318 if ( typeof observer !== "undefined" ) { 6319 $newBlocks.each( function(i, obj ) { 6320 observer.observe( obj, observerConfig ); 6321 } ); 6322 } 6323 }); 6324 }); 6325 })(); 6326 6327 // /* ====== Navigation Logic ====== */ 6328 var $nav = $('.nav--main'), 6329 $navTrigger = $('.navigation__trigger'), 6330 $navContainer = $('.main-navigation'), 6331 navTop = (typeof $navContainer.offset() !== 'undefined') ? $navContainer.offset().top : 0, 6332 navLeft = (typeof $navContainer.offset() !== 'undefined') ? $navContainer.offset().left : 0, 6333 navWidth = $nav.outerWidth(), 6334 containerWidth = $navContainer.outerWidth(),
6335 navHeight = $nav.outerHeight(), 6336 $toolbar = $('.toolbar'), 6337 isOpen = false, 6338 pageTop = $('#page').offset().top, 6339 pageWidth = $('#page').width(), 6340 pageLeft = $('#page').offset().left, 6341 sticked = false; 6342 6343 /** 6344 * bind toggling the navigation drawer to click and touchstart 6345 *in order to get rid of the 300ms delay on touch devices we use the touchstart event 6346 */ 6347 var triggerEvents = 'click touchstart'; 6348 if (is_android) triggerEvents = 'click'; 6349 6350 // Set the Navigation Container Height 6351 $navContainer.height(navHeight); 6352 6353 $navTrigger.on(triggerEvents, function (e) { 6354 // but we still have to prevent the default behavior of the touchstart event 6355 // because this way we're making sure the click event won't fire anymore 6356 e.preventDefault(); 6357 e.stopPropagation(); 6358 6359 isOpen = !isOpen; 6360 $('body').toggleClass('nav--is-open'); 6361 6362 var offset; 6363 6364 navWidth = $nav.outerWidth(); 6365 6366 if ($('body').hasClass('rtl')) { 6367 offset = -1 * navWidth; 6368 } else { 6369 offset = navWidth; 6370 } 6371 6372 if (!is_android) { 6373 if (!isOpen) { 6374 6375 $([$nav, $navTrigger]).each(function (i, obj) { 6376 $(obj).velocity({ 6377 translateX: 0, 6378 translateZ: 0.01 6379 }, {
6380 duration: 300, 6381 easing: "easeInQuart", 6382 begin: function () { 6383 $navContainer.removeClass('shadow'); 6384 } 6385 }); 6386 }); 6387 6388 } else { 6389 6390 $([$nav, $navTrigger]).each(function (i, obj) { 6391 $(obj).velocity({ 6392 translateX: offset, 6393 translateZ: 0.01 6394 }, { 6395 easing: "easeOutCubic", 6396 duration: 300, 6397 complete: function () { 6398 $navContainer.addClass('shadow'); 6399 } 6400 }); 6401 }); 6402 } 6403 } 6404 }); 6405 6406 var HandleParentMenuItems = (function() { 6407 // Handle parent menu items on tablet in landscape mode; 6408 // use case: normal, horizontal menu, touch events, 6409 // sub menus are not visible. 6410 function handleParentMenuItems() { 6411 // Make sure there are no open menu items 6412 $('[class*="has-children"]').removeClass('hover'); 6413 6414 $('[class*="has-children"] > a').each( function(){ 6415 // Add a class so we know the items to handle 6416 $(this).addClass('prevent-one').attr('hasbeenclicked', false); 6417 }); 6418 6419 $('a.prevent-one').on('click', function(e) { 6420 if( $( this ).attr( 'hasbeenclicked' ) == "false" ) { 6421 e.preventDefault(); 6422 e.stopPropagation(); 6423 6424 $(this).attr('hasbeenclicked', true); 6425 6426 // Open the sub menu of this parent item 6427 $(this).parent().addClass('hover') 6428 // When a parent menu item is activated, 6429 // close other menu items on the same level 6430 .siblings().removeClass('hover').find('> a.prevent-one').attr('hasbeenclicked', false); 6431 } 6432 }); 6433 } 6434 6435 // Restore the original behaviour when in portrait mode; 6436 // use case: vertical menu, all menu items are visible. 6437 function unHandleParentMenuItems() { 6438 $('a.prevent-one').each(function(){ 6439 // Unbind te click handler 6440 $(this).unbind(); 6441 }); 6442 } 6443 6444 // When a sub menu is open, close it by a touch on 6445 // any other part of the viewport than navigation. 6446 // use case: normal, horizontal menu, touch events, 6447 // sub menus are not visible. 6448 function bindOuterNavClick() { 6449 $('body').on( 'touchstart', function (e) { 6450 var container = $('.nav--main'); 6451 6452 if (!container.is(e.target) // if the target of the click isn't the container... 6453 && container.has(e.target).length === 0) // ... nor a descendant of the container 6454 { 6455 $('[class*="has-children"]').removeClass('hover'); 6456 $('a.prevent-one').attr('hasbeenclicked', false); 6457 } 6458 }); 6459 } 6460 6461 return { 6462 handle: handleParentMenuItems, 6463 unHandle: unHandleParentMenuItems, 6464 bindOuterNavClick: bindOuterNavClick 6465 } 6466 }()); 6467 6468 /** 6469 * function used to make the navigation bar on big screens 6470 * sticky or static depending on the scroll position 6471 */ 6472 6473 function updateStickyMenu() { 6474 6475 // pin 6476 if (latestKnownScrollY + pageTop > navTop && !sticked) { 6477 $nav.css({ 6478 position: 'fixed', 6479 top: pageTop, 6480 left: pageLeft, 6481 width: pageWidth 6482 }); 6483 6484 sticked = true; 6485 return; 6486 } 6487 6488 // unpin 6489 if (latestKnownScrollY + pageTop <= navTop && sticked) { 6490 6491 $nav.css({ 6492 position: '', 6493 left: '', 6494 top: '', 6495 width: '' 6496 }); 6497 6498 sticked = false; 6499 } 6500 6501 } 6502 6503 /** 6504 * because we hard-code properties on the navigation bar when we stick it at the top of the window 6505 * we need to update some properties on window resize 6506 */ 6507 6508 function refreshNavigation() { 6509 navTop = (typeof $navContainer.offset() !== 'undefined') ? $navContainer.offset().top : 0; 6510 navLeft = (typeof $navContainer.offset() !== 'undefined') ? $navContainer.offset().left : 0; 6511 containerWidth = $navContainer.outerWidth(); 6512 navWidth = $nav.outerWidth(); 6513 navHeight = $nav.outerHeight(); 6514 pageTop = $('#page').offset().top; 6515 pageWidth = $('#page').width(); 6516 6517 $navContainer.height(navHeight); 6518 6519 if (sticked) { 6520 $nav.velocity({ 6521 width: pageWidth 6522 }, { 6523 easing: "easeOutCubic", 6524 duration: 200 6525 }); 6526 } 6527 6528 if (isOpen) { 6529 $([$nav, $navTrigger]).each(function (i, obj) { 6530 $(obj).velocity({ 6531 translateX: navWidth 6532 }, { 6533 easing: "easeOutQuint", 6534 duration: 200 6535 }); 6536 }); 6537 } 6538 6539 navWidth = containerWidth; 6540 } 6541 6542 /** 6543 * cardHover jQuery plugin 6544 * 6545 * we need to create a jQuery plugin so we can easily create the hover animations on the archive 6546 * both an window.load and on jetpack's infinite scroll 'post-load' 6547 */ 6548 $.fn.addHoverAnimation = function () { 6549 6550 return this.each(function (i, obj) { 6551 6552 var $obj = $(obj), 6553 $handler = $obj.find('.hover__handler'), 6554 $hover = $obj.find('.hover'); 6555 6556 // if we don't have have elements that need to be animated return 6557 if (!$hover.length) { 6558 return; 6559 } 6560 6561 var $letter = $hover.find('.hover__letter'), 6562 letterWidth = $letter.outerWidth, 6563 letterHeight = $letter.outerHeight; 6564 6565 $hover.find('.hover__bg').velocity({ 6566 opacity: 0 6567 }, { 6568 duration: 0 6569 }); 6570 $hover.find('.hover__line').velocity({ 6571 height: 0 6572 }, { 6573 duration: 0 6574 }); 6575 $hover.find('.hover__more').velocity({ 6576 opacity: 0 6577 }, { 6578 duration: 0 6579 }); 6580 $hover.find('.hover__letter').velocity({ 6581 opacity: 0, 6582 translateX: '-50%', 6583 translateY: '-50%', 6584 scaleX: 1.25, 6585 scaleY: 1.25 6586 }, { 6587 duration: 0 6588 }); 6589 6590 // bind the tweens we created above to mouse events accordingly, through hoverIntent to avoid flickering 6591 if ($handler.length) { 6592 $handler.hoverIntent({ 6593 over: animateHoverIn, 6594 out: animateHoverOut, 6595 timeout: 100, 6596 interval: 50 6597 }); 6598 } 6599 6600 function animateHoverIn() { 6601 6602 $hover.find('.hover__bg').velocity("reverse", { 6603 easing: "easeOutQuart", 6604 duration: 500 6605 }); 6606 $hover.find('.hover__line').velocity("reverse", { 6607 easing: "easeOutQuart", 6608 duration: 350, 6609 delay: 150 6610 }); 6611 $hover.find('.hover__more').velocity("reverse", { 6612 easing: "easeOutQuart", 6613 duration: 350, 6614 delay: 150 6615 }); 6616 $hover.find('.hover__letter').velocity({ 6617 scaleX: 1, 6618 scaleY: 1, 6619 opacity: 0.2, 6620 translateX: '-50%', 6621 translateY: '-50%' 6622 }, { 6623 easing: "easeOutQuart", 6624 duration: 350, 6625 delay: 150 6626 }); 6627 6628 } 6629 6630 function animateHoverOut() { 6631 6632 var letterHeight = $hover.find('.hover__letter-mask').outerHeight(); 6633 6634 $hover.find('.hover__letter').velocity({ 6635 opacity: 0, 6636 translateX: '-50%', 6637 translateY: '-50%', 6638 scaleX: 1.25, 6639 scaleY: 1.25 6640 }, { 6641 easing: "easeOutQuart", 6642 duration: 350, 6643 delay: 0 6644 }); 6645 $hover.find('.hover__more').velocity("reverse", { 6646 duration: 350, 6647 delay: 0 6648 }); 6649 $hover.find('.hover__line').velocity("reverse", { 6650 duration: 350, 6651 delay: 0 6652 }); 6653 $hover.find('.hover__bg').velocity("reverse", { 6654 duration: 350, 6655 delay: 0 6656 }); 6657 6658 } 6659 6660 }); 6661 }; 6662 6663 // /* ====== Search Overlay Logic ====== */ 6664 (function () { 6665 6666 var isOpen = false,
6667 $overlay = $('.overlay--search'); 6668 6669 // update overlay position (if it's open) on window.resize 6670 $(window).on('smartresize', function () { 6671 6672 windowWidth = $(window).outerWidth(); 6673 6674 if (isOpen) { 6675 $overlay.velocity({ 6676 translateX: -1 * windowWidth 6677 }, { 6678 duration: 200, 6679 easing: "easeInCubic" 6680 }); 6681 } 6682 6683 }); 6684 6685 /** 6686 * dismiss overlay 6687 */ 6688 6689 function closeOverlay() { 6690 6691 if (!isOpen) { 6692 return; 6693 } 6694 6695 var offset; 6696 6697 if ($('body').hasClass('rtl')) { 6698 offset = windowWidth 6699 } else { 6700 offset = -1 * windowWidth 6701 } 6702 6703 // we don't need a timeline for this animations so we'll use a simple tween between two states 6704 $overlay.velocity({ 6705 translateX: offset 6706 }, { 6707 duration: 0 6708 }); 6709 $overlay.velocity({ 6710 translateX: 0 6711 }, { 6712 duration: 300, 6713 easing: "easeInCubic" 6714 }); 6715 6716 // remove focus from the search field 6717 $overlay.find('input').blur(); 6718 6719 isOpen = false; 6720 } 6721 6722 function escOverlay(e) { 6723 if (e.keyCode == 27) { 6724 closeOverlay(); 6725 } 6726 } 6727 6728 // create animation and run it on 6729 $('.nav__item--search').on('click touchstart', function (e) { 6730 // prevent default behavior and stop propagation 6731 e.preventDefault(); 6732 e.stopPropagation(); 6733 6734 // if through some kind of sorcery the navigation drawer is already open return 6735 if (isOpen) { 6736 return; 6737 } 6738 6739 var offset; 6740 6741 if ($('body').hasClass('rtl')) { 6742 offset = windowWidth 6743 } else { 6744 offset = -1 * windowWidth 6745 } 6746 6747 // automatically focus the search field so the user can type right away 6748 $overlay.find('input').focus(); 6749 6750 $overlay.velocity({ 6751 translateX: 0 6752 }, { 6753 duration: 0 6754 }).velocity({ 6755 translateX: offset 6756 }, { 6757 duration: 300, 6758 easing: "easeOut", 6759 queue: false 6760 }); 6761 6762 $('.search-form').velocity({ 6763 translateX: 300, 6764 opacity: 0 6765 }, { 6766 duration: 0 6767 }).velocity({ 6768 opacity: 1 6769 }, { 6770 duration: 200, 6771 easing: "easeOutQuad", 6772 delay: 200, 6773 queue: false 6774 }).velocity({ 6775 translateX: 0 6776 }, { 6777 duration: 400, 6778 easeing: [0.175, 0.885, 0.320, 1.275], 6779 delay: 50, 6780 queue: false 6781 }); 6782 6783 $('.overlay__wrapper > p').velocity({ 6784 translateX: 200, 6785 opacity: 0 6786 }, { 6787 duration: 0 6788 }).velocity({ 6789 opacity: 1 6790 }, { 6791 duration: 400, 6792 easing: "easeOutQuad", 6793 delay: 350, 6794 queue: false 6795 }).velocity({ 6796 translateX: 0 6797 }, { 6798 duration: 400, 6799 easing: [0.175, 0.885, 0.320, 1.275], 6800 delay: 250, 6801 queue: false 6802 }); 6803 6804 // bind overlay dismissal to escape key 6805 $(document).on('keyup', escOverlay); 6806 6807 isOpen = true; 6808 }); 6809 6810 // create function to hide the search overlay and bind it to the click event 6811 $('.overlay__close').on('click touchstart', function (e) { 6812 6813 e.preventDefault(); 6814 e.stopPropagation(); 6815 6816 closeOverlay(); 6817 6818 // unbind overlay dismissal from escape key 6819 $(document).off('keyup', escOverlay); 6820 6821 }); 6822 6823 })(); 6824 6825 // /* ====== Smart Resize Logic ====== */ 6826 // It's best to debounce the resize event to a void performance hiccups 6827 (function ($, sr) { 6828 6829 /** 6830 * debouncing function from John Hann 6831 * http://unscriptable.com/index.php/2009/03/20/debouncing-javascript-methods/ 6832 */ 6833 var debounce = function (func, threshold, execAsap) { 6834 var timeout; 6835 6836 return function debounced() { 6837 var obj = this, 6838 args = arguments; 6839 6840 function delayed() { 6841 if (!execAsap) func.apply(obj, args); 6842 timeout = null; 6843 } 6844 6845 if (timeout) clearTimeout(timeout); 6846 else if (execAsap) func.apply(obj, args); 6847 6848 timeout = setTimeout(delayed, threshold || 200); 6849 }; 6850 } 6851 // smartresize 6852 jQuery.fn[sr] = function (fn) { 6853 return fn ? this.bind('resize', debounce(fn)) : this.trigger(sr); 6854 }; 6855 6856 })(jQuery, 'smartresize'); 6857 var latestKnownScrollY = window.scrollY, 6858 ticking = false; 6859 6860 function requestTick() { 6861 "use strict"; 6862 6863 if (!ticking) { 6864 requestAnimationFrame(update); 6865 } 6866 ticking = true; 6867 } 6868 6869 function update() { 6870 "use strict"; 6871 6872 ticking = false; 6873 6874 if (!is_android) { 6875 updateStickyMenu(); 6876 } 6877 } 6878 6879 /* ====== INTERNAL FUNCTIONS END ====== */ 6880 6881 6882 /* ====== ONE TIME INIT ====== */ 6883
6884 function init() { 6885 6886 // /* GLOBAL VARS */ 6887 touch = false; 6888 6889 // GET BROWSER DIMENSIONS 6890 browserSize(); 6891 6892 // /* DETECT PLATFORM */ 6893 platformDetect(); 6894 } 6895 6896 6897 /* --- GLOBAL EVENT HANDLERS --- */ 6898 6899 6900 /* ====== ON DOCUMENT READY ====== */ 6901 6902 $(document).ready(function () { 6903 init(); 6904 }); 6905 6906 6907 /* ====== ON WINDOW LOAD ====== */ 6908 6909 $window.load(function () { 6910 6911 if ( !$html.hasClass( 'touch' ) ) { 6912 var $nav = $( '.nav--main' ).addClass( 'hover-intent' ); 6913 6914 $( '.nav--main li' ).hoverIntent({ 6915 over: showSubMenu, 6916 out: hideSubMenu, 6917 timeout: 300 6918 }); 6919 } else if ( $html.hasClass( 'touch') && windowWidth > 1000 ) { 6920 HandleParentMenuItems.handle(); 6921 HandleParentMenuItems.bindOuterNavClick(); 6922 } 6923 6924 function showSubMenu() { 6925 $(this).addClass('hover'); 6926 } 6927 6928 function hideSubMenu() { 6929 $(this).removeClass('hover'); 6930 } 6931 6932 }); 6933 6934 /* ====== ON RESIZE ====== */ 6935 6936 $(window).smartresize(function () { 6937 if (!is_android) { 6938 refreshNavigation(); 6939 } 6940 windowWidth = $(window).outerWidth(); 6941 windowHeight = $(window).outerHeight(); 6942 6943 if( $html.hasClass( 'touch' ) ) { 6944 if( windowWidth >= 1000 ) { 6945 // Handle parent menu items 6946 HandleParentMenuItems.handle(); 6947 } else if ( windowWidth < 1000 ) { 6948 // Remove handlers 6949 HandleParentMenuItems.unHandle(); 6950 } 6951 } 6952 }); 6953 6954 /* ====== ON SCROLL ====== */ 6955 6956 $window.on('scroll', function (e) { 6957 "use strict"; 6958 6959 latestKnownScrollY = window.scrollY; 6960 requestTick(); 6961 }); 6962 // /* ====== HELPER FUNCTIONS ====== */ 6963 /** 6964 * function similar to PHP's empty function 6965 */ 6966 6967 function empty(data) { 6968 if (typeof(data) == 'number' || typeof(data) == 'boolean') { 6969 return false; 6970 } 6971 if (typeof(data) == 'undefined' || data === null) { 6972 return true; 6973 } 6974 if (typeof(data.length) != 'undefined') { 6975 return data.length === 0; 6976 } 6977 var count = 0; 6978 for (var i in data) { 6979 // if(data.hasOwnProperty(i)) 6980 // 6981 // This doesn't work in ie8/ie9 due the fact that hasOwnProperty works only on native objects. 6982 // http://stackoverflow.com/questions/8157700/object-has-no-hasownproperty-method-i-e-its-undefined-ie8 6983 // 6984 // for hosts objects we do this 6985 if (Object.prototype.hasOwnProperty.call(data, i)) { 6986 count++; 6987 } 6988 } 6989 return count === 0; 6990 } 6991 6992 /** 6993 * function to add/modify a GET parameter 6994 */ 6995 6996 function setQueryParameter(uri, key, value) { 6997 var re = new RegExp("([?|&])" + key + "=.*?(&|$)", "i"); 6998 separator = uri.indexOf('?') !== -1 ? "&" : "?"; 6999 if (uri.match(re)) { 7000 return uri.replace(re, '$1' + key + "=" + value + '$2'); 7001 } 7002 else { 7003 return uri + separator + key + "=" + value; 7004 } 7005 } 7006 7007 /** 7008 * requestAnimationFrame polyfill by Erik Möller. 7009 * Fixes from Paul Irish, Tino Zijdel, Andrew Mao, Klemen SlaviÄ, Darius Bacon 7010 * 7011 * MIT license 7012 */ 7013 7014 if (!Date.now) 7015 Date.now = function () { 7016 return new Date().getTime(); 7017 }; 7018 7019 (function () { 7020 'use strict'; 7021 7022 var vendors = ['webkit', 'moz']; 7023 for (var i = 0; i < vendors.length && !window.requestAnimationFrame; ++i) { 7024 var vp = vendors[i]; 7025 window.requestAnimationFrame = window[vp + 'RequestAnimationFrame']; 7026 window.cancelAnimationFrame = (window[vp + 'CancelAnimationFrame'] 7027 || window[vp + 'CancelRequestAnimationFrame']); 7028 } 7029 if (/iP(ad|hone|od).*OS 6/.test(window.navigator.userAgent) // iOS6 is buggy 7030 || !window.requestAnimationFrame || !window.cancelAnimationFrame) { 7031 var lastTime = 0; 7032 window.requestAnimationFrame = function (callback) { 7033 var now = Date.now(); 7034 var nextTime = Math.max(lastTime + 16, now); 7035 return setTimeout(function () { 7036 callback(lastTime = nextTime); 7037 }, 7038 nextTime - now); 7039 }; 7040 window.cancelAnimationFrame = clearTimeout; 7041 } 7042 }()); 7043 7044})(jQuery, window);; 7045!function(){var e=document.currentScript;function t(t){var n=document.createElement("script"),o=e||document.getElementsByTagName("script")[0];n.setAttribute("async",!0),n.setAttribute("src",t),o.parentNode.insertBefore(n,o)}function n(e,t){return Element.prototype.matches?e.matches(t):Element.prototype.msMatchesSelector?e.msMatchesSelector(t):void 0}function o(e,t){if(e.closest)return e.closest(t);var o=e;do{if(n(o,t))return o;o=o.parentElement||o.parentNode}while(null!==o&&1===o.nodeType);return null}function i(e,t){for(var n=0;n<e.length;n++)t(e[n],n,e)}var r,s=".sharing-hidden .inner",a="data-sharing-more-button-id";function c(e){this.button=e,this.pane=o(e,"div").querySelector(s),this.openedBy=null,this.recentlyOpenedByHover=!1,c.instances.push(this),this.pane.setAttribute(a,c.instances.length-1),this.attachHandlers()}if(c.instances=[],c.hoverOpenDelay=200,c.recentOpenDelay=400,c.hoverCloseDelay=300,c.instantiateOrReuse=function(e){var t=o(e,"div").querySelector(s),n=t&&t.getAttribute(a),i=c.instances[n];return i||new c(e)},c.getButtonInstanceFromPane=function(e){var t=e&&e.getAttribute(a);return c.instances[t]},c.closeAll=function(){for(var e=0;e<c.instances.length;e++)c.instances[e].close()},c.prototype.open=function(){var e,t,n=[0,0];function o(e){var t=e.getBoundingClientRect();return[t.left+(window.scrollX||window.pageXOffset||0),t.top+(window.scrollY||window.pageYOffset||0)]}function i(e,t){return parseInt(getComputedStyle(e).getPropertyValue(t)||0)}for(e=o(this.button),t=this.button.offsetParent||document.documentElement;t&&(t===document.body||t===document.documentElement)&&"static"===getComputedStyle(t).getPropertyValue("position");)t=t.parentNode;t&&t!==this.button&&1===t.nodeType&&(n=[(n=o(t))[0]+i(t,"border-left-width"),n[1]+i(t,"border-top-width")]);var r,s=e[0]-n[0]-i(this.button,"margin-left"),a=e[1]-n[1]-i(this.button,"margin-top");this.pane.style.left=s+"px",this.pane.style.top=a+this.button.offsetHeight+3+"px",(r=this.pane)&&r.style.removeProperty("display")},c.prototype.close=function(){var e;(e=this.pane)&&(e.style.display="none"),this.openedBy=null},c.prototype.toggle=function(){var e;(e=this.pane)&&"none"!==e.style.display?this.close():this.open()}
7045,c.prototype.nonHoverOpen=function(){clearTimeout(this.openTimer),clearTimeout(this.closeTimer),this.recentlyOpenedByHover?(this.recentlyOpenedByHover=!1,clearTimeout(this.hoverOpenTimer),this.open()):this.toggle()},c.prototype.resetCloseTimer=function(){clearTimeout(this.closeTimer),this.closeTimer=setTimeout(this.close.bind(this),c.hoverCloseDelay)},c.prototype.attachHandlers=function(){this.buttonClick=function(e){e.preventDefault(),e.stopPropagation(),this.openedBy="click",this.nonHoverOpen()}.bind(this),this.buttonKeydown=function(e){13!==e.keyCode&&32!==e.keyCode||(e.preventDefault(),e.stopPropagation(),this.openedBy="keydown",this.nonHoverOpen())}.bind(this),this.buttonEnter=function(){this.openedBy||(this.openTimer=setTimeout(function(){this.open(),this.openedBy="hover",this.recentlyOpenedByHover=!0,this.hoverOpenTimer=setTimeout(function(){this.recentlyOpenedByHover=!1}.bind(this),c.recentOpenDelay)}.bind(this),c.hoverOpenDelay)),clearTimeout(this.closeTimer)}.bind(this),this.buttonLeave=function(){"hover"===this.openedBy&&this.resetCloseTimer(),clearTimeout(this.openTimer)}.bind(this),this.paneEnter=function(){clearTimeout(this.closeTimer)}.bind(this),this.paneLeave=function(){"hover"===this.openedBy&&this.resetCloseTimer()}.bind(this),this.documentClick=function(){this.close()}.bind(this),this.button.addEventListener("click",this.buttonClick),this.button.addEventListener("keydown",this.buttonKeydown),document.addEventListener("click",this.documentClick),void 0===document.ontouchstart&&(this.button.addEventListener("mouseenter",this.buttonEnter),this.button.addEventListener("mouseleave",this.buttonLeave),this.pane.addEventListener("mouseenter",this.paneEnter),this.pane.addEventListener("mouseleave",this.paneLeave))},window.sharing_js_options&&window.sharing_js_options.counts){var u={done_urls:[],get_counts:function(){var e,n,o,i,r;if("undefined"!=typeof WPCOM_sharing_counts)for(e in WPCOM_sharing_counts)if(o=WPCOM_sharing_counts[e],void 0===u.done_urls[o]){for(i in n={pinterest:[window.location.protocol+"//api.pinterest.com/v1/urls/count.json?callback=WPCOMSharing.update_pinterest_count&url="+encodeURIComponent(e)]})if(document.querySelector("a[data-shared=sharing-"+i+"-"+o+"]")){for(;r=n[i].pop();)t(r);window.sharing_js_options.is_stats_active&&u.bump_sharing_count_stat(i)}u.done_urls[o]=!0}},update_pinterest_count:function(e){void 0!==e.count&&1*e.count>0&&u.inject_share_count("sharing-pinterest-"+WPCOM_sharing_counts[e.url],e.count)},inject_share_count:function(e,t){i(document.querySelectorAll("a[data-shared="+e+"] > span"),function(e){var n,o=e.querySelector(".share-count");(n=o)&&n.parentNode&&n.parentNode.removeChild(n);var i=document.createElement("span");i.className="share-count",i.textContent=u.format_count(t),e.appendChild(i)})},format_count:function(e){return e<1e3?e:e>=1e3&&e<1e4?String(e).substring(0,1)+"K+":"10K+"},bump_sharing_count_stat:function(e){(new Image).src=document.location.protocol+"//pixel.wp.com/g.gif?v=wpcom-no-pv&x_sharing-count-request="+e+"&r="+Math.random()}};window.WPCOMSharing=u}function d(e,t){e.setAttribute("jetpack-share-click-count",t)}function l(e){var t=e.getAttribute("jetpack-share-click-count");return null===t?0:parseInt(t,10)}function h(e,t){var n,o=new XMLHttpRequest;o.open("POST",e,!0),o.setRequestHeader("Content-Type","application/x-www-form-urlencoded; charset=UTF-8"),o.setRequestHeader("x-requested-with","XMLHttpRequest"),o.send((n=t,(encodeURIComponent("email-share-nonce")+"="+encodeURIComponent(n)).replace(/%20/g,"+")))}function p(){m()}function m(){window.WPCOMSharing&&window.WPCOMSharing.get_counts(),i(document.querySelectorAll(".sharedaddy a"),function(e){var t=e.getAttribute("href");t&&-1!==t.indexOf("share=")&&-1===t.indexOf("&nb=1")&&e.setAttribute("href",t+"&nb=1")}),i(document.querySelectorAll(".sharedaddy a.sharing-anchor"),function(e){c.instantiateOrReuse(e)}),void 0!==document.ontouchstart&&document.body.classList.add("jp-sharing-input-touch"),i(document.querySelectorAll(".sharedaddy ul"),function(e){"true"!==e.getAttribute("data-sharing-events-added")&&(e.setAttribute("data-sharing-events-added","true"),i(e.querySelectorAll("a.share-print"),function(e){e.addEventListener("click",function(t){t.preventDefault(),t.stopPropagation();var n=e.getAttribute("href")||"",i=function(){if(-1===n.indexOf("#print")){var e=(new Date).getTime();t=e,o=n,(i=document.createElement("iframe")).setAttribute("style","position:fixed; top:100; left:100; height:1px; width:1px; border:none;"),i.setAttribute("id","printFrame-"+t),i.setAttribute("name",i.getAttribute("id")),i.setAttribute("src",o),i.setAttribute("onload",'frames["printFrame-'+t+'"].focus();frames["printFrame-'+t+'"].print();'),document.body.appendChild(i)}else window.print();var t,o,i}
7045,r=o(e,s);if(r){var a=c.getButtonInstanceFromPane(r);a&&(a.close(),i())}else i()})}),i(e.querySelectorAll("a.share-press-this"),function(e){var t="wp-press-this-"+Math.random().toString(36).slice(2);e.addEventListener("click",function(n){n.preventDefault(),n.stopPropagation();var o="";if(window.getSelection?o=window.getSelection():document.getSelection?o=document.getSelection():document.selection&&(o=document.selection.createRange().text),o){var i=e.getAttribute("href");e.setAttribute("href",i+"&sel="+encodeURI(o))}window.open(e.getAttribute("href"),t,"toolbar=0,resizable=1,scrollbars=1,status=1,width=720,height=570")||(document.location.href=e.getAttribute("href"))})}),i(e.querySelectorAll("a.share-email"),function(t){d(t,0);var n,o,r=t.getAttribute("data-email-share-nonce"),s=t.getAttribute("data-email-share-track-url");r&&s&&(n=s,o=window.location.protocol+"//"+window.location.hostname+"/",0===String(n).indexOf(o))&&t.addEventListener("click",function(){var n;d(n=t,l(n)+1),l(t)>2&&function(e,t){var n=t.parentElement;if(n.classList.contains("sd-content")){i(n.querySelectorAll(".share-email-error"),function(e){e.parentElement.removeChild(e)});var o=document.createElement("div");o.className="share-email-error";var r=document.createElement("h6");r.className="share-email-error-title",r.innerText=e.getAttribute("data-email-share-error-title"),o.appendChild(r);var s=document.createElement("p");s.className="share-email-error-text",s.innerText=e.getAttribute("data-email-share-error-text"),o.appendChild(s),n.appendChild(o)}}(t,e),h(s,r)})}))}),i(document.querySelectorAll("li.share-email, li.share-custom a.sharing-anchor"),function(e){e.classList.add("share-service-visible")})}"loading"!==document.readyState?p():document.addEventListener("DOMContentLoaded",p),document.body.addEventListener("is.post-load",m);var f={};!0!==window.WPCOM_sharing_popups_bound&&(document.body.addEventListener("click",function(e){var t=e.target;if(t&&1===t.nodeType){var n=function(e){if("A"===e.nodeName)return e;var t=e.parentNode;return t&&"A"===t.nodeName?t:null}(t);if(n){var o=function(e){var t=window.WPCOM_sharing_popups;if(!t)return null;for(var n="share-",o=(e.getAttribute("class")||"").split(/\s+/),i=0;i<o.length;i++)if(0===o[i].indexOf(n)){var r=o[i].slice(6);if("string"==typeof t[r])return r}return null}(n);o&&(e.preventDefault(),r&&r.close(),r=window.open(n.getAttribute("href"),function(e){return"string"!=typeof f[e]&&(f[e]="wpcom"+e+"-"+Math.random().toString(36).slice(2)),f[e]}(o),window.WPCOM_sharing_popups[o]))}}}),window.WPCOM_sharing_popups_bound=!0)}();;
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.