PageSourceSearch

https://bequilles.ch/_assets/975950faa830.js

js bequilles.ch collected 2026-10-07 07:16:31 UTC 221,916 bytes, 5,801 lines download raw bytes

1/* Detect-zoom
2 * -----------
3 * Cross Browser Zoom and Pixel Ratio Detector
4 * Version 1.0.4 | Apr 1 2013
5 * dual-licensed under the WTFPL and MIT license
6 * Maintained by https://github/tombigel
7 * Original developer https://github.com/yonran
8 */
9
10//AMD and CommonJS initialization copied from https://github.com/zohararad/audio5js
11(function (root, ns, factory) {
12    "use strict";
13
14    if (typeof (module) !== 'undefined' && module.exports) { // CommonJS
15        module.exports = factory(ns, root);
16    } else if (typeof (define) === 'function' && define.amd) { // AMD
17        define("factory", function () {
18            return factory(ns, root);
19        });
20    } else {
21        root[ns] = factory(ns, root);
22    }
23
24}(window, 'detectZoom', function () {
25
26    /**
27     * Use devicePixelRatio if supported by the browser
28     * @return {Number}
29     * @private
30     */
31    var devicePixelRatio = function () {
32        return window.devicePixelRatio || 1;
33    };
34
35    /**
36     * Fallback function to set default values
37     * @return {Object}
38     * @private
39     */
40    var fallback = function () {
41        return {
42            zoom: 1,
43            devicePxPerCssPx: 1
44        };
45    };
46    /**
47     * IE 8 and 9: no trick needed!
48     * TODO: Test on IE10 and Windows 8 RT
49     * @return {Object}
50     * @private
51     **/
52    var ie8 = function () {
53        var zoom = Math.round((screen.deviceXDPI / screen.logicalXDPI) * 100) / 100;
54        return {
55            zoom: zoom,
56            devicePxPerCssPx: zoom * devicePixelRatio()
57        };
58    };
59
60    /**
61     * For IE10 we need to change our technique again...
62     * thanks https://github.com/stefanvanburen
63     * @return {Object}
64     * @private
65     */
66    var ie10 = function () {
67        var zoom = Math.round((document.documentElement.offsetHeight / window.innerHeight) * 100) / 100;
68        return {
69            zoom: zoom,
70            devicePxPerCssPx: zoom * devicePixelRatio()
71        };
72    };
73
74    /**
75     * Mobile WebKit
76     * the trick: window.innerWIdth is in CSS pixels, while
77     * screen.width and screen.height are in system pixels.
78     * And there are no scrollbars to mess up the measurement.
79     * @return {Object}
80     * @private
81     */
82    var webkitMobile = function () {
83        var deviceWidth = (Math.abs(window.orientation) == 90) ? screen.height : screen.width;
84        var zoom = deviceWidth / window.innerWidth;
85        return {
86            zoom: zoom,
87            devicePxPerCssPx: zoom * devicePixelRatio()
88        };
89    };
90
91    /**
92     * Desktop Webkit
93     * the trick: an element's clientHeight is in CSS pixels, while you can
94     * set its line-height in system pixels using font-size and
95     * -webkit-text-size-adjust:none.
96     * device-pixel-ratio: http://www.webkit.org/blog/55/high-dpi-web-sites/
97     *
98     * Previous trick (used before http://trac.webkit.org/changeset/100847):
99     * documentElement.scrollWidth is in CSS pixels, while
100     * document.width was in system pixels. Note that this is the
101     * layout width of the document, which is slightly different from viewport
102     * because document width does not include scrollbars and might be wider
103     * due to big elements.
104     * @return {Object}
105     * @private
106     */
107    var webkit = function () {
108        var important = function (str) {
109            return str.replace(/;/g, " !important;");
110        };
111
112        var div = document.createElement('div');
113        div.innerHTML = "1<br>2<br>3<br>4<br>5<br>6<br>7<br>8<br>9<br>0";
114        div.setAttribute('style', important('font: 100px/1em sans-serif; -webkit-text-size-adjust: none; text-size-adjust: none; height: auto; width: 1em; padding: 0; overflow: visible;'));
115
116        // The container exists so that the div will be laid out in its own flow
117        // while not impacting the layout, viewport size, or display of the
118        // webpage as a whole.
119        // Add !important and relevant CSS rule resets
120        // so that other rules cannot affect the results.
121        var container = document.createElement('div');
122        container.setAttribute('style', important('width:0; height:0; overflow:hidden; visibility:hidden; position: absolute;'));
123        container.appendChild(div);
124
125        document.body.appendChild(container);
126        var zoom = 1000 / div.clientHeight;
127        zoom = Math.round(zoom * 100) / 100;
128        document.body.removeChild(container);
129
130        return{
131            zoom: zoom,
132            devicePxPerCssPx: zoom * devicePixelRatio()
133        };
134    };
135
136    /**
137     * no real trick; device-pixel-ratio is the ratio of device dpi / css dpi.
138     * (Note that this is a different interpretation than Webkit's device
139     * pixel ratio, which is the ratio device dpi / system dpi).
140     *
141     * Also, for Mozilla, there is no difference between the zoom factor and the device ratio.
142     *
143     * @return {Object}
144     * @private
145     */
146    var firefox4 = function () {
147        var zoom = mediaQueryBinarySearch('min--moz-device-pixel-ratio', '', 0, 10, 20, 0.0001);
148        zoom = Math.round(zoom * 100) / 100;
149        return {
150            zoom: zoom,
151            devicePxPerCssPx: zoom
152        };
153    };
154
155    /**
156     * Firefox 18.x
157     * Mozilla added support for devicePixelRatio to Firefox 18,
158     * but it is affected by the zoom level, so, like in older
159     * Firefox we can't tell if we are in zoom mode or in a device
160     * with a different pixel ratio
161     * @return {Object}
162     * @private
163     */
164    var firefox18 = function () {
165        return {
166            zoom: firefox4().zoom,
167            devicePxPerCssPx: devicePixelRatio()
168        };
169    };
170
171    /**
172     * works starting Opera 11.11
173     * the trick: outerWidth is the viewport width including scrollbars in
174     * system px, while innerWidth is the viewport width including scrollbars
175     * in CSS px
176     * @return {Object}
177     * @private
178     */
179    var opera11 = function () {
180        var zoom = window.top.outerWidth / window.top.innerWidth;
181        zoom = Math.round(zoom * 100) / 100;
182        return {
183            zoom: zoom,
184            devicePxPerCssPx: zoom * devicePixelRatio()
185        };
186    };
187
188    /**
189     * Use a binary search through media queries to find zoom level in Firefox
190     * @param property
191     * @param unit
192     * @param a
193     * @param b
194     * @param maxIter
195     * @param epsilon
196     * @return {Number}
197     */
198    var mediaQueryBinarySearch = function (property, unit, a, b, maxIter, epsilon) {
199        var matchMedia;
200        var head, style, div;
201        if (window.matchMedia) {
202            matchMedia = window.matchMedia;
203        } else {
204            head = document.getElementsByTagName('head')[0];
205            style = document.createElement('style');
206            head.appendChild(style);
207
208            div = document.createElement('div');
209            div.className = 'mediaQueryBinarySearch';
210            div.style.display = 'none';
211            document.body.appendChild(div);
212
213            matchMedia = function (query) {
214                style.sheet.insertRule('@media ' + query + '{.mediaQueryBinarySearch ' + '{text-decoration: underline} }', 0);
215                var matched = getComputedStyle(div, null).textDecoration == 'underline';
216                style.sheet.deleteRule(0);
217                return {matches: matched};
218            };
219        }
220        var ratio = binarySearch(a, b, maxIter);
221        if (div) {
222            head.removeChild(style);
223            document.body.removeChild(div);
224        }
225        return ratio;
226
227        function binarySearch(a, b, maxIter) {
228            var mid = (a + b) / 2;
229            if (maxIter <= 0 || b - a < epsilon) {
230                return mid;
231            }
232            var query = "(" + property + ":" + mid + unit + ")";
233            if (matchMedia(query).matches) {
234                return binarySearch(mid, b, maxIter - 1);
235            } else {
236                return binarySearch(a, mid, maxIter - 1);
237            }
238        }
239    };
240
241    /**
242     * Generate detection function
243     * @private
244     */
245    var detectFunction = (function () {
246        var func = fallback;
247        //IE8+
248        if (!isNaN(screen.logicalXDPI) && !isNaN(screen.systemXDPI)) {
249            func = ie8;
250        }
251        // IE10+ / Touch
252        else if (window.navigator.msMaxTouchPoints) {
253            func = ie10;
254        }
255        //Mobile Webkit
256        else if ('orientation' in window && typeof document.body.style.webkitMarquee === 'string') {
257            func = webkitMobile;
258        }
259        //WebKit
260        else if (typeof document.body.style.webkitMarquee === 'string') {
261            func = webkit;
262        }
263        //Opera
264        else if (navigator.userAgent.indexOf('Opera') >= 0) {
265            func = opera11;
266        }
267        //Last one is Firefox
268        //FF 18.x
269        else if (window.devicePixelRatio) {
270            func = firefox18;
271        }
272        //FF 4.0 - 17.x
273        else if (firefox4().zoom > 0.001) {
274            func = firefox4;
275        }
276
277        return func;
278    }());
279
280
281    return ({
282
283        /**
284         * Ratios.zoom shorthand
285         * @return {Number} Zoom level
286         */
287        zoom: function () {
288            return detectFunction().zoom;
289        },
290
291        /**
292         * Ratios.devicePxPerCssPx shorthand
293         * @return {Number} devicePxPerCssPx level
294         */
295        device: function () {
296            return detectFunction().devicePxPerCssPx;
297        }
298    });
299}));
300
301var wpcom_img_zoomer = {
302	zoomed: false,
303	timer: null,
304	interval: 1000, // zoom polling interval in millisecond
305
306	// Should we apply width/height attributes to control the image size?
307	imgNeedsSizeAtts: function( img ) {
308		// Do not overwrite existing width/height attributes.
309		if ( img.getAttribute('width') !== null || img.getAttribute('height') !== null )
310			return false;
311		// Do not apply the attributes if the image is already constrained by a parent element.
312		if ( img.width < img.naturalWidth || img.height < img.naturalHeight )
313			return false;
314		return true;
315	},
316
317	init: function() {
318		var t = this;
319		try{
320			t.zoomImages();
321			t.timer = setInterval( function() { t.zoomImages(); }, t.interval );
322		}
323		catch(e){
324		}
325	},
326
327	stop: function() {
328		if ( this.timer )
329			clearInterval( this.timer );
330	},
331
332	getScale: function() {
333		var scale = detectZoom.device();
334		// Round up to 1.5 or the next integer below the cap.
335		if      ( scale <= 1.0 ) scale = 1.0;
336		else if ( scale <= 1.5 ) scale = 1.5;
337		else if ( scale <= 2.0 ) scale = 2.0;
338		else if ( scale <= 3.0 ) scale = 3.0;
339		else if ( scale <= 4.0 ) scale = 4.0;
340		else                     scale = 5.0;
341		return scale;
342	},
343
344	shouldZoom: function( scale ) {
345		var t = this;
346		// Do not operate on hidden frames.
347		if ( "innerWidth" in window && !window.innerWidth )
348			return false;
349		// Don't do anything until scale > 1
350		if ( scale == 1.0 && t.zoomed == false )
351			return false;
352		return true;
353	},
354
355	zoomImages: function() {
356		var t = this;
357		var scale = t.getScale();
358		if ( ! t.shouldZoom( scale ) ){
359			return;
360		}
361		t.zoomed = true;
362		// Loop through all the <img> elements on the page.
363		var imgs = document.getElementsByTagName("img");
364
365		for ( var i = 0; i < imgs.length; i++ ) {
366			// Wait for original images to load
367			if ( "complete" in imgs[i] && ! imgs[i].complete )
368				continue;
369
370			// Skip images that don't need processing.
371			var imgScale = imgs[i].getAttribute("scale");
372			if ( imgScale == scale || imgScale == "0" )
373				continue;
374
375			// Skip images that have already failed at this scale
376			var scaleFail = imgs[i].getAttribute("scale-fail");
377			if ( scaleFail && scaleFail <= scale )
378				continue;
379
380			// Skip images that have no dimensions yet.
381			if ( ! ( imgs[i].width && imgs[i].height ) )
382				continue;
383
384			// Skip images from Lazy Load plugins
385			if ( ! imgScale && imgs[i].getAttribute("data-lazy-src") && (imgs[i].getAttribute("data-lazy-src") !== imgs[i].getAttribute("src")))
386				continue;
387
388			if ( t.scaleImage( imgs[i], scale ) ) {
389				// Mark the img as having been processed at this scale.
390				imgs[i].setAttribute("scale", scale);
391			}
392			else {
393				// Set the flag to skip this image.
394				imgs[i].setAttribute("scale", "0");
395			}
396		}
397	},
398
399	scaleImage: function( img, scale ) {
400		var t = this;
401		var newSrc = img.src;
402
403		// Skip slideshow images
404		if ( img.parentNode.className.match(/slideshow-slide/) )
405			return false;
406
407		// Scale gravatars that have ?s= or ?size=
408		if ( img.src.match( /^https?:\/\/([^\/]*\.)?gravatar\.com\/.+[?&](s|size)=/ ) ) {
409			newSrc = img.src.replace( /([?&](s|size)=)(\d+)/, function( $0, $1, $2, $3 ) {
410				// Stash the original size
411				var originalAtt = "originals",
412				originalSize = img.getAttribute(originalAtt);
413				if ( originalSize === null ) {
414					originalSize = $3;
415					img.setAttribute(originalAtt, originalSize);
416					if ( t.imgNeedsSizeAtts( img ) ) {
417						// Fix width and height attributes to rendered dimensions.
418						img.width = img.width;
419						img.height = img.height;
420					}
421				}
422				// Get the width/height of the image in CSS pixels
423				var size = img.clientWidth;
424				// Convert CSS pixels to device pixels
425				var targetSize = Math.ceil(img.clientWidth * scale);
426				// Don't go smaller than the original size
427				targetSize = Math.max( targetSize, originalSize );
428				// Don't go larger than the service supports
429				targetSize = Math.min( targetSize, 512 );
430				return $1 + targetSize;
431			});
432		}
433
434		// Scale resize queries (*.files.wordpress.com) that have ?w= or ?h=
435		else if ( img.src.match( /^https?:\/\/([^\/]+)\.files\.wordpress\.com\/.+[?&][wh]=/ ) ) {
436			if ( img.src.match( /[?&]crop/ ) )
437				return false;
438			var changedAttrs = {};
439			var matches = img.src.match( /([?&]([wh])=)(\d+)/g );
440			for ( var i = 0; i < matches.length; i++ ) {
441				var lr = matches[i].split( '=' );
442				var thisAttr = lr[0].replace(/[?&]/g, '' );
443				var thisVal = lr[1];
444
445				// Stash the original size
446				var originalAtt = 'original' + thisAttr, originalSize = img.getAttribute( originalAtt );
447				if ( originalSize === null ) {
448					originalSize = thisVal;
449					img.setAttribute(originalAtt, originalSize);
450					if ( t.imgNeedsSizeAtts( img ) ) {
451						// Fix width and height attributes to rendered dimensions.
452						img.width = img.width;
453						img.height = img.height;
454					}
455				}
456				// Get the width/height of the image in CSS pixels
457				var size = thisAttr == 'w' ? img.clientWidth : img.clientHeight;
458				var naturalSize = ( thisAttr == 'w' ? img.naturalWidth : img.naturalHeight );
459				// Convert CSS pixels to device pixels
460				var targetSize = Math.ceil(size * scale);
461				// Don't go smaller than the original size
462				targetSize = Math.max( targetSize, originalSize );
463				// Don't go bigger unless the current one is actually lacking
464				if ( scale > img.getAttribute("scale") && targetSize <= naturalSize )
465					targetSize = thisVal;
466				// Don't try to go bigger if the image is already smaller than was requested
467				if ( naturalSize < thisVal )
468					targetSize = thisVal;
469				if ( targetSize != thisVal )
470					changedAttrs[ thisAttr ] = targetSize;
471			}
472			var w = changedAttrs.w || false;
473			var h = changedAttrs.h || false;
474
475			if ( w ) {
476				newSrc = img.src.replace(/([?&])w=\d+/g, function( $0, $1 ) {
477					return $1 + 'w=' + w;
478				});
479			}
480			if ( h ) {
481				newSrc = newSrc.replace(/([?&])h=\d+/g, function( $0, $1 ) {
482					return $1 + 'h=' + h;
483				});
484			}
485		}
486
487		// Scale mshots that have width
488		else if ( img.src.match(/^https?:\/\/([^\/]+\.)*(wordpress|wp)\.com\/mshots\/.+[?&]w=\d+/) ) {
489			newSrc = img.src.replace( /([?&]w=)(\d+)/, function($0, $1, $2) {
490				// Stash the original size
491				var originalAtt = 'originalw', originalSize = img.getAttribute(originalAtt);
492				if ( originalSize === null ) {
493					originalSize = $2;
494					img.setAttribute(originalAtt, originalSize);
495					if ( t.imgNeedsSizeAtts( img ) ) {
496						// Fix width and height attributes to rendered dimensions.
497						img.width = img.width;
498						img.height = img.height;
499					}
500				}
501				// Get the width of the image in CSS pixels
502				var size = img.clientWidth;
503				// Convert CSS pixels to device pixels
504				var targetSize = Math.ceil(size * scale);
505				// Don't go smaller than the original size
506				targetSize = Math.max( targetSize, originalSize );
507				// Don't go bigger unless the current one is actually lacking
508				if ( scale > img.getAttribute("scale") && targetSize <= img.naturalWidth )
509					targetSize = $2;
510				if ( $2 != targetSize )
511					return $1 + targetSize;
512				return $0;
513			});
514
515			// Update height attribute to match width
516			newSrc = newSrc.replace( /([?&]h=)(\d+)/, function($0, $1, $2) {
517				if ( newSrc == img.src ) {
518					return $0;
519				}
520				// Stash the original size
521				var originalAtt = 'originalh', originalSize = img.getAttribute(originalAtt);
522				if ( originalSize === null ) {
523					originalSize = $2;
524					img.setAttribute(originalAtt, originalSize);
525				}
526				// Get the height of the image in CSS pixels
527				var size = img.clientHeight;
528				// Convert CSS pixels to device pixels
529				var targetSize = Math.ceil(size * scale);
530				// Don't go smaller than the original size
531				targetSize = Math.max( targetSize, originalSize );
532				// Don't go bigger unless the current one is actually lacking
533				if ( scale > img.getAttribute("scale") && targetSize <= img.naturalHeight )
534					targetSize = $2;
535				if ( $2 != targetSize )
536					return $1 + targetSize;
537				return $0;
538			});
539		}
540
541		// Scale simple imgpress queries (s0.wp.com) that only specify w/h/fit
542		else if ( img.src.match(/^https?:\/\/([^\/.]+\.)*(wp|wordpress)\.com\/imgpress\?(.+)/) ) {
543			var imgpressSafeFunctions = ["zoom", "url", "h", "w", "fit", "filter", "brightness", "contrast", "colorize", "smooth", "unsharpmask"];
544			// Search the query string for unsupported functions.
545			var qs = RegExp.$3.split('&');
546			for ( var q in qs ) {
547				q = qs[q].split('=')[0];
548				if ( imgpressSafeFunctions.indexOf(q) == -1 ) {
549					return false;
550				}
551			}
552			// Fix width and height attributes to rendered dimensions.
553			img.width = img.width;
554			img.height = img.height;
555			// Compute new src
556			if ( scale == 1 )
557				newSrc = img.src.replace(/\?(zoom=[^&]+&)?/, '?');
558			else
559				newSrc = img.src.replace(/\?(zoom=[^&]+&)?/, '?zoom=' + scale + '&');
560		}
561
562		// Scale LaTeX images or Photon queries (i#.wp.com)
563		else if (
564			img.src.match(/^https?:\/\/([^\/.]+\.)*(wp|wordpress)\.com\/latex\.php\?(latex|zoom)=(.+)/) ||
565			img.src.match(/^https?:\/\/i[\d]{1}\.wp\.com\/(.+)/)
566		) {
567			// Fix width and height attributes to rendered dimensions.
568			img.width = img.width;
569			img.height = img.height;
570			// Compute new src
571			if ( scale == 1 ) {
572				newSrc = img.src.replace(/\?(zoom=[^&]+&)?/, '?');
573			} else {
574				newSrc = img.src;
575
576				var url_var = newSrc.match( /\?w=(\d+)/ );
577				if ( url_var !== null && url_var[1] ) {
578					newSrc = newSrc.replace( new RegExp( 'w=' + url_var[1] ), 'w=' + img.width );
579				}
580
581				url_var = newSrc.match( /\?h=(\d+)/ );
582				if ( url_var !== null && url_var[1] ) {
583					newSrc = newSrc.replace( new RegExp( 'h=' + url_var[1] ), 'h=' + img.height );
584				}
585
586				var zoom_arg = '&zoom=2';
587				if ( !newSrc.match( /\?/ ) ) {
588					zoom_arg = '?zoom=2';
589				}
590				img.setAttribute( 'srcset', newSrc + zoom_arg + ' ' + scale + 'x' );
591			}
592		}
593
594		// Scale static assets that have a name matching *-1x.png or *@1x.png
595		else if ( img.src.match(/^https?:\/\/[^\/]+\/.*[-@]([12])x\.(gif|jpeg|jpg|png)(\?|$)/) ) {
596			// Fix width and height attributes to rendered dimensions.
597			img.width = img.width;
598			img.height = img.height;
599			var currentSize = RegExp.$1, newSize = currentSize;
600			if ( scale <= 1 )
601				newSize = 1;
602			else
603				newSize = 2;
604			if ( currentSize != newSize )
605				newSrc = img.src.replace(/([-@])[12]x\.(gif|jpeg|jpg|png)(\?|$)/, '$1'+newSize+'x.$2$3');
606		}
607
608		else {
609			return false;
610		}
611
612		// Don't set img.src unless it has changed. This avoids unnecessary reloads.
613		if ( newSrc != img.src ) {
614			// Store the original img.src
615			var prevSrc, origSrc = img.getAttribute("src-orig");
616			if ( !origSrc ) {
617				origSrc = img.src;
618				img.setAttribute("src-orig", origSrc);
619			}
620			// In case of error, revert img.src
621			prevSrc = img.src;
622			img.onerror = function(){
623				img.src = prevSrc;
624				if ( img.getAttribute("scale-fail") < scale )
625					img.setAttribute("scale-fail", scale);
626				img.onerror = null;
627			};
628			// Finally load the new image
629			img.src = newSrc;
630		}
631
632		return true;
633	}
634};
635
636wpcom_img_zoomer.init();
637;
638// WARNING: This file is distributed verbatim in Jetpack. There should be nothing WordPress.com specific in this file. @hide-in-jetpack
639(function($){ // Open closure
640// Local vars
641var Scroller, ajaxurl, stats, type, text, totop;
642
643// IE requires special handling
644var isIE = ( -1 != navigator.userAgent.search( 'MSIE' ) );
645if ( isIE ) {
646	var IEVersion = navigator.userAgent.match(/MSIE\s?(\d+)\.?\d*;/);
647	var IEVersion = parseInt( IEVersion[1] );
648}
649
650// HTTP ajaxurl when site is HTTPS causes Access-Control-Allow-Origin failure in Desktop and iOS Safari
651if ( "https:" == document.location.protocol ) {
652	infiniteScroll.settings.ajaxurl = infiniteScroll.settings.ajaxurl.replace( "http://", "https://" );
653}
654
655/**
656 * Loads new posts when users scroll near the bottom of the page.
657 */
658Scroller = function( settings ) {
659	var self = this;
660
661	// Initialize our variables
662	this.id               = settings.id;
663	this.body             = $( document.body );
664	this.window           = $( window );
665	this.element          = $( '#' + settings.id );
666	this.wrapperClass     = settings.wrapper_class;
667	this.ready            = true;
668	this.disabled         = false;
669	this.page             = 1;
670	this.offset           = settings.offset;
671	this.currentday       = settings.currentday;
672	this.order            = settings.order;
673	this.throttle         = false;
674	this.handle           = '<div id="infinite-handle"><span><button>' + text.replace( '\\', '' ) + '</button></span></div>';
675	this.click_handle     = settings.click_handle;
676	this.google_analytics = settings.google_analytics;
677	this.history          = settings.history;
678	this.origURL          = window.location.href;
679	this.pageCache        = {};
680
681	// Footer settings
682	this.footer           = $( '#infinite-footer' );
683	this.footer.wrap      = settings.footer;
684
685	// Core's native MediaElement.js implementation needs special handling
686	this.wpMediaelement   = null;
687
688	// We have two type of infinite scroll
689	// cases 'scroll' and 'click'
690
691	if ( type == 'scroll' ) {
692		// Bind refresh to the scroll event
693		// Throttle to check for such case every 300ms
694
695		// On event the case becomes a fact
696		this.window.bind( 'scroll.infinity', function() {
697			this.throttle = true;
698		});
699
700		// Go back top method
701		self.gotop();
702
703		setInterval( function() {
704			if ( this.throttle ) {
705				// Once the case is the case, the action occurs and the fact is no more
706				this.throttle = false;
707				// Reveal or hide footer
708				self.thefooter();
709				// Fire the refresh
710				self.refresh();
711                self.determineURL(); // determine the url 
712			}
713		}, 250 );
714
715		// Ensure that enough posts are loaded to fill the initial viewport, to compensate for short posts and large displays.
716		self.ensureFilledViewport();
717		this.body.bind( 'post-load', { self: self }, self.checkViewportOnLoad );
718	} else if ( type == 'click' ) {
719		if ( this.click_handle ) {
720			this.element.append( this.handle );
721		}
722
723		this.body.delegate( '#infinite-handle', 'click.infinity', function() {
724			// Handle the handle
725			if ( self.click_handle ) {
726				$( '#infinite-handle' ).remove();
727			}
728
729			// Fire the refresh
730			self.refresh();
731		});
732	}
733
734	// Initialize any Core audio or video players loaded via IS
735	this.body.bind( 'post-load', { self: self }, self.initializeMejs );
736};
737
738/**
739 * Check whether we should fetch any additional posts.
740 */
741Scroller.prototype.check = function() {
742	var container = this.element.offset();
743
744	// If the container can't be found, stop otherwise errors result
745	if ( 'object' !== typeof container ) {
746		return false;
747	}
748
749	var bottom = this.window.scrollTop() + this.window.height(),
750		threshold = container.top + this.element.outerHeight(false) - (this.window.height() * 2);
751
752	return bottom > threshold;
753};
754
755/**
756 * Renders the results from a successful response.
757 */
758Scroller.prototype.render = function( response ) {
759	this.body.addClass( 'infinity-success' );
760
761	// Check if we can wrap the html
762	this.element.append( response.html );
763	this.body.trigger( 'post-load', response );
764	this.ready = true;
765};
766
767/**
768 * Returns the object used to query for new posts.
769 */
770Scroller.prototype.query = function() {
771	return {
772		page           : this.page,
773		currentday     : this.currentday,
774		order          : this.order,
775		scripts        : window.infiniteScroll.settings.scripts,
776		styles         : window.infiniteScroll.settings.styles,
777		query_args     : window.infiniteScroll.settings.query_args,
778		last_post_date : window.infiniteScroll.settings.last_post_date
779	};
780};
781
782/**
783 * Scroll back to top.
784 */
785Scroller.prototype.gotop = function() {
786	var blog = $( '#infinity-blog-title' );
787
788	blog.attr( 'title', totop );
789
790	// Scroll to top on blog title
791	blog.bind( 'click', function( e ) {
792		$( 'html, body' ).animate( { scrollTop: 0 }, 'fast' );
793		e.preventDefault();
794	});
795};
796
797
798/**
799 * The infinite footer.
800 */
801Scroller.prototype.thefooter = function() {
802	var self  = this,
803		width;
804
805	// Check if we have an id for the page wrapper
806	if ( $.type( this.footer.wrap ) === "string" ) {
807		width = $( 'body #' + this.footer.wrap ).outerWidth( false );
808
809		// Make the footer match the width of the page
810		if ( width > 479 )
811			this.footer.find( '.container' ).css( 'width', width );
812	}
813
814	// Reveal footer
815	if ( this.window.scrollTop() >= 350 )
816		self.footer.animate( { 'bottom': 0 }, 'fast' );
817	else if ( this.window.scrollTop() < 350 )
818		self.footer.animate( { 'bottom': '-50px' }, 'fast' );
819};
820
821
822/**
823 * Controls the flow of the refresh. Don't mess.
824 */
825Scroller.prototype.refresh = function() {
826	var	self   = this,
827		query, jqxhr, load, loader, color;
828
829	// If we're disabled, ready, or don't pass the check, bail.
830	if ( this.disabled || ! this.ready || ! this.check() )
831		return;
832
833	// Let's get going -- set ready to false to prevent
834	// multiple refreshes from occurring at once.
835	this.ready = false;
836
837	// Create a loader element to show it's working.
838	if ( this.click_handle ) {
839		loader = '<span class="infinite-loader"></span>';
840		this.element.append( loader );
841
842		loader = this.element.find( '.infinite-loader' );
843		color = loader.css( 'color' );
844
845		try {
846			loader.spin( 'medium-left', color );
847		} catch ( error ) { }
848	}
849
850	// Generate our query vars.
851	query = $.extend({
852		action: 'infinite_scroll'
853	}, this.query() );
854
855	// Fire the ajax request.
856	jqxhr = $.post( infiniteScroll.settings.ajaxurl, query );
857
858	// Allow refreshes to occur again if an error is triggered.
859	jqxhr.fail( function() {
860		if ( self.click_handle ) {
861			loader.hide();
862		}
863
864		self.ready = true;
865	});
866
867	// Success handler
868	jqxhr.done( function( response ) {
869			// On success, let's hide the loader circle.
870			if ( self.click_handle ) {
871				loader.hide();
872			}
873
874			// Check for and parse our response.
875			if ( ! response )
876				return;
877
878			response = $.parseJSON( response );
879
880			if ( ! response || ! response.type )
881				return;
882
883			// If there are no remaining posts...
884			if ( response.type == 'empty' ) {
885				// Disable the scroller.
886				self.disabled = true;
887				// Update body classes, allowing the footer to return to static positioning
888				self.body.addClass( 'infinity-end' ).removeClass( 'infinity-success' );
889
890			// If we've succeeded...
891			} else if ( response.type == 'success' ) {
892				// If additional scripts are required by the incoming set of posts, parse them
893				if ( response.scripts ) {
894					$( response.scripts ).each( function() {
895						var elementToAppendTo = this.footer ? 'body' : 'head';
896
897						// Add script handle to list of those already parsed
898						window.infiniteScroll.settings.scripts.push( this.handle );
899
900						// Output extra data, if present
901						if ( this.extra_data ) {
902							var data = document.createElement('script'),
903								dataContent = document.createTextNode( "//<![CDATA[ \n" + this.extra_data + "\n//]]>" );
904
905							data.type = 'text/javascript';
906							data.appendChild( dataContent );
907
908							document.getElementsByTagName( elementToAppendTo )[0].appendChild(data);
909						}
910
911						// Build script tag and append to DOM in requested location
912						var script = document.createElement('script');
913						script.type = 'text/javascript';
914						script.src = this.src;
915						script.id = this.handle;
916
917						// If MediaElement.js is loaded in by this set of posts, don't initialize the players a second time as it breaks them all
918						if ( 'wp-mediaelement' === this.handle ) {
919							self.body.unbind( 'post-load', self.initializeMejs );
920						}
921
922						if ( 'wp-mediaelement' === this.handle && 'undefined' === typeof mejs ) {
923							self.wpMediaelement = {};
924							self.wpMediaelement.tag = script;
925							self.wpMediaelement.element = elementToAppendTo;
926							setTimeout( self.maybeLoadMejs.bind( self ), 250 );
927						} else {
928							document.getElementsByTagName( elementToAppendTo )[0].appendChild(script);
929						}
930					} );
931				}
932
933				// If additional stylesheets are required by the incoming set of posts, parse them
934				if ( response.styles ) {
935					$( response.styles ).each( function() {
936						// Add stylesheet handle to list of those already parsed
937						window.infiniteScroll.settings.styles.push( this.handle );
938
939						// Build link tag
940						var style = document.createElement('link');
941						style.rel = 'stylesheet';
942						style.href = this.src;
943						style.id = this.handle + '-css';
944
945						// Destroy link tag if a conditional statement is present and either the browser isn't IE, or the conditional doesn't evaluate true
946						if ( this.conditional && ( ! isIE || ! eval( this.conditional.replace( /%ver/g, IEVersion ) ) ) )
947							var style = false;
948
949						// Append link tag if necessary
950						if ( style )
951							document.getElementsByTagName('head')[0].appendChild(style);
952					} );
953				}
954
955				// stash the response in the page cache
956				self.pageCache[self.page] = response;
957
958				// Increment the page number
959				self.page++;
960
961				// Record pageview in WP Stats, if available.
962				if ( stats )
963					new Image().src = document.location.protocol + '//pixel.wp.com/g.gif?' + stats + '&post=0&baba=' + Math.random();
964
965				// Add new posts to the postflair object
966				if ( 'object' == typeof response.postflair && 'object' == typeof WPCOM_sharing_counts )
967					WPCOM_sharing_counts = $.extend( WPCOM_sharing_counts, response.postflair );
968
969				// Render the results
970				self.render.apply( self, arguments );
971
972				// If 'click' type and there are still posts to fetch, add back the handle
973				if ( type == 'click' ) {
974					if ( response.lastbatch ) {
975						if ( self.click_handle ) {
976							$( '#infinite-handle' ).remove();
977						} else {
978							self.body.trigger( 'infinite-scroll-posts-end' );
979						}
980					} else {
981						if ( self.click_handle ) {
982							self.element.append( self.handle );
983						} else {
984							self.body.trigger( 'infinite-scroll-posts-more' );
985						}
986					}
987				}
988
989				// Update currentday to the latest value returned from the server
990				if ( response.currentday ) {
991					self.currentday = response.currentday;
992				}
993
994				// Fire Google Analytics pageview
995				if ( self.google_analytics ) {
996					var ga_url = self.history.path.replace( /%d/, self.page );
997					if ( 'object' === typeof _gaq ) {
998						_gaq.push( [ '_trackPageview', ga_url ] );
999					}
1000					if ( 'function' === typeof ga ) {
1001						ga( 'send', 'pageview', ga_url );
1002					}
1003				}
1004			}
1005		});
1006
1007	return jqxhr;
1008};
1009
1010/**
1011 * Core's native media player uses MediaElement.js
1012 * The library's size is sufficient that it may not be loaded in time for Core's helper to invoke it, so we need to delay until `mejs` exists.
1013 */
1014Scroller.prototype.maybeLoadMejs = function() {
1015	if ( null === this.wpMediaelement ) {
1016		return;
1017	}
1018
1019	if ( 'undefined' === typeof mejs ) {
1020		setTimeout( this.maybeLoadMejs, 250 );
1021	} else {
1022		document.getElementsByTagName( this.wpMediaelement.element )[0].appendChild( this.wpMediaelement.tag );
1023		this.wpMediaelement = null;
1024
1025		// Ensure any subsequent IS loads initialize the players
1026		this.body.bind( 'post-load', { self: this }, this.initializeMejs );
1027	}
1028}
1029
1030/**
1031 * Initialize the MediaElement.js player for any posts not previously initialized
1032 */
1033Scroller.prototype.initializeMejs = function( ev, response ) {
1034	// Are there media players in the incoming set of posts?
1035	if ( ! response.html || -1 === response.html.indexOf( 'wp-audio-shortcode' ) && -1 === response.html.indexOf( 'wp-video-shortcode' ) ) {
1036		return;
1037	}
1038
1039	// Don't bother if mejs isn't loaded for some reason
1040	if ( 'undefined' === typeof mejs ) {
1041		return;
1042	}
1043
1044	// Adapted from wp-includes/js/mediaelement/wp-mediaelement.js
1045	// Modified to not initialize already-initialized players, as Mejs doesn't handle that well
1046	$(function () {
1047		var settings = {};
1048
1049		if ( typeof _wpmejsSettings !== 'undefined' ) {
1050			settings.pluginPath = _wpmejsSettings.pluginPath;
1051		}
1052
1053		settings.success = function (mejs) {
1054			var autoplay = mejs.attributes.autoplay && 'false' !== mejs.attributes.autoplay;
1055			if ( 'flash' === mejs.pluginType && autoplay ) {
1056				mejs.addEventListener( 'canplay', function () {
1057					mejs.play();
1058				}, false );
1059			}
1060		};
1061
1062		$('.wp-audio-shortcode, .wp-video-shortcode').not( '.mejs-container' ).mediaelementplayer( settings );
1063	});
1064}
1065
1066/**
1067 * Trigger IS to load additional posts if the initial posts don't fill the window.
1068 * On large displays, or when posts are very short, the viewport may not be filled with posts, so we overcome this by loading additional posts when IS initializes.
1069 */
1070Scroller.prototype.ensureFilledViewport = function() {
1071	var	self = this,
1072	   	windowHeight = self.window.height(),
1073	   	postsHeight = self.element.height(),
1074	   	aveSetHeight = 0,
1075	   	wrapperQty = 0;
1076
1077	// Account for situations where postsHeight is 0 because child list elements are floated
1078	if ( postsHeight === 0 ) {
1079		$( self.element.selector + ' > li' ).each( function() {
1080			postsHeight += $( this ).height();
1081		} );
1082
1083		if ( postsHeight === 0 ) {
1084			self.body.unbind( 'post-load', self.checkViewportOnLoad );
1085			return;
1086		}
1087	}
1088
1089	// Calculate average height of a set of posts to prevent more posts than needed from being loaded.
1090	$( '.' + self.wrapperClass ).each( function() {
1091		aveSetHeight += $( this ).height();
1092		wrapperQty++;
1093	} );
1094
1095	if ( wrapperQty > 0 )
1096		aveSetHeight = aveSetHeight / wrapperQty;
1097	else
1098		aveSetHeight = 0;
1099
1100	// Load more posts if space permits, otherwise stop checking for a full viewport
1101	if ( postsHeight < windowHeight && ( postsHeight + aveSetHeight < windowHeight ) ) {
1102		self.ready = true;
1103		self.refresh();
1104	}
1105	else {
1106		self.body.unbind( 'post-load', self.checkViewportOnLoad );
1107	}
1108}
1109
1110/**
1111 * Event handler for ensureFilledViewport(), tied to the post-load trigger.
1112 * Necessary to ensure that the variable `this` contains the scroller when used in ensureFilledViewport(). Since this function is tied to an event, `this` becomes the DOM element the event is tied to.
1113 */
1114Scroller.prototype.checkViewportOnLoad = function( ev ) {
1115	ev.data.self.ensureFilledViewport();
1116}
1117
1118/**
1119 * Identify archive page that corresponds to majority of posts shown in the current browser window.
1120 */
1121Scroller.prototype.determineURL = function () {
1122	var self         = this,
1123		windowTop    = $( window ).scrollTop(),
1124		windowBottom = windowTop + $( window ).height(),
1125		windowSize   = windowBottom - windowTop,
1126		setsInView   = [],
1127		setsHidden   = [],
1128		pageNum      = false;
1129
1130	// Find out which sets are in view
1131	$( '.' + self.wrapperClass ).each( function() {
1132		var id         = $( this ).attr( 'id' ),
1133			setTop     = $( this ).offset().top,
1134			setHeight  = $( this ).outerHeight( false ),
1135			setBottom  = 0,
1136			setPageNum = $( this ).data( 'page-num' );
1137
1138		// Account for containers that have no height because their children are floated elements.
1139		if ( 0 === setHeight ) {
1140			$( '> *', this ).each( function() {
1141				setHeight += $( this ).outerHeight( false );
1142			} );
1143		}
1144
1145		// Determine position of bottom of set by adding its height to the scroll position of its top.
1146		setBottom = setTop + setHeight;
1147
1148		// Populate setsInView object. While this logic could all be combined into a single conditional statement, this is easier to understand.
1149		if ( setTop < windowTop && setBottom > windowBottom ) { // top of set is above window, bottom is below
1150			setsInView.push({'id': id, 'top': setTop, 'bottom': setBottom, 'pageNum': setPageNum });
1151		}
1152		else if( setTop > windowTop && setTop < windowBottom ) { // top of set is between top (gt) and bottom (lt)
1153			setsInView.push({'id': id, 'top': setTop, 'bottom': setBottom, 'pageNum': setPageNum });
1154		}
1155		else if( setBottom > windowTop && setBottom < windowBottom ) { // bottom of set is between top (gt) and bottom (lt)
1156			setsInView.push({'id': id, 'top': setTop, 'bottom': setBottom, 'pageNum': setPageNum });
1157		} else {
1158			setsHidden.push({'id': id, 'top': setTop, 'bottom': setBottom, 'pageNum': setPageNum });
1159		}
1160	} );
1161
1162	$.each(setsHidden, function() {
1163		var $set = $('#' + this.id);
1164		if( $set.hasClass( 'is--replaced' ) ) {
1165			return;
1166		}
1167
1168	        self.pageCache[ this.pageNum].html = $set.html();
1169
1170		$set.css('min-height', ( this.bottom - this.top ) + 'px' )
1171		    .addClass('is--replaced')
1172		    .empty();
1173	});
1174
1175	$.each(setsInView, function() {
1176		var $set = $('#' + this.id);
1177
1178		if( $set.hasClass('is--replaced') ) {
1179			$set.css('min-height', '').removeClass('is--replaced');
1180			if( this.pageNum in self.pageCache ) {
1181				$set.html( self.pageCache[this.pageNum].html );
1182		        	self.body.trigger( 'post-load', self.pageCache[this.pageNum] );
1183			}
1184		}
1185
1186	});
1187
1188	// Parse number of sets found in view in an attempt to update the URL to match the set that comprises the majority of the window.
1189	if ( 0 == setsInView.length ) {
1190		pageNum = -1;
1191	}
1192	else if ( 1 == setsInView.length ) {
1193		var setData = setsInView.pop();
1194
1195		// If the first set of IS posts is in the same view as the posts loaded in the template by WordPress, determine how much of the view is comprised of IS-loaded posts
1196		if ( ( ( windowBottom - setData.top ) / windowSize ) < 0.5 )
1197			pageNum = -1;
1198		else
1199			pageNum = setData.pageNum;
1200	}
1201	else {
1202		var majorityPercentageInView = 0;
1203
1204		// Identify the IS set that comprises the majority of the current window and set the URL to it.
1205		$.each( setsInView, function( i, setData ) {
1206			var topInView     = 0,
1207				bottomInView  = 0,
1208				percentOfView = 0;
1209
1210			// Figure percentage of view the current set represents
1211			if ( setData.top > windowTop && setData.top < windowBottom )
1212				topInView = ( windowBottom - setData.top ) / windowSize;
1213
1214			if ( setData.bottom > windowTop && setData.bottom < windowBottom )
1215				bottomInView = ( setData.bottom - windowTop ) / windowSize;
1216
1217			// Figure out largest percentage of view for current set
1218			if ( topInView >= bottomInView )
1219				percentOfView = topInView;
1220			else if ( bottomInView >= topInView )
1221				percentOfView = bottomInView;
1222
1223			// Does current set's percentage of view supplant the largest previously-found set?
1224			if ( percentOfView > majorityPercentageInView ) {
1225				pageNum = setData.pageNum;
1226				majorityPercentageInView = percentOfView;
1227			}
1228		} );
1229	}
1230
1231	// If a page number could be determined, update the URL
1232	// -1 indicates that the original requested URL should be used.
1233	if ( 'number' == typeof pageNum ) {
1234		if ( pageNum != -1 )
1235			pageNum++;
1236
1237		self.updateURL( pageNum );
1238	}
1239}
1240
1241/**
1242 * Update address bar to reflect archive page URL for a given page number.
1243 * Checks if URL is different to prevent pollution of browser history.
1244 */
1245Scroller.prototype.updateURL = function( page ) {
1246	// IE only supports pushState() in v10 and above, so don't bother if those conditions aren't met.
1247	if ( ! window.history.pushState ) {
1248		return;
1249	}
1250	var self = this,
1251		offset = self.offset > 0 ? self.offset - 1 : 0,
1252		pageSlug = -1 == page ? self.origURL : window.location.protocol + '//' + self.history.host + self.history.path.replace( /%d/, page + offset ) + self.history.parameters;
1253
1254	if ( window.location.href != pageSlug ) {
1255		history.pushState( null, null, pageSlug );
1256	}
1257}
1258
1259/**
1260 * Ready, set, go!
1261 */
1262$( document ).ready( function() {
1263	// Check for our variables
1264	if ( 'object' != typeof infiniteScroll )
1265		return;
1266
1267	// Set ajaxurl (for brevity)
1268	ajaxurl = infiniteScroll.settings.ajaxurl;
1269
1270	// Set stats, used for tracking stats
1271	stats = infiniteScroll.settings.stats;
1272
1273	// Define what type of infinity we have, grab text for click-handle
1274	type  = infiniteScroll.settings.type;
1275	text  = infiniteScroll.settings.text;
1276	totop = infiniteScroll.settings.totop;
1277
1278	// Initialize the scroller (with the ID of the element from the theme)
1279	infiniteScroll.scroller = new Scroller( infiniteScroll.settings );
1280
1281	/**
1282	 * Monitor user scroll activity to update URL to correspond to archive page for current set of IS posts
1283	 */
1284    if( type == 'click' ) {
1285        var timer = null;
1286        $( window ).bind( 'scroll', function() {
1287            // run the real scroll handler once every 250 ms.
1288            if ( timer ) { return; }
1289            timer = setTimeout( function() {
1290                infiniteScroll.scroller.determineURL();
1291                timer = null;
1292            } , 250 );
1293        });
1294    }
1295});
1296
1297
1298})(jQuery); // Close closure
1299;
1300// WARNING: This file is distributed verbatim in Jetpack. @hide-in-jetpack
1301/*!
1302 * jQuery Cycle Plugin (with Transition Definitions)
1303 * Examples and documentation at: http://jquery.malsup.com/cycle/
1304 * Copyright (c) 2007-2010 M. Alsup
1305 * Version: 2.9999.8 (26-OCT-2012)
1306 * Dual licensed under the MIT and GPL licenses.
1307 * http://jquery.malsup.com/license.html
1308 * Requires: jQuery v1.3.2 or later
1309 */
1310;(function($, undefined) {
1311"use strict";
1312
1313var ver = '2.9999.8';
1314
1315// if $.support is not defined (pre jQuery 1.3) add what I need
1316if ($.support === undefined) {
1317	$.support = {
1318		opacity: !($.browser.msie)
1319	};
1320}
1321
1322function debug(s) {
1323	if ($.fn.cycle.debug)
1324		log(s);
1325}
1326function log() {
1327	if (window.console && console.log)
1328		console.log('[cycle] ' + Array.prototype.join.call(arguments,' '));
1329}
1330$.expr[':'].paused = function(el) {
1331	return el.cyclePause;
1332};
1333
1334
1335// the options arg can be...
1336//   a number  - indicates an immediate transition should occur to the given slide index
1337//   a string  - 'pause', 'resume', 'toggle', 'next', 'prev', 'stop', 'destroy' or the name of a transition effect (ie, 'fade', 'zoom', etc)
1338//   an object - properties to control the slideshow
1339//
1340// the arg2 arg can be...
1341//   the name of an fx (only used in conjunction with a numeric value for 'options')
1342//   the value true (only used in first arg == 'resume') and indicates
1343//	 that the resume should occur immediately (not wait for next timeout)
1344
1345$.fn.cycle = function(options, arg2) {
1346	var o = { s: this.selector, c: this.context };
1347
1348	// in 1.3+ we can fix mistakes with the ready state
1349	if (this.length === 0 && options != 'stop') {
1350		if (!$.isReady && o.s) {
1351			log('DOM not ready, queuing slideshow');
1352			$(function() {
1353				$(o.s,o.c).cycle(options,arg2);
1354			});
1355			return this;
1356		}
1357		// is your DOM ready?  http://docs.jquery.com/Tutorials:Introducing_$(document).ready()
1358		log('terminating; zero elements found by selector' + ($.isReady ? '' : ' (DOM not ready)'));
1359		return this;
1360	}
1361
1362	// iterate the matched nodeset
1363	return this.each(function() {
1364		var opts = handleArguments(this, options, arg2);
1365		if (opts === false)
1366			return;
1367
1368		opts.updateActivePagerLink = opts.updateActivePagerLink || $.fn.cycle.updateActivePagerLink;
1369
1370		// stop existing slideshow for this container (if there is one)
1371		if (this.cycleTimeout)
1372			clearTimeout(this.cycleTimeout);
1373		this.cycleTimeout = this.cyclePause = 0;
1374		this.cycleStop = 0; // issue #108
1375
1376		var $cont = $(this);
1377		var $slides = opts.slideExpr ? $(opts.slideExpr, this) : $cont.children();
1378		var els = $slides.get();
1379
1380		if (els.length < 2) {
1381			log('terminating; too few slides: ' + els.length);
1382			return;
1383		}
1384
1385		var opts2 = buildOptions($cont, $slides, els, opts, o);
1386		if (opts2 === false)
1387			return;
1388
1389		var startTime = opts2.continuous ? 10 : getTimeout(els[opts2.currSlide], els[opts2.nextSlide], opts2, !opts2.backwards);
1390
1391		// if it's an auto slideshow, kick it off
1392		if (startTime) {
1393			startTime += (opts2.delay || 0);
1394			if (startTime < 10)
1395				startTime = 10;
1396			debug('first timeout: ' + startTime);
1397			this.cycleTimeout = setTimeout(function(){go(els,opts2,0,!opts.backwards);}, startTime);
1398		}
1399	});
1400};
1401
1402function triggerPause(cont, byHover, onPager) {
1403	var opts = $(cont).data('cycle.opts');
1404	if (!opts)
1405		return;
1406	var paused = !!cont.cyclePause;
1407	if (paused && opts.paused)
1408		opts.paused(cont, opts, byHover, onPager);
1409	else if (!paused && opts.resumed)
1410		opts.resumed(cont, opts, byHover, onPager);
1411}
1412
1413// process the args that were passed to the plugin fn
1414function handleArguments(cont, options, arg2) {
1415	if (cont.cycleStop === undefined)
1416		cont.cycleStop = 0;
1417	if (options === undefined || options === null)
1418		options = {};
1419	if (options.constructor == String) {
1420		switch(options) {
1421		case 'destroy':
1422		case 'stop':
1423			var opts = $(cont).data('cycle.opts');
1424			if (!opts)
1425				return false;
1426			cont.cycleStop++; // callbacks look for change
1427			if (cont.cycleTimeout)
1428				clearTimeout(cont.cycleTimeout);
1429			cont.cycleTimeout = 0;
1430			if (opts.elements)
1431				$(opts.elements).stop();
1432			$(cont).removeData('cycle.opts');
1433			if (options == 'destroy')
1434				destroy(cont, opts);
1435			return false;
1436		case 'toggle':
1437			cont.cyclePause = (cont.cyclePause === 1) ? 0 : 1;
1438			checkInstantResume(cont.cyclePause, arg2, cont);
1439			triggerPause(cont);
1440			return false;
1441		case 'pause':
1442			cont.cyclePause = 1;
1443			triggerPause(cont);
1444			return false;
1445		case 'resume':
1446			cont.cyclePause = 0;
1447			checkInstantResume(false, arg2, cont);
1448			triggerPause(cont);
1449			return false;
1450		case 'prev':
1451		case 'next':
1452			opts = $(cont).data('cycle.opts');
1453			if (!opts) {
1454				log('options not found, "prev/next" ignored');
1455				return false;
1456			}
1457			$.fn.cycle[options](opts);
1458			return false;
1459		default:
1460			options = { fx: options };
1461		}
1462		return options;
1463	}
1464	else if (options.constructor == Number) {
1465		// go to the requested slide
1466		var num = options;
1467		options = $(cont).data('cycle.opts');
1468		if (!options) {
1469			log('options not found, can not advance slide');
1470			return false;
1471		}
1472		if (num < 0 || num >= options.elements.length) {
1473			log('invalid slide index: ' + num);
1474			return false;
1475		}
1476		options.nextSlide = num;
1477		if (cont.cycleTimeout) {
1478			clearTimeout(cont.cycleTimeout);
1479			cont.cycleTimeout = 0;
1480		}
1481		if (typeof arg2 == 'string')
1482			options.oneTimeFx = arg2;
1483		go(options.elements, options, 1, num >= options.currSlide);
1484		return false;
1485	}
1486	return options;
1487
1488	function checkInstantResume(isPaused, arg2, cont) {
1489		if (!isPaused && arg2 === true) { // resume now!
1490			var options = $(cont).data('cycle.opts');
1491			if (!options) {
1492				log('options not found, can not resume');
1493				return false;
1494			}
1495			if (cont.cycleTimeout) {
1496				clearTimeout(cont.cycleTimeout);
1497				cont.cycleTimeout = 0;
1498			}
1499			go(options.elements, options, 1, !options.backwards);
1500		}
1501	}
1502}
1503
1504function removeFilter(el, opts) {
1505	if (!$.support.opacity && opts.cleartype && el.style.filter) {
1506		try { el.style.removeAttribute('filter'); }
1507		catch(smother) {} // handle old opera versions
1508	}
1509}
1510
1511// unbind event handlers
1512function destroy(cont, opts) {
1513	if (opts.next)
1514		$(opts.next).unbind(opts.prevNextEvent);
1515	if (opts.prev)
1516		$(opts.prev).unbind(opts.prevNextEvent);
1517
1518	if (opts.pager || opts.pagerAnchorBuilder)
1519		$.each(opts.pagerAnchors || [], function() {
1520			this.unbind().remove();
1521		});
1522	opts.pagerAnchors = null;
1523	$(cont).unbind('mouseenter.cycle mouseleave.cycle');
1524	if (opts.destroy) // callback
1525		opts.destroy(opts);
1526}
1527
1528// one-time initialization
1529function buildOptions($cont, $slides, els, options, o) {
1530	var startingSlideSpecified;
1531	// support metadata plugin (v1.0 and v2.0)
1532	var opts = $.extend({}, $.fn.cycle.defaults, options || {}, $.metadata ? $cont.metadata() : $.meta ? $cont.data() : {});
1533	var meta = $.isFunction($cont.data) ? $cont.data(opts.metaAttr) : null;
1534	if (meta)
1535		opts = $.extend(opts, meta);
1536	if (opts.autostop)
1537		opts.countdown = opts.autostopCount || els.length;
1538
1539	var cont = $cont[0];
1540	$cont.data('cycle.opts', opts);
1541	opts.$cont = $cont;
1542	opts.stopCount = cont.cycleStop;
1543	opts.elements = els;
1544	opts.before = opts.before ? [opts.before] : [];
1545	opts.after = opts.after ? [opts.after] : [];
1546
1547	// push some after callbacks
1548	if (!$.support.opacity && opts.cleartype)
1549		opts.after.push(function() { removeFilter(this, opts); });
1550	if (opts.continuous)
1551		opts.after.push(function() { go(els,opts,0,!opts.backwards); });
1552
1553	saveOriginalOpts(opts);
1554
1555	// clearType corrections
1556	if (!$.support.opacity && opts.cleartype && !opts.cleartypeNoBg)
1557		clearTypeFix($slides);
1558
1559	// container requires non-static position so that slides can be position within
1560	if ($cont.css('position') == 'static')
1561		$cont.css('position', 'relative');
1562	if (opts.width)
1563		$cont.width(opts.width);
1564	if (opts.height && opts.height != 'auto')
1565		$cont.height(opts.height);
1566
1567	if (opts.startingSlide !== undefined) {
1568		opts.startingSlide = parseInt(opts.startingSlide,10);
1569		if (opts.startingSlide >= els.length || opts.startSlide < 0)
1570			opts.startingSlide = 0; // catch bogus input
1571		else
1572			startingSlideSpecified = true;
1573	}
1574	else if (opts.backwards)
1575		opts.startingSlide = els.length - 1;
1576	else
1577		opts.startingSlide = 0;
1578
1579	// if random, mix up the slide array
1580	if (opts.random) {
1581		opts.randomMap = [];
1582		for (var i = 0; i < els.length; i++)
1583			opts.randomMap.push(i);
1584		opts.randomMap.sort(function(a,b) {return Math.random() - 0.5;});
1585		if (startingSlideSpecified) {
1586			// try to find the specified starting slide and if found set start slide index in the map accordingly
1587			for ( var cnt = 0; cnt < els.length; cnt++ ) {
1588				if ( opts.startingSlide == opts.randomMap[cnt] ) {
1589					opts.randomIndex = cnt;
1590				}
1591			}
1592		}
1593		else {
1594			opts.randomIndex = 1;
1595			opts.startingSlide = opts.randomMap[1];
1596		}
1597	}
1598	else if (opts.startingSlide >= els.length)
1599		opts.startingSlide = 0; // catch bogus input
1600	opts.currSlide = opts.startingSlide || 0;
1601	var first = opts.startingSlide;
1602
1603	// set position and zIndex on all the slides
1604	$slides.css({position: 'absolute', top:0, left:0}).hide().each(function(i) {
1605		var z;
1606		if (opts.backwards)
1607			z = first ? i <= first ? els.length + (i-first) : first-i : els.length-i;
1608		else
1609			z = first ? i >= first ? els.length - (i-first) : first-i : els.length-i;
1610		$(this).css('z-index', z);
1611	});
1612
1613	// make sure first slide is visible
1614	$(els[first]).css('opacity',1).show(); // opacity bit needed to handle restart use case
1615	removeFilter(els[first], opts);
1616
1617	// stretch slides
1618	if (opts.fit) {
1619		if (!opts.aspect) {
1620	        if (opts.width)
1621	            $slides.width(opts.width);
1622	        if (opts.height && opts.height != 'auto')
1623	            $slides.height(opts.height);
1624		} else {
1625			$slides.each(function(){
1626				var $slide = $(this);
1627				var ratio = (opts.aspect === true) ? $slide.width()/$slide.height() : opts.aspect;
1628				if( opts.width && $slide.width() != opts.width ) {
1629					$slide.width( opts.width );
1630					$slide.height( opts.width / ratio );
1631				}
1632
1633				if( opts.height && $slide.height() < opts.height ) {
1634					$slide.height( opts.height );
1635					$slide.width( opts.height * ratio );
1636				}
1637			});
1638		}
1639	}
1640
1641	if (opts.center && ((!opts.fit) || opts.aspect)) {
1642		$slides.each(function(){
1643			var $slide = $(this);
1644			$slide.css({
1645				"margin-left": opts.width ?
1646					((opts.width - $slide.width()) / 2) + "px" :
1647					0,
1648				"margin-top": opts.height ?
1649					((opts.height - $slide.height()) / 2) + "px" :
1650					0
1651			});
1652		});
1653	}
1654
1655	if (opts.center && !opts.fit && !opts.slideResize) {
1656		$slides.each(function(){
1657			var $slide = $(this);
1658			$slide.css({
1659				"margin-left": opts.width ? ((opts.width - $slide.width()) / 2) + "px" : 0,
1660				"margin-top": opts.height ? ((opts.height - $slide.height()) / 2) + "px" : 0
1661			});
1662		});
1663	}
1664
1665	// stretch container
1666	var reshape = (opts.containerResize || opts.containerResizeHeight) && !$cont.innerHeight();
1667	if (reshape) { // do this only if container has no size http://tinyurl.com/da2oa9
1668		var maxw = 0, maxh = 0;
1669		for(var j=0; j < els.length; j++) {
1670			var $e = $(els[j]), e = $e[0], w = $e.outerWidth(), h = $e.outerHeight();
1671			if (!w) w = e.offsetWidth || e.width || $e.attr('width');
1672			if (!h) h = e.offsetHeight || e.height || $e.attr('height');
1673			maxw = w > maxw ? w : maxw;
1674			maxh = h > maxh ? h : maxh;
1675		}
1676		if (opts.containerResize && maxw > 0 && maxh > 0)
1677			$cont.css({width:maxw+'px',height:maxh+'px'});
1678		if (opts.containerResizeHeight && maxh > 0)
1679			$cont.css({height:maxh+'px'});
1680	}
1681
1682	var pauseFlag = false;  // https://github.com/malsup/cycle/issues/44
1683	if (opts.pause)
1684		$cont.bind('mouseenter.cycle', function(){
1685			pauseFlag = true;
1686			this.cyclePause++;
1687			triggerPause(cont, true);
1688		}).bind('mouseleave.cycle', function(){
1689				if (pauseFlag)
1690					this.cyclePause--;
1691				triggerPause(cont, true);
1692		});
1693
1694	if (supportMultiTransitions(opts) === false)
1695		return false;
1696
1697	// apparently a lot of people use image slideshows without height/width attributes on the images.
1698	// Cycle 2.50+ requires the sizing info for every slide; this block tries to deal with that.
1699	var requeue = false;
1700	options.requeueAttempts = options.requeueAttempts || 0;
1701	$slides.each(function() {
1702		// try to get height/width of each slide
1703		var $el = $(this);
1704		this.cycleH = (opts.fit && opts.height) ? opts.height : ($el.height() || this.offsetHeight || this.height || $el.attr('height') || 0);
1705		this.cycleW = (opts.fit && opts.width) ? opts.width : ($el.width() || this.offsetWidth || this.width || $el.attr('width') || 0);
1706
1707		if ( $el.is('img') ) {
1708			// sigh..  sniffing, hacking, shrugging...  this crappy hack tries to account for what browsers do when
1709			// an image is being downloaded and the markup did not include sizing info (height/width attributes);
1710			// there seems to be some "default" sizes used in this situation
1711			var loadingIE	= ($.browser.msie  && this.cycleW == 28 && this.cycleH == 30 && !this.complete);
1712			var loadingFF	= ($.browser.mozilla && this.cycleW == 34 && this.cycleH == 19 && !this.complete);
1713			var loadingOp	= ($.browser.opera && ((this.cycleW == 42 && this.cycleH == 19) || (this.cycleW == 37 && this.cycleH == 17)) && !this.complete);
1714			var loadingOther = (this.cycleH === 0 && this.cycleW === 0 && !this.complete);
1715			// don't requeue for images that are still loading but have a valid size
1716			if (loadingIE || loadingFF || loadingOp || loadingOther) {
1717				if (o.s && opts.requeueOnImageNotLoaded && ++options.requeueAttempts < 100) { // track retry count so we don't loop forever
1718					log(options.requeueAttempts,' - img slide not loaded, requeuing slideshow: ', this.src, this.cycleW, this.cycleH);
1719					setTimeout(function() {$(o.s,o.c).cycle(options);}, opts.requeueTimeout);
1720					requeue = true;
1721					return false; // break each loop
1722				}
1723				else {
1724					log('could not determine size of image: '+this.src, this.cycleW, this.cycleH);
1725				}
1726			}
1727		}
1728		return true;
1729	});
1730
1731	if (requeue)
1732		return false;
1733
1734	opts.cssBefore = opts.cssBefore || {};
1735	opts.cssAfter = opts.cssAfter || {};
1736	opts.cssFirst = opts.cssFirst || {};
1737	opts.animIn = opts.animIn || {};
1738	opts.animOut = opts.animOut || {};
1739
1740	$slides.not(':eq('+first+')').css(opts.cssBefore);
1741	$($slides[first]).css(opts.cssFirst);
1742
1743	if (opts.timeout) {
1744		opts.timeout = parseInt(opts.timeout,10);
1745		// ensure that timeout and speed settings are sane
1746		if (opts.speed.constructor == String)
1747			opts.speed = $.fx.speeds[opts.speed] || parseInt(opts.speed,10);
1748		if (!opts.sync)
1749			opts.speed = opts.speed / 2;
1750
1751		var buffer = opts.fx == 'none' ? 0 : opts.fx == 'shuffle' ? 500 : 250;
1752		while((opts.timeout - opts.speed) < buffer) // sanitize timeout
1753			opts.timeout += opts.speed;
1754	}
1755	if (opts.easing)
1756		opts.easeIn = opts.easeOut = opts.easing;
1757	if (!opts.speedIn)
1758		opts.speedIn = opts.speed;
1759	if (!opts.speedOut)
1760		opts.speedOut = opts.speed;
1761
1762	opts.slideCount = els.length;
1763	opts.currSlide = opts.lastSlide = first;
1764	if (opts.random) {
1765		if (++opts.randomIndex == els.length)
1766			opts.randomIndex = 0;
1767		opts.nextSlide = opts.randomMap[opts.randomIndex];
1768	}
1769	else if (opts.backwards)
1770		opts.nextSlide = opts.startingSlide === 0 ? (els.length-1) : opts.startingSlide-1;
1771	else
1772		opts.nextSlide = opts.startingSlide >= (els.length-1) ? 0 : opts.startingSlide+1;
1773
1774	// run transition init fn
1775	if (!opts.multiFx) {
1776		var init = $.fn.cycle.transitions[opts.fx];
1777		if ($.isFunction(init))
1778			init($cont, $slides, opts);
1779		else if (opts.fx != 'custom' && !opts.multiFx) {
1780			log('unknown transition: ' + opts.fx,'; slideshow terminating');
1781			return false;
1782		}
1783	}
1784
1785	// fire artificial events
1786	var e0 = $slides[first];
1787	if (!opts.skipInitializationCallbacks) {
1788		if (opts.before.length)
1789			opts.before[0].apply(e0, [e0, e0, opts, true]);
1790		if (opts.after.length)
1791			opts.after[0].apply(e0, [e0, e0, opts, true]);
1792	}
1793	if (opts.next)
1794		$(opts.next).bind(opts.prevNextEvent,function(){return advance(opts,1);});
1795	if (opts.prev)
1796		$(opts.prev).bind(opts.prevNextEvent,function(){return advance(opts,0);});
1797	if (opts.pager || opts.pagerAnchorBuilder)
1798		buildPager(els,opts);
1799
1800	exposeAddSlide(opts, els);
1801
1802	return opts;
1803}
1804
1805// save off original opts so we can restore after clearing state
1806function saveOriginalOpts(opts) {
1807	opts.original = { before: [], after: [] };
1808	opts.original.cssBefore = $.extend({}, opts.cssBefore);
1809	opts.original.cssAfter  = $.extend({}, opts.cssAfter);
1810	opts.original.animIn	= $.extend({}, opts.animIn);
1811	opts.original.animOut   = $.extend({}, opts.animOut);
1812	$.each(opts.before, function() { opts.original.before.push(this); });
1813	$.each(opts.after,  function() { opts.original.after.push(this); });
1814}
1815
1816function supportMultiTransitions(opts) {
1817	var i, tx, txs = $.fn.cycle.transitions;
1818	// look for multiple effects
1819	if (opts.fx.indexOf(',') > 0) {
1820		opts.multiFx = true;
1821		opts.fxs = opts.fx.replace(/\s*/g,'').split(',');
1822		// discard any bogus effect names
1823		for (i=0; i < opts.fxs.length; i++) {
1824			var fx = opts.fxs[i];
1825			tx = txs[fx];
1826			if (!tx || !txs.hasOwnProperty(fx) || !$.isFunction(tx)) {
1827				log('discarding unknown transition: ',fx);
1828				opts.fxs.splice(i,1);
1829				i--;
1830			}
1831		}
1832		// if we have an empty list then we threw everything away!
1833		if (!opts.fxs.length) {
1834			log('No valid transitions named; slideshow terminating.');
1835			return false;
1836		}
1837	}
1838	else if (opts.fx == 'all') {  // auto-gen the list of transitions
1839		opts.multiFx = true;
1840		opts.fxs = [];
1841		for (var p in txs) {
1842			if (txs.hasOwnProperty(p)) {
1843				tx = txs[p];
1844				if (txs.hasOwnProperty(p) && $.isFunction(tx))
1845					opts.fxs.push(p);
1846			}
1847		}
1848	}
1849	if (opts.multiFx && opts.randomizeEffects) {
1850		// munge the fxs array to make effect selection random
1851		var r1 = Math.floor(Math.random() * 20) + 30;
1852		for (i = 0; i < r1; i++) {
1853			var r2 = Math.floor(Math.random() * opts.fxs.length);
1854			opts.fxs.push(opts.fxs.splice(r2,1)[0]);
1855		}
1856		debug('randomized fx sequence: ',opts.fxs);
1857	}
1858	return true;
1859}
1860
1861// provide a mechanism for adding slides after the slideshow has started
1862function exposeAddSlide(opts, els) {
1863	opts.addSlide = function(newSlide, prepend) {
1864		var $s = $(newSlide), s = $s[0];
1865		if (!opts.autostopCount)
1866			opts.countdown++;
1867		els[prepend?'unshift':'push'](s);
1868		if (opts.els)
1869			opts.els[prepend?'unshift':'push'](s); // shuffle needs this
1870		opts.slideCount = els.length;
1871
1872		// add the slide to the random map and resort
1873		if (opts.random) {
1874			opts.randomMap.push(opts.slideCount-1);
1875			opts.randomMap.sort(function(a,b) {return Math.random() - 0.5;});
1876		}
1877
1878		$s.css('position','absolute');
1879		$s[prepend?'prependTo':'appendTo'](opts.$cont);
1880
1881		if (prepend) {
1882			opts.currSlide++;
1883			opts.nextSlide++;
1884		}
1885
1886		if (!$.support.opacity && opts.cleartype && !opts.cleartypeNoBg)
1887			clearTypeFix($s);
1888
1889		if (opts.fit && opts.width)
1890			$s.width(opts.width);
1891		if (opts.fit && opts.height && opts.height != 'auto')
1892			$s.height(opts.height);
1893		s.cycleH = (opts.fit && opts.height) ? opts.height : $s.height();
1894		s.cycleW = (opts.fit && opts.width) ? opts.width : $s.width();
1895
1896		$s.css(opts.cssBefore);
1897
1898		if (opts.pager || opts.pagerAnchorBuilder)
1899			$.fn.cycle.createPagerAnchor(els.length-1, s, $(opts.pager), els, opts);
1900
1901		if ($.isFunction(opts.onAddSlide))
1902			opts.onAddSlide($s);
1903		else
1904			$s.hide(); // default behavior
1905	};
1906}
1907
1908// reset internal state; we do this on every pass in order to support multiple effects
1909$.fn.cycle.resetState = function(opts, fx) {
1910	fx = fx || opts.fx;
1911	opts.before = []; opts.after = [];
1912	opts.cssBefore = $.extend({}, opts.original.cssBefore);
1913	opts.cssAfter  = $.extend({}, opts.original.cssAfter);
1914	opts.animIn	= $.extend({}, opts.original.animIn);
1915	opts.animOut   = $.extend({}, opts.original.animOut);
1916	opts.fxFn = null;
1917	$.each(opts.original.before, function() { opts.before.push(this); });
1918	$.each(opts.original.after,  function() { opts.after.push(this); });
1919
1920	// re-init
1921	var init = $.fn.cycle.transitions[fx];
1922	if ($.isFunction(init))
1923		init(opts.$cont, $(opts.elements), opts);
1924};
1925
1926// this is the main engine fn, it handles the timeouts, callbacks and slide index mgmt
1927function go(els, opts, manual, fwd) {
1928	var p = opts.$cont[0], curr = els[opts.currSlide], next = els[opts.nextSlide];
1929
1930	// opts.busy is true if we're in the middle of an animation
1931	if (manual && opts.busy && opts.manualTrump) {
1932		// let manual transitions requests trump active ones
1933		debug('manualTrump in go(), stopping active transition');
1934		$(els).stop(true,true);
1935		opts.busy = 0;
1936		clearTimeout(p.cycleTimeout);
1937	}
1938
1939	// don't begin another timeout-based transition if there is one active
1940	if (opts.busy) {
1941		debug('transition active, ignoring new tx request');
1942		return;
1943	}
1944
1945
1946	// stop cycling if we have an outstanding stop request
1947	if (p.cycleStop != opts.stopCount || p.cycleTimeout === 0 && !manual)
1948		return;
1949
1950	// check to see if we should stop cycling based on autostop options
1951	if (!manual && !p.cyclePause && !opts.bounce &&
1952		((opts.autostop && (--opts.countdown <= 0)) ||
1953		(opts.nowrap && !opts.random && opts.nextSlide < opts.currSlide))) {
1954		if (opts.end)
1955			opts.end(opts);
1956		return;
1957	}
1958
1959	// if slideshow is paused, only transition on a manual trigger
1960	var changed = false;
1961	if ((manual || !p.cyclePause) && (opts.nextSlide != opts.currSlide)) {
1962		changed = true;
1963		var fx = opts.fx;
1964		// keep trying to get the slide size if we don't have it yet
1965		curr.cycleH = curr.cycleH || $(curr).height();
1966		curr.cycleW = curr.cycleW || $(curr).width();
1967		next.cycleH = next.cycleH || $(next).height();
1968		next.cycleW = next.cycleW || $(next).width();
1969
1970		// support multiple transition types
1971		if (opts.multiFx) {
1972			if (fwd && (opts.lastFx === undefined || ++opts.lastFx >= opts.fxs.length))
1973				opts.lastFx = 0;
1974			else if (!fwd && (opts.lastFx === undefined || --opts.lastFx < 0))
1975				opts.lastFx = opts.fxs.length - 1;
1976			fx = opts.fxs[opts.lastFx];
1977		}
1978
1979		// one-time fx overrides apply to:  $('div').cycle(3,'zoom');
1980		if (opts.oneTimeFx) {
1981			fx = opts.oneTimeFx;
1982			opts.oneTimeFx = null;
1983		}
1984
1985		$.fn.cycle.resetState(opts, fx);
1986
1987		// run the before callbacks
1988		if (opts.before.length)
1989			$.each(opts.before, function(i,o) {
1990				if (p.cycleStop != opts.stopCount) return;
1991				o.apply(next, [curr, next, opts, fwd]);
1992			});
1993
1994		// stage the after callacks
1995		var after = function() {
1996			opts.busy = 0;
1997			$.each(opts.after, function(i,o) {
1998				if (p.cycleStop != opts.stopCount) return;
1999				o.apply(next, [curr, next, opts, fwd]);
2000			});
2001			if (!p.cycleStop) {
2002				// queue next transition
2003				queueNext();
2004			}
2005		};
2006
2007		debug('tx firing('+fx+'); currSlide: ' + opts.currSlide + '; nextSlide: ' + opts.nextSlide);
2008
2009		// get ready to perform the transition
2010		opts.busy = 1;
2011		if (opts.fxFn) // fx function provided?
2012			opts.fxFn(curr, next, opts, after, fwd, manual && opts.fastOnEvent);
2013		else if ($.isFunction($.fn.cycle[opts.fx])) // fx plugin ?
2014			$.fn.cycle[opts.fx](curr, next, opts, after, fwd, manual && opts.fastOnEvent);
2015		else
2016			$.fn.cycle.custom(curr, next, opts, after, fwd, manual && opts.fastOnEvent);
2017	}
2018	else {
2019		queueNext();
2020	}
2021
2022	if (changed || opts.nextSlide == opts.currSlide) {
2023		// calculate the next slide
2024		var roll;
2025		opts.lastSlide = opts.currSlide;
2026		if (opts.random) {
2027			opts.currSlide = opts.nextSlide;
2028			if (++opts.randomIndex == els.length) {
2029				opts.randomIndex = 0;
2030				opts.randomMap.sort(function(a,b) {return Math.random() - 0.5;});
2031			}
2032			opts.nextSlide = opts.randomMap[opts.randomIndex];
2033			if (opts.nextSlide == opts.currSlide)
2034				opts.nextSlide = (opts.currSlide == opts.slideCount - 1) ? 0 : opts.currSlide + 1;
2035		}
2036		else if (opts.backwards) {
2037			roll = (opts.nextSlide - 1) < 0;
2038			if (roll && opts.bounce) {
2039				opts.backwards = !opts.backwards;
2040				opts.nextSlide = 1;
2041				opts.currSlide = 0;
2042			}
2043			else {
2044				opts.nextSlide = roll ? (els.length-1) : opts.nextSlide-1;
2045				opts.currSlide = roll ? 0 : opts.nextSlide+1;
2046			}
2047		}
2048		else { // sequence
2049			roll = (opts.nextSlide + 1) == els.length;
2050			if (roll && opts.bounce) {
2051				opts.backwards = !opts.backwards;
2052				opts.nextSlide = els.length-2;
2053				opts.currSlide = els.length-1;
2054			}
2055			else {
2056				opts.nextSlide = roll ? 0 : opts.nextSlide+1;
2057				opts.currSlide = roll ? els.length-1 : opts.nextSlide-1;
2058			}
2059		}
2060	}
2061	if (changed && opts.pager)
2062		opts.updateActivePagerLink(opts.pager, opts.currSlide, opts.activePagerClass);
2063
2064	function queueNext() {
2065		// stage the next transition
2066		var ms = 0, timeout = opts.timeout;
2067		if (opts.timeout && !opts.continuous) {
2068			ms = getTimeout(els[opts.currSlide], els[opts.nextSlide], opts, fwd);
2069         if (opts.fx == 'shuffle')
2070            ms -= opts.speedOut;
2071      }
2072		else if (opts.continuous && p.cyclePause) // continuous shows work off an after callback, not this timer logic
2073			ms = 10;
2074		if (ms > 0)
2075			p.cycleTimeout = setTimeout(function(){ go(els, opts, 0, !opts.backwards); }, ms);
2076	}
2077}
2078
2079// invoked after transition
2080$.fn.cycle.updateActivePagerLink = function(pager, currSlide, clsName) {
2081   $(pager).each(function() {
2082       $(this).children().removeClass(clsName).eq(currSlide).addClass(clsName);
2083   });
2084};
2085
2086// calculate timeout value for current transition
2087function getTimeout(curr, next, opts, fwd) {
2088	if (opts.timeoutFn) {
2089		// call user provided calc fn
2090		var t = opts.timeoutFn.call(curr,curr,next,opts,fwd);
2091		while (opts.fx != 'none' && (t - opts.speed) < 250) // sanitize timeout
2092			t += opts.speed;
2093		debug('calculated timeout: ' + t + '; speed: ' + opts.speed);
2094		if (t !== false)
2095			return t;
2096	}
2097	return opts.timeout;
2098}
2099
2100// expose next/prev function, caller must pass in state
2101$.fn.cycle.next = function(opts) { advance(opts,1); };
2102$.fn.cycle.prev = function(opts) { advance(opts,0);};
2103
2104// advance slide forward or back
2105function advance(opts, moveForward) {
2106	var val = moveForward ? 1 : -1;
2107	var els = opts.elements;
2108	var p = opts.$cont[0], timeout = p.cycleTimeout;
2109	if (timeout) {
2110		clearTimeout(timeout);
2111		p.cycleTimeout = 0;
2112	}
2113	if (opts.random && val < 0) {
2114		// move back to the previously display slide
2115		opts.randomIndex--;
2116		if (--opts.randomIndex == -2)
2117			opts.randomIndex = els.length-2;
2118		else if (opts.randomIndex == -1)
2119			opts.randomIndex = els.length-1;
2120		opts.nextSlide = opts.randomMap[opts.randomIndex];
2121	}
2122	else if (opts.random) {
2123		opts.nextSlide = opts.randomMap[opts.randomIndex];
2124	}
2125	else {
2126		opts.nextSlide = opts.currSlide + val;
2127		if (opts.nextSlide < 0) {
2128			if (opts.nowrap) return false;
2129			opts.nextSlide = els.length - 1;
2130		}
2131		else if (opts.nextSlide >= els.length) {
2132			if (opts.nowrap) return false;
2133			opts.nextSlide = 0;
2134		}
2135	}
2136
2137	var cb = opts.onPrevNextEvent || opts.prevNextClick; // prevNextClick is deprecated
2138	if ($.isFunction(cb))
2139		cb(val > 0, opts.nextSlide, els[opts.nextSlide]);
2140	go(els, opts, 1, moveForward);
2141	return false;
2142}
2143
2144function buildPager(els, opts) {
2145	var $p = $(opts.pager);
2146	$.each(els, function(i,o) {
2147		$.fn.cycle.createPagerAnchor(i,o,$p,els,opts);
2148	});
2149	opts.updateActivePagerLink(opts.pager, opts.startingSlide, opts.activePagerClass);
2150}
2151
2152$.fn.cycle.createPagerAnchor = function(i, el, $p, els, opts) {
2153	var a;
2154	if ($.isFunction(opts.pagerAnchorBuilder)) {
2155		a = opts.pagerAnchorBuilder(i,el);
2156		debug('pagerAnchorBuilder('+i+', el) returned: ' + a);
2157	}
2158	else
2159		a = '<a href="#">'+(i+1)+'</a>';
2160
2161	if (!a)
2162		return;
2163	var $a = $(a);
2164	// don't reparent if anchor is in the dom
2165	if ($a.parents('body').length === 0) {
2166		var arr = [];
2167		if ($p.length > 1) {
2168			$p.each(function() {
2169				var $clone = $a.clone(true);
2170				$(this).append($clone);
2171				arr.push($clone[0]);
2172			});
2173			$a = $(arr);
2174		}
2175		else {
2176			$a.appendTo($p);
2177		}
2178	}
2179
2180	opts.pagerAnchors =  opts.pagerAnchors || [];
2181	opts.pagerAnchors.push($a);
2182
2183	var pagerFn = function(e) {
2184		e.preventDefault();
2185		opts.nextSlide = i;
2186		var p = opts.$cont[0], timeout = p.cycleTimeout;
2187		if (timeout) {
2188			clearTimeout(timeout);
2189			p.cycleTimeout = 0;
2190		}
2191		var cb = opts.onPagerEvent || opts.pagerClick; // pagerClick is deprecated
2192		if ($.isFunction(cb))
2193			cb(opts.nextSlide, els[opts.nextSlide]);
2194		go(els,opts,1,opts.currSlide < i); // trigger the trans
2195//		return false; // <== allow bubble
2196	};
2197
2198	if ( /mouseenter|mouseover/i.test(opts.pagerEvent) ) {
2199		$a.hover(pagerFn, function(){/* no-op */} );
2200	}
2201	else {
2202		$a.bind(opts.pagerEvent, pagerFn);
2203	}
2204
2205	if ( ! /^click/.test(opts.pagerEvent) && !opts.allowPagerClickBubble)
2206		$a.bind('click.cycle', function(){return false;}); // suppress click
2207
2208	var cont = opts.$cont[0];
2209	var pauseFlag = false; // https://github.com/malsup/cycle/issues/44
2210	if (opts.pauseOnPagerHover) {
2211		$a.hover(
2212			function() {
2213				pauseFlag = true;
2214				cont.cyclePause++;
2215				triggerPause(cont,true,true);
2216			}, function() {
2217				if (pauseFlag)
2218					cont.cyclePause--;
2219				triggerPause(cont,true,true);
2220			}
2221		);
2222	}
2223};
2224
2225// helper fn to calculate the number of slides between the current and the next
2226$.fn.cycle.hopsFromLast = function(opts, fwd) {
2227	var hops, l = opts.lastSlide, c = opts.currSlide;
2228	if (fwd)
2229		hops = c > l ? c - l : opts.slideCount - l;
2230	else
2231		hops = c < l ? l - c : l + opts.slideCount - c;
2232	return hops;
2233};
2234
2235// fix clearType problems in ie6 by setting an explicit bg color
2236// (otherwise text slides look horrible during a fade transition)
2237function clearTypeFix($slides) {
2238	debug('applying clearType background-color hack');
2239	function hex(s) {
2240		s = parseInt(s,10).toString(16);
2241		return s.length < 2 ? '0'+s : s;
2242	}
2243	function getBg(e) {
2244		for ( ; e && e.nodeName.toLowerCase() != 'html'; e = e.parentNode) {
2245			var v = $.css(e,'background-color');
2246			if (v && v.indexOf('rgb') >= 0 ) {
2247				var rgb = v.match(/\d+/g);
2248				return '#'+ hex(rgb[0]) + hex(rgb[1]) + hex(rgb[2]);
2249			}
2250			if (v && v != 'transparent')
2251				return v;
2252		}
2253		return '#ffffff';
2254	}
2255	$slides.each(function() { $(this).css('background-color', getBg(this)); });
2256}
2257
2258// reset common props before the next transition
2259$.fn.cycle.commonReset = function(curr,next,opts,w,h,rev) {
2260	$(opts.elements).not(curr).hide();
2261	if (typeof opts.cssBefore.opacity == 'undefined')
2262		opts.cssBefore.opacity = 1;
2263	opts.cssBefore.display = 'block';
2264	if (opts.slideResize && w !== false && next.cycleW > 0)
2265		opts.cssBefore.width = next.cycleW;
2266	if (opts.slideResize && h !== false && next.cycleH > 0)
2267		opts.cssBefore.height = next.cycleH;
2268	opts.cssAfter = opts.cssAfter || {};
2269	opts.cssAfter.display = 'none';
2270	$(curr).css('zIndex',opts.slideCount + (rev === true ? 1 : 0));
2271	$(next).css('zIndex',opts.slideCount + (rev === true ? 0 : 1));
2272};
2273
2274// the actual fn for effecting a transition
2275$.fn.cycle.custom = function(curr, next, opts, cb, fwd, speedOverride) {
2276	var $l = $(curr), $n = $(next);
2277	var speedIn = opts.speedIn, speedOut = opts.speedOut, easeIn = opts.easeIn, easeOut = opts.easeOut;
2278	$n.css(opts.cssBefore);
2279	if (speedOverride) {
2280		if (typeof speedOverride == 'number')
2281			speedIn = speedOut = speedOverride;
2282		else
2283			speedIn = speedOut = 1;
2284		easeIn = easeOut = null;
2285	}
2286	var fn = function() {
2287		$n.animate(opts.animIn, speedIn, easeIn, function() {
2288			cb();
2289		});
2290	};
2291	$l.animate(opts.animOut, speedOut, easeOut, function() {
2292		$l.css(opts.cssAfter);
2293		if (!opts.sync)
2294			fn();
2295	});
2296	if (opts.sync) fn();
2297};
2298
2299// transition definitions - only fade is defined here, transition pack defines the rest
2300$.fn.cycle.transitions = {
2301	fade: function($cont, $slides, opts) {
2302		$slides.not(':eq('+opts.currSlide+')').css('opacity',0);
2303		opts.before.push(function(curr,next,opts) {
2304			$.fn.cycle.commonReset(curr,next,opts);
2305			opts.cssBefore.opacity = 0;
2306		});
2307		opts.animIn	   = { opacity: 1 };
2308		opts.animOut   = { opacity: 0 };
2309		opts.cssBefore = { top: 0, left: 0 };
2310	}
2311};
2312
2313$.fn.cycle.ver = function() { return ver; };
2314
2315// override these globally if you like (they are all optional)
2316$.fn.cycle.defaults = {
2317    activePagerClass: 'activeSlide', // class name used for the active pager link
2318    after:            null,     // transition callback (scope set to element that was shown):  function(currSlideElement, nextSlideElement, options, forwardFlag)
2319    allowPagerClickBubble: false, // allows or prevents click event on pager anchors from bubbling
2320    animIn:           null,     // properties that define how the slide animates in
2321    animOut:          null,     // properties that define how the slide animates out
2322    aspect:           false,    // preserve aspect ratio during fit resizing, cropping if necessary (must be used with fit option)
2323    autostop:         0,        // true to end slideshow after X transitions (where X == slide count)
2324    autostopCount:    0,        // number of transitions (optionally used with autostop to define X)
2325    backwards:        false,    // true to start slideshow at last slide and move backwards through the stack
2326    before:           null,     // transition callback (scope set to element to be shown):     function(currSlideElement, nextSlideElement, options, forwardFlag)
2327    center:           null,     // set to true to have cycle add top/left margin to each slide (use with width and height options)
2328    cleartype:        !$.support.opacity,  // true if clearType corrections should be applied (for IE)
2329    cleartypeNoBg:    false,    // set to true to disable extra cleartype fixing (leave false to force background color setting on slides)
2330    containerResize:  1,        // resize container to fit largest slide
2331    containerResizeHeight:  0,  // resize containers height to fit the largest slide but leave the width dynamic
2332    continuous:       0,        // true to start next transition immediately after current one completes
2333    cssAfter:         null,     // properties that defined the state of the slide after transitioning out
2334    cssBefore:        null,     // properties that define the initial state of the slide before transitioning in
2335    delay:            0,        // additional delay (in ms) for first transition (hint: can be negative)
2336    easeIn:           null,     // easing for "in" transition
2337    easeOut:          null,     // easing for "out" transition
2338    easing:           null,     // easing method for both in and out transitions
2339    end:              null,     // callback invoked when the slideshow terminates (use with autostop or nowrap options): function(options)
2340    fastOnEvent:      0,        // force fast transitions when triggered manually (via pager or prev/next); value == time in ms
2341    fit:              0,        // force slides to fit container
2342    fx:               'fade',   // name of transition effect (or comma separated names, ex: 'fade,scrollUp,shuffle')
2343    fxFn:             null,     // function used to control the transition: function(currSlideElement, nextSlideElement, options, afterCalback, forwardFlag)
2344    height:           'auto',   // container height (if the 'fit' option is true, the slides will be set to this height as well)
2345    manualTrump:      true,     // causes manual transition to stop an active transition instead of being ignored
2346    metaAttr:         'cycle',  // data- attribute that holds the option data for the slideshow
2347    next:             null,     // element, jQuery object, or jQuery selector string for the element to use as event trigger for next slide
2348    nowrap:           0,        // true to prevent slideshow from wrapping
2349    onPagerEvent:     null,     // callback fn for pager events: function(zeroBasedSlideIndex, slideElement)
2350    onPrevNextEvent:  null,     // callback fn for prev/next events: function(isNext, zeroBasedSlideIndex, slideElement)
2351    pager:            null,     // element, jQuery object, or jQuery selector string for the element to use as pager container
2352    pagerAnchorBuilder: null,   // callback fn for building anchor links:  function(index, DOMelement)
2353    pagerEvent:       'click.cycle', // name of event which drives the pager navigation
2354    pause:            0,        // true to enable "pause on hover"
2355    pauseOnPagerHover: 0,       // true to pause when hovering over pager link
2356    prev:             null,     // element, jQuery object, or jQuery selector string for the element to use as event trigger for previous slide
2357    prevNextEvent:    'click.cycle',// event which drives the manual transition to the previous or next slide
2358    random:           0,        // true for random, false for sequence (not applicable to shuffle fx)
2359    randomizeEffects: 1,        // valid when multiple effects are used; true to make the effect sequence random
2360    requeueOnImageNotLoaded: true, // requeue the slideshow if any image slides are not yet loaded
2361    requeueTimeout:   250,      // ms delay for requeue
2362    rev:              0,        // causes animations to transition in reverse (for effects that support it such as scrollHorz/scrollVert/shuffle)
2363    shuffle:          null,     // coords for shuffle animation, ex: { top:15, left: 200 }
2364    skipInitializationCallbacks: false, // set to true to disable the first before/after callback that occurs prior to any transition
2365    slideExpr:        null,     // expression for selecting slides (if something other than all children is required)
2366    slideResize:      1,        // force slide width/height to fixed size before every transition
2367    speed:            1000,     // speed of the transition (any valid fx speed value)
2368    speedIn:          null,     // speed of the 'in' transition
2369    speedOut:         null,     // speed of the 'out' transition
2370    startingSlide:    undefined,// zero-based index of the first slide to be displayed
2371    sync:             1,        // true if in/out transitions should occur simultaneously
2372    timeout:          4000,     // milliseconds between slide transitions (0 to disable auto advance)
2373    timeoutFn:        null,     // callback for determining per-slide timeout value:  function(currSlideElement, nextSlideElement, options, forwardFlag)
2374    updateActivePagerLink: null,// callback fn invoked to update the active pager link (adds/removes activePagerClass style)
2375    width:            null      // container width (if the 'fit' option is true, the slides will be set to this width as well)
2376};
2377
2378}
2378)(jQuery);
2379
2380
2381/*!
2382 * jQuery Cycle Plugin Transition Definitions
2383 * This script is a plugin for the jQuery Cycle Plugin
2384 * Examples and documentation at: http://malsup.com/jquery/cycle/
2385 * Copyright (c) 2007-2010 M. Alsup
2386 * Version:	 2.73
2387 * Dual licensed under the MIT and GPL licenses:
2388 * http://www.opensource.org/licenses/mit-license.php
2389 * http://www.gnu.org/licenses/gpl.html
2390 */
2391(function($) {
2392"use strict";
2393
2394//
2395// These functions define slide initialization and properties for the named
2396// transitions. To save file size feel free to remove any of these that you
2397// don't need.
2398//
2399$.fn.cycle.transitions.none = function($cont, $slides, opts) {
2400	opts.fxFn = function(curr,next,opts,after){
2401		$(next).show();
2402		$(curr).hide();
2403		after();
2404	};
2405};
2406
2407// not a cross-fade, fadeout only fades out the top slide
2408$.fn.cycle.transitions.fadeout = function($cont, $slides, opts) {
2409	$slides.not(':eq('+opts.currSlide+')').css({ display: 'block', 'opacity': 1 });
2410	opts.before.push(function(curr,next,opts,w,h,rev) {
2411		$(curr).css('zIndex',opts.slideCount + (rev !== true ? 1 : 0));
2412		$(next).css('zIndex',opts.slideCount + (rev !== true ? 0 : 1));
2413	});
2414	opts.animIn.opacity = 1;
2415	opts.animOut.opacity = 0;
2416	opts.cssBefore.opacity = 1;
2417	opts.cssBefore.display = 'block';
2418	opts.cssAfter.zIndex = 0;
2419};
2420
2421// scrollUp/Down/Left/Right
2422$.fn.cycle.transitions.scrollUp = function($cont, $slides, opts) {
2423	$cont.css('overflow','hidden');
2424	opts.before.push($.fn.cycle.commonReset);
2425	var h = $cont.height();
2426	opts.cssBefore.top = h;
2427	opts.cssBefore.left = 0;
2428	opts.cssFirst.top = 0;
2429	opts.animIn.top = 0;
2430	opts.animOut.top = -h;
2431};
2432$.fn.cycle.transitions.scrollDown = function($cont, $slides, opts) {
2433	$cont.css('overflow','hidden');
2434	opts.before.push($.fn.cycle.commonReset);
2435	var h = $cont.height();
2436	opts.cssFirst.top = 0;
2437	opts.cssBefore.top = -h;
2438	opts.cssBefore.left = 0;
2439	opts.animIn.top = 0;
2440	opts.animOut.top = h;
2441};
2442$.fn.cycle.transitions.scrollLeft = function($cont, $slides, opts) {
2443	$cont.css('overflow','hidden');
2444	opts.before.push($.fn.cycle.commonReset);
2445	var w = $cont.width();
2446	opts.cssFirst.left = 0;
2447	opts.cssBefore.left = w;
2448	opts.cssBefore.top = 0;
2449	opts.animIn.left = 0;
2450	opts.animOut.left = 0-w;
2451};
2452$.fn.cycle.transitions.scrollRight = function($cont, $slides, opts) {
2453	$cont.css('overflow','hidden');
2454	opts.before.push($.fn.cycle.commonReset);
2455	var w = $cont.width();
2456	opts.cssFirst.left = 0;
2457	opts.cssBefore.left = -w;
2458	opts.cssBefore.top = 0;
2459	opts.animIn.left = 0;
2460	opts.animOut.left = w;
2461};
2462$.fn.cycle.transitions.scrollHorz = function($cont, $slides, opts) {
2463	$cont.css('overflow','hidden').width();
2464	opts.before.push(function(curr, next, opts, fwd) {
2465		if (opts.rev)
2466			fwd = !fwd;
2467		$.fn.cycle.commonReset(curr,next,opts);
2468		opts.cssBefore.left = fwd ? (next.cycleW-1) : (1-next.cycleW);
2469		opts.animOut.left = fwd ? -curr.cycleW : curr.cycleW;
2470	});
2471	opts.cssFirst.left = 0;
2472	opts.cssBefore.top = 0;
2473	opts.animIn.left = 0;
2474	opts.animOut.top = 0;
2475};
2476$.fn.cycle.transitions.scrollVert = function($cont, $slides, opts) {
2477	$cont.css('overflow','hidden');
2478	opts.before.push(function(curr, next, opts, fwd) {
2479		if (opts.rev)
2480			fwd = !fwd;
2481		$.fn.cycle.commonReset(curr,next,opts);
2482		opts.cssBefore.top = fwd ? (1-next.cycleH) : (next.cycleH-1);
2483		opts.animOut.top = fwd ? curr.cycleH : -curr.cycleH;
2484	});
2485	opts.cssFirst.top = 0;
2486	opts.cssBefore.left = 0;
2487	opts.animIn.top = 0;
2488	opts.animOut.left = 0;
2489};
2490
2491// slideX/slideY
2492$.fn.cycle.transitions.slideX = function($cont, $slides, opts) {
2493	opts.before.push(function(curr, next, opts) {
2494		$(opts.elements).not(curr).hide();
2495		$.fn.cycle.commonReset(curr,next,opts,false,true);
2496		opts.animIn.width = next.cycleW;
2497	});
2498	opts.cssBefore.left = 0;
2499	opts.cssBefore.top = 0;
2500	opts.cssBefore.width = 0;
2501	opts.animIn.width = 'show';
2502	opts.animOut.width = 0;
2503};
2504$.fn.cycle.transitions.slideY = function($cont, $slides, opts) {
2505	opts.before.push(function(curr, next, opts) {
2506		$(opts.elements).not(curr).hide();
2507		$.fn.cycle.commonReset(curr,next,opts,true,false);
2508		opts.animIn.height = next.cycleH;
2509	});
2510	opts.cssBefore.left = 0;
2511	opts.cssBefore.top = 0;
2512	opts.cssBefore.height = 0;
2513	opts.animIn.height = 'show';
2514	opts.animOut.height = 0;
2515};
2516
2517// shuffle
2518$.fn.cycle.transitions.shuffle = function($cont, $slides, opts) {
2519	var i, w = $cont.css('overflow', 'visible').width();
2520	$slides.css({left: 0, top: 0});
2521	opts.before.push(function(curr,next,opts) {
2522		$.fn.cycle.commonReset(curr,next,opts,true,true,true);
2523	});
2524	// only adjust speed once!
2525	if (!opts.speedAdjusted) {
2526		opts.speed = opts.speed / 2; // shuffle has 2 transitions
2527		opts.speedAdjusted = true;
2528	}
2529	opts.random = 0;
2530	opts.shuffle = opts.shuffle || {left:-w, top:15};
2531	opts.els = [];
2532	for (i=0; i < $slides.length; i++)
2533		opts.els.push($slides[i]);
2534
2535	for (i=0; i < opts.currSlide; i++)
2536		opts.els.push(opts.els.shift());
2537
2538	// custom transition fn (hat tip to Benjamin Sterling for this bit of sweetness!)
2539	opts.fxFn = function(curr, next, opts, cb, fwd) {
2540		if (opts.rev)
2541			fwd = !fwd;
2542		var $el = fwd ? $(curr) : $(next);
2543		$(next).css(opts.cssBefore);
2544		var count = opts.slideCount;
2545		$el.animate(opts.shuffle, opts.speedIn, opts.easeIn, function() {
2546			var hops = $.fn.cycle.hopsFromLast(opts, fwd);
2547			for (var k=0; k < hops; k++) {
2548				if (fwd)
2549					opts.els.push(opts.els.shift());
2550				else
2551					opts.els.unshift(opts.els.pop());
2552			}
2553			if (fwd) {
2554				for (var i=0, len=opts.els.length; i < len; i++)
2555					$(opts.els[i]).css('z-index', len-i+count);
2556			}
2557			else {
2558				var z = $(curr).css('z-index');
2559				$el.css('z-index', parseInt(z,10)+1+count);
2560			}
2561			$el.animate({left:0, top:0}, opts.speedOut, opts.easeOut, function() {
2562				$(fwd ? this : curr).hide();
2563				if (cb) cb();
2564			});
2565		});
2566	};
2567	$.extend(opts.cssBefore, { display: 'block', opacity: 1, top: 0, left: 0 });
2568};
2569
2570// turnUp/Down/Left/Right
2571$.fn.cycle.transitions.turnUp = function($cont, $slides, opts) {
2572	opts.before.push(function(curr, next, opts) {
2573		$.fn.cycle.commonReset(curr,next,opts,true,false);
2574		opts.cssBefore.top = next.cycleH;
2575		opts.animIn.height = next.cycleH;
2576		opts.animOut.width = next.cycleW;
2577	});
2578	opts.cssFirst.top = 0;
2579	opts.cssBefore.left = 0;
2580	opts.cssBefore.height = 0;
2581	opts.animIn.top = 0;
2582	opts.animOut.height = 0;
2583};
2584$.fn.cycle.transitions.turnDown = function($cont, $slides, opts) {
2585	opts.before.push(function(curr, next, opts) {
2586		$.fn.cycle.commonReset(curr,next,opts,true,false);
2587		opts.animIn.height = next.cycleH;
2588		opts.animOut.top   = curr.cycleH;
2589	});
2590	opts.cssFirst.top = 0;
2591	opts.cssBefore.left = 0;
2592	opts.cssBefore.top = 0;
2593	opts.cssBefore.height = 0;
2594	opts.animOut.height = 0;
2595};
2596$.fn.cycle.transitions.turnLeft = function($cont, $slides, opts) {
2597	opts.before.push(function(curr, next, opts) {
2598		$.fn.cycle.commonReset(curr,next,opts,false,true);
2599		opts.cssBefore.left = next.cycleW;
2600		opts.animIn.width = next.cycleW;
2601	});
2602	opts.cssBefore.top = 0;
2603	opts.cssBefore.width = 0;
2604	opts.animIn.left = 0;
2605	opts.animOut.width = 0;
2606};
2607$.fn.cycle.transitions.turnRight = function($cont, $slides, opts) {
2608	opts.before.push(function(curr, next, opts) {
2609		$.fn.cycle.commonReset(curr,next,opts,false,true);
2610		opts.animIn.width = next.cycleW;
2611		opts.animOut.left = curr.cycleW;
2612	});
2613	$.extend(opts.cssBefore, { top: 0, left: 0, width: 0 });
2614	opts.animIn.left = 0;
2615	opts.animOut.width = 0;
2616};
2617
2618// zoom
2619$.fn.cycle.transitions.zoom = function($cont, $slides, opts) {
2620	opts.before.push(function(curr, next, opts) {
2621		$.fn.cycle.commonReset(curr,next,opts,false,false,true);
2622		opts.cssBefore.top = next.cycleH/2;
2623		opts.cssBefore.left = next.cycleW/2;
2624		$.extend(opts.animIn, { top: 0, left: 0, width: next.cycleW, height: next.cycleH });
2625		$.extend(opts.animOut, { width: 0, height: 0, top: curr.cycleH/2, left: curr.cycleW/2 });
2626	});
2627	opts.cssFirst.top = 0;
2628	opts.cssFirst.left = 0;
2629	opts.cssBefore.width = 0;
2630	opts.cssBefore.height = 0;
2631};
2632
2633// fadeZoom
2634$.fn.cycle.transitions.fadeZoom = function($cont, $slides, opts) {
2635	opts.before.push(function(curr, next, opts) {
2636		$.fn.cycle.commonReset(curr,next,opts,false,false);
2637		opts.cssBefore.left = next.cycleW/2;
2638		opts.cssBefore.top = next.cycleH/2;
2639		$.extend(opts.animIn, { top: 0, left: 0, width: next.cycleW, height: next.cycleH });
2640	});
2641	opts.cssBefore.width = 0;
2642	opts.cssBefore.height = 0;
2643	opts.animOut.opacity = 0;
2644};
2645
2646// blindX
2647$.fn.cycle.transitions.blindX = function($cont, $slides, opts) {
2648	var w = $cont.css('overflow','hidden').width();
2649	opts.before.push(function(curr, next, opts) {
2650		$.fn.cycle.commonReset(curr,next,opts);
2651		opts.animIn.width = next.cycleW;
2652		opts.animOut.left   = curr.cycleW;
2653	});
2654	opts.cssBefore.left = w;
2655	opts.cssBefore.top = 0;
2656	opts.animIn.left = 0;
2657	opts.animOut.left = w;
2658};
2659// blindY
2660$.fn.cycle.transitions.blindY = function($cont, $slides, opts) {
2661	var h = $cont.css('overflow','hidden').height();
2662	opts.before.push(function(curr, next, opts) {
2663		$.fn.cycle.commonReset(curr,next,opts);
2664		opts.animIn.height = next.cycleH;
2665		opts.animOut.top   = curr.cycleH;
2666	});
2667	opts.cssBefore.top = h;
2668	opts.cssBefore.left = 0;
2669	opts.animIn.top = 0;
2670	opts.animOut.top = h;
2671};
2672// blindZ
2673$.fn.cycle.transitions.blindZ = function($cont, $slides, opts) {
2674	var h = $cont.css('overflow','hidden').height();
2675	var w = $cont.width();
2676	opts.before.push(function(curr, next, opts) {
2677		$.fn.cycle.commonReset(curr,next,opts);
2678		opts.animIn.height = next.cycleH;
2679		opts.animOut.top   = curr.cycleH;
2680	});
2681	opts.cssBefore.top = h;
2682	opts.cssBefore.left = w;
2683	opts.animIn.top = 0;
2684	opts.animIn.left = 0;
2685	opts.animOut.top = h;
2686	opts.animOut.left = w;
2687};
2688
2689// growX - grow horizontally from centered 0 width
2690$.fn.cycle.transitions.growX = function($cont, $slides, opts) {
2691	opts.before.push(function(curr, next, opts) {
2692		$.fn.cycle.commonReset(curr,next,opts,false,true);
2693		opts.cssBefore.left = this.cycleW/2;
2694		opts.animIn.left = 0;
2695		opts.animIn.width = this.cycleW;
2696		opts.animOut.left = 0;
2697	});
2698	opts.cssBefore.top = 0;
2699	opts.cssBefore.width = 0;
2700};
2701// growY - grow vertically from centered 0 height
2702$.fn.cycle.transitions.growY = function($cont, $slides, opts) {
2703	opts.before.push(function(curr, next, opts) {
2704		$.fn.cycle.commonReset(curr,next,opts,true,false);
2705		opts.cssBefore.top = this.cycleH/2;
2706		opts.animIn.top = 0;
2707		opts.animIn.height = this.cycleH;
2708		opts.animOut.top = 0;
2709	});
2710	opts.cssBefore.height = 0;
2711	opts.cssBefore.left = 0;
2712};
2713
2714// curtainX - squeeze in both edges horizontally
2715$.fn.cycle.transitions.curtainX = function($cont, $slides, opts) {
2716	opts.before.push(function(curr, next, opts) {
2717		$.fn.cycle.commonReset(curr,next,opts,false,true,true);
2718		opts.cssBefore.left = next.cycleW/2;
2719		opts.animIn.left = 0;
2720		opts.animIn.width = this.cycleW;
2721		opts.animOut.left = curr.cycleW/2;
2722		opts.animOut.width = 0;
2723	});
2724	opts.cssBefore.top = 0;
2725	opts.cssBefore.width = 0;
2726};
2727// curtainY - squeeze in both edges vertically
2728$.fn.cycle.transitions.curtainY = function($cont, $slides, opts) {
2729	opts.before.push(function(curr, next, opts) {
2730		$.fn.cycle.commonReset(curr,next,opts,true,false,true);
2731		opts.cssBefore.top = next.cycleH/2;
2732		opts.animIn.top = 0;
2733		opts.animIn.height = next.cycleH;
2734		opts.animOut.top = curr.cycleH/2;
2735		opts.animOut.height = 0;
2736	});
2737	opts.cssBefore.height = 0;
2738	opts.cssBefore.left = 0;
2739};
2740
2741// cover - curr slide covered by next slide
2742$.fn.cycle.transitions.cover = function($cont, $slides, opts) {
2743	var d = opts.direction || 'left';
2744	var w = $cont.css('overflow','hidden').width();
2745	var h = $cont.height();
2746	opts.before.push(function(curr, next, opts) {
2747		$.fn.cycle.commonReset(curr,next,opts);
2748		opts.cssAfter.display = '';
2749		if (d == 'right')
2750			opts.cssBefore.left = -w;
2751		else if (d == 'up')
2752			opts.cssBefore.top = h;
2753		else if (d == 'down')
2754			opts.cssBefore.top = -h;
2755		else
2756			opts.cssBefore.left = w;
2757	});
2758	opts.animIn.left = 0;
2759	opts.animIn.top = 0;
2760	opts.cssBefore.top = 0;
2761	opts.cssBefore.left = 0;
2762};
2763
2764// uncover - curr slide moves off next slide
2765$.fn.cycle.transitions.uncover = function($cont, $slides, opts) {
2766	var d = opts.direction || 'left';
2767	var w = $cont.css('overflow','hidden').width();
2768	var h = $cont.height();
2769	opts.before.push(function(curr, next, opts) {
2770		$.fn.cycle.commonReset(curr,next,opts,true,true,true);
2771		if (d == 'right')
2772			opts.animOut.left = w;
2773		else if (d == 'up')
2774			opts.animOut.top = -h;
2775		else if (d == 'down')
2776			opts.animOut.top = h;
2777		else
2778			opts.animOut.left = -w;
2779	});
2780	opts.animIn.left = 0;
2781	opts.animIn.top = 0;
2782	opts.cssBefore.top = 0;
2783	opts.cssBefore.left = 0;
2784};
2785
2786// toss - move top slide and fade away
2787$.fn.cycle.transitions.toss = function($cont, $slides, opts) {
2788	var w = $cont.css('overflow','visible').width();
2789	var h = $cont.height();
2790	opts.before.push(function(curr, next, opts) {
2791		$.fn.cycle.commonReset(curr,next,opts,true,true,true);
2792		// provide default toss settings if animOut not provided
2793		if (!opts.animOut.left && !opts.animOut.top)
2794			$.extend(opts.animOut, { left: w*2, top: -h/2, opacity: 0 });
2795		else
2796			opts.animOut.opacity = 0;
2797	});
2798	opts.cssBefore.left = 0;
2799	opts.cssBefore.top = 0;
2800	opts.animIn.left = 0;
2801};
2802
2803// wipe - clip animation
2804$.fn.cycle.transitions.wipe = function($cont, $slides, opts) {
2805	var w = $cont.css('overflow','hidden').width();
2806	var h = $cont.height();
2807	opts.cssBefore = opts.cssBefore || {};
2808	var clip;
2809	if (opts.clip) {
2810		if (/l2r/.test(opts.clip))
2811			clip = 'rect(0px 0px '+h+'px 0px)';
2812		else if (/r2l/.test(opts.clip))
2813			clip = 'rect(0px '+w+'px '+h+'px '+w+'px)';
2814		else if (/t2b/.test(opts.clip))
2815			clip = 'rect(0px '+w+'px 0px 0px)';
2816		else if (/b2t/.test(opts.clip))
2817			clip = 'rect('+h+'px '+w+'px '+h+'px 0px)';
2818		else if (/zoom/.test(opts.clip)) {
2819			var top = parseInt(h/2,10);
2820			var left = parseInt(w/2,10);
2821			clip = 'rect('+top+'px '+left+'px '+top+'px '+left+'px)';
2822		}
2823	}
2824
2825	opts.cssBefore.clip = opts.cssBefore.clip || clip || 'rect(0px 0px 0px 0px)';
2826
2827	var d = opts.cssBefore.clip.match(/(\d+)/g);
2828	var t = parseInt(d[0],10), r = parseInt(d[1],10), b = parseInt(d[2],10), l = parseInt(d[3],10);
2829
2830	opts.before.push(function(curr, next, opts) {
2831		if (curr == next) return;
2832		var $curr = $(curr), $next = $(next);
2833		$.fn.cycle.commonReset(curr,next,opts,true,true,false);
2834		opts.cssAfter.display = 'block';
2835
2836		var step = 1, count = parseInt((opts.speedIn / 13),10) - 1;
2837		(function f() {
2838			var tt = t ? t - parseInt(step * (t/count),10) : 0;
2839			var ll = l ? l - parseInt(step * (l/count),10) : 0;
2840			var bb = b < h ? b + parseInt(step * ((h-b)/count || 1),10) : h;
2841			var rr = r < w ? r + parseInt(step * ((w-r)/count || 1),10) : w;
2842			$next.css({ clip: 'rect('+tt+'px '+rr+'px '+bb+'px '+ll+'px)' });
2843			(step++ <= count) ? setTimeout(f, 13) : $curr.css('display', 'none');
2844		})();
2845	});
2846	$.extend(opts.cssBefore, { display: 'block', opacity: 1, top: 0, left: 0 });
2847	opts.animIn	   = { left: 0 };
2848	opts.animOut   = { left: 0 };
2849};
2850
2851})(jQuery);
2852;
2853/* jshint onevar:false, loopfunc:true */
2854/* global jetpackSlideshowSettings, escape */
2855
2856// WARNING: This file is distributed verbatim in Jetpack. @hide-in-jetpack
2857function JetpackSlideshow( element, transition, autostart ) {
2858	this.element = element;
2859	this.images = [];
2860	this.controls = {};
2861	this.transition = transition || 'fade';
2862	this.autostart = autostart;
2863}
2864
2865JetpackSlideshow.prototype.showLoadingImage = function( toggle ) {
2866	if ( toggle ) {
2867		this.loadingImage_ = document.createElement( 'div' );
2868		this.loadingImage_.className = 'slideshow-loading';
2869		var img = document.createElement( 'img' );
2870		img.src = jetpackSlideshowSettings.spinner;
2871		this.loadingImage_.appendChild( img );
2872		this.loadingImage_.appendChild( this.makeZeroWidthSpan() );
2873		this.element.append( this.loadingImage_ );
2874	} else if ( this.loadingImage_ ) {
2875		this.loadingImage_.parentNode.removeChild( this.loadingImage_ );
2876		this.loadingImage_ = null;
2877	}
2878};
2879
2880JetpackSlideshow.prototype.init = function() {
2881	this.showLoadingImage(true);
2882
2883	var self = this;
2884	// Set up DOM.
2885	for ( var i = 0; i < this.images.length; i++ ) {
2886		var imageInfo = this.images[i];
2887		var img = document.createElement( 'img' );
2888		img.src = imageInfo.src;
2889		img.title = typeof( imageInfo.title ) !== 'undefined' ? imageInfo.title : '';
2890		img.alt = typeof( imageInfo.alt ) !== 'undefined' ? imageInfo.alt : '';
2891		img.align = 'middle';
2892		img.nopin = 'nopin';
2893		var caption = document.createElement( 'div' );
2894		caption.className = 'slideshow-slide-caption';
2895		caption.innerHTML = imageInfo.caption;
2896		var container = document.createElement('div');
2897		container.className = 'slideshow-slide';
2898
2899		// Hide loading image once first image has loaded.
2900		if ( i === 0 ) {
2901			if ( img.complete ) {
2902				// IE, image in cache
2903				setTimeout( function() {
2904					self.finishInit_();
2905				}, 1);
2906			} else {
2907				jQuery( img ).load(function() {
2908					self.finishInit_();
2909				});
2910			}
2911		}
2912		container.appendChild( img );
2913		// I'm not sure where these were coming from, but IE adds
2914		// bad values for width/height for portrait-mode images
2915		img.removeAttribute('width');
2916		img.removeAttribute('height');
2917		container.appendChild( this.makeZeroWidthSpan() );
2918		container.appendChild( caption );
2919		this.element.append( container );
2920	}
2921};
2922
2923JetpackSlideshow.prototype.makeZeroWidthSpan = function() {
2924	var emptySpan = document.createElement( 'span' );
2925	emptySpan.className = 'slideshow-line-height-hack';
2926	// Having a NBSP makes IE act weird during transitions, but other
2927	// browsers ignore a text node with a space in it as whitespace.
2928	if ( -1 !== window.navigator.userAgent.indexOf( 'MSIE ' ) ) {
2929		emptySpan.appendChild( document.createTextNode(' ') );
2930	} else {
2931		emptySpan.innerHTML = '&nbsp;';
2932	}
2933	return emptySpan;
2934};
2935
2936JetpackSlideshow.prototype.finishInit_ = function() {
2937	this.showLoadingImage( false );
2938	this.renderControls_();
2939
2940	var self = this;
2941	if ( this.images.length > 1 ) {
2942		// Initialize Cycle instance.
2943		this.element.cycle( {
2944			fx: this.transition,
2945			prev: this.controls.prev,
2946			next: this.controls.next,
2947			slideExpr: '.slideshow-slide',
2948			onPrevNextEvent: function() {
2949				return self.onCyclePrevNextClick_.apply( self, arguments );
2950			}
2951		} );
2952
2953		var slideshow = this.element;
2954
2955		if ( ! this.autostart ) {
2956			slideshow.cycle( 'pause' );
2957			jQuery(this.controls.stop).removeClass( 'running' );
2958			jQuery(this.controls.stop).addClass( 'paused' );
2959		}
2960
2961		jQuery( this.controls.stop ).click( function() {
2962			var button = jQuery(this);
2963			if ( ! button.hasClass( 'paused' ) ) {
2964				slideshow.cycle( 'pause' );
2965				button.removeClass( 'running' );
2966				button.addClass( 'paused' );
2967			} else {
2968				button.addClass( 'running' );
2969				button.removeClass( 'paused' );
2970				slideshow.cycle( 'resume', true );
2971			}
2972			return false;
2973		} );
2974	} else {
2975		this.element.children( ':first' ).show();
2976		this.element.css( 'position', 'relative' );
2977	}
2978	this.initialized_ = true;
2979};
2980
2981JetpackSlideshow.prototype.renderControls_ = function() {
2982	if ( this.controlsDiv_ ) {
2983		return;
2984	}
2985
2986	var controlsDiv = document.createElement( 'div' );
2987	controlsDiv.className = 'slideshow-controls';
2988
2989	var controls = [ 'prev', 'stop', 'next' ];
2990	for ( var i = 0; i < controls.length; i++ ) {
2991		var controlName = controls[i];
2992		var a = document.createElement( 'a' );
2993		a.href = '#';
2994		controlsDiv.appendChild( a );
2995		this.controls[controlName] = a;
2996	}
2997	this.element.append( controlsDiv );
2998	this.controlsDiv_ = controlsDiv;
2999};
3000
3001JetpackSlideshow.prototype.onCyclePrevNextClick_ = function( isNext, i/*, slideElement*/ ) {
3002	// If blog_id not present don't track page views
3003	if ( ! jetpackSlideshowSettings.blog_id ) {
3004		return;
3005	}
3006
3007	var postid = this.images[i].id;
3008	var stats = new Image();
3009	stats.src = document.location.protocol +
3010		'//pixel.wp.com/g.gif?host=' +
3011		escape( document.location.host ) +
3012		'&rand=' + Math.random() +
3013		'&blog=' + jetpackSlideshowSettings.blog_id +
3014		'&subd=' + jetpackSlideshowSettings.blog_subdomain +
3015		'&user_id=' + jetpackSlideshowSettings.user_id +
3016		'&post=' + postid +
3017		'&ref=' + escape( document.location );
3018};
3019
3020( function ( $ ) {
3021	function jetpack_slideshow_init() {
3022		$( '.jetpack-slideshow-noscript' ).remove();
3023
3024		$( '.jetpack-slideshow' ).each( function () {
3025			var container = $( this );
3026
3027			if ( container.data( 'processed' ) ) {
3028				return;
3029			}
3030
3031			var slideshow = new JetpackSlideshow( container, container.data( 'trans' ), container.data( 'autostart' ) );
3032			slideshow.images = container.data( 'gallery' );
3033			slideshow.init();
3034
3035			container.data( 'processed', true );
3036		} );
3037	}
3038
3039	$( document ).ready( jetpack_slideshow_init );
3040	$( 'body' ).on( 'post-load', jetpack_slideshow_init );
3041} )( jQuery );
3042;
3043/*jslint indent: 2, browser: true, bitwise: true, plusplus: true */
3044
3045/*! Copyright Twitter Inc. and other contributors. Licensed under MIT *//*
3046https://github.com/twitter/twemoji/blob/gh-pages/LICENSE
3047*/
3048
3049// WARNING:   this file is generated automatically via
3050//            `node twemoji-generator.js`
3051//            please update its `createTwemoji` function
3052//            at the bottom of the same file instead.
3053var twemoji = (function ( ) {
3054  'use strict';
3055
3056  /*jshint maxparams:4 */
3057
3058  var
3059    // the exported module object
3060    twemoji = {
3061
3062
3063    /////////////////////////
3064    //      properties     //
3065    /////////////////////////
3066
3067      // default assets url, by default will be Twitter Inc. CDN
3068      base: (location.protocol === 'https:' ? 'https:' : 'http:') +
3069            '//twemoji.maxcdn.com/',
3070
3071      // default assets file extensions, by default '.png'
3072      ext: '.svg',
3073
3074      // default assets/folder size, by default "36x36"
3075      // available via Twitter CDN: 16, 36, 72
3076      size: 'svg',
3077
3078      // basic utilities / helpers to convert code points
3079      // to JavaScript surrogates and vice versa
3080      convert: {
3081
3082        /**
3083         * Given an HEX codepoint, returns UTF16 surrogate pairs.
3084         *
3085         * @param   string  generic codepoint, i.e. '1F4A9'
3086         * @return  string  codepoint transformed into utf16 surrogates pair,
3087         *          i.e. \uD83D\uDCA9
3088         *
3089         * @example
3090         *  twemoji.convert.fromCodePoint('1f1e8');
3091         *  // "\ud83c\udde8"
3092         *
3093         *  '1f1e8-1f1f3'.split('-').map(twemoji.convert.fromCodePoint).join('')
3094         *  // "\ud83c\udde8\ud83c\uddf3"
3095         */
3096        fromCodePoint: fromCodePoint,
3097
3098        /**
3099         * Given UTF16 surrogate pairs, returns the equivalent HEX codepoint.
3100         *
3101         * @param   string  generic utf16 surrogates pair, i.e. \uD83D\uDCA9
3102         * @param   string  optional separator for double code points, default='-'
3103         * @return  string  utf16 transformed into codepoint, i.e. '1F4A9'
3104         *
3105         * @example
3106         *  twemoji.convert.toCodePoint('\ud83c\udde8\ud83c\uddf3');
3107         *  // "1f1e8-1f1f3"
3108         *
3109         *  twemoji.convert.toCodePoint('\ud83c\udde8\ud83c\uddf3', '~');
3110         *  // "1f1e8~1f1f3"
3111         */
3112        toCodePoint: toCodePoint
3113      },
3114
3115
3116    /////////////////////////
3117    //       methods       //
3118    /////////////////////////
3119
3120      /**
3121       * User first: used to remove missing images
3122       * preserving the original text intent when
3123       * a fallback for network problems is desired.
3124       * Automatically added to Image nodes via DOM
3125       * It could be recycled for string operations via:
3126       *  $('img.emoji').on('error', twemoji.onerror)
3127       */
3128      onerror: function onerror() {
3129        if (this.parentNode) {
3130          this.parentNode.replaceChild(createText(this.alt), this);
3131        }
3132      },
3133
3134      /**
3135       * Main method/logic to generate either <img> tags or HTMLImage nodes.
3136       *  "emojify" a generic text or DOM Element.
3137       *
3138       * @overloads
3139       *
3140       * String replacement for `innerHTML` or server side operations
3141       *  twemoji.parse(string);
3142       *  twemoji.parse(string, Function);
3143       *  twemoji.parse(string, Object);
3144       *
3145       * HTMLElement tree parsing for safer operations over existing DOM
3146       *  twemoji.parse(HTMLElement);
3147       *  twemoji.parse(HTMLElement, Function);
3148       *  twemoji.parse(HTMLElement, Object);
3149       *
3150       * @param   string|HTMLElement  the source to parse and enrich with emoji.
3151       *
3152       *          string              replace emoji matches with <img> tags.
3153       *                              Mainly used to inject emoji via `innerHTML`
3154       *                              It does **not** parse the string or validate it,
3155       *                              it simply replaces found emoji with a tag.
3156       *                              NOTE: be sure this won't affect security.
3157       *
3158       *          HTMLElement         walk through the DOM tree and find emoji
3159       *                              that are inside **text node only** (nodeType === 3)
3160       *                              Mainly used to put emoji in already generated DOM
3161       *                              without compromising surrounding nodes and
3162       *                              **avoiding** the usage of `innerHTML`.
3163       *                              NOTE: Using DOM elements instead of strings should
3164       *                              improve security without compromising too much
3165       *                              performance compared with a less safe `innerHTML`.
3166       *
3167       * @param   Function|Object  [optional]
3168       *                              either the callback that will be invoked or an object
3169       *                              with all properties to use per each found emoji.
3170       *
3171       *          Function            if specified, this will be invoked per each emoji
3172       *                              that has been found through the RegExp except
3173       *                              those follwed by the invariant \uFE0E ("as text").
3174       *                              Once invoked, parameters will be:
3175       *
3176       *                                codePoint:string  the lower case HEX code point
3177       *                                                  i.e. "1f4a9"
3178       *
3179       *                                options:Object    all info for this parsing operation
3180       *
3181       *                                variant:char      the optional \uFE0F ("as image")
3182       *                                                  variant, in case this info
3183       *                                                  is anyhow meaningful.
3184       *                                                  By default this is ignored.
3185       *
3186       *                              If such callback will return a falsy value instead
3187       *                              of a valid `src` to use for the image, nothing will
3188       *                              actually change for that specific emoji.
3189       *
3190       *
3191       *          Object              if specified, an object containing the following properties
3192       *
3193       *            callback   Function  the callback to invoke per each found emoji.
3194       *            base       string    the base url, by default twemoji.base
3195       *            ext        string    the image extension, by default twemoji.ext
3196       *            size       string    the assets size, by default twemoji.size
3197       *
3198       * @example
3199       *
3200       *  twemoji.parse("I \u2764\uFE0F emoji!");
3201       *  // I <img class="emoji" draggable="false" alt="❤️" src="/assets/2764.gif"> emoji!
3202       *
3203       *
3204       *  twemoji.parse("I \u2764\uFE0F emoji!", function(icon, options, variant) {
3205       *    return '/assets/' + icon + '.gif';
3206       *  });
3207       *  // I <img class="emoji" draggable="false" alt="❤️" src="/assets/2764.gif"> emoji!
3208       *
3209       *
3210       * twemoji.parse("I \u2764\uFE0F emoji!", {
3211       *   size: 72,
3212       *   callback: function(icon, options, variant) {
3213       *     return '/assets/' + options.size + '/' + icon + options.ext;
3214       *   }
3215       * });
3216       *  // I <img class="emoji" draggable="false" alt="❤️" src="/assets/72x72/2764.png"> emoji!
3217       *
3218       */
3219      parse: parse,
3220
3221      /**
3222       * Given a string, invokes the callback argument
3223       *  per each emoji found in such string.
3224       * This is the most raw version used by
3225       *  the .parse(string) method itself.
3226       *
3227       * @param   string    generic string to parse
3228       * @param   Function  a generic callback that will be
3229       *                    invoked to replace the content.
3230       *                    This calback wil receive standard
3231       *                    String.prototype.replace(str, callback)
3232       *                    arguments such:
3233       *  callback(
3234       *    match,  // the emoji match
3235       *    icon,   // the emoji text (same as text)
3236       *    variant // either '\uFE0E' or '\uFE0F', if present
3237       *  );
3238       *
3239       *                    and others commonly received via replace.
3240       *
3241       *  NOTE: When the variant \uFE0E is found, remember this is an explicit intent
3242       *  from the user: the emoji should **not** be replaced with an image.
3243       *  In \uFE0F case one, it's the opposite, it should be graphic.
3244       *  This utility convetion is that only \uFE0E are not translated into images.
3245       */
3246      replace: replace,
3247
3248      /**
3249       * Simplify string tests against emoji.
3250       *
3251       * @param   string  some text that might contain emoji
3252       * @return  boolean true if any emoji was found, false otherwise.
3253       *
3254       * @example
3255       *
3256       *  if (twemoji.test(someContent)) {
3257       *    console.log("emoji All The Things!");
3258       *  }
3259       */
3260      test: test
3261    },
3262
3263    // RegExp based on emoji's official Unicode standards
3264    // http://www.unicode.org/Public/UNIDATA/EmojiSources.txt
3265    re = /((?:\ud83c\udde8\ud83c\uddf3|\ud83c\uddfa\ud83c\uddf8|\ud83c\uddf7\ud83c\uddfa|\ud83c\uddf0\ud83c\uddf7|\ud83c\uddef\ud83c\uddf5|\ud83c\uddee\ud83c\uddf9|\ud83c\uddec\ud83c\udde7|\ud83c\uddeb\ud83c\uddf7|\ud83c\uddea\ud83c\uddf8|\ud83c\udde9\ud83c\uddea|\u0039\u20e3|\u0038\u20e3|\u0037\u20e3|\u0036\u20e3|\u0035\u20e3|\u0034\u20e3|\u0033\u20e3|\u0032\u20e3|\u0031\u20e3|\u0030\u20e3|\u0023\u20e3|\ud83d\udeb3|\ud83d\udeb1|\ud83d\udeb0|\ud83d\udeaf|\ud83d\udeae|\ud83d\udea6|\ud83d\udea3|\ud83d\udea1|\ud83d\udea0|\ud83d\ude9f|\ud83d\ude9e|\ud83d\ude9d|\ud83d\ude9c|\ud83d\ude9b|\ud83d\ude98|\ud83d\ude96|\ud83d\ude94|\ud83d\ude90|\ud83d\ude8e|\ud83d\ude8d|\ud83d\ude8b|\ud83d\ude8a|\ud83d\ude88|\ud83d\ude86|\ud83d\ude82|\ud83d\ude81|\ud83d\ude36|\ud83d\ude34|\ud83d\ude2f|\ud83d\ude2e|\ud83d\ude2c|\ud83d\ude27|\ud83d\ude26|\ud83d\ude1f|\ud83d\ude1b|\ud83d\ude19|\ud83d\ude17|\ud83d\ude15|\ud83d\ude11|\ud83d\ude10|\ud83d\ude0e|\ud83d\ude08|\ud83d\ude07|\ud83d\ude00|\ud83d\udd67|\ud83d\udd66|\ud83d\udd65|\ud83d\udd64|\ud83d\udd63|\ud83d\udd62|\ud83d\udd61|\ud83d\udd60|\ud83d\udd5f|\ud83d\udd5e|\ud83d\udd5d|\ud83d\udd5c|\ud83d\udd2d|\ud83d\udd2c|\ud83d\udd15|\ud83d\udd09|\ud83d\udd08|\ud83d\udd07|\ud83d\udd06|\ud83d\udd05|\ud83d\udd04|\ud83d\udd02|\ud83d\udd01|\ud83d\udd00|\ud83d\udcf5|\ud83d\udcef|\ud83d\udced|\ud83d\udcec|\ud83d\udcb7|\ud83d\udcb6|\ud83d\udcad|\ud83d\udc6d|\ud83d\udc6c|\ud83d\udc65|\ud83d\udc2a|\ud83d\udc16|\ud83d\udc15|\ud83d\udc13|\ud83d\udc10|\ud83d\udc0f|\ud83d\udc0b|\ud83d\udc0a|\ud83d\udc09|\ud83d\udc08|\ud83d\udc07|\ud83d\udc06|\ud83d\udc05|\ud83d\udc04|\ud83d\udc03|\ud83d\udc02|\ud83d\udc01|\ud83d\udc00|\ud83c\udfe4|\ud83c\udfc9|\ud83c\udfc7|\ud83c\udf7c|\ud83c\udf50|\ud83c\udf4b|\ud83c\udf33|\ud83c\udf32|\ud83c\udf1e|\ud83c\udf1d|\ud83c\udf1c|\ud83c\udf1a|\ud83c\udf18|\ud83c\udccf|\ud83c\udd70|\ud83c\udd71|\ud83c\udd7e|\ud83c\udd8e|\ud83c\udd91|\ud83c\udd92|\ud83c\udd93|\ud83c\udd94|\ud83c\udd95|\ud83c\udd96|\ud83c\udd97|\ud83c\udd98|\ud83c\udd99|\ud83c\udd9a|\ud83d\ude0d|\ud83d\udec5|\ud83d\udec4|\ud83d\udec3|\ud83d\udec2|\ud83d\udec1|\ud83d\udebf|\ud83d\udeb8|\ud83d\udeb7|\ud83d\udeb5|\ud83c\ude01|\ud83c\ude02|\ud83c\ude32|\ud83c\ude33|\ud83c\ude34|\ud83c\ude35|\ud83c\ude36|\ud83c\ude37|\ud83c\ude38|\ud83c\ude39|\ud83c\ude3a|\ud83c\ude50|\ud83c\ude51|\ud83c\udf00|\ud83c\udf01|\ud83c\udf02|\ud83c\udf03|\ud83c\udf04|\ud83c\udf05|\ud83c\udf06|\ud83c\udf07|\ud83c\udf08|\ud83c\udf09|\ud83c\udf0a|\ud83c\udf0b|\ud83c\udf0c|\ud83c\udf0f|\ud83c\udf11|\ud83c\udf13|\ud83c\udf14|\ud83c\udf15|\ud83c\udf19|\ud83c\udf1b|\ud83c\udf1f|\ud83c\udf20|\ud83c\udf30|\ud83c\udf31|\ud83c\udf34|\ud83c\udf35|\ud83c\udf37|\ud83c\udf38|\ud83c\udf39|\ud83c\udf3a|\ud83c\udf3b|\ud83c\udf3c|\ud83c\udf3d|\ud83c\udf3e|\ud83c\udf3f|\ud83c\udf40|\ud83c\udf41|\ud83c\udf42|\ud83c\udf43|\ud83c\udf44|\ud83c\udf45|\ud83c\udf46|\ud83c\udf47|\ud83c\udf48|\ud83c\udf49|\ud83c\udf4a|\ud83c\udf4c|\ud83c\udf4d|\ud83c\udf4e|\ud83c\udf4f|\ud83c\udf51|\ud83c\udf52|\ud83c\udf53|\ud83c\udf54|\ud83c\udf55|\ud83c\udf56|\ud83c\udf57|\ud83c\udf58|\ud83c\udf59|\ud83c\udf5a|\ud83c\udf5b|\ud83c\udf5c|\ud83c\udf5d|\ud83c\udf5e|\ud83c\udf5f|\ud83c\udf60|\ud83c\udf61|\ud83c\udf62|\ud83c\udf63|\ud83c\udf64|\ud83c\udf65|\ud83c\udf66|\ud83c\udf67|\ud83c\udf68|\ud83c\udf69|\ud83c\udf6a|\ud83c\udf6b|\ud83c\udf6c|\ud83c\udf6d|\ud83c\udf6e|\ud83c\udf6f|\ud83c\udf70|\ud83c\udf71|\ud83c\udf72|\ud83c\udf73|\ud83c\udf74|\ud83c\udf75|\ud83c\udf76|\ud83c\udf77|\ud83c\udf78|\ud83c\udf79|\ud83c\udf7a|\ud83c\udf7b|\ud83c\udf80|\ud83c\udf81|\ud83c\udf82|\ud83c\udf83|\ud83c\udf84|\ud83c\udf85|\ud83c\udf86|\ud83c\udf87|\ud83c\udf88|\ud83c\udf89|\ud83c\udf8a|\ud83c\udf8b|\ud83c\udf8c|\ud83c\udf8d|\ud83c\udf8e|\ud83c\udf8f|\ud83c\udf90|\ud83c\udf91|\ud83c\udf92|\ud83c\udf93|\ud83c\udfa0|\ud83c\udfa1|\ud83c\udfa2|\ud83c\udfa3|\ud83c\udfa4|\ud83c\udfa5|\ud83c\udfa6|\ud83c\udfa7|\ud83c\udfa8|\ud83c\udfa9|\ud83c\udfaa|\ud83c\udfab|\ud83c\udfac|\ud83c\udfad|\ud83c\udfae|\ud83c\udfaf|\ud83c\udfb0|\ud83c\udfb1|\ud83c\udfb2|\ud83c\udfb3|\ud83c\udfb4|\ud83c\udfb5|\ud83c\udfb6|\ud83c\udfb7|\ud83c\udfb8|\ud83c\udfb9|\ud83c\udfba|\ud83c\udfbb|\ud83c\udfbc|\ud83c\udfbd|\ud83c\udfbe|\ud83c\udfbf|\ud83c\udfc0|\ud83c\udfc1|\ud83c\udfc2|\ud83c\udfc3|\ud83c\udfc4|\ud83c\udfc6|\ud83c\udfc8|\ud83c\udfca|\ud83c\udfe0|\ud83c\udfe1|\ud83c\udfe2|\ud83c\udfe3|\ud83c\udfe5|\ud83c\udfe6|\ud83c\udfe7|\ud83c\udfe8|\ud83c\udfe9|\ud83c\udfea|\ud83c\udfeb|\ud83c\udfec|\ud83c\udfed|\ud83c\udfee|\ud83c\udfef|\ud83c\udff0|\ud83d\udc0c|\ud83d\udc0d|\ud83d\udc0e|\ud83d\udc11|\ud83d\udc12|\ud83d\udc14|\ud83d\udc17|\ud83d\udc18|\ud83d\udc19|\ud83d\udc1a|\ud83d\udc1b|\ud83d\udc1c|\ud83d\udc1d|\ud83d\udc1e|\ud83d\udc1f|\ud83d\udc20|\ud83d\udc21|\ud83d\udc22|\ud83d\udc23|\ud83d\udc24|\ud83d\udc25|\ud83d\udc26|\ud83d\udc27|\ud83d\udc28|\ud83d\udc29|\ud83d\udc2b|\ud83d\udc2c|\ud83d\udc2d|\ud83d\udc2e|\ud83d\udc2f|\ud83d\udc30|\ud83d\udc31|\ud83d\udc32|\ud83d\udc33|\ud83d\udc34|\ud83d\udc35|\ud83d\udc36|\ud83d\udc37|\ud83d\udc38|\ud83d\udc39|\ud83d\udc3a|\ud83d\udc3b|\ud83d\udc3c|\ud83d\udc3d|\ud83d\udc3e|\ud83d\udc40|\ud83d\udc42|\ud83d\udc43|\ud83d\udc44|\ud83d\udc45|\ud83d\udc46|\ud83d\udc47|\ud83d\udc48|\ud83d\udc49|\ud83d\udc4a|\ud83d\udc4b|\ud83d\udc4c|\ud83d\udc4d|\ud83d\udc4e|\ud83d\udc4f|\ud83d\udc50|\ud83d\udc51|\ud83d\udc52|\ud83d\udc53|\ud83d\udc54|\ud83d\udc55|\ud83d\udc56|\ud83d\udc57|\ud83d\udc58|\ud83d\udc59|\ud83d\udc5a|\ud83d\udc5b|\ud83d\udc5c|\ud83d\udc5d|\ud83d\udc5e|\ud83d\udc5f|\ud83d\udc60|\ud83d\udc61|\ud83d\udc62|\ud83d\udc63|\ud83d\udc64|\ud83d\udc66|\ud83d\udc67|\ud83d\udc68|\ud83d\udc69|\ud83d\udc6a|\ud83d\udc6b|\ud83d\udc6e|\ud83d\udc6f|\ud83d\udc70|\ud83d\udc71|\ud83d\udc72|\ud83d\udc73|\ud83d\udc74|\ud83d\udc75|\ud83d\udc76|\ud83d\udc77|\ud83d\udc78|\ud83d\udc79|\ud83d\udc7a|\ud83d\udc7b|\ud83d\udc7c|\ud83d\udc7d|\ud83d\udc7e|\ud83d\udc7f|\ud83d\udc80|\ud83d\udc81|\ud83d\udc82|\ud83d\udc83|\ud83d\udc84|\ud83d\udc85|\ud83d\udc86|\ud83d\udc87|\ud83d\udc88|\ud83d\udc89|\ud83d\udc8a|\ud83d\udc8b|\ud83d\udc8c|\ud83d\udc8d|\ud83d\udc8e|\ud83d\udc8f|\ud83d\udc90|\ud83d\udc91|\ud83d\udc92|\ud83d\udc93|\ud83d\udc94|\ud83d\udc95|\ud83d\udc96|\ud83d\udc97|\ud83d\udc98|\ud83d\udc99|\ud83d\udc9a|\ud83d\udc9b|\ud83d\udc9c|\ud83d\udc9d|\ud83d\udc9e|\ud83d\udc9f|\ud83d\udca0|\ud83d\udca1|\ud83d\udca2|\ud83d\udca3|\ud83d\udca4|\ud83d\udca5|\ud83d\udca6|\ud83d\udca7|\ud83d\udca8|\ud83d\udca9|\ud83d\udcaa|\ud83d\udcab|\ud83d\udcac|\ud83d\udcae|\ud83d\udcaf|\ud83d\udcb0|\ud83d\udcb1|\ud83d\udcb2|\ud83d\udcb3|\ud83d\udcb4|\ud83d\udcb5|\ud83d\udcb8|\ud83d\udcb9|\ud83d\udcba|\ud83d\udcbb|\ud83d\udcbc|\ud83d\udcbd|\ud83d\udcbe|\ud83d\udcbf|\ud83d\udcc0|\ud83d\udcc1|\ud83d\udcc2|\ud83d\udcc3|\ud83d\udcc4|\ud83d\udcc5|\ud83d\udcc6|\ud83d\udcc7|\ud83d\udcc8|\ud83d\udcc9|\ud83d\udcca|\ud83d\udccb|\ud83d\udccc|\ud83d\udccd|\ud83d\udcce|\ud83d\udccf|\ud83d\udcd0|\ud83d\udcd1|\ud83d\udcd2|\ud83d\udcd3|\ud83d\udcd4|\ud83d\udcd5|\ud83d\udcd6|\ud83d\udcd7|\ud83d\udcd8|\ud83d\udcd9|\ud83d\udcda|\ud83d\udcdb|\ud83d\udcdc|\ud83d\udcdd|\ud83d\udcde|\ud83d\udcdf|\ud83d\udce0|\ud83d\udce1|\ud83d\udce2|\ud83d\udce3|\ud83d\udce4|\ud83d\udce5|\ud83d\udce6|\ud83d\udce7|\ud83d\udce8|\ud83d\udce9|\ud83d\udcea|\ud83d\udceb|\ud83d\udcee|\ud83d\udcf0|\ud83d\udcf1|\ud83d\udcf2|\ud83d\udcf3|\ud83d\udcf4|\ud83d\udcf6|\ud83d\udcf7|\ud83d\udcf9|\ud83d\udcfa|\ud83d\udcfb|\ud83d\udcfc|\ud83d\udd03|\ud83d\udd0a|\ud83d\udd0b|\ud83d\udd0c|\ud83d\udd0d|\ud83d\udd0e|\ud83d\udd0f|\ud83d\udd10|\ud83d\udd11|\ud83d\udd12|\ud83d\udd13|\ud83d\udd14|\ud83d\udd16|\ud83d\udd17|\ud83d\udd18|\ud83d\udd19|\ud83d\udd1a|\ud83d\udd1b|\ud83d\udd1c|\ud83d\udd1d|\ud83d\udd1e|\ud83d\udd1f|\ud83d\udd20|\ud83d\udd21|\ud83d\udd22|\ud83d\udd23|\ud83d\udd24|\ud83d\udd25|\ud83d\udd26|\ud83d\udd27|\ud83d\udd28|\ud83d\udd29|\ud83d\udd2a|\ud83d\udd2b|\ud83d\udd2e|\ud83d\udd2f|\ud83d\udd30|\ud83d\udd31|\ud83d\udd32|\ud83d\udd33|\ud83d\udd34|\ud83d\udd35|\ud83d\udd36|\ud83d\udd37|\ud83d\udd38|\ud83d\udd39|\ud83d\udd3a|\ud83d\udd3b|\ud83d\udd3c|\ud83d\udd3d|\ud83d\udd50|\ud83d\udd51|\ud83d\udd52|\ud83d\udd53|\ud83d\udd54|\ud83d\udd55|\ud83d\udd56|\ud83d\udd57|\ud83d\udd58|\ud83d\udd59|\ud83d\udd5a|\ud83d\udd5b|\ud83d\uddfb|\ud83d\uddfc|\ud83d\uddfd|\ud83d\uddfe|\ud83d\uddff|\ud83d\ude01|\ud83d\ude02|\ud83d\ude03|\ud83d\ude04|\ud83d\ude05|\ud83d\ude06|\ud83d\ude09|\ud83d\ude0a|\ud83d\ude0b|\ud83d\ude0c|\ud83d\udeb4|\ud83d\ude0f|\ud83d\ude12|\ud83d\ude13|\ud83d\ude14|\ud83d\ude16|\ud83d\ude18|\ud83d\ude1a|\ud83d\ude1c|\ud83d\ude1d|\ud83d\ude1e|\ud83d\ude20|\ud83d\ude21|\ud83d\ude22|\ud83d\ude23|\ud83d\ude24|\ud83d\ude25|\ud83d\ude28|\ud83d\ude29|\ud83d\ude2a|\ud83d\ude2b|\ud83d\ude2d|\ud83d\ude30|\ud83d\ude31|\ud83d\ude32|\ud83d\ude33|\ud83d\ude35|\ud83d\ude37|\ud83d\ude38|\ud83d\ude39|\ud83d\ude3a|\ud83d\ude3b|\ud83d\ude3c|\ud83d\ude3d|\ud83d\ude3e|\ud83d\ude3f|\ud83d\ude40|\ud83d\ude45|\ud83d\ude46|\ud83d\ude47|\ud83d\ude48|\ud83d\ude49|\ud83d\ude4a|\ud83d\ude4b|\ud83d\ude4c|\ud83d\ude4d|\ud83d\ude4e|\ud83d\ude4f|\ud83d\ude80|\ud83d\ude83|\ud83d\ude84|\ud83d\ude85|\ud83d\ude87|\ud83d\ude89|\ud83d\ude8c|\ud83d\ude8f|\ud83d\ude91|\ud83d\ude92|\ud83d\ude93|\ud83d\ude95|\ud83d\ude97|\ud83d\ude99|\ud83d\ude9a|\ud83d\udea2|\ud83d\udea4|\ud83d\udea5|\ud83d\udea7|\ud83d\udea8|\ud83d\udea9|\ud83d\udeaa|\ud83d\udeab|\ud83d\udeac|\ud83d\udead|\ud83d\udeb2|\ud83d\udeb6|\ud83d\udeb9|\ud83d\udeba|\ud83d\udebb|\ud83d\udebc|\ud83d\udebd|\ud83d\udebe|\ud83d\udec0|\ud83c\udde6|\ud83c\udde7|\ud83c\udde8|\ud83c\udde9|\ud83c\uddea|\ud83c\uddeb|\ud83c\uddec|\ud83c\udded|\ud83c\uddee|\ud83c\uddef|\ud83c\uddf0|\ud83c\uddf1|\ud83c\uddf2|\ud83c\uddf3|\ud83c\uddf4|\ud83c\uddf5|\ud83c\uddf6|\ud83c\uddf7|\ud83c\uddf8|\ud83c\uddf9|\ud83c\uddfa|\ud83c\uddfb|\ud83c\uddfc|\ud83c\uddfd|\ud83c\uddfe|\ud83c\uddff|\ud83c\udf0d|\ud83c\udf0e|\ud83c\udf10|\ud83c\udf12|\ud83c\udf16|\ud83c\udf17|\ud83c\udf18|\ud83c\udf1a|\ud83c\udf1c|\ud83c\udf1d|\ud83c\udf1e|\ud83c\udf32|\ud83c\udf33|\ud83c\udf4b|\ud83c\udf50|\ud83c\udf7c|\ud83c\udfc7|\ud83c\udfc9|\ud83c\udfe4|\ud83d\udc00|\ud83d\udc01|\ud83d\udc02|\ud83d\udc03|\ud83d\udc04|\ud83d\udc05|\ud83d\udc06|\ud83d\udc07|\ud83d\udc08|\ud83d\udc09|\ud83d\udc0a|\ud83d\udc0b|\ud83d\udc0f|\ud83d\udc10|\ud83d\udc13|\ud83d\udc15|\ud83d\udc16|\ud83d\udc2a|\ud83d\udc65|\ud83d\udc6c|\ud83d\udc6d|\ud83d\udcad|\ud83d\udcb6|\ud83d\udcb7|\ud83d\udcec|\ud83d\udced|\ud83d\udcef|\ud83d\udcf5|\ud83d\udd00|\ud83d\udd01|\ud83d\udd02|\ud83d\udd04|\ud83d\udd05|\ud83d\udd06|\ud83d\udd07|\ud83d\udd08|\ud83d\udd09|\ud83d\udd15|\ud83d\udd2c|\ud83d\udd2d|\ud83d\udd5c|\ud83d\udd5d|\ud83d\udd5e|\ud83d\udd5f|\ud83d\udd60|\ud83d\udd61|\ud83d\udd62|\ud83d\udd63|\ud83d\udd64|\ud83d\udd65|\ud83d\udd66|\ud83d\udd67|\ud83d\ude00|\ud83d\ude07|\ud83d\ude08|\ud83d\ude0e|\ud83d\ude10|\ud83d\ude11|\ud83d\ude15|\ud83d\ude17|\ud83d\ude19|\ud83d\ude1b|\ud83d\ude1f|\ud83d\ude26|\ud83d\ude27|\ud83d\ude2c|\ud83d\ude2e|\ud83d\ude2f|\ud83d\ude34|\ud83d\ude36|\ud83d\ude81|\ud83d\ude82|\ud83d\ude86|\ud83d\ude88|\ud83d\ude8a|\ud83d\ude8b|\ud83d\ude8d|\ud83d\ude8e|\ud83d\ude90|\ud83d\ude94|\ud83d\ude96|\ud83d\ude98|\ud83d\ude9b|\ud83d\ude9c|\ud83d\ude9d|\ud83d\ude9e|\ud83d\ude9f|\ud83d\udea0|\ud83d\udea1|\ud83d\udea3|\ud83d\udea6|\ud83d\udeae|\ud83d\udeaf|\ud83d\udeb0|\ud83d\udeb1|\ud83d\udeb3|\ud83d\udeb4|\ud83d\udeb5|\ud83d\udeb7|\ud83d\udeb8|\ud83d\udebf|\ud83d\udec1|\ud83d\udec2|\ud83d\udec3|\ud83d\udec4|\ud83d\udec5|\ud83c\udf17|\ud83c\udf16|\ud83c\udde6|\ud83c\udde7|\ud83c\udde8|\ud83c\udde9|\ud83c\uddea|\ud83c\uddeb|\ud83c\uddec|\ud83c\udded|\ud83c\uddee|\ud83c\uddef|\ud83c\uddf0|\ud83c\uddf1|\ud83c\uddf2|\ud83c\uddf3|\ud83c\uddf4|\ud83c\uddf5|\ud83c\uddf6|\ud83c\uddf7|\ud83c\uddf8|\ud83c\uddf9|\ud83c\uddfa|\ud83c\uddfb|\ud83c\uddfc|\ud83c\uddfd|\ud83c\uddfe|\ud83c\uddff|\ud83c\udf0d|\ud83c\udf0e|\ud83c\udf10|\ud83c\udf12|\ud83c\udf16|\ud83c\udf17|\ud83c\udf18|\ud83c\udf1a|\ud83c\udf1c|\ud83c\udf1d|\ud83c\udf1e|\ud83c\udf32|\ud83c\udf33|\ud83c\udf4b|\ud83c\udf50|\ud83c\udf7c|\ud83c\udfc7|\ud83c\udfc9|\ud83c\udfe4|\ud83d\udc00|\ud83d\udc01|\ud83d\udc02|\ud83d\udc03|\ud83d\udc04|\ud83d\udc05|\ud83d\udc06|\ud83d\udc07|\ud83d\udc08|\ud83d\udc09|\ud83d\udc0a|\ud83d\udc0b|\ud83d\udc0f|\ud83d\udc10|\ud83d\udc13|\ud83d\udc15|\ud83d\udc16|\ud83d\udc2a|\ud83d\udc65|\ud83d\udc6c|\ud83d\udc6d|\ud83d\udcad|\ud83d\udcb6|\ud83d\udcb7|\ud83d\udcec|\ud83d\udced|\ud83d\udcef|\ud83d\udcf5|\ud83d\udd00|\ud83d\udd01|\ud83d\udd02|\ud83d\udd04|\ud83d\udd05|\ud83d\udd06|\ud83d\udd07|\ud83d\udd08|\ud83d\udd09|\ud83d\udd15|\ud83d\udd2c|\ud83d\udd2d|\ud83d\udd5c|\ud83d\udd5d|\ud83d\udd5e|\ud83d\udd5f|\ud83d\udd60|\ud83d\udd61|\ud83d\udd62|\ud83d\udd63|\ud83d\udd64|\ud83d\udd65|\ud83d\udd66|\ud83d\udd67|\ud83d\ude00|\ud83d\ude07|\ud83d\ude08|\ud83d\ude0e|\ud83d\ude10|\ud83d\ude11|\ud83d\ude15|\ud83d\ude17|\ud83d\ude19|\ud83d\ude1b|\ud83d\ude1f|\ud83d\ude26|\ud83d\ude27|\ud83d\ude2c|\ud83d\ude2e|\ud83d\ude2f|\ud83d\ude34|\ud83d\ude36|\ud83d\ude81|\ud83d\ude82|\ud83d\ude86|\ud83d\ude88|\ud83d\ude8a|\ud83d\ude8b|\ud83d\ude8d|\ud83d\ude8e|\ud83d\ude90|\ud83d\ude94|\ud83d\ude96|\ud83d\ude98|\ud83d\ude9b|\ud83d\ude9c|\ud83d\ude9d|\ud83d\ude9e|\ud83d\ude9f|\ud83d\udea0|\ud83d\udea1|\ud83d\udea3|\ud83d\udea6|\ud83d\udeae|\ud83d\udeaf|\ud83d\udeb0|\ud83d\udeb1|\ud83d\udeb3|\ud83d\udeb4|\ud83d\udeb5|\ud83d\udeb7|\ud83d\udeb8|\ud83d\udebf|\ud83d\udec1|\ud83d\udec2|\ud83d\udec3|\ud83d\udec4|\ud83d\udec5|\ud83c\udf12|\ud83c\udf10|\ud83c\udde6|\ud83c\udde7|\ud83c\udde8|\ud83c\udde9|\ud83c\uddea|\ud83c\uddeb|\ud83c\uddec|\ud83c\udded|\ud83c\uddee|\ud83c\uddef|\ud83c\uddf0|\ud83c\uddf1|\ud83c\uddf2|\ud83c\uddf3|\ud83c\uddf4|\ud83c\uddf5|\ud83c\uddf6|\ud83c\uddf7|\ud83c\uddf8|\ud83c\uddf9|\u
3265d83c\uddfa|\ud83c\uddfb|\ud83c\uddfc|\ud83c\uddfd|\ud83c\uddfe|\ud83c\uddff|\ud83c\udf0d|\ud83c\udf0e|\ue50a|\ue50a|\ue50a|\u27bf|\u3030|\u27b0|\u2797|\u2796|\u2795|\u2755|\u2754|\u2753|\u274e|\u274c|\u2728|\u270b|\u270a|\u2705|\u26ce|\u27bf|\u23f3|\u23f0|\u23ec|\u23eb|\u23ea|\u23e9|\u2122|\u27bf|\u00a9|\u00ae)|(?:(?:\ud83c\udc04|\ud83c\udd7f|\ud83c\ude1a|\ud83c\ude2f|\u3299|\u3297|\u303d|\u2b55|\u2b50|\u2b1c|\u2b1b|\u2b07|\u2b06|\u2b05|\u2935|\u2934|\u27a1|\u2764|\u2757|\u2747|\u2744|\u2734|\u2733|\u2716|\u2714|\u2712|\u270f|\u270c|\u2709|\u2708|\u2702|\u26fd|\u26fa|\u26f5|\u26f3|\u26f2|\u26ea|\u26d4|\u26c5|\u26c4|\u26be|\u26bd|\u26ab|\u26aa|\u26a1|\u26a0|\u2693|\u267b|\u2668|\u2666|\u2665|\u2663|\u2660|\u2653|\u2652|\u2651|\u2650|\u264f|\u264e|\u264d|\u264c|\u264b|\u264a|\u2649|\u2648|\u263a|\u261d|\u2615|\u2614|\u2611|\u260e|\u2601|\u2600|\u25fe|\u25fd|\u25fc|\u25fb|\u25c0|\u25b6|\u25ab|\u25aa|\u24c2|\u231b|\u231a|\u21aa|\u21a9|\u2199|\u2198|\u2197|\u2196|\u2195|\u2194|\u2139|\u2049|\u203c|\u267f)([\uFE0E\uFE0F]?)))/g,
3266
3267    // nodes with type 1 which should **not** be parsed
3268    shouldntBeParsed = /IFRAME|NOFRAMES|NOSCRIPT|SCRIPT|STYLE/,
3269
3270    // just a private shortcut
3271    fromCharCode = String.fromCharCode;
3272
3273  return twemoji;
3274
3275
3276  /////////////////////////
3277  //  private functions  //
3278  //     declaration     //
3279  /////////////////////////
3280
3281  /**
3282   * Shortcut to create text nodes
3283   * @param   string  text used to create DOM text node
3284   * @return  Node  a DOM node with that text
3285   */
3286  function createText(text) {
3287    return document.createTextNode(text);
3288  }
3289
3290  /**
3291   * Default callback used to generate emoji src
3292   *  based on Twitter CDN
3293   * @param   string    the emoji codepoint string
3294   * @param   string    the default size to use, i.e. "36x36"
3295   * @param   string    optional "\uFE0F" variant char, ignored by default
3296   * @return  string    the image source to use
3297   */
3298  function defaultImageSrcGenerator(icon, options) {
3299    return ''.concat(options.base, options.size, '/', icon, options.ext);
3300  }
3301
3302  /**
3303   * Given a generic DOM nodeType 1, walk through all children
3304   * and store every nodeType 3 (#text) found in the tree.
3305   * @param   Element a DOM Element with probably some text in it
3306   * @param   Array the list of previously discovered text nodes
3307   * @return  Array same list with new discovered nodes, if any
3308   */
3309  function grabAllTextNodes(node, allText) {
3310    var
3311      childNodes = node.childNodes,
3312      length = childNodes.length,
3313      subnode,
3314      nodeType;
3315    while (length--) {
3316      subnode = childNodes[length];
3317      nodeType = subnode.nodeType;
3318      // parse emoji only in text nodes
3319      if (nodeType === 3) {
3320        // collect them to process emoji later
3321        allText.push(subnode);
3322      }
3323      // ignore all nodes that are not type 1 or that
3324      // should not be parsed as script, style, and others
3325      else if (nodeType === 1 && !shouldntBeParsed.test(subnode.nodeName)) {
3326        grabAllTextNodes(subnode, allText);
3327      }
3328    }
3329    return allText;
3330  }
3331
3332  /**
3333   * Used to both remove the possible variant
3334   *  and to convert utf16 into code points
3335   * @param   string    the emoji surrogate pair
3336   * @param   string    the optional variant char, if any
3337   */
3338  function grabTheRightIcon(icon, variant) {
3339    // if variant is present as \uFE0F
3340    return toCodePoint(
3341      variant === '\uFE0F' ?
3342        // the icon should not contain it
3343        icon.slice(0, -1) :
3344        icon
3345    );
3346  }
3347
3348  /**
3349   * DOM version of the same logic / parser:
3350   *  emojify all found sub-text nodes placing images node instead.
3351   * @param   Element   generic DOM node with some text in some child node
3352   * @param   Object    options  containing info about how to parse
3353    *
3354    *            .callback   Function  the callback to invoke per each found emoji.
3355    *            .base       string    the base url, by default twemoji.base
3356    *            .ext        string    the image extension, by default twemoji.ext
3357    *            .size       string    the assets size, by default twemoji.size
3358    *
3359   * @return  Element same generic node with emoji in place, if any.
3360   */
3361  function parseNode(node, options) {
3362    var
3363      allText = grabAllTextNodes(node, []),
3364      length = allText.length,
3365      fragment,
3366      subnode,
3367      text,
3368      match,
3369      i,
3370      index,
3371      img,
3372      alt,
3373      icon,
3374      variant,
3375      src;
3376    while (length--) {
3377      fragment = document.createDocumentFragment();
3378      subnode = allText[length];
3379      text = subnode.nodeValue;
3380      i = 0;
3381      while ((match = re.exec(text))) {
3382        index = match.index;
3383        if (index !== i) {
3384          fragment.appendChild(
3385            createText(text.slice(i, index))
3386          );
3387        }
3388        alt = match[0];
3389        icon = match[1];
3390        variant = match[2];
3391        i = index + alt.length;
3392        if (variant !== '\uFE0E') {
3393          src = options.callback(
3394            grabTheRightIcon(icon, variant),
3395            options,
3396            variant
3397          );
3398          if (src) {
3399            img = new Image();
3400            img.onerror = twemoji.onerror;
3401            img.className = 'emoji';
3402            img.setAttribute('draggable', 'false');
3403            img.alt = alt;
3404            img.src = src;
3405          }
3406        }
3407        fragment.appendChild(img || createText(alt));
3408        img = null;
3409      }
3410      // is there actually anything to replace in here ?
3411      if (0 < i) {
3412        // any text left to be added ?
3413        if (i < text.length) {
3414          fragment.appendChild(
3415            createText(text.slice(i))
3416          );
3417        }
3418        // replace the text node only, leave intact
3419        // anything else surrounding such text
3420        subnode.parentNode.replaceChild(fragment, subnode);
3421      }
3422    }
3423    return node;
3424  }
3425
3426  /**
3427   * String/HTML version of the same logic / parser:
3428   *  emojify a generic text placing images tags instead of surrogates pair.
3429   * @param   string    generic string with possibly some emoji in it
3430   * @param   Object    options  containing info about how to parse
3431   *
3432   *            .callback   Function  the callback to invoke per each found emoji.
3433   *            .base       string    the base url, by default twemoji.base
3434   *            .ext        string    the image extension, by default twemoji.ext
3435   *            .size       string    the assets size, by default twemoji.size
3436   *
3437   * @return  the string with <img tags> replacing all found and parsed emoji
3438   */
3439  function parseString(str, options) {
3440    return replace(str, function (match, icon, variant) {
3441      var src;
3442      // verify the variant is not the FE0E one
3443      // this variant means "emoji as text" and should not
3444      // require any action/replacement
3445      // http://unicode.org/Public/UNIDATA/StandardizedVariants.html
3446      if (variant !== '\uFE0E') {
3447        src = options.callback(
3448          grabTheRightIcon(icon, variant),
3449          options,
3450          variant
3451        );
3452        if (src) {
3453          // recycle the match string replacing the emoji
3454          // with its image counter part
3455          match = '<img '.concat(
3456            'class="emoji" ',
3457            'draggable="false" ',
3458            // needs to preserve user original intent
3459            // when variants should be copied and pasted too
3460            'alt="',
3461            match,
3462            '" ',
3463            'src="',
3464            src,
3465            '"',
3466            '>'
3467          );
3468        }
3469      }
3470      return match;
3471    });
3472  }
3473
3474  /**
3475   * Given a generic value, creates its squared counterpart if it's a number.
3476   *  As example, number 36 will return '36x36'.
3477   * @param   any     a generic value.
3478   * @return  any     a string representing asset size, i.e. "36x36"
3479   *                  only in case the value was a number.
3480   *                  Returns initial value otherwise.
3481   */
3482  function toSizeSquaredAsset(value) {
3483    return typeof value === 'number' ?
3484      value + 'x' + value :
3485      value;
3486  }
3487
3488
3489  /////////////////////////
3490  //  exported functions //
3491  //     declaration     //
3492  /////////////////////////
3493
3494  function fromCodePoint(codepoint) {
3495    var code = typeof codepoint === 'string' ?
3496          parseInt(codepoint, 16) : codepoint;
3497    if (code < 0x10000) {
3498      return fromCharCode(code);
3499    }
3500    code -= 0x10000;
3501    return fromCharCode(
3502      0xD800 + (code >> 10),
3503      0xDC00 + (code & 0x3FF)
3504    );
3505  }
3506
3507  function parse(what, how) {
3508    if (!how || typeof how === 'function') {
3509      how = {callback: how};
3510    }
3511    // if first argument is string, inject html <img> tags
3512    // otherwise use the DOM tree and parse text nodes only
3513    return (typeof what === 'string' ? parseString : parseNode)(what, {
3514      callback: how.callback || defaultImageSrcGenerator,
3515      base:     typeof how.base === 'string' ? how.base : twemoji.base,
3516      ext:      how.ext || twemoji.ext,
3517      size:     "svg" // toSizeSquaredAsset(how.size || twemoji.size)
3518    });
3519  }
3520
3521  function replace(text, callback) {
3522    return String(text).replace(re, callback);
3523  }
3524
3525  function test(text) {
3526    // IE6 needs a reset before too
3527    re.lastIndex = 0;
3528    var result = re.test(text);
3529    re.lastIndex = 0;
3530    return result;
3531  }
3532
3533  function toCodePoint(unicodeSurrogates, sep) {
3534    var
3535      r = [],
3536      c = 0,
3537      p = 0,
3538      i = 0;
3539    while (i < unicodeSurrogates.length) {
3540      c = unicodeSurrogates.charCodeAt(i++);
3541      if (p) {
3542        r.push((0x10000 + ((p - 0xD800) << 10) + (c - 0xDC00)).toString(16));
3543        p = 0;
3544      } else if (0xD800 <= c && c <= 0xDBFF) {
3545        p = c;
3546      } else {
3547        r.push(c.toString(16));
3548      }
3549    }
3550    return r.join(sep || '-');
3551  }
3552
3553}());
3554
3555;
3556var JetpackEmoji = {
3557		EMOJI_SIZE: 72,
3558		BASE_URL: '//s0.wp.com/wp-content/mu-plugins/emoji/twemoji/',
3559
3560        parse: function( element, size, base_url ) {
3561            twemoji.parse( element, {
3562                size: size || this.EMOJI_SIZE,
3563				base: base_url || this.BASE_URL,
3564				callback: function(icon, options, variant) {
3565					// Ignore some standard characters that TinyMCE recommends in its character map.
3566					// https://teamtinker.wordpress.com/2014/10/29/emoji-launch-week/#comment-2103
3567					switch ( icon ) {
3568						case 'a9':
3569						case 'ae':
3570						case '2122':
3571						case '2194':
3572						case '2660':
3573						case '2663':
3574						case '2665':
3575						case '2666':
3576							return false;
3577					}
3578				
3579					// directly from twemoji
3580					return ''.concat(options.base, options.size, '/', icon, options.ext);
3581				}
3582            } );
3583        }
3584}
3585;
3586(function() {
3587    var emoji = {	
3588        init: function() {
3589			if ( typeof JetpackEmoji === 'undefined' ) {
3590				console.error( 'JetpackEmoji not found.' );
3591				return;
3592			}
3593
3594			var size, base_url;
3595			if ( typeof JetpackEmojiSettings !== 'undefined' ) {
3596				size = JetpackEmojiSettings.size || null;
3597				base_url = JetpackEmojiSettings.base_url || null;
3598			}	
3599
3600            JetpackEmoji.parse( document.body, size, base_url );
3601
3602            if ( typeof infiniteScroll !== 'undefined' ) {
3603                jQuery( document.body ).on( 'post-load', function( response ) {
3604                    // TODO: ideally, we should only target the newly added elements
3605                    JetpackEmoji.parse( document.body, size, base_url );
3606                } );
3607            }
3608        }
3609    };
3610
3611    if ( window.addEventListener ) {
3612        window.addEventListener( 'load', emoji.init, false );
3613    } else if ( window.attachEvent ) {
3614        window.attachEvent( 'onload', emoji.init );
3615    }
3616})();
3617;
3618/*	SWFObject v2.2 <http://code.google.com/p/swfobject/> 
3619	is released under the MIT License <http://www.opensource.org/licenses/mit-license.php> 
3620*/
3621var swfobject=function(){var D="undefined",r="object",S="Shockwave Flash",W="ShockwaveFlash.ShockwaveFlash",q="application/x-shockwave-flash",R="SWFObjectExprInst",x="onreadystatechange",O=window,j=document,t=navigator,T=false,U=[h],o=[],N=[],I=[],l,Q,E,B,J=false,a=false,n,G,m=true,M=function(){var aa=typeof j.getElementById!=D&&typeof j.getElementsByTagName!=D&&typeof j.createElement!=D,ah=t.userAgent.toLowerCase(),Y=t.platform.toLowerCase(),ae=Y?/win/.test(Y):/win/.test(ah),ac=Y?/mac/.test(Y):/mac/.test(ah),af=/webkit/.test(ah)?parseFloat(ah.replace(/^.*webkit\/(\d+(\.\d+)?).*$/,"$1")):false,X=!+"\v1",ag=[0,0,0],ab=null;if(typeof t.plugins!=D&&typeof t.plugins[S]==r){ab=t.plugins[S].description;if(ab&&!(typeof t.mimeTypes!=D&&t.mimeTypes[q]&&!t.mimeTypes[q].enabledPlugin)){T=true;X=false;ab=ab.replace(/^.*\s+(\S+\s+\S+$)/,"$1");ag[0]=parseInt(ab.replace(/^(.*)\..*$/,"$1"),10);ag[1]=parseInt(ab.replace(/^.*\.(.*)\s.*$/,"$1"),10);ag[2]=/[a-zA-Z]/.test(ab)?parseInt(ab.replace(/^.*[a-zA-Z]+(.*)$/,"$1"),10):0}}else{if(typeof O.ActiveXObject!=D){try{var ad=new ActiveXObject(W);if(ad){ab=ad.GetVariable("$version");if(ab){X=true;ab=ab.split(" ")[1].split(",");ag=[parseInt(ab[0],10),parseInt(ab[1],10),parseInt(ab[2],10)]}}}catch(Z){}}}return{w3:aa,pv:ag,wk:af,ie:X,win:ae,mac:ac}}(),k=function(){if(!M.w3){return}if((typeof j.readyState!=D&&j.readyState=="complete")||(typeof j.readyState==D&&(j.getElementsByTagName("body")[0]||j.body))){f()}if(!J){if(typeof j.addEventListener!=D){j.addEventListener("DOMContentLoaded",f,false)}if(M.ie&&M.win){j.attachEvent(x,function(){if(j.readyState=="complete"){j.detachEvent(x,arguments.callee);f()}});if(O==top){(function(){if(J){return}try{j.documentElement.doScroll("left")}catch(X){setTimeout(arguments.callee,0);return}f()})()}}if(M.wk){(function(){if(J){return}if(!/loaded|complete/.test(j.readyState)){setTimeout(arguments.callee,0);return}f()})()}s(f)}}();function f(){if(J){return}try{var Z=j.getElementsByTagName("body")[0].appendChild(C("span"));Z.parentNode.removeChild(Z)}catch(aa){return}J=true;var X=U.length;for(var Y=0;Y<X;Y++){U[Y]()}}function K(X){if(J){X()}else{U[U.length]=X}}function s(Y){if(typeof O.addEventListener!=D){O.addEventListener("load",Y,false)}else{if(typeof j.addEventListener!=D){j.addEventListener("load",Y,false)}else{if(typeof O.attachEvent!=D){i(O,"onload",Y)}else{if(typeof O.onload=="function"){var X=O.onload;O.onload=function(){X();Y()}}else{O.onload=Y}}}}}function h(){if(T){V()}else{H()}}function V(){var X=j.getElementsByTagName("body")[0];var aa=C(r);aa.setAttribute("type",q);var Z=X.appendChild(aa);if(Z){var Y=0;(function(){if(typeof Z.GetVariable!=D){var ab=Z.GetVariable("$version");if(ab){ab=ab.split(" ")[1].split(",");M.pv=[parseInt(ab[0],10),parseInt(ab[1],10),parseInt(ab[2],10)]}}else{if(Y<10){Y++;setTimeout(arguments.callee,10);return}}X.removeChild(aa);Z=null;H()})()}else{H()}}function H(){var ag=o.length;if(ag>0){for(var af=0;af<ag;af++){var Y=o[af].id;var ab=o[af].callbackFn;var aa={success:false,id:Y};if(M.pv[0]>0){var ae=c(Y);if(ae){if(F(o[af].swfVersion)&&!(M.wk&&M.wk<312)){w(Y,true);if(ab){aa.success=true;aa.ref=z(Y);ab(aa)}}
3621else{if(o[af].expressInstall&&A()){var ai={};ai.data=o[af].expressInstall;ai.width=ae.getAttribute("width")||"0";ai.height=ae.getAttribute("height")||"0";if(ae.getAttribute("class")){ai.styleclass=ae.getAttribute("class")}if(ae.getAttribute("align")){ai.align=ae.getAttribute("align")}var ah={};var X=ae.getElementsByTagName("param");var ac=X.length;for(var ad=0;ad<ac;ad++){if(X[ad].getAttribute("name").toLowerCase()!="movie"){ah[X[ad].getAttribute("name")]=X[ad].getAttribute("value")}}P(ai,ah,Y,ab)}else{p(ae);if(ab){ab(aa)}}}}}else{w(Y,true);if(ab){var Z=z(Y);if(Z&&typeof Z.SetVariable!=D){aa.success=true;aa.ref=Z}ab(aa)}}}}}function z(aa){var X=null;var Y=c(aa);if(Y&&Y.nodeName=="OBJECT"){if(typeof Y.SetVariable!=D){X=Y}else{var Z=Y.getElementsByTagName(r)[0];if(Z){X=Z}}}return X}function A(){return !a&&F("6.0.65")&&(M.win||M.mac)&&!(M.wk&&M.wk<312)}function P(aa,ab,X,Z){a=true;E=Z||null;B={success:false,id:X};var ae=c(X);if(ae){if(ae.nodeName=="OBJECT"){l=g(ae);Q=null}else{l=ae;Q=X}aa.id=R;if(typeof aa.width==D||(!/%$/.test(aa.width)&&parseInt(aa.width,10)<310)){aa.width="310"}if(typeof aa.height==D||(!/%$/.test(aa.height)&&parseInt(aa.height,10)<137)){aa.height="137"}j.title=j.title.slice(0,47)+" - Flash Player Installation";var ad=M.ie&&M.win?"ActiveX":"PlugIn",ac="MMredirectURL="+encodeURI(O.location).toString().replace(/&/g,"%26")+"&MMplayerType="+ad+"&MMdoctitle="+j.title;if(typeof ab.flashvars!=D){ab.flashvars+="&"+ac}else{ab.flashvars=ac}if(M.ie&&M.win&&ae.readyState!=4){var Y=C("div");X+="SWFObjectNew";Y.setAttribute("id",X);ae.parentNode.insertBefore(Y,ae);ae.style.display="none";(function(){if(ae.readyState==4){ae.parentNode.removeChild(ae)}else{setTimeout(arguments.callee,10)}})()}u(aa,ab,X)}}function p(Y){if(M.ie&&M.win&&Y.readyState!=4){var X=C("div");Y.parentNode.insertBefore(X,Y);X.parentNode.replaceChild(g(Y),X);Y.style.display="none";(function(){if(Y.readyState==4){Y.parentNode.removeChild(Y)}else{setTimeout(arguments.callee,10)}})()}else{Y.parentNode.replaceChild(g(Y),Y)}}function g(ab){var aa=C("div");if(M.win&&M.ie){aa.innerHTML=ab.innerHTML}else{var Y=ab.getElementsByTagName(r)[0];if(Y){var ad=Y.childNodes;if(ad){var X=ad.length;for(var Z=0;Z<X;Z++){if(!(ad[Z].nodeType==1&&ad[Z].nodeName=="PARAM")&&!(ad[Z].nodeType==8)){aa.appendChild(ad[Z].cloneNode(true))}}}}}return aa}function u(ai,ag,Y){var X,aa=c(Y);if(M.wk&&M.wk<312){return X}if(aa){if(typeof ai.id==D){ai.id=Y}if(M.ie&&M.win){var ah="";for(var ae in ai){if(ai[ae]!=Object.prototype[ae]){if(ae.toLowerCase()=="data"){ag.movie=ai[ae]}else{if(ae.toLowerCase()=="styleclass"){ah+=' class="'+ai[ae]+'"'}else{if(ae.toLowerCase()!="classid"){ah+=" "+ae+'="'+ai[ae]+'"'}}}}}var af="";for(var ad in ag){if(ag[ad]!=Object.prototype[ad]){af+='<param name="'+ad+'" value="'+ag[ad]+'" />'}}aa.outerHTML='<object classid="clsid:D27CDB6E-AE6D-11cf-96B8-444553540000"'+ah+">"+af+"</object>";N[N.length]=ai.id;X=c(ai.id)}else{var Z=C(r);Z.setAttribute("type",q);for(var ac in ai){if(ai[ac]!=Object.prototype[ac]){if(ac.toLowerCase()=="styleclass"){Z.setAttribute("class",ai[ac])}else{if(ac.toLowerCase()!="classid"){Z.setAttribute(ac,ai[ac])}}}}for(var ab in ag){if(ag[ab]!=Object.prototype[ab]&&ab.toLowerCase()!="movie"){e(Z,ab,ag[ab])}}aa.parentNode.replaceChild(Z,aa);X=Z}}return X}function e(Z,X,Y){var aa=C("param");aa.setAttribute("name",X);aa.setAttribute("value",Y);Z.appendChild(aa)}function y(Y){var X=c(Y);if(X&&X.nodeName=="OBJECT"){if(M.ie&&M.win){X.style.display="none";(function(){if(X.readyState==4){b(Y)}else{setTimeout(arguments.callee,10)}})()}else{X.parentNode.removeChild(X)}}}function b(Z){var Y=c(Z);if(Y){for(var X in Y){if(typeof Y[X]=="function"){Y[X]=null}}Y.parentNode.removeChild(Y)}}function c(Z){var X=null;try{X=j.getElementById(Z)}catch(Y){}return X}function C(X){return j.createElement(X)}function i(Z,X,Y){Z.attachEvent(X,Y);I[I.length]=[Z,X,Y]}function F(Z){var Y=M.pv,X=Z.split(".");X[0]=parseInt(X[0],10);X[1]=parseInt(X[1],10)||0;X[2]=parseInt(X[2],10)||0;return(Y[0]>X[0]||(Y[0]==X[0]&&Y[1]>X[1])||(Y[0]==X[0]&&Y[1]==X[1]&&Y[2]>=X[2]))?true:false}function v(ac,Y,ad,ab){if(M.ie&&M.mac){return}var aa=j.getElementsByTagName("head")[0];if(!aa){return}var X=(ad&&typeof ad=="string")?ad:"screen";if(ab){n=null;G=null}if(!n||G!=X){var Z=C("style");Z.setAttribute("type","text/css");Z.setAttribute("media",X);n=aa.appendChild(Z);if(M.ie&&M.win&&typeof j.styleSheets!=D&&j.styleSheets.length>0){n=j.styleSheets[j.styleSheets.length-1]}G=X}if(M.ie&&M.win){if(n&&typeof n.addRule==r){n.addRule(ac,Y)}}else{if(n&&typeof j.createTextNode!=D){n.appendChild(j.createTextNode(ac+" {"+Y+"}"))}}}function w(Z,X){if(!m){return}var Y=X?"visible":"hidden";if(J&&c(Z)){c(Z).style.visibility=Y}else{v("#"+Z,"visibility:"+Y)}}function L(Y){var Z=/[\\\"<>\.;]/;var X=Z.exec(Y)!=null;return X&&typeof encodeURIComponent!=D?encodeURIComponent(Y):Y}var d=function(){if(M.ie&&M.win){window.attachEvent("onunload",function(){var ac=I.length;for(var ab=0;ab<ac;ab++){I[ab][0].detachEvent(I[ab][1],I[ab][2])}var Z=N.length;for(var aa=0;aa<Z;aa++){y(N[aa])}for(var Y in M){M[Y]=null}M=null;for(var X in swfobject){swfobject[X]=null}swfobject=null})}}();return{registerObject:function(ab,X,aa,Z){if(M.w3&&ab&&X){var Y={};Y.id=ab;Y.swfVersion=X;Y.expressInstall=aa;Y.callbackFn=Z;o[o.length]=Y;w(ab,false)}else{if(Z){Z({success:false,id:ab})}}},getObjectById:function(X){if(M.w3){return z(X)}},embedSWF:function(ab,ah,ae,ag,Y,aa,Z,ad,af,ac){var X={success:false,id:ah};if(M.w3&&!(M.wk&&M.wk<312)&&ab&&ah&&ae&&ag&&Y){w(ah,false);K(function(){ae+="";ag+="";var aj={};if(af&&typeof af===r){for(var al in af){aj[al]=af[al]}}aj.data=ab;aj.width=ae;aj.height=ag;var am={};if(ad&&typeof ad===r){for(var ak in ad){am[ak]=ad[ak]}}if(Z&&typeof Z===r){for(var ai in Z){if(typeof am.flashvars!=D){am.flashvars+="&"+ai+"="+Z[ai]}else{am.flashvars=ai+"="+Z[ai]}}}if(F(Y)){var an=u(aj,am,ah);if(aj.id==ah){w(ah,true)}X.success=true;X.ref=an}else{if(aa&&A()){aj.data=aa;P(aj,am,ah,ac);return}else{w(ah,true)}}if(ac){ac(X)}})}else{if(ac){ac(X)}}},switchOffAutoHideShow:function(){m=false},ua:M,getFlashPlayerVersion:function(){return{major:M.pv[0],minor:M.pv[1],release:M.pv[2]}},hasFlashPlayerVersion:F,createSWF:function(Z,Y,X){if(M.w3){return u(Z,Y,X)}else{return undefined}},showExpressInstall:function(Z,aa,X,Y){if(M.w3&&A()){P(Z,aa,X,Y)}},removeSWF:function(X){if(M.w3){y(X)}},createCSS:function(aa,Z,Y,X){if(M.w3){v(aa,Z,Y,X)}},addDomLoadEvent:K,addLoadEvent:s,getQueryParamValue:function(aa){var Z=j.location.search||j.location.hash;if(Z){if(/\?/.test(Z)){Z=Z.split("?")[1]}if(aa==null){return L(Z)}var Y=Z.split("&");for(var X=0;X<Y.length;X++){if(Y[X].substring(0,Y[X].indexOf("="))==aa){return L(Y[X].substring((Y[X].indexOf("=")+1)))}}}return""},expressInstallCallback:function(){if(a){var X=c(R);if(X&&l){X.parentNode.replaceChild(l,X);if(Q){w(Q,true);if(M.ie&&M.win){l.style.display="block"}}if(E){E(B)}}a=false}}}}();;
3622/*!
3623 * VideoPress JavaScript Loader 1.09
3624 *
3625 * Copyright 2011 Automattic Inc.
3626 * Licensed under GNU General Public License (GPL) Version 2 or later
3627 * http://www.gnu.org/licenses/gpl-2.0.html
3628 */
3629
3630// jQuery.type() was introduced in 1.4.3, WP 3.0 has 1.4.2 
3631// To maintain backwards compatibility, insert our own version if it doesn't exist 
3632if ( typeof jQuery.type != 'function' ) { 
3633	jQuery.extend({
3634		class2type: {},
3635	
3636		type: function( obj ) { 
3637	 		return obj == null ? 
3638	 	    	String( obj ) : 
3639	 	    	jQuery.class2type[ toString.call(obj) ] || "object"; 
3640		}
3641	});
3642	
3643	jQuery.each("Boolean Number String Function Array Date RegExp Object".split(" "), function(i, name) { 
3644	 	jQuery.class2type[ "[object " + name + "]" ] = name.toLowerCase(); 
3645	}); 
3646	
3647} 
3648
3649jQuery.extend({VideoPress: {
3650	error:{
3651		messages:{
3652			age:"You are of insufficient age to view this video.",
3653			error:"Unable to download and play video",
3654			flash:"Error loading Flash on your system",
3655			freedom:'You do not have sufficient <a href="http://www.gnu.org/philosophy/free-sw.html">Freedom levels</a> to view this video.',
3656			incompatible:'VideoPress requires either HTML5 video or <a href="http://www.adobe.com/go/getflashplayer">
3656Adobe Flash Player 10</a> or above for playback.',
3657			incapable:"Your device is incapable of playing the requested video.",
3658			network:"A network error prevented video download and playback"
3659		}
3660	},
3661	support:{
3662		flash: function() {
3663			if ( typeof swfobject !== "undefined" && swfobject.hasFlashPlayerVersion( jQuery.VideoPress.video.flash.min_version ) ) {
3664				return true;
3665			} else {
3666				return false;
3667			}
3668		},
3669		html5Video: function(sourcetype) {
3670			var v = document.createElement("video");
3671			if ( !!v.canPlayType ) {
3672				if ( jQuery.type(sourcetype)==="string" ) {
3673					return !!v.canPlayType(sourcetype).replace(/no/,"");
3674				}
3675				return true;
3676			} else {
3677				return false;
3678			}
3679		}
3680	},
3681	data:[],
3682	analytics:{
3683		wpcom:{
3684			base_uri:"//pixel.wp.com/v.gif?",
3685			params:function(guid, filetype) {
3686				var data = jQuery.VideoPress.data[guid];
3687				if ( !jQuery.isPlainObject(data) ) {
3688					return;
3689				}
3690
3691				var params = {blog:data.blog,post:data.post,page_url:top.document.URL};
3692				if ( filetype==="mp4" && data.mp4 && data.mp4.size ) {
3693					params.video_fmt = "fmt_" + data.mp4.size;
3694				} else {
3695					params.video_fmt = "fmt_std";
3696				}
3697				return params;
3698			},
3699			send:function(params) {
3700				var beacon = new Image();
3701				beacon.src = jQuery.VideoPress.analytics.wpcom.base_uri + jQuery.param(params);
3702				beacon=null;
3703			}
3704		},
3705		ga:{
3706			is_active:function(){
3707				if ( (typeof _gaq !== "undefined") && jQuery.type( _gaq )==="array" ) {
3708					return true;
3709				} else {
3710					return false;
3711				}
3712			}
3713		},
3714		/*comscore:{
3715			is_active()function(){
3716				if ( typeof COMSCORE !== "undefined" )
3717					return true;
3718			}
3719		}*/
3720		impression:function(guid) {
3721			var params = jQuery.VideoPress.analytics.wpcom.params( guid, "mp4" );
3722			if ( !jQuery.isPlainObject( params ) ) {
3723				return;
3724			}
3725			params.video_impression = 1;
3726			jQuery.VideoPress.analytics.wpcom.send( params );
3727		},
3728		played:function(guid, filetype) {
3729			var params = jQuery.VideoPress.analytics.wpcom.params( guid, filetype );
3730			if ( !jQuery.isPlainObject( params ) ) {
3731				return;
3732			}
3733			params.video_play = 1;
3734			jQuery.VideoPress.analytics.wpcom.send( params );
3735
3736			if ( jQuery.VideoPress.analytics.ga.is_active() ) {
3737				/* Google Analytics tracks unique events per visit.
3738				 * Unique video value omitted since two videos on the same page that are both played would only be recorded as one play event.
3739				 */
3740				_gaq.push( ["_trackEvent", "Videos", "Play" ] );
3741			}
3742		},
3743		watched:function( guid, filetype, current_time, initial_time ) {
3744			if ( jQuery.VideoPress.data[guid].playback === undefined ) {
3745				var start = 0;
3746				if ( initial_time !== undefined && jQuery.type(initial_time)==="number" && initial_time > 0 ) {
3747					if ( current_time > initial_time ) {
3748						start = current_time - initial_time;
3749					}
3750				}
3751				jQuery.VideoPress.data[guid].playback = { last_observed:current_time, time_elapsed:start };
3752			}
3753			var time_watched = current_time - jQuery.VideoPress.data[guid].playback.last_observed;
3754			if ( time_watched > 0 ) {
3755				jQuery.VideoPress.data[guid].playback.time_elapsed += time_watched;
3756				jQuery.VideoPress.data[guid].playback.last_observed = current_time;
3757				if ( jQuery.VideoPress.data[guid].playback.time_elapsed > 15 ) {
3758					var params = jQuery.VideoPress.analytics.wpcom.params( guid, filetype );
3759					if ( !jQuery.isPlainObject( params ) ) {
3760			  			return;
3761					}
3762					params.t = 15;
3763					jQuery.VideoPress.analytics.wpcom.send( params );
3764					jQuery.VideoPress.data[guid].playback.time_elapsed -= 15;
3765				} else if ( current_time === jQuery.VideoPress.data[guid].duration ) {
3766					jQuery.VideoPress.analytics.wpcom.send( params );
3767					jQuery.VideoPress.data[guid].playback.time_elapsed = 0;
3768					delete jQuery.VideoPress.data[guid].playback;
3769				}
3770			}
3771		}
3772	},
3773	requirements:{
3774		isSufficientAge: function( container_el, min_age ) {
3775			var birthday = new Date( parseInt( jQuery( "select[name=year]", container_el ).val() ), parseInt( jQuery( "select[name=month]", container_el ).val() ), parseInt( jQuery( "select[name=day]", container_el ).val() ) );
3776			var allowed_age = new Date( Date.now() - (1000*60*60*24*365*min_age) );
3777			if ( birthday > allowed_age ) {
3778				container_el.html( '<p class="error" style="color:#fff;font-weight:bold">' + jQuery.VideoPress.error.messages.age + "</p>");
3779			} else {
3780				jQuery.VideoPress.video.play(container_el);
3781			}
3782		},
3783		allowedDomain: function (allowed_domains) {
3784			if ( jQuery.type(allowed_domains)==="array" ) {
3785				if ( jQuery.inArray( top.document.location.hostname, allowed_domains )===-1 ) {
3786					return false;
3787				}
3788			}
3789			return true;
3790		}
3791	},
3792	video:{
3793		flash:{
3794			// Protocol and domain for player_uri and expressinstall set in video.play()
3795			player_uri: ( 'https:' == location.protocol ? 'https://v0.wordpress.com' : 'http://s0.videopress.com' ) + "/player.swf?v=1.04",
3796			min_version:"10.0.0",
3797			params:{wmode:"direct",quality:"autohigh",seamlesstabbing:"true",allowfullscreen:"true",allowscriptaccess:"always",overstretch:"true"},
3798			expressinstall: ( 'https:' == location.protocol ? 'https://v0.wordpress.com' : 'http://s0.videopress.com' ) + "/playerProductInstall.swf",
3799			embedCallback: function(event) {
3800				if ( event.success===false ) {
3801					jQuery("#" + event.id).html("<p>" + jQuery.VideoPress.error.messages.flash + "</p>");
3802				}
3803			}
3804		},
3805		types:{mp4:'video/mp4; codecs="avc1.64001E, mp4a.40.2"',ogv:'video/ogg; codecs="theora, vorbis"'},
3806		canPlay:function () {
3807			if ( jQuery.VideoPress.support.flash() ) {
3808				jQuery.VideoPress.video.playerSupport = "flash";
3809			} else if ( jQuery.VideoPress.support.html5Video() ) {
3810				if ( jQuery.VideoPress.support.html5Video( jQuery.VideoPress.video.types.mp4 ) ) {
3811					jQuery.VideoPress.video.playerSupport = "mp4";
3812				} else if ( jQuery.VideoPress.support.html5Video( jQuery.VideoPress.video.types.ogv ) ) {
3813					jQuery.VideoPress.video.playerSupport = "ogv";
3814				} else {
3815					jQuery.VideoPress.video.playerSupport = "html5";
3816				}
3817			} else {
3818				jQuery.VideoPress.video.playerSupport = "";
3819			}
3820		},
3821		prepare: function ( guid, config, count ) {
3822			var video = jQuery.VideoPress.data[guid][count];
3823			if ( config.container === undefined || jQuery.type(video)!=="object" ) {
3824				return;
3825			}
3826			
3827			var width = 0;
3828			if ( config.width !== undefined ) {
3829				width = config.width;
3830			} else {
3831				config.container.width();
3832			}
3833
3834
3835			var height = 0;
3836			if ( config.height !== undefined ) {
3837				height = config.height;
3838			} else {
3839				config.container.height();
3840			}
3841			
3842			var div_id = "#v-" + guid + '-' + count;
3843			
3844			var parent_width = jQuery( div_id ).parent().width();
3845			var diffw = 0;
3846			var diffh = 0;
3847			var ratio = 0;
3848			if ( width > parent_width ) {
3849				diffw = width - parent_width + 11;
3850				ratio = ( width * 1.0 ) / ( height * 1.0 );
3851				diffh = diffw / ratio;
3852				
3853				width -= diffw;
3854				height -= Math.round( diffh );
3855			}
3856			
3857			if ( width < 60 || height < 60 ) {
3858				width = 400;
3859				height = 300;
3860			}
3861			
3862			jQuery.VideoPress.data[guid][count].dimensions = {};
3863			
3864			if( 0 == ratio ) {
3865				jQuery.VideoPress.data[guid][count].dimensions.width = width;
3866				jQuery.VideoPress.data[guid][count].dimensions.height = height;
3867			}
3868			else {
3869				jQuery.VideoPress.data[guid][count].dimensions.width = width - 7;
3870				jQuery.VideoPress.data[guid][count].dimensions.height = height - Math.round( 7 / ratio );
3871				
3872				jQuery( div_id ).width( width );
3873				jQuery( div_id ).height( height + 50 );
3874	
3875				jQuery( div_id + "-placeholder" ).width( jQuery.VideoPress.data[guid][count].dimensions.width );
3876				jQuery( div_id + "-placeholder" ).height( jQuery.VideoPress.data[guid][count].dimensions.height );
3877	
3878				jQuery( div_id + "-placeholder img.videopress-poster" ).width( jQuery.VideoPress.data[guid][count].dimensions.width );
3879				jQuery( div_id + "-placeholder img.videopress-poster" ).height( jQuery.VideoPress.data[guid][count].dimensions.height );
3880			}
3881
3882			config.container.data( "guid", guid );
3883			config.container.data( "count", count );
3884			if ( jQuery.VideoPress.video.playerSupport === undefined ) {
3885				jQuery.VideoPress.video.canPlay();
3886			}
3887
3888			if ( config.freedom===true && jQuery.type(video.ogv)==="string" ) {
3889				jQuery.VideoPress.video.insert( config.container, guid, count, video, "ogv", jQuery.VideoPress.data[guid][count].dimensions.width, jQuery.VideoPress.data[guid].dimensions.height );
3890				config.container.data( "player", "ogv" );
3891			} else if ( jQuery.VideoPress.video.playerSupport === "flash" ) {
3892				config.container.data( "player", "flash" );
3893				config.container.append( '<div id="v-' + guid + "-" + count + '-video">' );
3894			} else if ( jQuery.inArray( jQuery.VideoPress.video.playerSupport, ["html5","mp4","ogv"] ) && ( jQuery.type(video.mp4.uri)==="str
3894ing" || jQuery.type(video.ogv.uri)==="string" ) ) {
3895				var load_type = "html5";
3896				if ( jQuery.VideoPress.video.playerSupport==="mp4" && video.mp4!==undefined && jQuery.type(video.mp4.uri)==="string" ) {
3897					load_type = "mp4";
3898				} else if ( jQuery.VideoPress.video.playerSupport==="ogv" && video.ogv!==undefined && jQuery.type(video.ogv.uri)==="string" ) {
3899					load_type = "ogv";
3900				}
3901				jQuery.VideoPress.video.insert( config.container, guid, count, video, load_type, jQuery.VideoPress.data[guid][count].dimensions.width, jQuery.VideoPress.data[guid][count].dimensions.height );
3902				config.container.data( "player", load_type );
3903				load_type=null;
3904			} else {
3905				config.container.html('<p class="videopress-error">' + jQuery.VideoPress.error.messages.incompatible + '</p>');
3906				return false;
3907			}
3908			return true;
3909		},
3910		insert: function( container_el, guid, count, video_data, video_type, width, height ) {
3911			var video_id = "v-" + guid + "-" + count + "-video";
3912			var video_el = jQuery("<video />");
3913			video_el.attr( "id", video_id );
3914			video_el.attr( "width", width );
3915			video_el.attr( "height", height );
3916			video_el.attr( "poster", video_data.poster );
3917			if ( video_type==="ogv" ) {
3918				video_el.attr( "preload", "metadata" );
3919			} else {
3920				video_el.attr( "preload", "none" );
3921			}
3922			video_el.attr( "controls", "true" );
3923			video_el.attr( "x-webkit-airplay", "allow" );
3924			if ( video_type==="mp4" && video_data.mp4!==undefined && jQuery.type(video_data.mp4.uri)==="string" ) {
3925				video_el.attr( "src", video_data.mp4.uri );
3926			} else if ( video_type==="ogv" && video_data.ogv!==undefined && jQuery.type(video_data.ogv.uri)==="string" ) {
3927				video_el.attr( "src", video_data.ogv.uri );
3928			} else {
3929				// Purposely omit source type attribute since the browser does not seem to support specifics such as canPlayType
3930				if ( video_data.mp4!==undefined && jQuery.type(video_data.mp4.uri)==="string" ) {
3931					video_el.append( '<source src="' + video_data.mp4.uri + '" />' );
3932				}
3933				if ( video_data.ogv!==undefined && jQuery.type(video_data.ogv.uri)==="string" ) {
3934					video_el.append( '<source src="' + video_data.ogv.uri + '" />' );
3935				}
3936			}
3937			
3938			if ( video_data.tracks !== undefined ) {
3939				video_el.append( video_data.tracks );
3940			}
3941			video_el.append( '<p class="videopress-error">' + jQuery.VideoPress.error.messages.incompatible + "</p>" );
3942			video_el.hide();
3943			container_el.append( video_el );
3944			video_el=null;
3945			video_id=null;
3946		},
3947		play: function( container_el ) {
3948			var player = container_el.data( "player" );
3949			if ( player===undefined ) {
3950				player="flash";
3951			}
3952
3953			var guid = container_el.data( "guid" );
3954			var count = container_el.data( "count" );
3955
3956			if ( player === "flash" ) {
3957				jQuery( "#" + container_el.attr("id") + "-placeholder", container_el ).remove();
3958
3959				var player_uri = jQuery.VideoPress.video.flash.player_uri;
3960				var expressinstall = jQuery.VideoPress.video.flash.expressinstall;
3961
3962				swfobject.embedSWF( player_uri, "v-" + guid + "-" + count + "-video", jQuery.VideoPress.data[guid][count].dimensions.width, jQuery.VideoPress.data[guid][count].dimensions.height, jQuery.VideoPress.video.flash.min_version, expressinstall, {guid:guid,autoPlay:"true",isDynamicSeeking:"true",hd:jQuery.VideoPress.data[guid][count].hd}, jQuery.VideoPress.video.flash.params, null, jQuery.VideoPress.video.flash.embedCallback );
3963			} else if ( jQuery.inArray( player, ["html5", "mp4", "ogv"] ) ) {
3964				var video_el = jQuery("video", container_el);
3965				if ( video_el ) {
3966					jQuery( "#" + container_el.attr("id") + "-placeholder", container_el ).remove();
3967					if ( player==="html5" ) {
3968						player = "mp4";
3969					}
3970					jQuery.VideoPress.video.playHTML5( video_el, guid, player );
3971				}
3972			} else {
3973				jQuery( "#" + container_el.attr("id") + "-placeholder", container_el ).remove();
3974				container_el.append( '<p class="videopress-error">Unable to play video. No suitable player.</p>' );
3975			}
3976
3977			var play_event = new CustomEvent( 'videopress_play_video', { 'detail': { 'video_id': guid } } );
3978			window.dispatchEvent( play_event );
3979		},
3980		playHTML5: function( video_el, guid, filetype ) {
3981			video_el.show();
3982			video_el[0].load();
3983
3984			/* It seems load() sometimes does not work, but play() will trigger load.
3985			 * Tried attaching play() to a data event but data might not load
3986			 * So we trigger play() even if there is not enough data loaded to begin playback
3987			 */
3988			video_el[0].play();
3989			jQuery.VideoPress.analytics.played(guid, filetype);
3990			video_el.bind( "error stalled", function(e) {
3991				var message = jQuery.VideoPress.error.messages.error;
3992				try {
3993					// provide a more detailed error message if a failure reason is communicated
3994					switch (e.target.error.code) {
3995						case e.target.error.MEDIA_ERR_NETWORK:
3996							message = jQuery.VideoPress.error.messages.network;
3997							break;
3998						case e.target.error.MEDIA_ERR_DECODE:
3999						case e.target.error.MEDIA_ERR_SRC_NOT_SUPPORTED:
4000							message = jQuery.VideoPress.error.messages.incapable + " " + filetype.toUpperCase() + ".";
4001							break;
4002						default:
4003							break;
4004					}
4005				} catch( err ){}
4006				// provide an opportunity to silence an error with an empty string
4007				if ( message.length > 0 ) {
4008					video_el.html( '<p class="videopress-error">' + message + "</p>" );
4009				}
4010				message=null;
4011			} );
4012			video_el.bind( "durationchange", {guid:guid}, function( event ) {
4013				var duration = jQuery(event.target).attr("duration");
4014				if ( jQuery.type(duration)==="number" ) {
4015					jQuery.VideoPress.data[event.data.guid].duration = duration;
4016				}
4017				duration=null;
4018			} );
4019
4020			/* Only record stats after video data has loaded
4021			 * If html5 video seems to work but we could not match on a specific codec descriptor then there may be multiple source elements. Browser chooses a source at runtime in source order. We check the loaded video filetype instead of assuming MP4.
4022			 */
4023			video_el.one( "loadeddata", {guid:guid, filetype:filetype}, function( event ){
4024				var filetype = event.data.filetype;
4025				var loaded_file = jQuery(event.target).attr("currentSrc");
4026				if ( jQuery.type(loaded_file)==="string" && loaded_file.length > 3 ) {
4027					var ext = loaded_file.substr( loaded_file.lastIndexOf(".") + 1 ).toLowerCase();
4028					if ( jQuery.inArray( ext, ["mp4","ogv"] ) ) {
4029						filetype = ext;
4030					}
4031					ext=null;
4032				}
4033				video_el.bind( "play", {guid:event.data.guid,filetype:filetype}, function( event ) {
4034					jQuery.VideoPress.analytics.played(event.data.guid, event.data.filetype);
4035				} );
4036				video_el.bind( "timeupdate", {guid:event.data.guid,filetype:filetype}, function( event ) {
4037					var target = jQuery(event.target);
4038					jQuery.VideoPress.analytics.watched( event.data.guid, event.data.filetype, target.attr("currentTime"), target.attr("initialTime") );
4039					target=null;
4040				} );
4041				video_el.bind( "ended", {guid:event.data.guid,filetype:filetype}, function( event ) {
4042					jQuery.VideoPress.analytics.watched( event.data.guid, event.data.filetype, jQuery.VideoPress.data[guid].duration, jQuery(event.target).attr("initialTime") );
4043				} );
4044			} );
4045		}
4046	}
4047}});
4048;
4049/* jshint sub: true, onevar: false, multistr: true, devel: true, smarttabs: true */
4050/* global jetpackCarouselStrings, DocumentTouch, jetpackLikesWidgetQueue */
4051
4052// @start-hide-in-jetpack
4053if (typeof wpcom === 'undefined') {
4054	var wpcom = {};
4055}
4056wpcom.carousel = (function(/*$*/) {
4057	var prebuilt_widths = jetpackCarouselStrings.widths;
4058	var pageviews_stats_args = jetpackCarouselStrings.stats_query_args;
4059
4060	var findFirstLargeEnoughWidth = function(original_w, original_h, dest_w, dest_h) {
4061		var inverse_ratio = original_h / original_w;
4062
4063		for ( var i = 0; i < prebuilt_widths.length; ++i ) {
4064			if ( prebuilt_widths[i] >= dest_w || prebuilt_widths[i] * inverse_ratio >
4064= dest_h ) {
4065				return prebuilt_widths[i];
4066			}
4067		}
4068
4069		return original_w;
4070	};
4071
4072	var addWidthToImageURL = function(url, width) {
4073		width = parseInt(width, 10);
4074		// Give devices with a higher devicePixelRatio higher-res images (Retina display = 2, Android phones = 1.5, etc)
4075		if ('undefined' !== typeof window.devicePixelRatio && window.devicePixelRatio > 1) {
4076			width = Math.round( width * window.devicePixelRatio );
4077		}
4078		url = addArgToURL(url, 'w', width);
4079		url = addArgToURL(url, 'h', '');
4080		return url;
4081	};
4082
4083	var addArgToURL = function(url, arg, value) {
4084		var re = new RegExp(arg+'=[^?&]+');
4085		if ( url.match(re) ) {
4086			return url.replace(re, arg + '=' + value);
4087		} else {
4088			var divider = url.indexOf('?') !== -1 ? '&' : '?';
4089			return url + divider + arg + '=' + value;
4090		}
4091	};
4092
4093	var stat = function ( names ) {
4094		if ( typeof names !== 'string' ) {
4095			names = names.join( ',' );
4096		}
4097
4098		new Image().src = window.location.protocol +
4099			'//pixel.wp.com/g.gif?v=wpcom-no-pv' +
4100			'&x_carousel=' + names +
4101			'&baba=' + Math.random();
4102	};
4103
4104	var pageview = function ( post_id ) {
4105		new Image().src = window.location.protocol +
4106			'//pixel.wp.com/g.gif?host=' + encodeURIComponent( window.location.host ) +
4107			'&ref=' + encodeURIComponent( document.referrer ) +
4108			'&rand=' + Math.random() +
4109			'&' + pageviews_stats_args +
4110			'&post=' + encodeURIComponent( post_id );
4111	};
4112
4113
4114	return {
4115		findFirstLargeEnoughWidth: findFirstLargeEnoughWidth,
4116		addWidthToImageURL: addWidthToImageURL,
4117		stat: stat,
4118		pageview: pageview
4119	};
4120
4121})(jQuery);
4122// @end-hide-in-jetpack
4123
4124jQuery(document).ready(function($) {
4125
4126	// gallery faded layer and container elements
4127	var overlay, comments, gallery, container, nextButton, previousButton, info, transitionBegin,
4128	caption, resizeTimeout, photo_info, close_hint, commentInterval, lastSelectedSlide,
4129	screenPadding = 110, originalOverflow = $('body').css('overflow'), originalHOverflow = $('html').css('overflow'), proportion = 85,
4130	last_known_location_hash = '', imageMeta, titleAndDescription, commentForm, leftColWrapper;
4131
4132	if ( window.innerWidth <= 760 ) {
4133		screenPadding = Math.round( ( window.innerWidth / 760 ) * 110 );
4134
4135		if ( screenPadding < 40 && ( ( 'ontouchstart' in window ) || window.DocumentTouch && document instanceof DocumentTouch ) ) {
4136			screenPadding = 0;
4137		}
4138	}
4139
4140	var keyListener = function(e){
4141		switch(e.which){
4142			case 38: // up
4143				e.preventDefault();
4144				container.scrollTop(container.scrollTop() - 100);
4145				break;
4146			case 40: // down
4147				e.preventDefault();
4148				container.scrollTop(container.scrollTop() + 100);
4149				break;
4150			case 39: // right
4151				e.preventDefault();
4152				gallery.jp_carousel('clearCommentTextAreaValue');
4153				gallery.jp_carousel('next');
4154				break;
4155			case 37: // left
4156			case 8: // backspace
4157				e.preventDefault();
4158				gallery.jp_carousel('clearCommentTextAreaValue');
4159				gallery.jp_carousel('previous');
4160				break;
4161			case 27: // escape
4162				e.preventDefault();
4163				gallery.jp_carousel('clearCommentTextAreaValue');
4164				container.jp_carousel('close');
4165				break;
4166			default:
4167				// making jslint happy
4168				break;
4169		}
4170	};
4171
4172	var resizeListener = function(/*e*/){
4173		clearTimeout(resizeTimeout);
4174		resizeTimeout = setTimeout(function(){
4175			gallery
4176				.jp_carousel('slides')
4177				.jp_carousel('fitSlide', true);
4178			gallery.jp_carousel('updateSlidePositions', true);
4179			gallery.jp_carousel('fitMeta', true);
4180		}, 200);
4181	};
4182
4183	var prepareGallery = function( /*dataCarouselExtra*/ ){
4184		if (!overlay) {
4185			overlay = $('<div></div>')
4186				.addClass('jp-carousel-overlay')
4187				.css({
4188					'position' : 'absolute',
4189					'top'      : 0,
4190					'right'    : 0,
4191					'bottom'   : 0,
4192					'left'     : 0
4193				});
4194
4195			var buttons  = '<a class="jp-carousel-commentlink" href="#">' + jetpackCarouselStrings.comment + '</a>';
4196			if ( 1 === Number( jetpackCarouselStrings.is_logged_in ) ) {
4197// @start-hide-in-jetpack
4198				if ( 1 === Number( jetpackCarouselStrings.is_public && 1 === Number( jetpackCarouselStrings.reblog_enabled ) ) ) {
4199					buttons += '<a class="jp-carousel-reblog" href="#">' + jetpackCarouselStrings.reblog + '</a>';
4200				}
4201// @end-hide-in-jetpack
4202			}
4203
4204			buttons  = $('<div class="jp-carousel-buttons">' + buttons + '</div>');
4205
4206			caption    = $('<h2></h2>');
4207			photo_info = $('<div class="jp-carousel-photo-info"></div>').append(caption);
4208
4209			imageMeta = $('<div></div>')
4210				.addClass('jp-carousel-image-meta')
4211				.css({
4212					'float'      : 'right',
4213					'margin-top' : '20px',
4214					'width'      :  '250px'
4215				});
4216
4217			imageMeta
4218				.append( buttons )
4219				.append( '<ul class=\'jp-carousel-image-exif\' style=\'display:none;\'></ul>' )
4220				.append( '<a class=\'jp-carousel-image-download\' style=\'display:none;\'></a>' )
4221				.append( '<div class=\'jp-carousel-image-map\' style=\'display:none;\'></div>' );
4222
4223			titleAndDescription = $('<div></div>')
4224				.addClass('jp-carousel-titleanddesc')
4225				.css({
4226					'width'      : '100%',
4227					'margin-top' : imageMeta.css('margin-top')
4228				});
4229
4230			var commentFormMarkup = '<div id="jp-carousel-comment-form-container">';
4231
4232			if ( jetpackCarouselStrings.local_comments_commenting_as && jetpackCarouselStrings.local_comments_commenting_as.length ) {
4233				// Jetpack comments not enabled, fallback to local comments
4234
4235				if ( 1 !== Number( jetpackCarouselStrings.is_logged_in ) && 1 === Number( jetpackCarouselStrings.comment_registration ) ) {
4236					commentFormMarkup += '<div id="jp-carousel-comment-form-commenting-as">' + jetpackCarouselStrings.local_comments_commenting_as + '</div>';
4237				} else {
4238					commentFormMarkup += '<form id="jp-carousel-comment-form">';
4239					commentFormMarkup += '<textarea name="comment" class="jp-carousel-comment-form-field jp-carousel-comment-form-textarea" id="jp-carousel-comment-form-comment-field" placeholder="' + jetpackCarouselStrings.write_comment + '"></textarea>';
4240					commentFormMarkup += '<div id="jp-carousel-comment-form-submit-and-info-wrapper">';
4241					commentFormMarkup += '<div id="jp-carousel-comment-form-commenting-as">' + jetpackCarouselStrings.local_comments_commenting_as + '</div>';
4242					commentFormMarkup += '<input type="submit" name="submit" class="jp-carousel-comment-form-button" id="jp-carousel-comment-form-button-submit" value="'+jetpackCarouselStrings.post_comment+'" />';
4243					commentFormMarkup += '<span id="jp-carousel-comment-form-spinner">&nbsp;</span>';
4244					commentFormMarkup += '<div id="jp-carousel-comment-post-results"></div>';
4245					commentFormMarkup += '</div>';
4246					commentFormMarkup += '</form>';
4247				}
4248			}
4249			commentFormMarkup += '</div>';
4250
4251			commentForm = $(commentFormMarkup)
4252				.css({
4253					'width'      : '100%',
4254					'margin-top' : '20px',
4255					'color'      : '#999'
4256				});
4257
4258			comments = $('<div></div>')
4259				.addClass('jp-carousel-comments')
4260				.css({
4261					'width'      : '100%',
4262					'bottom'     : '10px',
4263					'margin-top' : '20px'
4264				});
4265
4266			var commentsLoading = $('<div id="jp-carousel-comments-loading"><span>'+jetpackCarouselStrings.loading_comments+'</span></div>')
4267				.css({
4268					'width'      : '100%',
4269					'bottom'     : '10px',
4270					'margin-top' : '20px'
4271				});
4272
4273			var leftWidth = ( $(window).width() - ( screenPadding * 2 ) ) - (imageMeta.width() + 40);
4274			leftWidth += 'px';
4275
4276			leftColWrapper = $('<div></div>')
4277				.addClass('jp-carousel-left-column-wrapper')
4278				.css({
4279					'width' : Math.floor( leftWidth )
4280				})
4281				.append(titleAndDescription)
4282				.append(commentForm)
4283				.append(comments)
4284				.append(commentsLoading);
4285
4286			var fadeaway = $('<div></div>')
4287				.addClass('jp-carousel-fadeaway');
4288
4289			info = $('<div></div>')
4290				.addClass('jp-carousel-info')
4291				.css({
4292					'top'   : Math.floor( ($(window).height() / 100) * proportion ),
4293					'left'  : screenPadding,
4294					'right' : screenPadding
4295				})
4296				.append(photo_info)
4297				.append(imageMeta);
4298
4299			if ( window.innerWidth <= 760 ) {
4300				photo_info.remove().insertAfter( titleAndDescription );
4301				info.prepend( leftColWrapper );
4302			}
4303			else {
4304				info.append( leftColWrapper );
4305			}
4306
4307			var targetBottomPos = ( $(window).height() - parseInt( info.css('top'), 10 ) ) + 'px';
4308
4309			nextButton = $('<div><span></span></div>')
4310				.addClass('jp-carousel-next-button')
4311				.css({
4312					'right'    : '15px'
4313				});
4314
4315			previousButton = $('<div><span></span></div>')
4316				.addClass('jp-carousel-previous-button')
4317				.css({
4318					'left'     : 0
4319				});
4320
4321			nextButton.add( previousButton ).css( {
4322				'position' : 'fixed',
4323				'top' : '40px',
4324				'bottom' : targetBottomPos,
4325				'width' : screenPadding
4326			} );
4327
4328			gallery = $('<div></div>')
4329				.addClass('jp-carousel')
4330				.css({
4331					'position' : 'absolute',
4332					'top'      : 0,
4333					'bottom'   : targetBottomPos,
4334					'left'     : 0,
4335					'right'    : 0
4336				});
4337
4338			close_hint = $('<div class="jp-carousel-close-hint"><span>&times;</span></div>')
4339				.css({
4340					position : 'fixed'
4341				});
4342
4343			container = $('<div></div>')
4344				.addClass('jp-carousel-wrap')
4345				.addClass( 'jp-carousel-transitions' );
4346
4347			if ( 'white' === jetpackCarouselStrings.background_color ) {
4348				 container.addClass('jp-carousel-light');
4349			}
4350
4351			container.css({
4352					'position'   : 'fixed',
4353					'top'        : 0,
4354					'right'      : 0,
4355					'bottom'     : 0,
4356					'left'       : 0,
4357					'z-index'    : 2147483647,
4358					'overflow-x' : 'hidden',
4359					'overflow-y' : 'auto',
4360					'direction'  : 'ltr'
4361				})
4362				.hide()
4363				.append(overlay)
4364				.append(gallery)
4365				.append(fadeaway)
4366				.append(info)
4367				.append(nextButton)
4368				.append(previousButton)
4369				.append(close_hint)
4370				.appendTo($('body'))
4371				.click(function(e){
4372					var target = $(e.target), wrap = target.parents('div.jp-carousel-wrap'), data = wrap.data('carousel-extra'),
4373						slide = wrap.find('div.selected'), attachment_id = slide.data('attachment-id');
4374					data = data || [];
4375
4376					if ( target.is(gallery) || target.parents().add(target).is(close_hint) ) {
4377						container.jp_carousel('close');
4378// @start-hide-in-jetpack
4379					} else if ( target.hasClass('jp-carousel-reblog') ) {
4380						e.preventDefault();
4381						e.stopPropagation();
4382						if ( !target.hasClass('reblogged') ) {
4383							target.jp_carousel('show_reblog_box');
4384							wpcom.carousel.stat('reblog_show_box');
4385						}
4386					} else if ( target.parents('#carousel-reblog-box').length ) {
4387						if ( target.is('a.cancel') ) {
4388							e.preventDefault();
4389							e.stopPropagation();
4390							target.jp_carousel('hide_reblog_box');
4391							wpcom.carousel.stat('reblog_cancel');
4392						} else if ( target.is( 'input[type="submit"]' ) ) {
4393							e.preventDefault();
4394							e.stopPropagation();
4395
4396							var note = $('#carousel-reblog-box textarea').val();
4397							if ( jetpackCarouselStrings.reblog_add_thoughts === note ) {
4398								note = '';
4399							}
4400
4401							$('#carousel-reblog-submit').val( jetpackCarouselStrings.reblogging );
4402							$('#carousel-reblog-submit').prop('disabled', true);
4403							$( '#carousel-reblog-box div.submit span.canceltext' ).spin( 'small' );
4404
4405							$.post( jetpackCarouselStrings.ajaxurl, {
4406								'action': 'post_reblog',
4407								'reblog_source': 'carousel',
4408								'original_blog_id': $('#carousel-reblog-box input#carousel-reblog-blog-id').val(),
4409								'original_post_id': $('.jp-carousel div.selected').data('attachment-id'),
4410								'blog_id': $('#carousel-reblog-box select').val(),
4411								'blog_url': $('#carousel-reblog-box input#carousel-reblog-blog-url').val(),
4412								'blog_title': $('#carousel-reblog-box input#carousel-reblog-blog-title').val(),
4413								'post_url': $('#carousel-reblog-box input#carousel-reblog-post-url').val(),
4414								'post_title': slide.data( 'caption' ) || $('#carousel-reblog-box input#carousel-reblog-post-title').val(),
4415								'note': note,
4416								'_wpnonce': $('#carousel-reblog-box #_wpnonce').val()
4417							},
4418							function(/*result*/) {
4419								$('#carousel-reblog-box').css({ 'height': $('#carousel-reblog-box').height() + 'px' }).slideUp('fast');
4420								$('a.jp-carousel-reblog').html( jetpackCarouselStrings.reblogged ).removeClass( 'reblog' ).addClass( 'reblogged' );
4421								$( '#carousel-reblog-box div.submit span.canceltext' ).spin( false );
4422								$('#carousel-reblog-submit').val( jetpackCarouselStrings.post_reblog );
4423								$('div.jp-carousel-info').children().not('#carousel-reblog-box').fadeIn('fast');
4424								slide.data('reblogged', 1);
4425								$('div.gallery').find('img[data-attachment-id="' + slide.data('attachment-id') + '"]').data('reblogged', 1);
4426
4427
4428							}, 'json' );
4429							wpcom.carousel.stat('reblog_submit');
4430						}
4431					} else if ( target.hasClass( 'jp-carousel-image-download' ) ) {
4432						wpcom.carousel.stat( 'download_original_click' );
4433// @end-hide-in-jetpack
4434					} else if ( target.hasClass('jp-carousel-commentlink') ) {
4435						e.preventDefault();
4436						e.stopPropagation();
4437						$(window).unbind('keydown', keyListener);
4438						container.animate({scrollTop: parseInt(info.position()['top'], 10)}, 'fast');
4439						$('#jp-carousel-comment-form-submit-and-info-wrapper').slideDown('fast');
4440						$('#jp-carousel-comment-form-comment-field').focus();
4441					} else if ( target.hasClass('jp-carousel-comment-login') ) {
4442						var url = jetpackCarouselStrings.login_url + '%23jp-carousel-' + attachment_id;
4443
4444						window.location.href = url;
4445					} else if ( target.parents('#jp-carousel-comment-form-container').length ) {
4446						var textarea = $('#jp-carousel-comment-form-comment-field')
4447							.blur(function(){
4448								$(window).bind('keydown', keyListener);
4449							})
4450							.focus(function(){
4451								$(window).unbind('keydown', keyListener);
4452							});
4453
4454						var emailField = $('#jp-carousel-comment-form-email-field')
4455							.blur(function(){
4456								$(window).bind('keydown', keyListener);
4457							})
4458							.focus(function(){
4459								$(window).unbind('keydown', keyListener);
4460							});
4461
4462						var authorField = $('#jp-carousel-comment-form-author-field')
4463							.blur(function(){
4464								$(window).bind('keydown', keyListener);
4465							})
4466							.focus(function(){
4467								$(window).unbind('keydown', keyListener);
4468							});
4469
4470						var urlField = $('#jp-carousel-comment-form-url-field')
4471							.blur(function(){
4472								$(window).bind('keydown', keyListener);
4473							})
4474							.focus(function(){
4475								$(window).unbind('keydown', keyListener);
4476							});
4477
4478						if ( textarea && textarea.attr('id') === target.attr('id')) {
4479							// For first page load
4480							$(window).unbind('keydown', keyListener);
4481							$('#jp-carousel-comment-form-submit-and-info-wrapper').slideDown('fast');
4482						} else if ( target.is( 'input[type="submit"]' ) ) {
4483							e.preventDefault();
4484							e.stopPropagation();
4485
4486							$('#jp-carousel-comment-form-spinner').spin('small', 'white');
4487
4488							var ajaxData = {
4489								action: 'post_attachment_comment',
4490								nonce:   jetpackCarouselStrings.nonce,
4491								blog_id: data['blog_id'],
4492								id:      attachment_id,
4493								comment: textarea.val()
4494							};
4495
4496							if ( ! ajaxData['comment'].length ) {
4497								gallery.jp_carousel('postCommentError', {'field': 'jp-carousel-comment-form-comment-field', 'error': jetpackCarouselStrings.no_comment_text});
4498								return;
4499							}
4500
4501							if ( 1 !== Number( jetpackCarouselStrings.is_logged_in ) ) {
4502								ajaxData['email']  = emailField.val();
4503								ajaxData['author'] = authorField.val();
4504								ajaxData['url']    = urlField.val();
4505
4506								if ( 1 === Number( jetpackCarouselStrings.require_name_email ) ) {
4507									if ( ! ajaxData['email'].length || ! ajaxData['email'].match('@') ) {
4508										gallery.jp_carousel('postCommentError', {'field': 'jp-carousel-comment-form-email-field', 'error': jetpackCarouselStrings.no_comment_email});
4509										return;
4510									} else if ( ! ajaxData['author'].length ) {
4511										gallery.jp_carousel('postCommentError', {'field': 'jp-carousel-comment-form-author-field', 'error': jetpackCarouselStrings.no_comment_author});
4512										return;
4513									}
4514								}
4515							}
4516
4517							$.ajax({
4518								type:       'POST',
4519								url:        jetpackCarouselStrings.ajaxurl,
4520								data:       ajaxData,
4521								dataType:   'json',
4522								success: function(response/*, status, xhr*/) {
4523									if ( 'approved' === response.comment_status ) {
4524										$('#jp-carousel-comment-post-results').slideUp('fast').html('<span class="jp-carousel-comment-post-success">' + jetpackCarouselStrings.comment_approved + '</span>').slideDown('fast');
4525									} else if ( 'unapproved' === response.comment_status ) {
4526										$('#jp-carousel-comment-post-results').slideUp('fast').html('<span class="jp-carousel-comment-post-success">' + jetpackCarouselStrings.comment_unapproved + '</span>').slideDown('fast');
4527									} else {
4528										// 'deleted', 'spam', false
4529										$('#jp-carousel-comment-post-results').slideUp('fast').html('<span class="jp-carousel-comment-post-error">' + jetpackCarouselStrings.comment_post_error + '</span>').slideDown('fast');
4530									}
4531									gallery.jp_carousel('clearCommentTextAreaValue');
4532									gallery.jp_carousel('getComments', {attachment_id: attachment_id, offset: 0, clear: true});
4533									$('#jp-carousel-comment-form-button-submit').val(jetpackCarouselStrings.post_comment);
4534									$('#jp-carousel-comment-form-spinner').spin(false);
4535								},
4536								error: function(/*xhr, status, error*/) {
4537									// TODO: Add error handling and display here
4538									gallery.jp_carousel('postCommentError', {'field': 'jp-carousel-comment-form-comment-field', 'error': jetpackCarouselStrings.comment_post_error});
4539									return;
4540								}
4541							});
4542						}
4543					} else if ( ! target.parents( '.jp-carousel-info' ).length ) {
4544						container.jp_carousel('next');
4545					}
4546				})
4547				.bind('jp_carousel.afterOpen', function(){
4548					$(window).bind('keydown', keyListener);
4549					$(window).bind('resize', resizeListener);
4550					gallery.opened = true;
4551
4552					resizeListener();
4553				})
4554				.bind('jp_carousel.beforeClose', function(){
4555					var scroll = $(window).scrollTop();
4556
4557					$(window).unbind('keydown', keyListener);
4558					$(window).unbind('resize', resizeListener);
4559					$(window).scrollTop(scroll);
4560					gallery.jp_carousel( 'hide_reblog_box' ); // @hide-in-jetpack
4561				})
4562				.bind('jp_carousel.afterClose', function(){
4563					if ( history.pushState ) {
4564						history.pushState('', document.title, window.location.pathname + window.location.search);
4565					} else {
4566						last_known_location_hash = '';
4567						window.location.hash = '';
4568					}
4569					gallery.opened = false;
4570				})
4571				.on( 'transitionend.jp-carousel ', '.jp-carousel-slide', function ( e ) {
4572					// If the movement transitions take more than twice the allotted time, disable them.
4573					// There is some wiggle room in the 2x, since some of that time is taken up in
4574					// JavaScript, setting up the transition and calling the events.
4575					if ( 'transform' === e.originalEvent.propertyName ) {
4576						var transitionMultiplier = ( ( Date.now() - transitionBegin ) / 1000 ) / e.originalEvent.elapsedTime;
4577
4578						container.off( 'transitionend.jp-carousel' );
4579
4580						if ( transitionMultiplier >= 2 ) {
4581							$( '.jp-carousel-transitions' ).removeClass( 'jp-carousel-transitions' );
4582						}
4583					}
4584				} );
4585
4586				$( '.jp-carousel-wrap' ).touchwipe( {
4587					wipeLeft : function ( e ) {
4588						e.preventDefault();
4589						gallery.jp_carousel( 'next' );
4590					},
4591					wipeRight : function ( e ) {
4592						e.preventDefault();
4593						gallery.jp_carousel( 'previous' );
4594					},
4595					preventDefaultEvents : false
4596				} );
4597
4598			$( '.jetpack-likes-widget-unloaded' ).each( function() {
4599				jetpackLikesWidgetQueue.push( this.id );
4600				});
4601
4602			nextButton.add(previousButton).click(function(e){
4603				e.preventDefault();
4604				e.stopPropagation();
4605				if ( nextButton.is(this) ) {
4606					gallery.jp_carousel('next');
4607				} else {
4608					gallery.jp_carousel('previous');
4609				}
4610			});
4611		}
4612	};
4613
4614	var methods = {
4615		testForData: function(gallery) {
4616			gallery = $( gallery ); // make sure we have it as a jQuery object.
4617			return !( ! gallery.length || ! gallery.data( 'carousel-extra' ) );
4618		},
4619
4620		testIfOpened: function() {
4621			return !!( 'undefined' !== typeof(gallery) && 'undefined' !== typeof(gallery.opened) && gallery.opened );
4622		},
4623
4624		openOrSelectSlide: function( index ) {
4625			// The `open` method triggers an asynchronous effect, so we will get an
4626			// error if we try to use `open` then `selectSlideAtIndex` immediately
4627			// after it. We can only use `selectSlideAtIndex` if the carousel is
4628			// already open.
4629			if ( ! $( this ).jp_carousel( 'testIfOpened' ) ) {
4630				// The `open` method selects the correct slide during the
4631				// initialization.
4632				$( this ).jp_carousel( 'open', { start_index: index } );
4633			} else {
4634				gallery.jp_carousel( 'selectSlideAtIndex', index );
4635			}
4636		},
4637
4638		open: function(options) {
4639			var settings = {
4640				'items_selector' : '.gallery-item [data-attachment-id], .tiled-gallery-item [data-attachment-id]',
4641				'start_index': 0
4642			},
4643			data = $(this).data('carousel-extra');
4644
4645			if ( !data ) {
4646				return; // don't run if the default gallery functions weren't used
4647			}
4648
4649			prepareGallery( data );
4650
4651			if ( gallery.jp_carousel( 'testIfOpened' ) ) {
4652				return; // don't open if already opened
4653			}
4654
4655			// make sure to stop the page from scrolling behind the carousel overlay, so we don't trigger
4656			// infiniscroll for it when enabled (Reader, theme infiniscroll, etc).
4657			originalOverflow = $('body').css('overflow');
4658			$('body').css('overflow', 'hidden');
4659			// prevent html from overflowing on some of the new themes.
4660			originalHOverflow = $('html').css('overflow');
4661			$('html').css('overflow', 'hidden');
4662
4663			// Re-apply inline-block style here and give an initial value for the width
4664			// This value will get replaced with a more appropriate value once the slide is loaded
4665			// This avoids the likes widget appearing initially full width below the comment button and then shuffling up
4666			jQuery( '.slim-likes-widget' ).find( 'iframe' ).css( 'display', 'inline-block' ).css( 'width', '60px' );
4667
4668			container.data('carousel-extra', data);
4669// @start-hide-in-jetpack
4670			wpcom.carousel.stat( ['open', 'view_image'] );
4671// @end-hide-in-jetpack
4672
4673			return this.each(function() {
4674				// If options exist, lets merge them
4675				// with our default settings
4676				var $this = $(this);
4677
4678				if ( options ) {
4679					$.extend( settings, options );
4680				}
4681				if ( -1 === settings.start_index ) {
4682					settings.start_index = 0; //-1 returned if can't find index, so start from beginning
4683				}
4684
4685				container.trigger('jp_carousel.beforeOpen').fadeIn('fast',function(){
4686					container.trigger('jp_carousel.afterOpen');
4687					gallery
4688						.jp_carousel('initSlides', $this.find(settings.items_selector), settings.start_index)
4689						.jp_carousel('selectSlideAtIndex', settings.start_index);
4690				});
4691				gallery.html('');
4692			});
4693		},
4694
4695		selectSlideAtIndex : function(index){
4696			var slides = this.jp_carousel('slides'), selected = slides.eq(index);
4697
4698			if ( 0 === selected.length ) {
4699				selected = slides.eq(0);
4700			}
4701
4702			gallery.jp_carousel('selectSlide', selected, false);
4703			return this;
4704		},
4705
4706		close : function(){
4707			// make sure to let the page scroll again
4708			$('body').css('overflow', originalOverflow);
4709			$('html').css('overflow', originalHOverflow);
4710			return container
4711				.trigger('jp_carousel.beforeClose')
4712				.fadeOut('fast', function(){
4713					container.trigger('jp_carousel.afterClose');
4714				});
4715
4716		},
4717
4718		next : function(){
4719			var slide = gallery.jp_carousel( 'nextSlide' );
4720			container.animate({scrollTop:0}, 'fast');
4721			gallery.jp_carousel( 'hide_reblog_box' ); // @hide-in-jetpack
4722
4723			if ( slide ) {
4724				this.jp_carousel('selectSlide', slide);
4725				wpcom.carousel.stat( ['next', 'view_image'] ); // @hide-in-jetpack
4726			}
4727		},
4728
4729		previous : function(){
4730			var slide = gallery.jp_carousel( 'prevSlide' );
4731			container.animate({scrollTop:0}, 'fast');
4732			gallery.jp_carousel( 'hide_reblog_box' ); // @hide-in-jetpack
4733
4734			if ( slide ) {
4735				this.jp_carousel('selectSlide', slide);
4736				wpcom.carousel.stat( ['previous', 'view_image'] ); // @hide-in-jetpack
4737			}
4738		},
4739
4740		resetButtons : function(current) {
4741			if ( current.data('liked') ) {
4742				$('.jp-carousel-buttons a.jp-carousel-like').addClass('liked').text(jetpackCarouselStrings.unlike);
4743			} else {
4744				$('.jp-carousel-buttons a.jp-carousel-like').removeClass('liked').text(jetpackCarouselStrings.like);
4745			}
4746// @start-hide-in-jetpack
4747			if ( current.data( 'reblogged' ) ) {
4748				$('.jp-carousel-buttons a.jp-carousel-reblog').addClass( 'reblogged' ).text( jetpackCarouselStrings.reblogged );
4749			} else {
4750				$('.jp-carousel-buttons a.jp-carousel-reblog').removeClass( 'reblogged' ).text( jetpackCarouselStrings.reblog );
4751			}
4752
4753			// Must also take care of reblog/reblogged here
4754// @end-hide-in-jetpack
4755		},
4756
4757		selectedSlide : function(){
4758			return this.find('.selected');
4759		},
4760
4761		setSlidePosition : function(x) {
4762			transitionBegin = Date.now();
4763
4764			return this.css({
4765					'-webkit-transform':'translate3d(' + x + 'px,0,0)',
4766					'-moz-transform':'translate3d(' + x + 'px,0,0)',
4767					'-ms-transform':'translate(' + x + 'px,0)',
4768					'-o-transform':'translate(' + x + 'px,0)',
4769					'transform':'translate3d(' + x + 'px,0,0)'
4770			});
4771		},
4772
4773		updateSlidePositions : function(animate) {
4774			var current = this.jp_carousel( 'selectedSlide' ),
4775				galleryWidth = gallery.width(),
4776				currentWidth = current.width(),
4777				previous = gallery.jp_carousel( 'prevSlide' ),
4778				next = gallery.jp_carousel( 'nextSlide' ),
4779				previousPrevious = previous.prev(),
4780				nextNext = next.next(),
4781				left = Math.floor( ( galleryWidth - currentWidth ) * 0.5 );
4782
4783			current.jp_carousel( 'setSlidePosition', left ).show();
4784
4785			// minimum width
4786			gallery.jp_carousel( 'fitInfo', animate );
4787
4788			// prep the slides
4789			var direction = lastSelectedSlide.is( current.prevAll() ) ? 1 : -1;
4790
4791			// Since we preload the `previousPrevious` and `nextNext` slides, we need
4792			// to make sure they technically visible in the DOM, but invisible to the
4793			// user. To hide them from the user, we position them outside the edges
4794			// of the window.
4795			//
4796			// This section of code only applies when there are more than three
4797			// slides. Otherwise, the `previousPrevious` and `nextNext` slides will
4798			// overlap with the `previous` and `next` slides which must be visible
4799			// regardless.
4800			if ( 1 === direction ) {
4801				if ( ! nextNext.is( previous ) ) {
4802					nextNext.jp_carousel( 'setSlidePosition', galleryWidth + next.width() ).show();
4803				}
4804
4805				if ( ! previousPrevious.is( next ) ) {
4806					previousPrevious.jp_carousel( 'setSlidePosition', -previousPrevious.width() - currentWidth ).show();
4807				}
4808			} else {
4809				if ( ! nextNext.is( previous ) ) {
4810					nextNext.jp_carousel( 'setSlidePosition', galleryWidth + currentWidth ).show();
4811				}
4812			}
4813
4814			previous.jp_carousel( 'setSlidePosition', Math.floor( -previous.width() + ( screenPadding * 0.75 ) ) ).show();
4815			next.jp_carousel( 'setSlidePosition', Math.ceil( galleryWidth - ( screenPadding * 0.75 ) ) ).show();
4816		},
4817
4818		selectSlide : function(slide, animate){
4819			lastSelectedSlide = this.find( '.selected' ).removeClass( 'selected' );
4820
4821			var slides = gallery.jp_carousel( 'slides' ).css({ 'position': 'fixed' }),
4822				current = $( slide ).addClass( 'selected' ).css({ 'position': 'relative' }),
4823				attachmentId = current.data( 'attachment-id' ),
4824				previous = gallery.jp_carousel( 'prevSlide' ),
4825				next = gallery.jp_carousel( 'nextSlide' ),
4826				previousPrevious = previous.prev(),
4827				nextNext = next.next(),
4828				animated,
4829				captionHtml;
4830
4831			// center the main image
4832			gallery.jp_carousel( 'loadFullImage', current );
4833
4834			caption.hide();
4835
4836			if ( next.length === 0 && slides.length <= 2 ) {
4837				$( '.jp-carousel-next-button' ).hide();
4838			} else {
4839				$( '.jp-carousel-next-button' ).show();
4840			}
4841
4842			if ( previous.length === 0 && slides.length <= 2 ) {
4843				$( '.jp-carousel-previous-button' ).hide();
4844			} else {
4845				$( '.jp-carousel-previous-button' ).show();
4846			}
4847
4848			animated = current
4849				.add( previous )
4850				.add( previousPrevious )
4851				.add( next )
4852				.add( nextNext )
4853				.jp_carousel( 'loadSlide' );
4854
4855			// slide the whole view to the x we want
4856			slides.not( animated ).hide();
4857
4858			gallery.jp_carousel( 'updateSlidePositions', animate );
4859
4860			gallery.jp_carousel( 'resetButtons', current );
4861			container.trigger( 'jp_carousel.selectSlide', [current] );
4862
4863			gallery.jp_carousel( 'getTitleDesc', {
4864				title: current.data( 'title' ),
4865				desc: current.data( 'desc' )
4866			});
4867
4868			// Lazy-load the Likes iframe for the current, next, and previous slides.
4869			gallery.jp_carousel( 'loadLikes', attachmentId );
4870			gallery.jp_carousel( 'updateLikesWidgetVisibility', attachmentId );
4871
4872			if ( next.length > 0 ) {
4873				gallery.jp_carousel( 'loadLikes', next.data( 'attachment-id' ) );
4874			}
4875
4876			if ( previous.length > 0 ) {
4877				gallery.jp_carousel( 'loadLikes', previous.data( 'attachment-id' ) );
4878			}
4879
4880			var imageMeta = current.data( 'image-meta' );
4881			gallery.jp_carousel( 'updateExif', imageMeta );
4882			gallery.jp_carousel( 'updateFullSizeLink', current );
4883			gallery.jp_carousel( 'updateMap', imageMeta );
4884			gallery.jp_carousel( 'testCommentsOpened', current.data( 'comments-opened' ) );
4885			gallery.jp_carousel( 'getComments', {
4886				'attachment_id': attachmentId,
4887				'offset': 0,
4888				'clear': true
4889			});
4890			$( '#jp-carousel-comment-post-results' ).slideUp();
4891
4892			// $('<div />').text(sometext).html() is a trick to go to HTML to plain
4893			// text (including HTML entities decode, etc)
4894			if ( current.data( 'caption' ) ) {
4895				captionHtml = $( '<div />' ).text( current.data( 'caption' ) ).html();
4896
4897				if ( captionHtml === $( '<div />' ).text( current.data( 'title' ) ).html() ) {
4898					$( '.jp-carousel-titleanddesc-title' ).fadeOut( 'fast' ).empty();
4899				}
4900
4901				if ( captionHtml === $( '<div />' ).text( current.data( 'desc' ) ).html() ) {
4902					$( '.jp-carousel-titleanddesc-desc' ).fadeOut( 'fast' ).empty();
4903				}
4904
4905				caption.html( current.data( 'caption' ) ).fadeIn( 'slow' );
4906			} else {
4907				caption.fadeOut( 'fast' ).empty();
4908			}
4909
4910			wpcom.carousel.pageview( attachmentId ); // @hide-in-jetpack
4911
4912			// Load the images for the next and previous slides.
4913			$( next ).add( previous ).each( function() {
4914				gallery.jp_carousel( 'loadFullImage', $( this ) );
4915			});
4916
4917			window.location.hash = last_known_location_hash = '#jp-carousel-' + attachmentId;
4918		},
4919
4920		slides : function(){
4921			return this.find('.jp-carousel-slide');
4922		},
4923
4924		slideDimensions : function(){
4925			return {
4926				width: $(window).width() - (screenPadding * 2),
4927				height: Math.floor( $(window).height() / 100 * proportion - 60 )
4928			};
4929		},
4930
4931		loadSlide : function() {
4932			return this.each(function(){
4933				var slide = $(this);
4934				slide.find('img')
4935					.one('load', function(){
4936						// set the width/height of the image if it's too big
4937						slide
4938							.jp_carousel('fitSlide',false);
4939					});
4940			});
4941		},
4942
4943		bestFit : function(){
4944			var max        = gallery.jp_carousel('slideDimensions'),
4945			    orig       = this.jp_carousel('originalDimensions'),
4946			    orig_ratio = orig.width / orig.height,
4947			    w_ratio    = 1,
4948			    h_ratio    = 1,
4949			    width, height;
4950
4951			if ( orig.width > max.width ) {
4952				w_ratio = max.width / orig.width;
4953			}
4954			if ( orig.height > max.height ) {
4955				h_ratio = max.height / orig.height;
4956			}
4957
4958			if ( w_ratio < h_ratio ) {
4959				width = max.width;
4960				height = Math.floor( width / orig_ratio );
4961			} else if ( h_ratio < w_ratio ) {
4962				height = max.height;
4963				width = Math.floor( height * orig_ratio );
4964			} else {
4965				width = orig.width;
4966				height = orig.height;
4967			}
4968
4969			return {
4970				width: width,
4971				height: height
4972			};
4973		},
4974
4975		fitInfo : function(/*animated*/){
4976			var current = this.jp_carousel('selectedSlide'),
4977				size = current.jp_carousel('bestFit');
4978
4979			photo_info.css({
4980				'left'  : Math.floor( (info.width() - size.width) * 0.5 ),
4981				'width' : Math.floor( size.width )
4982			});
4983
4984			return this;
4985		},
4986
4987		fitMeta : function(animated){
4988			var newInfoTop   = { top: Math.floor( $(window).height() / 100 * proportion + 5 ) + 'px' };
4989			var newLeftWidth = { width: ( info.width() - (imageMeta.width() + 80) ) + 'px' };
4990
4991			if (animated) {
4992				info.animate(newInfoTop);
4993				leftColWrapper.animate(newLeftWidth);
4994			} else {
4995				info.animate(newInfoTop);
4996				leftColWrapper.css(newLeftWidth);
4997			}
4998		},
4999
5000		fitSlide : function(/*animated*/){
5001			return this.each(function(){
5002				var $this      = $(this),
5003				    dimensions = $this.jp_carousel('bestFit'),
5004				    method     = 'css',
5005				    max        = gallery.jp_carousel('slideDimensions');
5006
5007				dimensions.left = 0;
5008				dimensions.top = Math.floor( (max.height - dimensions.height) * 0.5 ) + 40;
5009				$this[method](dimensions);
5010			});
5011		},
5012
5013		texturize : function(text) {
5014				text = '' + text; // make sure we get a string. Title "1" came in as int 1, for example, which did not support .replace().
5015				text = text.replace(/'/g, '&#8217;').replace(/&#039;/g, '&#8217;').replace(/[\u2019]/g, '&#8217;');
5016				text = text.replace(/"/g, '&#8221;').replace(/&#034;/g, '&#8221;').replace(/&quot;/g, '&#8221;').replace(/[\u201D]/g, '&#8221;');
5017				text = text.replace(/([\w]+)=&#[\d]+;(.+?)&#[\d]+;/g, '$1="$2"'); // untexturize allowed HTML tags params double-quotes
5018				return $.trim(text);
5019		},
5020
5021		initSlides : function(items, start_index){
5022			if ( items.length < 2 ) {
5023				$( '.jp-carousel-next-button, .jp-carousel-previous-button' ).hide();
5024			} else {
5025				$( '.jp-carousel-next-button, .jp-carousel-previous-button' ).show();
5026			}
5027
5028			// Calculate the new src.
5029			items.each(function(/*i*/){
5030				var src_item  = $(this),
5031					orig_size = src_item.data('orig-size') || '',
5032					max       = gallery.jp_carousel('slideDimensions'),
5033					parts     = orig_size.split(','),
5034					medium_file     = src_item.data('medium-file') || '',
5035					large_file      = src_item.data('large-file') || '',
5036					src;
5037				orig_size = {width: parseInt(parts[0], 10), height: parseInt(parts[1], 10)};
5038
5039// @start-hide-in-jetpack
5040				if ( 'undefined' !== typeof wpcom ) {
5041					src = src_item.attr('src') || src_item.attr('original') || src_item.data('original') || src_item.data('lazy-src');
5042					if (src.indexOf('imgpress') !== -1) {
5043						src = src_item.data('orig-file');
5044					}
5045					src = wpcom.carousel.addWidthToImageURL( src, wpcom.carousel.findFirstLargeEnoughWidth( orig_size.width, orig_size.height, max.width, max.height ) );
5046				} else {
5047// @end-hide-in-jetpack
5048					src = src_item.data('orig-file');
5049
5050					src = gallery.jp_carousel('selectBestImageSize', {
5051						orig_file   : src,
5052						orig_width  : orig_size.width,
5053						orig_height : orig_size.height,
5054						max_width   : max.width,
5055						max_height  : max.height,
5056						medium_file : medium_file,
5057						large_file  : large_file
5058					});
5059// @start-hide-in-jetpack
5060				} // end else of if ( 'undefined' != typeof wpcom )
5061// @end-hide-in-jetpack
5062
5063				// Set the final src
5064				$(this).data( 'gallery-src', src );
5065			});
5066
5067			// If the start_index is not 0 then preload the clicked image first.
5068			if ( 0 !== start_index ) {
5069				$('<img/>')[0].src = $(items[start_index]).data('gallery-src');
5070			}
5071
5072			var useInPageThumbnails = items.first().closest( '.tiled-gallery.type-rectangular' ).length > 0;
5073
5074			// create the 'slide'
5075			items.each(function(i){
5076				var src_item        = $(this),
5077					liked           = src_item.data('liked') || 0, // @hide-in-jetpack
5078					reblogged       = src_item.data( 'reblogged' ) || 0, // @hide-in-jetpack
5079					attachment_id   = src_item.data('attachment-id') || 0,
5080					comments_opened = src_item.data('comments-opened') || 0,
5081					image_meta      = src_item.data('image-meta') || {},
5082					orig_size       = src_item.data('orig-size') || '',
5083					thumb_size      = { width : src_item[0].naturalWidth, height : src_item[0].naturalHeight },
5084					title           = src_item.data('image-title') || '',
5085					description     = src_item.data('image-description') || '',
5086					caption         = src_item.parents('.gallery-item').find('.gallery-caption').html() || '',
5087					src		= src_item.data('gallery-src') || '',
5088					medium_file     = src_item.data('medium-file') || '',
5089					large_file      = src_item.data('large-file') || '',
5090					orig_file	= src_item.data('orig-file') || '';
5091
5092				var tiledCaption = src_item.parents('div.tiled-gallery-item').find('div.tiled-gallery-caption').html();
5093				if ( tiledCaption ) {
5094					caption = tiledCaption;
5095				}
5096
5097				if ( attachment_id && orig_size.length ) {
5098					title       = gallery.jp_carousel('texturize', title);
5099					description = gallery.jp_carousel('texturize', description);
5100					caption     = gallery.jp_carousel('texturize', caption);
5101
5102					// Initially, the image is a 1x1 transparent gif.  The preview is shown as a background image on the slide itself.
5103					var image = $( '<img/>' )
5104						.attr( 'src', 'data:image/gif;base64,R0lGODlhAQABAIAAAAAAAP///yH5BAEAAAAALAAAAAABAAEAAAIBRAA7' )
5105						.css( 'width', '100%' )
5106						.css( 'height', '100%' );
5107
5108					var slide = $('<div class="jp-carousel-slide"></div>')
5109							.hide()
5110							.css({
5111								//'position' : 'fixed',
5112								'left'     : i < start_index ? -1000 : gallery.width()
5113							})
5114							.append( image )
5115							.appendTo(gallery)
5116							.data('src', src )
5117							.data('title', title)
5118							.data('desc', description)
5119							.data('caption', caption)
5120							.data('attachment-id', attachment_id)
5121							.data('permalink', src_item.parents('a').attr('href'))
5122							.data('orig-size', orig_size)
5123							.data('comments-opened', comments_opened)
5124							.data('image-meta', image_meta)
5125							.data('medium-file', medium_file)
5126							.data('large-file', large_file)
5127							.data('orig-file', orig_file)
5128							.data('thumb-size', thumb_size)
5129							.data('liked', liked) // @hide-in-jetpack
5130							.data( 'reblogged', reblogged ) // @hide-in-jetpack
5131							;
5132
5133						if ( useInPageThumbnails ) {
5134							// Use the image already loaded in the gallery as a preview.
5135							slide
5136								.data( 'preview-image', src_item.attr( 'src' ) )
5137								.css( {
5138									'background-image' : 'url("' + src_item.attr( 'src' ) + '")',
5139									'background-size' : '100% 100%',
5140									'background-position' : 'center center'
5141								} );
5142						}
5143
5144						slide.jp_carousel( 'fitSlide', false );
5145				}
5146			});
5147			return this;
5148		},
5149
5150		selectBestImageSize: function(args) {
5151			if ( 'object' !== typeof args ) {
5152				args = {};
5153			}
5154
5155			if ( 'undefined' === typeof args.orig_file ) {
5156				return '';
5157			}
5158
5159			if ( 'undefined' === typeof args.orig_width || 'undefined' === typeof args.max_width ) {
5160				return args.orig_file;
5161			}
5162
5163			if ( 'undefined' === typeof args.medium_file || 'undefined' === typeof args.large_file ) {
5164				return args.orig_file;
5165			}
5166
5167			var medium_size       = args.medium_file.replace(/-([\d]+x[\d]+)\..+$/, '$1'),
5168				medium_size_parts = (medium_size !== args.medium_file) ? medium_size.split('x') : [args.orig_width, 0],
5169				medium_width      = parseInt( medium_size_parts[0], 10 ),
5170				medium_height     = parseInt( medium_size_parts[1], 10 ),
5171				large_size        = args.large_file.replace(/-([\d]+x[\d]+)\..+$/, '$1'),
5172				large_size_parts  = (large_size !== args.large_file) ? large_size.split('x') : [args.orig_width, 0],
5173				large_width       = parseInt( large_size_parts[0], 10 ),
5174				large_height      = parseInt( large_size_parts[1], 10 );
5175
5176			// Give devices with a higher devicePixelRatio higher-res images (Retina display = 2, Android phones = 1.5, etc)
5177			if ( 'undefined' !== typeof window.devicePixelRatio && window.devicePixelRatio > 1 ) {
5178				args.max_width  = args.max_width * window.devicePixelRatio;
5179				args.max_height = args.max_height * window.devicePixelRatio;
5180			}
5181
5182			if ( large_width >= args.max_width || large_height >= args.max_height ) {
5183				return args.large_file;
5184			}
5185
5186			if ( medium_width >= args.max_width || medium_height >= args.max_height ) {
5187				return args.medium_file;
5188			}
5189
5190			return args.orig_file;
5191		},
5192// @start-hide-in-jetpack
5193		show_reblog_box: function() {
5194			$('#carousel-reblog-box textarea').val(jetpackCarouselStrings.reblog_add_thoughts);
5195			//t.addClass('selected');
5196			$('#carousel-reblog-box p.response').remove();
5197			$('#carousel-reblog-box div.submit, #carousel-reblog-box div.submit span.canceltext').show();
5198			$('#carousel-reblog-box div.submit input[type=submit]').prop('disabled', false);
5199
5200			var current = $('.jp-carousel div.selected');
5201			$('#carousel-reblog-box input#carousel-reblog-post-url').val( current.data('permalink') );
5202			$('#carousel-reblog-box input#carousel-reblog-post-title').val( $('div.jp-carousel-info').children('h2').text() );
5203
5204			$('div.jp-carousel-info').append( $('#carousel-reblog-box') ).children().fadeOut('fast');
5205			$('#carousel-reblog-box').fadeIn('fast');
5206		},
5207
5208		hide_reblog_box: function () {
5209			$( 'div.jp-carousel-info' ).children().not( '#carousel-reblog-box' ).fadeIn( 'fast' );
5210			$( '#carousel-reblog-box' ).fadeOut( 'fast' );
5211		},
5212// @end-hide-in-jetpack
5213
5214		originalDimensions: function() {
5215			var splitted = $(this).data('orig-size').split(',');
5216			return {width: parseInt(splitted[0], 10), height: parseInt(splitted[1], 10)};
5217		},
5218
5219		format: function( args ) {
5220			if ( 'object' !== typeof args ) {
5221				args = {};
5222			}
5223			if ( ! args.text || 'undefined' === typeof args.text ) {
5224				return;
5225			}
5226			if ( ! args.replacements || 'undefined' === typeof args.replacements ) {
5227				return args.text;
5228			}
5229			return args.text.replace(/{(\d+)}/g, function( match, number ) {
5230				return typeof args.replacements[number] !== 'undefined' ? args.replacements[number] : match;
5231			});
5232		},
5233
5234		shutterSpeed: function(d) {
5235			if (d >= 1) {
5236				return Math.round(d*10)/10 + 's'; // round to one decimal if value >
5236 1s by multiplying it by 10, rounding, then dividing by 10 again
5237			}
5238			var df = 1, top = 1, bot = 1;
5239			var tol = 1e-8;
5240			// iterate while value not reached and difference (positive or negative, hence the Math.abs) between value 
5241			// and approximated value greater than given tolerance
5242			while (df !== d && Math.abs(df-d) > tol) {
5243				if (df < d) {
5244					top += 1;
5245				} else {
5246					bot += 1;
5247					top = parseInt(d * bot, 10);
5248				}
5249				df = top / bot;
5250			}
5251			if (top > 1) {
5252				bot = Math.round(bot / top);
5253				top = 1;
5254			}
5255			if (bot <= 1) {
5256				return '1s';
5257			}
5258			return top + '/' + bot + 's';
5259		},
5260
5261		parseTitleDesc: function( value ) {
5262			if ( !value.match(' ') && value.match('_') ) {
5263				return '';
5264			}
5265			// Prefix list originally based on http://commons.wikimedia.org/wiki/MediaWiki:Filename-prefix-blacklist
5266			$([
5267				'CIMG',                   // Casio
5268				'DSC_',                   // Nikon
5269				'DSCF',                   // Fuji
5270				'DSCN',                   // Nikon
5271				'DUW',                    // some mobile phones
5272				'GEDC',                   // GE
5273				'IMG',                    // generic
5274				'JD',                     // Jenoptik
5275				'MGP',                    // Pentax
5276				'PICT',                   // misc.
5277				'Imagen',                 // misc.
5278				'Foto',                   // misc.
5279				'DSC',                    // misc.
5280				'Scan',                   // Scanners
5281				'SANY',                   // Sanyo
5282				'SAM',                    // Samsung
5283				'Screen Shot [0-9]+'      // Mac screenshots
5284			])
5285			.each(function(key, val){
5286				var regex = new RegExp('^' + val);
5287				if ( regex.test(value) ) {
5288					value = '';
5289					return;
5290				}
5291			});
5292			return value;
5293		},
5294
5295		getTitleDesc: function( data ) {
5296			var title ='', desc = '', markup = '', target;
5297
5298			target = $( 'div.jp-carousel-titleanddesc', 'div.jp-carousel-wrap' );
5299			target.hide();
5300
5301			title = gallery.jp_carousel('parseTitleDesc', data.title) || '';
5302			desc  = gallery.jp_carousel('parseTitleDesc', data.desc)  || '';
5303
5304			if ( title.length || desc.length ) {
5305				// $('<div />').text(sometext).html() is a trick to go to HTML to plain text (including HTML entities decode, etc)
5306				if ( $('<div />').text(title).html() === $('<div />').text(desc).html() ) {
5307					title = '';
5308				}
5309
5310				markup  = ( title.length ) ? '<div class="jp-carousel-titleanddesc-title">' + title + '</div>' : '';
5311				markup += ( desc.length )  ? '<div class="jp-carousel-titleanddesc-desc">' + desc + '</div>'   : '';
5312
5313				target.html( markup ).fadeIn('slow');
5314			}
5315
5316			$( 'div#jp-carousel-comment-form-container' ).css('margin-top', '20px');
5317			$( 'div#jp-carousel-comments-loading' ).css('margin-top', '20px');
5318		},
5319
5320		updateLikesWidgetVisibility: function ( attachmentId ) {
5321			// Only do this if likes is enabled
5322			if ( 'undefined' === typeof jetpackLikesWidgetQueue ) {
5323				return;
5324			}
5325
5326			// Hide all likes widgets except for the one for the attachmentId passed in
5327			$( '.jp-carousel-buttons .jetpack-likes-widget-wrapper' ).css( 'display', 'none' ).each( function () {
5328				var widgetWrapper = $( this );
5329				if ( widgetWrapper.attr( 'data-attachment-id' ) == attachmentId ) { // jshint ignore:line
5330					widgetWrapper.css( 'display', 'inline-block' );
5331					return false;
5332				}
5333			});
5334		},
5335
5336		loadLikes : function ( attachmentId ) {
5337			var dataCarouselExtra = $( '.jp-carousel-wrap' ).data( 'carousel-extra' );
5338			var blogId = dataCarouselExtra.likes_blog_id;
5339
5340			if ( $( '#like-post-wrapper-' + blogId + '-' + attachmentId ).length === 0 ) {
5341				// Add the iframe the first time the slide is shown.
5342				var protocol = 'http';
5343				var originDomain = 'http://wordpress.com';
5344
5345				if ( dataCarouselExtra.permalink.length ) {
5346					protocol = dataCarouselExtra.permalink.split( ':' )[0];
5347
5348					if ( ( protocol !== 'http' ) && ( protocol !== 'https' ) ) {
5349						protocol = 'http';
5350					}
5351
5352					var parts = dataCarouselExtra.permalink.split( '/' );
5353
5354					if ( parts.length >= 2 ) {
5355						originDomain = protocol + '://' + parts[2];
5356					}
5357				}
5358
5359				var dataSource = protocol + '://widgets.wp.com/likes/#blog_id=' + encodeURIComponent( blogId ) +
5360					'&post_id=' + encodeURIComponent( attachmentId ) +
5361					'&slim=1&origin=' + encodeURIComponent( originDomain );
5362
5363				if ( 'en' !== jetpackCarouselStrings.lang ) {
5364					dataSource += '&lang=' + encodeURIComponent( jetpackCarouselStrings.lang );
5365				}
5366
5367				var likesWidget = $( '<iframe class=\'post-likes-widget jetpack-likes-widget jetpack-resizeable\'></iframe>' )
5368					.attr( 'name', 'like-post-frame-' + blogId + '-' + attachmentId )
5369					.attr( 'src', dataSource )
5370					.css( 'display', 'inline-block' );
5371
5372				var likesWidgetWrapper = $( '<div/>' )
5373					.addClass( 'jetpack-likes-widget-wrapper jetpack-likes-widget-unloaded slim-likes-widget' )
5374					.attr( 'id', 'like-post-wrapper-' + blogId + '-' + attachmentId )
5375					.attr( 'data-src', dataSource )
5376					.attr( 'data-name', 'like-post-frame-' + blogId + '-' + attachmentId )
5377					.attr( 'data-attachment-id', attachmentId )
5378					.css( 'display', 'none' )
5379					.css( 'vertical-align', 'middle' )
5380					.append( likesWidget )
5381					.append( '<div class=\'post-likes-widget-placeholder\'></div>' );
5382
5383				$( '.jp-carousel-buttons' ).append( likesWidgetWrapper );
5384			}
5385
5386		},
5387
5388		// updateExif updates the contents of the exif UL (.jp-carousel-image-exif)
5389		updateExif: function( meta ) {
5390			if ( !meta || 1 !== Number( jetpackCarouselStrings.display_exif ) ) {
5391				return false;
5392			}
5393
5394			var $ul = $( '<ul class=\'jp-carousel-image-exif\'></ul>' );
5395
5396			$.each( meta, function( key, val ) {
5397				if ( 0 === parseFloat(val) || !val.length || -1 === $.inArray( key, [ 'camera', 'aperture', 'shutter_speed', 'focal_length' ] ) ) {
5398					return;
5399				}
5400
5401				switch( key ) {
5402					case 'focal_length':
5403						val = val + 'mm';
5404						break;
5405					case 'shutter_speed':
5406						val = gallery.jp_carousel('shutterSpeed', val);
5407						break;
5408					case 'aperture':
5409						val = 'f/' + val;
5410						break;
5411				}
5412
5413				$ul.append( '<li><h5>' + jetpackCarouselStrings[key] + '</h5>' + val + '</li>' );
5414			});
5415
5416			// Update (replace) the content of the ul
5417			$( 'div.jp-carousel-image-meta ul.jp-carousel-image-exif' ).replaceWith( $ul );
5418		},
5419
5420		// updateFullSizeLink updates the contents of the jp-carousel-image-download link
5421		updateFullSizeLink: function(current) {
5422			if(!current || !current.data) {
5423				return false;
5424			}
5425			var original  = current.data('orig-file').replace(/\?.+$/, ''),
5426				origSize  = current.data('orig-size').split(','),
5427				permalink = $( '<a>'+gallery.jp_carousel('format', {'text': jetpackCarouselStrings.download_original, 'replacements': origSize})+'</a>' )
5428					.addClass( 'jp-carousel-image-download' )
5429					.attr( 'href', original )
5430					.attr( 'target', '_blank' );
5431
5432			// Update (replace) the content of the anchor
5433			$( 'div.jp-carousel-image-meta a.jp-carousel-image-download' ).replaceWith( permalink );
5434		},
5435
5436		updateMap: function( meta ) {
5437			if ( !meta.latitude || !meta.longitude || 1 !== Number( jetpackCarouselStrings.display_geo ) ) {
5438				return;
5439			}
5440
5441			var latitude  = meta.latitude,
5442				longitude = meta.longitude,
5443				$metabox  = $( 'div.jp-carousel-image-meta', 'div.jp-carousel-wrap' ),
5444				$mapbox   = $( '<div></div>' ),
5445				style     = '&scale=2&style=feature:all|element:all|invert_lightness:true|hue:0x0077FF|saturation:-50|lightness:-5|gamma:0.91';
5446
5447			$mapbox
5448				.addClass( 'jp-carousel-image-map' )
5449				.html( '<img width="154" height="154" src="https://maps.googleapis.com/maps/api/staticmap?\
5450							center=' + latitude + ',' + longitude + '&\
5451							zoom=8&\
5452							size=154x154&\
5453							sensor=false&\
5454							markers=size:medium%7Ccolor:blue%7C' + latitude + ',' + longitude + style +'" class="gmap-main" />\
5455							\
5456						<div class="gmap-topright"><div class="imgclip"><img width="175" height="154" src="https://maps.googleapis.com/maps/api/staticmap?\
5457							center=' + latitude + ',' + longitude + '&\
5458							zoom=3&\
5459							size=175x154&\
5460							sensor=false&\
5461							markers=size:small%7Ccolor:blue%7C' + latitude + ',' + longitude + style + '"c /></div></div>\
5462							\
5463						' )
5464				.prependTo( $metabox );
5465		},
5466
5467		testCommentsOpened: function( opened ) {
5468			if ( 1 === parseInt( opened, 10 ) ) {
5469// @start-hide-in-jetpack
5470				if ( 1 === Number( jetpackCarouselStrings.is_logged_in ) ) {
5471					$('.jp-carousel-commentlink').fadeIn('fast');
5472				} else {
5473// @end-hide-in-jetpack
5474					$('.jp-carousel-buttons').fadeIn('fast');
5475// @start-hide-in-jetpack
5476				}
5477// @end-hide-in-jetpack
5478				commentForm.fadeIn('fast');
5479			} else {
5480// @start-hide-in-jetpack
5481				if ( 1 === Number( jetpackCarouselStrings.is_logged_in ) ) {
5482					$('.jp-carousel-commentlink').fadeOut('fast');
5483				} else {
5484// @end-hide-in-jetpack
5485					$('.jp-carousel-buttons').fadeOut('fast');
5486// @start-hide-in-jetpack
5487				}
5488// @end-hide-in-jetpack
5489				commentForm.fadeOut('fast');
5490			}
5491		},
5492
5493		getComments: function( args ) {
5494			clearInterval( commentInterval );
5495
5496			if ( 'object' !== typeof args ) {
5497				return;
5498			}
5499
5500			if ( 'undefined' === typeof args.attachment_id || ! args.attachment_id ) {
5501				return;
5502			}
5503
5504			if ( ! args.offset || 'undefined' === typeof args.offset || args.offset < 1 ) {
5505				args.offset = 0;
5506			}
5507
5508			var comments        = $('.jp-carousel-comments'),
5509				commentsLoading = $('#jp-carousel-comments-loading').show();
5510
5511			if ( args.clear ) {
5512				comments.hide().empty();
5513			}
5514
5515			$.ajax({
5516				type:       'GET',
5517				url:        jetpackCarouselStrings.ajaxurl,
5518				dataType:   'json',
5519				data: {
5520					action: 'get_attachment_comments',
5521					nonce:  jetpackCarouselStrings.nonce,
5522					id:     args.attachment_id,
5523					offset: args.offset
5524				},
5525				success: function(data/*, status, xhr*/) {
5526					if ( args.clear ) {
5527						comments.fadeOut('fast').empty();
5528					}
5529
5530					$( data ).each(function(){
5531						var comment = $('<div></div>')
5532							.addClass('jp-carousel-comment')
5533							.attr('id', 'jp-carousel-comment-' + this['id'])
5534							.html(
5535								  '<div class="comment-gravatar">' +
5536								    this['gravatar_markup'] +
5537								  '</div>' +
5538								  '<div class="comment-author">' +
5539								    this['author_markup'] +
5540								  '</div>' +
5541								  '<div class="comment-date">' +
5542								    this['date_gmt'] +
5543								  '</div>' +
5544								  '<div class="comment-content">' +
5545								    this['content'] +
5546								  '</div>'
5547							);
5548						comments.append(comment);
5549
5550						// Set the interval to check for a new page of comments.
5551						clearInterval( commentInterval );
5552						commentInterval = setInterval( function() {
5553							if ( ( $('.jp-carousel-overlay').height() - 150 ) < $('.jp-carousel-wrap').scrollTop() + $(window).height() ) {
5554								gallery.jp_carousel('getComments',{ attachment_id: args.attachment_id, offset: args.offset + 10, clear: false });
5555								clearInterval( commentInterval );
5556							}
5557						}, 300 );
5558					});
5559
5560					// Verify (late) that the user didn't repeatldy click the arrows really fast, in which case the requested
5561					// attachment id might no longer match the current attachment id by the time we get the data back or a now
5562					// registered infiniscroll event kicks in, so we don't ever display comments for the wrong image by mistake.
5563					var current = $('.jp-carousel div.selected');
5564					if ( current && current.data && current.data('attachment-id') != args.attachment_id ) { // jshint ignore:line
5565						comments.fadeOut('fast');
5566						comments.empty();
5567						return;
5568					}
5569
5570					// Increase the height of the background, semi-transparent overlay to match the new length of the comments list.
5571					$('.jp-carousel-overlay').height( $(window).height() + titleAndDescription.height() + commentForm.height() + ( (comments.height() > 0) ? comments.height() : imageMeta.height() ) + 200 );
5572
5573					comments.show();
5574					commentsLoading.hide();
5575				},
5576				error: function(xhr, status, error) {
5577					// TODO: proper error handling
5578					console.log( 'Comment get fail...', xhr, status, error );
5579					comments.fadeIn('fast');
5580					commentsLoading.fadeOut('fast');
5581				}
5582			});
5583		},
5584
5585		postCommentError: function(args) {
5586			if ( 'object' !== typeof args ) {
5587				args = {};
5588			}
5589			if ( ! args.field || 'undefined' === typeof args.field ||  ! args.error || 'undefined' === typeof args.error ) {
5590				return;
5591			}
5592			$('#jp-carousel-comment-post-results').slideUp('fast').html('<span class="jp-carousel-comment-post-error">'+args.error+'</span>').slideDown('fast');
5593			$('#jp-carousel-comment-form-spinner').spin(false);
5594		},
5595
5596		setCommentIframeSrc: function(attachment_id) {
5597			var iframe = $('#jp-carousel-comment-iframe');
5598			// Set the proper irame src for the current attachment id
5599			if (iframe && iframe.length) {
5600				iframe.attr('src', iframe.attr('src').replace(/(postid=)\d+/, '$1'+attachment_id) );
5601				iframe.attr('src', iframe.attr('src').replace(/(%23.+)?$/, '%23jp-carousel-'+attachment_id) );
5602			}
5603		},
5604
5605		clearCommentTextAreaValue: function() {
5606			var commentTextArea = $('#jp-carousel-comment-form-comment-field');
5607			if ( commentTextArea ) {
5608				commentTextArea.val('');
5609			}
5610		},
5611
5612		nextSlide : function () {
5613			var slides = this.jp_carousel( 'slides' );
5614			var selected = this.jp_carousel( 'selectedSlide' );
5615
5616			if ( selected.length === 0 || ( slides.length > 2 && selected.is( slides.last() ) ) ) {
5617				return slides.first();
5618			}
5619
5620			return selected.next();
5621		},
5622
5623		prevSlide : function () {
5624			var slides = this.jp_carousel( 'slides' );
5625			var selected = this.jp_carousel( 'selectedSlide' );
5626
5627			if ( selected.length === 0 || ( slides.length > 2 && selected.is( slides.first() ) ) ) {
5628				return slides.last();
5629			}
5630
5631			return selected.prev();
5632		},
5633
5634		loadFullImage : function ( slide ) {
5635			var image = slide.find( 'img:first' );
5636
5637			if ( ! image.data( 'loaded' ) ) {
5638				// If the width of the slide is smaller than the width of the "thumbnail" we're already using,
5639				// don't load the full image.
5640
5641				image.on( 'load.jetpack', function () {
5642					image.off( 'load.jetpack' );
5643					$( this ).closest( '.jp-carousel-slide' ).css( 'background-image', '' );
5644				} );
5645
5646				if ( ! slide.data( 'preview-image' ) || ( slide.data( 'thumb-size' ) && slide.width() > slide.data( 'thumb-size' ).width ) ) {
5647					image.attr( 'src', image.closest( '.jp-carousel-slide' ).data( 'src' ) );
5648				} else {
5649					image.attr( 'src', slide.data( 'preview-image' ) );
5650				}
5651
5652				image.data( 'loaded', 1 );
5653			}
5654		}
5655	};
5656
5657	$.fn.jp_carousel = function(method){
5658		// ask for the HTML of the gallery
5659		// Method calling logic
5660		if ( methods[method] ) {
5661			return methods[ method ].apply( this, Array.prototype.slice.call( arguments, 1 ));
5662		} else if ( typeof method === 'object' || ! method ) {
5663			return methods.open.apply( this, arguments );
5664		} else {
5665			$.error( 'Method ' +	method + ' does not exist on jQuery.jp_carousel' );
5666		}
5667
5668	};
5669
5670	// register the event listener for starting the gallery
5671	$( document.body ).on( 'click', 'div.gallery,div.tiled-gallery', function(e) {
5672		if ( ! $(this).jp_carousel( 'testForData', e.currentTarget ) ) {
5673			return;
5674		}
5675		if ( $(e.target).parent().hasClass('gallery-caption') ) {
5676			return;
5677		}
5678		e.preventDefault();
5679		$(this).jp_carousel('open', {start_index: $(this).find('.gallery-item, .tiled-gallery-item').index($(e.target).parents('.gallery-item, .tiled-gallery-item'))});
5680	});
5681
5682	// Makes carousel work on page load and when back button leads to same URL with carousel hash (ie: no actual document.ready trigger)
5683	$( window ).on( 'hashchange', function () {
5684		var hashRegExp = /jp-carousel-(\d+)/,
5685			matches, attachmentId, galleries, selectedThumbnail;
5686
5687		if ( ! window.location.hash || ! hashRegExp.test( window.location.hash ) ) {
5688			return;
5689		}
5690
5691		if ( window.location.hash === last_known_location_hash ) {
5692			return;
5693		}
5694
5695		last_known_location_hash = window.location.hash;
5696		matches = window.location.hash.match( hashRegExp );
5697		attachmentId = parseInt( matches[1], 10 );
5698		galleries = $( 'div.gallery, div.tiled-gallery' );
5699
5700		// Find the first thumbnail that matches the attachment ID in the location
5701		// hash, then open the gallery that contains it.
5702		galleries.each( function( _, galleryEl ) {
5703			$( galleryEl ).find('img').each( function( imageIndex, imageEl ) {
5704				if ( $( imageEl ).data( 'attachment-id' ) === parseInt( attachmentId, 10 ) ) {
5705					selectedThumbnail = { index: imageIndex, gallery: galleryEl };
5706					return false;
5707				}
5708			});
5709
5710			if ( selectedThumbnail ) {
5711				$( selectedThumbnail.gallery )
5712					.jp_carousel( 'openOrSelectSlide', selectedThumbnail.index );
5713			}
5714		});
5715	});
5716
5717	if ( window.location.hash ) {
5718		$( window ).trigger( 'hashchange' );
5719	}
5720});
5721
5722/**
5723 * jQuery Plugin to obtain touch gestures from iPhone, iPod Touch and iPad, should also work with Android mobile phones (not tested yet!)
5724 * Common usage: wipe images (left and right to show the previous or next image)
5725 *
5726 * @author Andreas Waltl, netCU Internetagentur (http://www.netcu.de)
5727 * Version 1.1.1, modified to pass the touchmove event to the callbacks.
5728 */
5729(function($) {
5730$.fn.touchwipe = function(settings) {
5731	var config = {
5732			min_move_x: 20,
5733			min_move_y: 20,
5734			wipeLeft: function(/*e*/) { },
5735			wipeRight: function(/*e*/) { },
5736			wipeUp: function(/*e*/) { },
5737			wipeDown: function(/*e*/) { },
5738			preventDefaultEvents: true
5739	};
5740
5741	if (settings) {
5742		$.extend(config, settings);
5743	}
5744
5745	this.each(function() {
5746		var startX;
5747		var startY;
5748		var isMoving = false;
5749
5750		function cancelTouch() {
5751			this.removeEventListener('touchmove', onTouchMove);
5752			startX = null;
5753			isMoving = false;
5754		}
5755
5756		function onTouchMove(e) {
5757			if(config.preventDefaultEvents) {
5758				e.preventDefault();
5759			}
5760			if(isMoving) {
5761				var x = e.touches[0].pageX;
5762				var y = e.touches[0].pageY;
5763				var dx = startX - x;
5764				var dy = startY - y;
5765				if(Math.abs(dx) >= config.min_move_x) {
5766					cancelTouch();
5767					if(dx > 0) {
5768						config.wipeLeft(e);
5769					} else {
5770						config.wipeRight(e);
5771					}
5772				}
5773				else if(Math.abs(dy) >= config.min_move_y) {
5774						cancelTouch();
5775						if(dy > 0) {
5776							config.wipeDown(e);
5777						} else {
5778							config.wipeUp(e);
5779						}
5780					}
5781			}
5782		}
5783
5784		function onTouchStart(e)
5785		{
5786			if (e.touches.length === 1) {
5787				startX = e.touches[0].pageX;
5788				startY = e.touches[0].pageY;
5789				isMoving = true;
5790				this.addEventListener('touchmove', onTouchMove, false);
5791			}
5792		}
5793		if ('ontouchstart' in document.documentElement) {
5794			this.addEventListener('touchstart', onTouchStart, false);
5795		}
5796	});
5797
5798	return this;
5799};
5800})(jQuery);
5801;

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.