PageSourceSearch

https://eclecticlight.co/_static/??-eJyVkMtqAzEMRX8oE1EIpF2UrvsVQb…3BsnDxYwEIevV+uKet4N+xHvj/2n/uv3a5H/APkO7bf

js eclecticlight.co collected 2026-09-24 08:43:11 UTC 340,619 bytes, 7,045 lines download raw bytes

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' : ''}">&bull;</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.