PageSourceSearch

https://theelephantsdebt.com/script/ANW1qpyWwIqhP4PnOBWUpfInvL6-nt-PxdD6c/Q8XNLUd.js

js theelephantsdebt.com collected 2026-09-27 07:38:59 UTC 169,467 bytes, 4,635 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        clientHintSupport: {
303                gravatar: false,
304                files: false,
305                photon: false,
306                mshots: false,
307                staticAssets: false,
308                latex: false,
309                imgpress: false,
310        },
311	useHints: false,
312	zoomed: false,
313	timer: null,
314	interval: 1000, // zoom polling interval in millisecond
315
316	// Should we apply width/height attributes to control the image size?
317	imgNeedsSizeAtts: function( img ) {
318		// Do not overwrite existing width/height attributes.
319		if ( img.getAttribute('width') !== null || img.getAttribute('height') !== null )
320			return false;
321		// Do not apply the attributes if the image is already constrained by a parent element.
322		if ( img.width < img.naturalWidth || img.height < img.naturalHeight )
323			return false;
324		return true;
325	},
326
327        hintsFor: function( service ) {
328                if ( this.useHints === false ) {
329                        return false;
330                }
331                if ( this.hints() === false ) {
332                        return false;
333                }
334                if ( typeof this.clientHintSupport[service] === "undefined" ) {
335                        return false;
336                }
337                if ( this.clientHintSupport[service] === true ) {
338                        return true;
339                }
340                return false;
341        },
342
343	hints: function() {
344		try {
345			var chrome = window.navigator.userAgent.match(/\sChrome\/([0-9]+)\.[.0-9]+\s/)
346			if (chrome !== null) {
347				var version = parseInt(chrome[1], 10)
348				if (isNaN(version) === false && version >= 46) {
349					return true
350				}
351			}
352		} catch (e) {
353			return false
354		}
355		return false
356	},
357
358	init: function() {
359		var t = this;
360		try{
361			t.zoomImages();
362			t.timer = setInterval( function() { t.zoomImages(); }, t.interval );
363		}
364		catch(e){
365		}
366	},
367
368	stop: function() {
369		if ( this.timer )
370			clearInterval( this.timer );
371	},
372
373	getScale: function() {
374		var scale = detectZoom.device();
375		// Round up to 1.5 or the next integer below the cap.
376		if      ( scale <= 1.0 ) scale = 1.0;
377		else if ( scale <= 1.5 ) scale = 1.5;
378		else if ( scale <= 2.0 ) scale = 2.0;
379		else if ( scale <= 3.0 ) scale = 3.0;
380		else if ( scale <= 4.0 ) scale = 4.0;
381		else                     scale = 5.0;
382		return scale;
383	},
384
385	shouldZoom: function( scale ) {
386		var t = this;
387		// Do not operate on hidden frames.
388		if ( "innerWidth" in window && !window.innerWidth )
389			return false;
390		// Don't do anything until scale > 1
391		if ( scale == 1.0 && t.zoomed == false )
392			return false;
393		return true;
394	},
395
396	zoomImages: function() {
397		var t = this;
398		var scale = t.getScale();
399		if ( ! t.shouldZoom( scale ) ){
400			return;
401		}
402		t.zoomed = true;
403		// Loop through all the <img> elements on the page.
404		var imgs = document.getElementsByTagName("img");
405
406		for ( var i = 0; i < imgs.length; i++ ) {
407			// Wait for original images to load
408			if ( "complete" in imgs[i] && ! imgs[i].complete )
409				continue;
410
411			// Skip images that have srcset attributes.
412			if ( imgs[i].hasAttribute('srcset') ) {
413				continue;
414			}
415
416			// Skip images that don't need processing.
417			var imgScale = imgs[i].getAttribute("scale");
418			if ( imgScale == scale || imgScale == "0" )
419				continue;
420
421			// Skip images that have already failed at this scale
422			var scaleFail = imgs[i].getAttribute("scale-fail");
423			if ( scaleFail && scaleFail <= scale )
424				continue;
425
426			// Skip images that have no dimensions yet.
427			if ( ! ( imgs[i].width && imgs[i].height ) )
428				continue;
429
430			// Skip images from Lazy Load plugins
431			if ( ! imgScale && imgs[i].getAttribute("data-lazy-src") && (imgs[i].getAttribute("data-lazy-src") !== imgs[i].getAttribute("src")))
432				continue;
433
434			if ( t.scaleImage( imgs[i], scale ) ) {
435				// Mark the img as having been processed at this scale.
436				imgs[i].setAttribute("scale", scale);
437			}
438			else {
439				// Set the flag to skip this image.
440				imgs[i].setAttribute("scale", "0");
441			}
442		}
443	},
444
445	scaleImage: function( img, scale ) {
446		var t = this;
447		var newSrc = img.src;
448
449                var isFiles = false;
450                var isLatex = false;
451                var isPhoton = false;
452
453		// Skip slideshow images
454		if ( img.parentNode.className.match(/slideshow-slide/) )
455			return false;
456
457		// Scale gravatars that have ?s= or ?size=
458		if ( img.src.match( /^https?:\/\/([^\/]*\.)?gravatar\.com\/.+[?&](s|size)=/ ) ) {
459                        if ( this.hintsFor( "gravatar" ) === true ) {
460                                return false;
461                        }
462			newSrc = img.src.replace( /([?&](s|size)=)(\d+)/, function( $0, $1, $2, $3 ) {
463				// Stash the original size
464				var originalAtt = "originals",
465				originalSize = img.getAttribute(originalAtt);
466				if ( originalSize === null ) {
467					originalSize = $3;
468					img.setAttribute(originalAtt, originalSize);
469					if ( t.imgNeedsSizeAtts( img ) ) {
470						// Fix width and height attributes to rendered dimensions.
471						img.width = img.width;
472						img.height = img.height;
473					}
474				}
475				// Get the width/height of the image in CSS pixels
476				var size = img.clientWidth;
477				// Convert CSS pixels to device pixels
478				var targetSize = Math.ceil(img.clientWidth * scale);
479				// Don't go smaller than the original size
480				targetSize = Math.max( targetSize, originalSize );
481				// Don't go larger than the service supports
482				targetSize = Math.min( targetSize, 512 );
483				return $1 + targetSize;
484			});
485		}
486
487		// Scale mshots that have width
488		else if ( img.src.match(/^https?:\/\/([^\/]+\.)*(wordpress|wp)\.com\/mshots\/.+[?&]w=\d+/) ) {
489                        if ( this.hintsFor( "mshots" ) === true ) {
490                                return false;
491                        }
492			newSrc = img.src.replace( /([?&]w=)(\d+)/, function($0, $1, $2) {
493				// Stash the original size
494				var originalAtt = 'originalw', originalSize = img.getAttribute(originalAtt);
495				if ( originalSize === null ) {
496					originalSize = $2;
497					img.setAttribute(originalAtt, originalSize);
498					if ( t.imgNeedsSizeAtts( img ) ) {
499						// Fix width and height attributes to rendered dimensions.
500						img.width = img.width;
501						img.height = img.height;
502					}
503				}
504				// Get the width of the image in CSS pixels
505				var size = img.clientWidth;
506				// Convert CSS pixels to device pixels
507				var targetSize = Math.ceil(size * scale);
508				// Don't go smaller than the original size
509				targetSize = Math.max( targetSize, originalSize );
510				// Don't go bigger unless the current one is actually lacking
511				if ( scale > img.getAttribute("scale") && targetSize <= img.naturalWidth )
512					targetSize = $2;
513				if ( $2 != targetSize )
514					return $1 + targetSize;
515				return $0;
516			});
517
518			// Update height attribute to match width
519			newSrc = newSrc.replace( /([?&]h=)(\d+)/, function($0, $1, $2) {
520				if ( newSrc == img.src ) {
521					return $0;
522				}
523				// Stash the original size
524				var originalAtt = 'originalh', originalSize = img.getAttribute(originalAtt);
525				if ( originalSize === null ) {
526					originalSize = $2;
527					img.setAttribute(originalAtt, originalSize);
528				}
529				// Get the height of the image in CSS pixels
530				var size = img.clientHeight;
531				// Convert CSS pixels to device pixels
532				var targetSize = Math.ceil(size * scale);
533				// Don't go smaller than the original size
534				targetSize = Math.max( targetSize, originalSize );
535				// Don't go bigger unless the current one is actually lacking
536				if ( scale > img.getAttribute("scale") && targetSize <= img.naturalHeight )
537					targetSize = $2;
538				if ( $2 != targetSize )
539					return $1 + targetSize;
540				return $0;
541			});
542		}
543
544		// Scale simple imgpress queries (s0.wp.com) that only specify w/h/fit
545		else if ( img.src.match(/^https?:\/\/([^\/.]+\.)*(wp|wordpress)\.com\/imgpress\?(.+)/) ) {
546                        if ( this.hintsFor( "imgpress" ) === true ) {
547                                return false; 
548                        }
549			var imgpressSafeFunctions = ["zoom", "url", "h", "w", "fit", "filter", "brightness", "contrast", "colorize", "smooth", "unsharpmask"];
550			// Search the query string for unsupported functions.
551			var qs = RegExp.$3.split('&');
552			for ( var q in qs ) {
553				q = qs[q].split('=')[0];
554				if ( imgpressSafeFunctions.indexOf(q) == -1 ) {
555					return false;
556				}
557			}
558			// Fix width and height attributes to rendered dimensions.
559			img.width = img.width;
560			img.height = img.height;
561			// Compute new src
562			if ( scale == 1 )
563				newSrc = img.src.replace(/\?(zoom=[^&]+&)?/, '?');
564			else
565				newSrc = img.src.replace(/\?(zoom=[^&]+&)?/, '?zoom=' + scale + '&');
566		}
567
568		// Scale files.wordpress.com, LaTeX, or Photon images (i#.wp.com)
569		else if (
570			( isFiles = img.src.match(/^https?:\/\/([^\/]+)\.files\.wordpress\.com\/.+[?&][wh]=/) ) ||
571			( isLatex = img.src.match(/^https?:\/\/([^\/.]+\.)*(wp|wordpress)\.com\/latex\.php\?(latex|zoom)=(.+)/) ) ||
572			( isPhoton = img.src.match(/^https?:\/\/i[\d]{1}\.wp\.com\/(.+)/) )
573		) {
574                        if ( false !== isFiles && this.hintsFor( "files" ) === true ) {
575                                return false
576                        }
577                        if ( false !== isLatex && this.hintsFor( "latex" ) === true ) {
578                                return false
579                        }
580                        if ( false !== isPhoton && this.hintsFor( "photon" ) === true ) {
581                                return false
582                        }
583			// Fix width and height attributes to rendered dimensions.
584			img.width = img.width;
585			img.height = img.height;
586			// Compute new src
587			if ( scale == 1 ) {
588				newSrc = img.src.replace(/\?(zoom=[^&]+&)?/, '?');
589			} else {
590				newSrc = img.src;
591
592				var url_var = newSrc.match( /([?&]w=)(\d+)/ );
593				if ( url_var !== null && url_var[2] ) {
594					newSrc = newSrc.replace( url_var[0], url_var[1] + img.width );
595				}
596
597				url_var = newSrc.match( /([?&]h=)(\d+)/ );
598				if ( url_var !== null && url_var[2] ) {
599					newSrc = newSrc.replace( url_var[0], url_var[1] + img.height );
600				}
601
602				var zoom_arg = '&zoom=2';
603				if ( !newSrc.match( /\?/ ) ) {
604					zoom_arg = '?zoom=2';
605				}
606				img.setAttribute( 'srcset', newSrc + zoom_arg + ' ' + scale + 'x' );
607			}
608		}
609
610		// Scale static assets that have a name matching *-1x.png or *@1x.png
611		else if ( img.src.match(/^https?:\/\/[^\/]+\/.*[-@]([12])x\.(gif|jpeg|jpg|png)(\?|$)/) ) {
612                        if ( this.hintsFor( "staticAssets" ) === true ) {
613                                return false; 
614                        }
615			// Fix width and height attributes to rendered dimensions.
616			img.width = img.width;
617			img.height = img.height;
618			var currentSize = RegExp.$1, newSize = currentSize;
619			if ( scale <= 1 )
620				newSize = 1;
621			else
622				newSize = 2;
623			if ( currentSize != newSize )
624				newSrc = img.src.replace(/([-@])[12]x\.(gif|jpeg|jpg|png)(\?|$)/, '$1'+newSize+'x.$2$3');
625		}
626
627		else {
628			return false;
629		}
630
631		// Don't set img.src unless it has changed. This avoids unnecessary reloads.
632		if ( newSrc != img.src ) {
633			// Store the original img.src
634			var prevSrc, origSrc = img.getAttribute("src-orig");
635			if ( !origSrc ) {
636				origSrc = img.src;
637				img.setAttribute("src-orig", origSrc);
638			}
639			// In case of error, revert img.src
640			prevSrc = img.src;
641			img.onerror = function(){
642				img.src = prevSrc;
643				if ( img.getAttribute("scale-fail") < scale )
644					img.setAttribute("scale-fail", scale);
645				img.onerror = null;
646			};
647			// Finally load the new image
648			img.src = newSrc;
649		}
650
651		return true;
652	}
653};
654
655wpcom_img_zoomer.init();
656;
657/* global pm, wpcom_reblog */
658
659var jetpackLikesWidgetQueue = [];
660var jetpackLikesWidgetBatch = [];
661var jetpackLikesMasterReady = false;
662
663function JetpackLikespostMessage( message, target ) {
664	if ( 'string' === typeof message ){
665		try {
666			message = JSON.parse( message );
667		} catch(e) {
668			return;
669		}
670	}
671
672	pm( {
673		target: target,
674		type: 'likesMessage',
675		data: message,
676		origin: '*'
677	} );
678}
679
680function JetpackLikesBatchHandler() {
681	var requests = [];
682	jQuery( 'div.jetpack-likes-widget-unloaded' ).each( function() {
683		if ( jetpackLikesWidgetBatch.indexOf( this.id ) > -1 ) {
684			return;
685		}
686		jetpackLikesWidgetBatch.push( this.id );
687		var regex = /like-(post|comment)-wrapper-(\d+)-(\d+)-(\w+)/,
688			match = regex.exec( this.id ),
689			info;
690
691		if ( ! match || match.length !== 5 ) {
692			return;
693		}
694
695		info = {
696			blog_id: match[2],
697			width:   this.width
698		};
699
700		if ( 'post' === match[1] ) {
701			info.post_id = match[3];
702		} else if ( 'comment' === match[1] ) {
703			info.comment_id = match[3];
704		}
705
706		info.obj_id = match[4];
707
708		requests.push( info );
709	});
710
711	if ( requests.length > 0 ) {
712		JetpackLikespostMessage( { event: 'initialBatch', requests: requests }, window.frames['likes-master'] );
713	}
714}
715
716function JetpackLikesMessageListener( event, message ) {
717	var allowedOrigin, $container, $list, offset, rowLength, height, scrollbarWidth;
718
719	if ( 'undefined' === typeof event.event ) {
720		return;
721	}
722
723	// We only allow messages from one origin
724	allowedOrigin = window.location.protocol + 'https://widgets.wp.com';
725	if ( allowedOrigin !== message.origin ) {
726		return;
727	}
728
729	if ( 'masterReady' === event.event ) {
730		jQuery( document ).ready( function() {
731			jetpackLikesMasterReady = true;
732
733			var stylesData = {
734					event: 'injectStyles'
735				},
736				$sdTextColor = jQuery( '.sd-text-color' ),
737				$sdLinkColor = jQuery( '.sd-link-color' );
738
739			if ( jQuery( 'iframe.admin-bar-likes-widget' ).length > 0 ) {
740				JetpackLikespostMessage( { event: 'adminBarEnabled' }, window.frames[ 'likes-master' ] );
741
742				stylesData.adminBarStyles = {
743					background: jQuery( '#wpadminbar .quicklinks li#wp-admin-bar-wpl-like > a' ).css( 'background' ),
744					isRtl: ( 'rtl' === jQuery( '#wpadminbar' ).css( 'direction' ) )
745				};
746			}
747
748			// enable reblogs if we're on a single post page
749			if ( jQuery( 'body' ).hasClass( 'single' ) ) {
750				JetpackLikespostMessage( { event: 'reblogsEnabled' }, window.frames[ 'likes-master' ] );
751			}
752
753			if ( ! window.addEventListener ) {
754				jQuery( '#wp-admin-bar-admin-bar-likes-widget' ).hide();
755			}
756
757			stylesData.textStyles = {
758				color:          $sdTextColor.css( 'color' ),
759				fontFamily:     $sdTextColor.css( 'font-family' ),
760				fontSize:       $sdTextColor.css( 'font-size' ),
761				direction:      $sdTextColor.css( 'direction' ),
762				fontWeight:     $sdTextColor.css( 'font-weight' ),
763				fontStyle:      $sdTextColor.css( 'font-style' ),
764				textDecoration: $sdTextColor.css('text-decoration')
765			};
766
767			stylesData.linkStyles = {
768				color:          $sdLinkColor.css('color'),
769				fontFamily:     $sdLinkColor.css('font-family'),
770				fontSize:       $sdLinkColor.css('font-size'),
771				textDecoration: $sdLinkColor.css('text-decoration'),
772				fontWeight:     $sdLinkColor.css( 'font-weight' ),
773				fontStyle:      $sdLinkColor.css( 'font-style' )
774			};
775
776			JetpackLikespostMessage( stylesData, window.frames[ 'likes-master' ] );
777
778			JetpackLikesBatchHandler();
779
780			jQuery( document ).on( 'inview', 'div.jetpack-likes-widget-unloaded', function() {
781				jetpackLikesWidgetQueue.push( this.id );
782			});
783		});
784	}
785
786	if ( 'showLikeWidget' === event.event ) {
787		jQuery( '#' + event.id + ' .post-likes-widget-placeholder'  ).fadeOut( 'fast', function() {
788			jQuery( '#' + event.id + ' .post-likes-widget' ).fadeIn( 'fast', function() {
789				JetpackLikespostMessage( { event: 'likeWidgetDisplayed', blog_id: event.blog_id, post_id: event.post_id, obj_id: event.obj_id }, window.frames['likes-master'] );
790			});
791		});
792	}
793
794	if ( 'clickReblogFlair' === event.event ) {
795		wpcom_reblog.toggle_reblog_box_flair( event.obj_id );
796	}
797
798	if ( 'showOtherGravatars' === event.event ) {
799		$container = jQuery( '#likes-other-gravatars' );
800		$list = $container.find( 'ul' );
801
802		$container.hide();
803		$list.html( '' );
804
805		$container.find( '.likes-text span' ).text( event.total );
806
807		jQuery.each( event.likers, function( i, liker ) {
808			var element = jQuery( '<li><a><img /></a></li>' );
809			element.addClass( liker.css_class );
810
811			element.find( 'a' ).
812				attr({
813					href: liker.profile_URL,
814					rel: 'nofollow',
815					target: '_parent'
816				}).
817				addClass( 'wpl-liker' );
818
819			element.find( 'img' ).
820				attr({
821					src: liker.avatar_URL,
822					alt: liker.name
823				}).
824				css({
825					width: '30px',
826					height: '30px',
827					paddingRight: '3px'
828				});
829
830			$list.append( element );
831		} );
832
833		offset = jQuery( '[name=\'' + event.parent + '\']' ).offset();
834
835		$container.css( 'left', offset.left + event.position.left - 10 + 'px' );
836		$container.css( 'top', offset.top + event.position.top - 33 + 'px' );
837
838		rowLength = Math.floor( event.width / 37 );
839		height = ( Math.ceil( event.likers.length / rowLength ) * 37 ) + 13;
840		if ( height > 204 ) {
841			height = 204;
842		}
843
844		$container.css( 'height', height + 'px' );
845		$container.css( 'width', rowLength * 37 - 7 + 'px' );
846
847		$list.css( 'width', rowLength * 37 + 'px' );
848
849		$container.fadeIn( 'slow' );
850
851		scrollbarWidth = $list[0].offsetWidth - $list[0].clientWidth;
852		if ( scrollbarWidth > 0 ) {
853			$container.width( $container.width() + scrollbarWidth );
854			$list.width( $list.width() + scrollbarWidth );
855		}
856	}
857}
858
859pm.bind( 'likesMessage', JetpackLikesMessageListener );
860
861jQuery( document ).click( function( e ) {
862	var $container = jQuery( '#likes-other-gravatars' );
863
864	if ( $container.has( e.target ).length === 0 ) {
865		$container.fadeOut( 'slow' );
866	}
867});
868
869function JetpackLikesWidgetQueueHandler() {
870	var $wrapper, wrapperID, found;
871	if ( ! jetpackLikesMasterReady ) {
872		setTimeout( JetpackLikesWidgetQueueHandler, 500 );
873		return;
874	}
875
876	if ( jetpackLikesWidgetQueue.length > 0 ) {
877		// We may have a widget that needs creating now
878		found = false;
879		while( jetpackLikesWidgetQueue.length > 0 ) {
880			// Grab the first member of the queue that isn't already loading.
881			wrapperID = jetpackLikesWidgetQueue.splice( 0, 1 )[0];
882			if ( jQuery( '#' + wrapperID ).hasClass( 'jetpack-likes-widget-unloaded' ) ) {
883				found = true;
884				break;
885			}
886		}
887		if ( ! found ) {
888			setTimeout( JetpackLikesWidgetQueueHandler, 500 );
889			return;
890		}
891	} else if ( jQuery( 'div.jetpack-likes-widget-unloaded' ).length > 0 ) {
892		// Grab any unloaded widgets for a batch request
893		JetpackLikesBatchHandler();
894
895		// Get the next unloaded widget
896		wrapperID = jQuery( 'div.jetpack-likes-widget-unloaded' ).first()[0].id;
897		if ( ! wrapperID ) {
898			// Everything is currently loaded
899			setTimeout( JetpackLikesWidgetQueueHandler, 500 );
900			return;
901		}
902	}
903
904	if ( 'undefined' === typeof wrapperID ) {
905		setTimeout( JetpackLikesWidgetQueueHandler, 500 );
906		return;
907	}
908
909	$wrapper = jQuery( '#' + wrapperID );
910	$wrapper.find( 'iframe' ).remove();
911
912	if ( $wrapper.hasClass( 'slim-likes-widget' ) ) {
913		$wrapper.find( '.post-likes-widget-placeholder' ).after( '<iframe class="post-likes-widget jetpack-likes-widget" name="' + $wrapper.data( 'name' ) + '" height="22px" width="68px" frameBorder="0" scrolling="no" src="' + $wrapper.data( 'src' ) + '"></iframe>' );
914	} else {
915		$wrapper.find( '.post-likes-widget-placeholder' ).after( '<iframe class="post-likes-widget jetpack-likes-widget" name="' + $wrapper.data( 'name' ) + '" height="55px" width="100%" frameBorder="0" src="' + $wrapper.data( 'src' ) + '"></iframe>' );
916	}
917
918	$wrapper.removeClass( 'jetpack-likes-widget-unloaded' ).addClass( 'jetpack-likes-widget-loading' );
919
920	$wrapper.find( 'iframe' ).load( function( e ) {
921		var $iframe = jQuery( e.target );
922		$wrapper.removeClass( 'jetpack-likes-widget-loading' ).addClass( 'jetpack-likes-widget-loaded' );
923
924		JetpackLikespostMessage( { event: 'loadLikeWidget', name: $iframe.attr( 'name' ), width: $iframe.width() }, window.frames[ 'likes-master' ] );
925
926		if ( $wrapper.hasClass( 'slim-likes-widget' ) ) {
927			$wrapper.find( 'iframe' ).Jetpack( 'resizeable' );
928		}
929	});
930	setTimeout( JetpackLikesWidgetQueueHandler, 250 );
931}
932JetpackLikesWidgetQueueHandler();
933;
934// WARNING: This file is distributed verbatim in Jetpack. There should be nothing WordPress.com specific in this file. @hide-in-jetpack
935(function($){ // Open closure
936// Local vars
937var Scroller, ajaxurl, stats, type, text, totop;
938
939// IE requires special handling
940var isIE = ( -1 != navigator.userAgent.search( 'MSIE' ) );
941if ( isIE ) {
942	var IEVersion = navigator.userAgent.match(/MSIE\s?(\d+)\.?\d*;/);
943	var IEVersion = parseInt( IEVersion[1] );
944}
945
946// HTTP ajaxurl when site is HTTPS causes Access-Control-Allow-Origin failure in Desktop and iOS Safari
947if ( "https:" == document.location.protocol ) {
948	infiniteScroll.settings.ajaxurl = infiniteScroll.settings.ajaxurl.replace( "http://", "https://" );
949}
950
951/**
952 * Loads new posts when users scroll near the bottom of the page.
953 */
954Scroller = function( settings ) {
955	var self = this;
956
957	// Initialize our variables
958	this.id               = settings.id;
959	this.body             = $( document.body );
960	this.window           = $( window );
961	this.element          = $( '#' + settings.id );
962	this.wrapperClass     = settings.wrapper_class;
963	this.ready            = true;
964	this.disabled         = false;
965	this.page             = 1;
966	this.offset           = settings.offset;
967	this.currentday       = settings.currentday;
968	this.order            = settings.order;
969	this.throttle         = false;
970	this.handle           = '<div id="infinite-handle"><span><button>' + text.replace( '\\', '' ) + '</button></span></div>';
971	this.click_handle     = settings.click_handle;
972	this.google_analytics = settings.google_analytics;
973	this.history          = settings.history;
974	this.origURL          = window.location.href;
975	this.pageCache        = {};
976
977	// Footer settings
978	this.footer           = $( '#infinite-footer' );
979	this.footer.wrap      = settings.footer;
980
981	// Core's native MediaElement.js implementation needs special handling
982	this.wpMediaelement   = null;
983
984	// We have two type of infinite scroll
985	// cases 'scroll' and 'click'
986
987	if ( type == 'scroll' ) {
988		// Bind refresh to the scroll event
989		// Throttle to check for such case every 300ms
990
991		// On event the case becomes a fact
992		this.window.bind( 'scroll.infinity', function() {
993			this.throttle = true;
994		});
995
996		// Go back top method
997		self.gotop();
998
999		setInterval( function() {
1000			if ( this.throttle ) {
1001				// Once the case is the case, the action occurs and the fact is no more
1002				this.throttle = false;
1003				// Reveal or hide footer
1004				self.thefooter();
1005				// Fire the refresh
1006				self.refresh();
1007                self.determineURL(); // determine the url
1008			}
1009		}, 250 );
1010
1011		// Ensure that enough posts are loaded to fill the initial viewport, to compensate for short posts and large displays.
1012		self.ensureFilledViewport();
1013		this.body.bind( 'post-load', { self: self }, self.checkViewportOnLoad );
1014	} else if ( type == 'click' ) {
1015		if ( this.click_handle ) {
1016			this.element.append( this.handle );
1017		}
1018
1019		this.body.delegate( '#infinite-handle', 'click.infinity', function() {
1020			// Handle the handle
1021			if ( self.click_handle ) {
1022				$( '#infinite-handle' ).remove();
1023			}
1024
1025			// Fire the refresh
1026			self.refresh();
1027		});
1028	}
1029
1030	// Initialize any Core audio or video players loaded via IS
1031	this.body.bind( 'post-load', { self: self }, self.initializeMejs );
1032};
1033
1034/**
1035 * Check whether we should fetch any additional posts.
1036 */
1037Scroller.prototype.check = function() {
1038	var container = this.element.offset();
1039
1040	// If the container can't be found, stop otherwise errors result
1041	if ( 'object' !== typeof container ) {
1042		return false;
1043	}
1044
1045	var bottom = this.window.scrollTop() + this.window.height(),
1046		threshold = container.top + this.element.outerHeight(false) - (this.window.height() * 2);
1047
1048	return bottom > threshold;
1049};
1050
1051/**
1052 * Renders the results from a successful response.
1053 */
1054Scroller.prototype.render = function( response ) {
1055	this.body.addClass( 'infinity-success' );
1056
1057	// Check if we can wrap the html
1058	this.element.append( response.html );
1059	this.body.trigger( 'post-load', response );
1060	this.ready = true;
1061};
1062
1063/**
1064 * Returns the object used to query for new posts.
1065 */
1066Scroller.prototype.query = function() {
1067	return {
1068		page          : this.page + this.offset, // Load the next page.
1069		currentday    : this.currentday,
1070		order         : this.order,
1071		scripts       : window.infiniteScroll.settings.scripts,
1072		styles        : window.infiniteScroll.settings.styles,
1073		query_args    : window.infiniteScroll.settings.query_args,
1074		query_before  : window.infiniteScroll.settings.query_before,
1075		last_post_date: window.infiniteScroll.settings.last_post_date
1076	};
1077};
1078
1079/**
1080 * Scroll back to top.
1081 */
1082Scroller.prototype.gotop = function() {
1083	var blog = $( '#infinity-blog-title' );
1084
1085	blog.attr( 'title', totop );
1086
1087	// Scroll to top on blog title
1088	blog.bind( 'click', function( e ) {
1089		$( 'html, body' ).animate( { scrollTop: 0 }, 'fast' );
1090		e.preventDefault();
1091	});
1092};
1093
1094
1095/**
1096 * The infinite footer.
1097 */
1098Scroller.prototype.thefooter = function() {
1099	var self  = this,
1100		width;
1101
1102	// Check if we have an id for the page wrapper
1103	if ( $.type( this.footer.wrap ) === "string" ) {
1104		width = $( 'body #' + this.footer.wrap ).outerWidth( false );
1105
1106		// Make the footer match the width of the page
1107		if ( width > 479 )
1108			this.footer.find( '.container' ).css( 'width', width );
1109	}
1110
1111	// Reveal footer
1112	if ( this.window.scrollTop() >= 350 )
1113		self.footer.animate( { 'bottom': 0 }, 'fast' );
1114	else if ( this.window.scrollTop() < 350 )
1115		self.footer.animate( { 'bottom': '-50px' }, 'fast' );
1116};
1117
1118
1119/**
1120 * Controls the flow of the refresh. Don't mess.
1121 */
1122Scroller.prototype.refresh = function() {
1123	var	self   = this,
1124		query, jqxhr, load, loader, color, customized;
1125
1126	// If we're disabled, ready, or don't pass the check, bail.
1127	if ( this.disabled || ! this.ready || ! this.check() )
1128		return;
1129
1130	// Let's get going -- set ready to false to prevent
1131	// multiple refreshes from occurring at once.
1132	this.ready = false;
1133
1134	// Create a loader element to show it's working.
1135	if ( this.click_handle ) {
1136		loader = '<span class="infinite-loader"></span>';
1137		this.element.append( loader );
1138
1139		loader = this.element.find( '.infinite-loader' );
1140		color = loader.css( 'color' );
1141
1142		try {
1143			loader.spin( 'medium-left', color );
1144		} catch ( error ) { }
1145	}
1146
1147	// Generate our query vars.
1148	query = $.extend({
1149		action: 'infinite_scroll'
1150	}, this.query() );
1151
1152	// Inject Customizer state.
1153	if ( 'undefined' !== typeof wp && wp.customize && wp.customize.settings.theme ) {
1154		customized = {};
1155		query.wp_customize = 'on';
1156		query.theme = wp.customize.settings.theme.stylesheet;
1157		wp.customize.each( function( setting ) {
1158			if ( setting._dirty ) {
1159				customized[ setting.id ] = setting();
1160			}
1161		} );
1162		query.customized = JSON.stringify( customized );
1163		query.nonce = wp.customize.settings.nonce.preview;
1164	}
1165
1166	// Fire the ajax request.
1167	jqxhr = $.post( infiniteScroll.settings.ajaxurl, query );
1168
1169	// Allow refreshes to occur again if an error is triggered.
1170	jqxhr.fail( function() {
1171		if ( self.click_handle ) {
1172			loader.hide();
1173		}
1174
1175		self.ready = true;
1176	});
1177
1178	// Success handler
1179	jqxhr.done( function( response ) {
1180			// On success, let's hide the loader circle.
1181			if ( self.click_handle ) {
1182				loader.hide();
1183			}
1184
1185			// Check for and parse our response.
1186			if ( ! response || ! response.type ) {
1187				return;
1188			}
1189
1190			// If we've succeeded...
1191			if ( response.type == 'success' ) {
1192				// If additional scripts are required by the incoming set of posts, parse them
1193				if ( response.scripts ) {
1194					$( response.scripts ).each( function() {
1195						var elementToAppendTo = this.footer ? 'body' : 'head';
1196
1197						// Add script handle to list of those already parsed
1198						window.infiniteScroll.settings.scripts.push( this.handle );
1199
1200						// Output extra data, if present
1201						if ( this.extra_data ) {
1202							var data = document.createElement('script'),
1203								dataContent = document.createTextNode( "//<![CDATA[ \n" + this.extra_data + "\n//]]>" );
1204
1205							data.type = 'text/javascript';
1206							data.appendChild( dataContent );
1207
1208							document.getElementsByTagName( elementToAppendTo )[0].appendChild(data);
1209						}
1210
1211						// Build script tag and append to DOM in requested location
1212						var script = document.createElement('script');
1213						script.type = 'text/javascript';
1214						script.src = this.src;
1215						script.id = this.handle;
1216
1217						// If MediaElement.js is loaded in by this set of posts, don't initialize the players a second time as it breaks them all
1218						if ( 'wp-mediaelement' === this.handle ) {
1219							self.body.unbind( 'post-load', self.initializeMejs );
1220						}
1221
1222						if ( 'wp-mediaelement' === this.handle && 'undefined' === typeof mejs ) {
1223							self.wpMediaelement = {};
1224							self.wpMediaelement.tag = script;
1225							self.wpMediaelement.element = elementToAppendTo;
1226							setTimeout( self.maybeLoadMejs.bind( self ), 250 );
1227						} else {
1228							document.getElementsByTagName( elementToAppendTo )[0].appendChild(script);
1229						}
1230					} );
1231				}
1232
1233				// If additional stylesheets are required by the incoming set of posts, parse them
1234				if ( response.styles ) {
1235					$( response.styles ).each( function() {
1236						// Add stylesheet handle to list of those already parsed
1237						window.infiniteScroll.settings.styles.push( this.handle );
1238
1239						// Build link tag
1240						var style = document.createElement('link');
1241						style.rel = 'stylesheet';
1242						style.href = this.src;
1243						style.id = this.handle + '-css';
1244
1245						// Destroy link tag if a conditional statement is present and either the browser isn't IE, or the conditional doesn't evaluate true
1246						if ( this.conditional && ( ! isIE || ! eval( this.conditional.replace( /%ver/g, IEVersion ) ) ) )
1247							var style = false;
1248
1249						// Append link tag if necessary
1250						if ( style )
1251							document.getElementsByTagName('head')[0].appendChild(style);
1252					} );
1253				}
1254
1255				// stash the response in the page cache
1256				self.pageCache[self.page+self.offset] = response;
1257
1258				// Increment the page number
1259				self.page++;
1260
1261				// Record pageview in WP Stats, if available.
1262				if ( stats )
1263					new Image().src = document.location.protocol + 'https://pixel.wp.com/g.gif?' + stats + '&post=0&baba=' + Math.random();
1264
1265				// Add new posts to the postflair object
1266				if ( 'object' == typeof response.postflair && 'object' == typeof WPCOM_sharing_counts )
1267					WPCOM_sharing_counts = $.extend( WPCOM_sharing_counts, response.postflair );
1268
1269				// Render the results
1270				self.render.apply( self, arguments );
1271
1272				// If 'click' type and there are still posts to fetch, add back the handle
1273				if ( type == 'click' ) {
1274					if ( response.lastbatch ) {
1275						if ( self.click_handle ) {
1276							$( '#infinite-handle' ).remove();
1277							// Update body classes
1278							self.body.addClass( 'infinity-end' ).removeClass( 'infinity-success' );
1279						} else {
1280							self.body.trigger( 'infinite-scroll-posts-end' );
1281						}
1282					} else {
1283						if ( self.click_handle ) {
1284							self.element.append( self.handle );
1285						} else {
1286							self.body.trigger( 'infinite-scroll-posts-more' );
1287						}
1288					}
1289				} else if ( response.lastbatch ) {
1290					self.disabled = true;
1291					self.body.addClass( 'infinity-end' ).removeClass( 'infinity-success' );
1292				}
1293
1294				// Update currentday to the latest value returned from the server
1295				if ( response.currentday ) {
1296					self.currentday = response.currentday;
1297				}
1298
1299				// Fire Google Analytics pageview
1300				if ( self.google_analytics ) {
1301					var ga_url = self.history.path.replace( /%d/, self.page );
1302					if ( 'object' === typeof _gaq ) {
1303						_gaq.push( [ '_trackPageview', ga_url ] );
1304					}
1305					if ( 'function' === typeof ga ) {
1306						ga( 'send', 'pageview', ga_url );
1307					}
1308				}
1309			}
1310		});
1311
1312	return jqxhr;
1313};
1314
1315/**
1316 * Core's native media player uses MediaElement.js
1317 * 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.
1318 */
1319Scroller.prototype.maybeLoadMejs = function() {
1320	if ( null === this.wpMediaelement ) {
1321		return;
1322	}
1323
1324	if ( 'undefined' === typeof mejs ) {
1325		setTimeout( this.maybeLoadMejs, 250 );
1326	} else {
1327		document.getElementsByTagName( this.wpMediaelement.element )[0].appendChild( this.wpMediaelement.tag );
1328		this.wpMediaelement = null;
1329
1330		// Ensure any subsequent IS loads initialize the players
1331		this.body.bind( 'post-load', { self: this }, this.initializeMejs );
1332	}
1333}
1334
1335/**
1336 * Initialize the MediaElement.js player for any posts not previously initialized
1337 */
1338Scroller.prototype.initializeMejs = function( ev, response ) {
1339	// Are there media players in the incoming set of posts?
1340	if ( ! response.html || -1 === response.html.indexOf( 'wp-audio-shortcode' ) && -1 === response.html.indexOf( 'wp-video-shortcode' ) ) {
1341		return;
1342	}
1343
1344	// Don't bother if mejs isn't loaded for some reason
1345	if ( 'undefined' === typeof mejs ) {
1346		return;
1347	}
1348
1349	// Adapted from wp-includes/js/mediaelement/wp-mediaelement.js
1350	// Modified to not initialize already-initialized players, as Mejs doesn't handle that well
1351	$(function () {
1352		var settings = {};
1353
1354		if ( typeof _wpmejsSettings !== 'undefined' ) {
1355			settings.pluginPath = _wpmejsSettings.pluginPath;
1356		}
1357
1358		settings.success = function (mejs) {
1359			var autoplay = mejs.attributes.autoplay && 'false' !== mejs.attributes.autoplay;
1360			if ( 'flash' === mejs.pluginType && autoplay ) {
1361				mejs.addEventListener( 'canplay', function () {
1362					mejs.play();
1363				}, false );
1364			}
1365		};
1366
1367		$('.wp-audio-shortcode, .wp-video-shortcode').not( '.mejs-container' ).mediaelementplayer( settings );
1368	});
1369}
1370
1371/**
1372 * Trigger IS to load additional posts if the initial posts don't fill the window.
1373 * 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.
1374 */
1375Scroller.prototype.ensureFilledViewport = function() {
1376	var	self = this,
1377	   	windowHeight = self.window.height(),
1378	   	postsHeight = self.element.height(),
1379	   	aveSetHeight = 0,
1380	   	wrapperQty = 0;
1381
1382	// Account for situations where postsHeight is 0 because child list elements are floated
1383	if ( postsHeight === 0 ) {
1384		$( self.element.selector + ' > li' ).each( function() {
1385			postsHeight += $( this ).height();
1386		} );
1387
1388		if ( postsHeight === 0 ) {
1389			self.body.unbind( 'post-load', self.checkViewportOnLoad );
1390			return;
1391		}
1392	}
1393
1394	// Calculate average height of a set of posts to prevent more posts than needed from being loaded.
1395	$( '.' + self.wrapperClass ).each( function() {
1396		aveSetHeight += $( this ).height();
1397		wrapperQty++;
1398	} );
1399
1400	if ( wrapperQty > 0 )
1401		aveSetHeight = aveSetHeight / wrapperQty;
1402	else
1403		aveSetHeight = 0;
1404
1405	// Load more posts if space permits, otherwise stop checking for a full viewport
1406	if ( postsHeight < windowHeight && ( postsHeight + aveSetHeight < windowHeight ) ) {
1407		self.ready = true;
1408		self.refresh();
1409	}
1410	else {
1411		self.body.unbind( 'post-load', self.checkViewportOnLoad );
1412	}
1413}
1414
1415/**
1416 * Event handler for ensureFilledViewport(), tied to the post-load trigger.
1417 * 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.
1418 */
1419Scroller.prototype.checkViewportOnLoad = function( ev ) {
1420	ev.data.self.ensureFilledViewport();
1421}
1422
1423function fullscreenState() {
1424	return document.fullscreenElement || document.mozFullScreenElement || document.webkitFullscreenElement || document.msFullscreenElement
1425		? 1
1426		: 0;
1427}
1428
1429var previousFullScrenState = fullscreenState();
1430
1431/**
1432 * Identify archive page that corresponds to majority of posts shown in the current browser window.
1433 */
1434Scroller.prototype.determineURL = function () {
1435	var self         = this,
1436		windowTop    = $( window ).scrollTop(),
1437		windowBottom = windowTop + $( window ).height(),
1438		windowSize   = windowBottom - windowTop,
1439		setsInView   = [],
1440		setsHidden   = [],
1441		pageNum      = false,
1442		currentFullScreenState = fullscreenState();
1443
1444	// xor - check if the state has changed
1445	if ( previousFullScrenState ^ currentFullScreenState ) {
1446		// If we just switched to/from fullscreen,
1447		// don't do the div clearing/caching or the
1448		// URL setting. Doing so can break video playback
1449		// if the video goes to fullscreen.
1450
1451		previousFullScrenState = currentFullScreenState;
1452		return;
1453	}
1454	previousFullScrenState = currentFullScreenState;
1455
1456	// Find out which sets are in view
1457	$( '.' + self.wrapperClass ).each( function() {
1458		var id         = $( this ).attr( 'id' ),
1459			setTop     = $( this ).offset().top,
1460			setHeight  = $( this ).outerHeight( false ),
1461			setBottom  = 0,
1462			setPageNum = $( this ).data( 'page-num' );
1463
1464		// Account for containers that have no height because their children are floated elements.
1465		if ( 0 === setHeight ) {
1466			$( '> *', this ).each( function() {
1467				setHeight += $( this ).outerHeight( false );
1468			} );
1469		}
1470
1471		// Determine position of bottom of set by adding its height to the scroll position of its top.
1472		setBottom = setTop + setHeight;
1473
1474		// Populate setsInView object. While this logic could all be combined into a single conditional statement, this is easier to understand.
1475		if ( setTop < windowTop && setBottom > windowBottom ) { // top of set is above window, bottom is below
1476			setsInView.push({'id': id, 'top': setTop, 'bottom': setBottom, 'pageNum': setPageNum });
1477		}
1478		else if( setTop > windowTop && setTop < windowBottom ) { // top of set is between top (gt) and bottom (lt)
1479			setsInView.push({'id': id, 'top': setTop, 'bottom': setBottom, 'pageNum': setPageNum });
1480		}
1481		else if( setBottom > windowTop && setBottom < windowBottom ) { // bottom of set is between top (gt) and bottom (lt)
1482			setsInView.push({'id': id, 'top': setTop, 'bottom': setBottom, 'pageNum': setPageNum });
1483		} else {
1484			setsHidden.push({'id': id, 'top': setTop, 'bottom': setBottom, 'pageNum': setPageNum });
1485		}
1486	} );
1487
1488	$.each(setsHidden, function() {
1489		var $set = $('#' + this.id);
1490		if( $set.hasClass( 'is--replaced' ) ) {
1491			return;
1492		}
1493
1494	        self.pageCache[ this.pageNum].html = $set.html();
1495
1496		$set.css('min-height', ( this.bottom - this.top ) + 'px' )
1497		    .addClass('is--replaced')
1498		    .empty();
1499	});
1500
1501	$.each(setsInView, function() {
1502		var $set = $('#' + this.id);
1503
1504		if( $set.hasClass('is--replaced') ) {
1505			$set.css('min-height', '').removeClass('is--replaced');
1506			if( this.pageNum in self.pageCache ) {
1507				$set.html( self.pageCache[this.pageNum].html );
1508		        	self.body.trigger( 'post-load', self.pageCache[this.pageNum] );
1509			}
1510		}
1511
1512	});
1513
1514	// 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.
1515	if ( 0 == setsInView.length ) {
1516		pageNum = -1;
1517	}
1518	else if ( 1 == setsInView.length ) {
1519		var setData = setsInView.pop();
1520
1521		// 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
1522		if ( ( ( windowBottom - setData.top ) / windowSize ) < 0.5 )
1523			pageNum = -1;
1524		else
1525			pageNum = setData.pageNum;
1526	}
1527	else {
1528		var majorityPercentageInView = 0;
1529
1530		// Identify the IS set that comprises the majority of the current window and set the URL to it.
1531		$.each( setsInView, function( i, setData ) {
1532			var topInView     = 0,
1533				bottomInView  = 0,
1534				percentOfView = 0;
1535
1536			// Figure percentage of view the current set represents
1537			if ( setData.top > windowTop && setData.top < windowBottom )
1538				topInView = ( windowBottom - setData.top ) / windowSize;
1539
1540			if ( setData.bottom > windowTop && setData.bottom < windowBottom )
1541				bottomInView = ( setData.bottom - windowTop ) / windowSize;
1542
1543			// Figure out largest percentage of view for current set
1544			if ( topInView >= bottomInView )
1545				percentOfView = topInView;
1546			else if ( bottomInView >= topInView )
1547				percentOfView = bottomInView;
1548
1549			// Does current set's percentage of view supplant the largest previously-found set?
1550			if ( percentOfView > majorityPercentageInView ) {
1551				pageNum = setData.pageNum;
1552				majorityPercentageInView = percentOfView;
1553			}
1554		} );
1555	}
1556
1557	// If a page number could be determined, update the URL
1558	// -1 indicates that the original requested URL should be used.
1559	if ( 'number' == typeof pageNum ) {
1560		self.updateURL( pageNum );
1561	}
1562}
1563
1564/**
1565 * Update address bar to reflect archive page URL for a given page number.
1566 * Checks if URL is different to prevent pollution of browser history.
1567 */
1568Scroller.prototype.updateURL = function( page ) {
1569	// IE only supports pushState() in v10 and above, so don't bother if those conditions aren't met.
1570	if ( ! window.history.pushState ) {
1571		return;
1572	}
1573	var self = this,
1574		pageSlug = -1 == page ? self.origURL : window.location.protocol + '//' + self.history.host + self.history.path.replace( /%d/, page ) + self.history.parameters;
1575
1576	if ( window.location.href != pageSlug ) {
1577		history.pushState( null, null, pageSlug );
1578	}
1579}
1580
1581/**
1582 * Pause scrolling.
1583 */
1584Scroller.prototype.pause = function() {
1585	this.disabled = true;
1586};
1587
1588/**
1589 * Resume scrolling.
1590 */
1591Scroller.prototype.resume = function() {
1592	this.disabled = false;
1593};
1594
1595/**
1596 * Ready, set, go!
1597 */
1598$( document ).ready( function() {
1599	// Check for our variables
1600	if ( 'object' != typeof infiniteScroll )
1601		return;
1602
1603	$( document.body ).addClass( infiniteScroll.settings.body_class );
1604
1605	// Set ajaxurl (for brevity)
1606	ajaxurl = infiniteScroll.settings.ajaxurl;
1607
1608	// Set stats, used for tracking stats
1609	stats = infiniteScroll.settings.stats;
1610
1611	// Define what type of infinity we have, grab text for click-handle
1612	type  = infiniteScroll.settings.type;
1613	text  = infiniteScroll.settings.text;
1614	totop = infiniteScroll.settings.totop;
1615
1616	// Initialize the scroller (with the ID of the element from the theme)
1617	infiniteScroll.scroller = new Scroller( infiniteScroll.settings );
1618
1619	/**
1620	 * Monitor user scroll activity to update URL to correspond to archive page for current set of IS posts
1621	 */
1622    if( type == 'click' ) {
1623        var timer = null;
1624        $( window ).bind( 'scroll', function() {
1625            // run the real scroll handler once every 250 ms.
1626            if ( timer ) { return; }
1627            timer = setTimeout( function() {
1628                infiniteScroll.scroller.determineURL();
1629                timer = null;
1630            } , 250 );
1631        });
1632    }
1633
1634	// Integrate with Selective Refresh in the Customizer.
1635	if ( 'undefined' !== typeof wp && wp.customize && wp.customize.selectiveRefresh ) {
1636
1637		/**
1638		 * Handle rendering of selective refresh partials.
1639		 *
1640		 * Make sure that when a partial is rendered, the Jetpack post-load event
1641		 * will be triggered so that any dynamic elements will be re-constructed,
1642		 * such as ME.js elements, Photon replacements, social sharing, and more.
1643		 * Note that this is applying here not strictly to posts being loaded.
1644		 * If a widget contains a ME.js element and it is previewed via selective
1645		 * refresh, the post-load would get triggered allowing any dynamic elements
1646		 * therein to also be re-constructed.
1647		 *
1648		 * @param {wp.customize.selectiveRefresh.Placement} placement
1649		 */
1650		wp.customize.selectiveRefresh.bind( 'partial-content-rendered', function( placement ) {
1651			var content;
1652			if ( 'string' === typeof placement.addedContent ) {
1653				content = placement.addedContent;
1654			} else if ( placement.container ) {
1655				content = $( placement.container ).html();
1656			}
1657
1658			if ( content ) {
1659				$( document.body ).trigger( 'post-load', { html: content } );
1660			}
1661		} );
1662
1663		/*
1664		 * Add partials for posts added via infinite scroll.
1665		 *
1666		 * This is unnecessary when MutationObserver is supported by the browser
1667		 * since then this will be handled by Selective Refresh in core.
1668		 */
1669		if ( 'undefined' === typeof MutationObserver ) {
1670			$( document.body ).on( 'post-load', function( e, response ) {
1671				var rootElement = null;
1672				if ( response.html && -1 !== response.html.indexOf( 'data-customize-partial' ) ) {
1673					if ( infiniteScroll.settings.id ) {
1674						rootElement = $( '#' + infiniteScroll.settings.id );
1675					}
1676					wp.customize.selectiveRefresh.addPartials( rootElement );
1677				}
1678			} );
1679		}
1680	}
1681});
1682
1683
1684})(jQuery); // Close closure
1685;
1686/* jshint ignore:start */
1687!function(d,s,id){
1688	var js,fjs=d.getElementsByTagName(s)[0],p=/^http:/.test(d.location)?'http':'https';
1689	if(!d.getElementById(id)){
1690		js=d.createElement(s);
1691		js.id=id;js.src=p+"://platform.twitter.com/widgets.js";
1692		fjs.parentNode.insertBefore(js,fjs);
1693	}
1694}(document,"script","twitter-wjs");
1695/* jshint ignore:end */
1696;
1697/***
1698 * Warning: This file is remotely enqueued in Jetpack's Masterbar module.
1699 * Changing it will also affect Jetpack sites.
1700 */
1701jQuery( document ).ready( function( $, wpcom ) {
1702	var masterbar,
1703		menupops = $( 'li#wp-admin-bar-blog.menupop, li#wp-admin-bar-newdash.menupop, li#wp-admin-bar-my-account.menupop' ),
1704		newmenu = $( '#wp-admin-bar-new-post-types' );
1705
1706	// Unbind hoverIntent, we want clickable menus.
1707	menupops
1708		.unbind( 'mouseenter mouseleave' )
1709		.removeProp( 'hoverIntent_t' )
1710		.removeProp( 'hoverIntent_s' )
1711		.on( 'mouseover', function(e) {
1712			var li = $(e.target).closest( 'li.menupop' );
1713			menupops.not(li).removeClass( 'ab-hover' );
1714			li.toggleClass( 'ab-hover' );
1715		} )
1716		.on( 'click touchstart', function(e) {
1717			var $target = $( e.target );
1718
1719			if ( masterbar.focusSubMenus( $target ) ) {
1720				return;
1721			}
1722
1723			e.preventDefault();
1724			masterbar.toggleMenu( $target );
1725		} );
1726
1727	masterbar = {
1728		focusSubMenus: function( $target ) {
1729			// Handle selection of menu items
1730			if ( ! $target.closest( 'ul' ).hasClass( 'ab-top-menu' ) ) {
1731				$target
1732					.closest( 'li' );
1733
1734				return true;
1735			}
1736
1737			return false;
1738		},
1739
1740		toggleMenu: function( $target ) {
1741			var $li = $target.closest( 'li.menupop' ),
1742				$html = $( 'html' );
1743
1744			$( 'body' ).off( 'click.ab-menu' );
1745			$( '#wpadminbar li.menupop' ).not($li).removeClass( 'ab-active wpnt-stayopen wpnt-show' );
1746
1747			if ( $li.hasClass( 'ab-active' ) ) {
1748				$li.removeClass( 'ab-active' );
1749				$html.removeClass( 'ab-menu-open' );
1750			} else {
1751				$li.addClass( 'ab-active' );
1752				$html.addClass( 'ab-menu-open' );
1753
1754				$( 'body' ).on( 'click.ab-menu', function( e ) {
1755					if ( ! $( e.target ).parents( '#wpadminbar' ).length ) {
1756						e.preventDefault();
1757						masterbar.toggleMenu( $li );
1758						$( 'body' ).off( 'click.ab-menu' );
1759					}
1760				} );
1761			}
1762		}
1763	};
1764} );;
1765/*globals JSON */
1766( function( $ ) {
1767	var eventName = 'wpcom_masterbar_click';
1768
1769	var linksTracksEvents = {
1770		//top level items
1771		'wp-admin-bar-blog'                        : 'my_sites',
1772		'wp-admin-bar-newdash'                     : 'reader',
1773		'wp-admin-bar-ab-new-post'                 : 'write_button',
1774		'wp-admin-bar-my-account'                  : 'my_account',
1775		'wp-admin-bar-notes'                       : 'notifications',
1776		//my sites - top items
1777		'wp-admin-bar-switch-site'                 : 'my_sites_switch_site',
1778		'wp-admin-bar-blog-info'                   : 'my_sites_site_info',
1779		'wp-admin-bar-site-view'                   : 'my_sites_view_site',
1780		'wp-admin-bar-blog-stats'                  : 'my_sites_site_stats',
1781		'wp-admin-bar-plan'                        : 'my_sites_plan',
1782		'wp-admin-bar-plan-badge'                  : 'my_sites_plan_badge',
1783		//my sites - manage
1784		'wp-admin-bar-edit-page'                   : 'my_sites_manage_site_pages',
1785		'wp-admin-bar-new-page-badge'              : 'my_sites_manage_add_page',
1786		'wp-admin-bar-edit-post'                   : 'my_sites_manage_blog_posts',
1787		'wp-admin-bar-new-post-badge'              : 'my_sites_manage_add_post',
1788		'wp-admin-bar-edit-attachment'             : 'my_sites_manage_media',
1789		'wp-admin-bar-new-attachment-badge'        : 'my_sites_manage_add_media',
1790		'wp-admin-bar-comments'                    : 'my_sites_manage_comments',
1791		'wp-admin-bar-edit-jetpack-testimonial'    : 'my_sites_manage_testimonials',
1792		'wp-admin-bar-new-jetpack-testimonial'     : 'my_sites_manage_add_testimonial',
1793		'wp-admin-bar-edit-jetpack-portfolio'      : 'my_sites_manage_portfolio',
1794		'wp-admin-bar-new-jetpack-portfolio'       : 'my_sites_manage_add_portfolio',
1795		//my sites - personalize
1796		'wp-admin-bar-themes'                      : 'my_sites_personalize_themes',
1797		'wp-admin-bar-cmz'                         : 'my_sites_personalize_themes_customize',
1798		//my sites - configure
1799		'wp-admin-bar-sharing'                     : 'my_sites_configure_sharing',
1800		'wp-admin-bar-people'                      : 'my_sites_configure_people',
1801		'wp-admin-bar-people-add'                  : 'my_sites_configure_people_add_button',
1802		'wp-admin-bar-plugins'                     : 'my_sites_configure_plugins',
1803		'wp-admin-bar-domains'                     : 'my_sites_configure_domains',
1804		'wp-admin-bar-domains-add'                 : 'my_sites_configure_add_domain',
1805		'wp-admin-bar-blog-settings'               : 'my_sites_configure_settings',
1806		'wp-admin-bar-legacy-dashboard'            : 'my_sites_configure_wp_admin',
1807		//reader
1808		'wp-admin-bar-followed-sites'              : 'reader_followed_sites',
1809		'wp-admin-bar-reader-followed-sites-manage': 'reader_manage_followed_sites',
1810		'wp-admin-bar-discover-discover'           : 'reader_discover',
1811		'wp-admin-bar-discover-search'             : 'reader_search',
1812		'wp-admin-bar-my-activity-my-likes'        : 'reader_my_likes',
1813		//account
1814		'wp-admin-bar-user-info'                   : 'my_account_user_name',
1815		// account - profile
1816		'wp-admin-bar-my-profile'                  : 'my_account_profile_my_profile',
1817		'wp-admin-bar-account-settings'            : 'my_account_profile_account_settings',
1818		'wp-admin-bar-billing'                     : 'my_account_profile_manage_purchases',
1819		'wp-admin-bar-security'                    : 'my_account_profile_security',
1820		'wp-admin-bar-notifications'               : 'my_account_profile_notifications',
1821		//account - special
1822		'wp-admin-bar-get-apps'                    : 'my_account_special_get_apps',
1823		'wp-admin-bar-next-steps'                  : 'my_account_special_next_steps',
1824		'wp-admin-bar-help'                        : 'my_account_special_help',
1825	};
1826
1827	var notesTracksEvents = {
1828		openSite: function( data ) {
1829			return {
1830				clicked: 'masterbar_notifications_panel_site',
1831				site_id: data.siteId
1832			};
1833		},
1834		openPost: function( data ) {
1835			return {
1836				clicked: 'masterbar_notifications_panel_post',
1837				site_id: data.siteId,
1838				post_id: data.postId
1839			};
1840		},
1841		openComment: function( data ) {
1842			return {
1843				clicked: 'masterbar_notifications_panel_comment',
1844				site_id: data.siteId,
1845				post_id: data.postId,
1846				comment_id: data.commentId
1847			};
1848		}
1849	};
1850
1851	function recordTracksEvent( eventProps ) {
1852		eventProps = eventProps || {};
1853		window._tkq = window._tkq || [];
1854		window._tkq.push( [ 'recordEvent', eventName, eventProps ] );
1855	}
1856
1857	function parseJson( s, defaultValue ) {
1858		try {
1859			return JSON.parse( s );
1860		} catch ( e ) {
1861			return defaultValue;
1862		}
1863	}
1864
1865	$( document ).ready( function() {
1866		var trackableLinks = '.mb-trackable .ab-item:not(div),' +
1867			'#wp-admin-bar-notes .ab-item,' +
1868			'#wp-admin-bar-user-info .ab-item,' +
1869			'.mb-trackable .ab-secondary';
1870
1871		$( trackableLinks ).on( 'click touchstart', function( e ) {
1872			var $target = $( e.target ),
1873				$parent = $target.closest( 'li' );
1874
1875			if ( ! $parent ) {
1876				return;
1877			}
1878
1879			var trackingId = $target.attr( 'ID' ) || $parent.attr( 'ID' );
1880
1881			if ( ! linksTracksEvents.hasOwnProperty( trackingId ) ) {
1882				return;
1883			}
1884
1885			var eventProps = { 'clicked': linksTracksEvents[ trackingId ] };
1886
1887			recordTracksEvent( eventProps );
1888		} );
1889	} );
1890
1891	// listen for postMessage events from the notifications iframe
1892	$( window ).on( 'message', function( e ) {
1893		var event = ! e.data && e.originalEvent.data ? e.originalEvent : e;
1894		if ( event.origin !== 'https://widgets.wp.com' ) {
1895			return;
1896		}
1897
1898		var data = ( 'string' === typeof event.data ) ? parseJson( event.data, {} ) : event.data;
1899		if ( 'notesIframeMessage' !== data.type ) {
1900			return;
1901		}
1902
1903		var eventData = notesTracksEvents[ data.action ];
1904		if ( ! eventData ) {
1905			return;
1906		}
1907
1908		recordTracksEvent( eventData( data ) );
1909	} );
1910
1911} )( jQuery );
1912;
1913var wpcom = window.wpcom || {};
1914wpcom.actionbar = {};
1915wpcom.actionbar.data = actionbardata;
1916
1917// This might be better in another file, but is here for now
1918(function($){
1919	var fbd = wpcom.actionbar.data,
1920			d = document,
1921			docHeight = $( d ).height(),
1922			b = d.getElementsByTagName( 'body' )[0],
1923			lastScrollTop = 0,
1924			lastScrollDir, fb, fhtml, fbhtml, fbHtmlLi,
1925			followingbtn, followbtn, fbdf, action,
1926			slkhtml = '', foldhtml = '', reporthtml = '',
1927			customizeIcon, editIcon, statsIcon, themeHtml = '', signupHtml = '', loginHtml = '',
1928			viewReaderHtml = '', editSubsHtml = '', editFollowsHtml = '',
1929			toggleactionbar, $actionbar;
1930
1931	// Don't show actionbar when iframed
1932	if ( window != window.top ) {
1933		return;
1934	}
1935
1936	fhtml = '<ul>';
1937
1938	// Customize Icon
1939	customizeIcon = '<svg class="gridicon gridicon__customize" height="20px" width="20px" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"><g><path d="M2 6c0-1.505.78-3.08 2-4 0 .845.69 2 2 2 1.657 0 3 1.343 3 3 0 .386-.08.752-.212 1.09.74.594 1.476 1.19 2.19 1.81L8.9 11.98c-.62-.716-1.214-1.454-1.807-2.192C6.753 9.92 6.387 10 6 10c-2.21 0-4-1.79-4-4zm12.152 6.848l1.34-1.34c.607.304 1.283.492 2.008.492 2.485 0 4.5-2.015 4.5-4.5 0-.725-.188-1.4-.493-2.007L18 9l-2-2 3.507-3.507C18.9 3.188 18.225 3 17.5 3 15.015 3 13 5.
1939015 13 7.5c0 .725.188 1.4.493 2.007L3 20l2 2 6.848-6.848c1.885 1.928 3.874 3.753 5.977 5.45l1.425 1.148 1.5-1.5-1.15-1.425c-1.695-2.103-3.52-4.092-5.448-5.977z" data-reactid=".2.1.1:0.1b.0"></path></g></svg>';
1940
1941	if ( fbd.canCustomizeSite && fbd.isLoggedIn ) {
1942		fhtml += '<li class="actnbr-btn actnbr-customize"><a href="'+ fbd.customizeLink +'">' + customizeIcon + '<span>' + fbd.i18n.customize + '<span></a></li>';
1943	}
1944
1945	// Edit Icon
1946	editIcon = '<svg class="gridicon gridicon__pencil" height="20px" width="20px" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"><g><path d="M13 6l5 5-9.507 9.507c-.686-.686-.69-1.794-.012-2.485l-.002-.003c-.69.676-1.8.673-2.485-.013-.677-.677-.686-1.762-.036-2.455l-.008-.008c-.694.65-1.78.64-2.456-.036L13 6zm7.586-.414l-2.172-2.172c-.78-.78-2.047-.78-2.828 0L14 5l5 5 1.586-1.586c.78-.78.78-2.047 0-2.828zM3 18v3h3c0-1.657-1.343-3-3-3z"></path></g></svg>';
1947
1948	if ( fbd.canEditPost ) {
1949		fhtml += '<li class="actnbr-btn actnbr-edit"><a href="'+ fbd.editLink +'">' + editIcon + '<span>' + fbd.i18n.edit + '</span></a></li>';
1950	}
1951
1952	// Stats Icon
1953	statsIcon = '<svg class="gridicon gridicon__stats-alt" height="20px" width="20px" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"><g><path d="M21,21H3v-2h18V21z M8,10H4v7h4V10z M14,3h-4v14h4V3z M20,6h-4v11h4V6z"/></path></g></svg>';
1954
1955	if ( fbd.canEditPost ) {
1956		fhtml += '<li class="actnbr-btn actnbr-stats"><a href="'+ fbd.statsLink +'">' + statsIcon + '<span>' + fbd.i18n.stats + '</span></a></li>';
1957	}
1958
1959	// Follow/Unfollow Icon
1960	followingbtn = '<svg class="gridicon gridicon__following" height="24px" width="24px" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"><g><path d="M23 13.482L15.508 21 12 17.4l1.412-1.388 2.106 2.188 6.094-6.094L23 13.482zm-7.455 1.862L20 10.89V2H2v14c0 1.1.9 2 2 2h4.538l4.913-4.832 2.095 2.176zM8 13H4v-1h4v1zm3-2H4v-1h7v1zm0-2H4V8h7v1zm7-3H4V4h14v2z"/></g></svg>';
1961	followbtn = '<svg class="gridicon gridicon__follow" height="24px" width="24px" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"><g><path d="M23 16v2h-3v3h-2v-3h-3v-2h3v-3h2v3h3zM20 2v9h-4v3h-3v4H4c-1.1 0-2-.9-2-2V2h18zM8 13v-1H4v1h4zm3-3H4v1h7v-1zm0-2H4v1h7V8zm7-4H4v2h14V4z"/></g></svg>';
1962
1963	fbhtml = '<a class="actnbr-action actnbr-actn-follow" href="">' + followbtn + '<span>' + fbd.i18n.follow + '</span></a>';
1964	if ( fbd.isFollowing ) {
1965		fbhtml = '<a class="actnbr-action actnbr-actn-following" href="">' + followingbtn + '<span>' + fbd.i18n.following + '</span></a>';
1966	}
1967
1968	// Show follow/unfollow icon on top level, when this is not your own site
1969	if ( fbd.canFollow && ! ( fbd.canEditPost || fbd.canCustomizeSite ) ) {
1970		fhtml += '<li class="actnbr-btn actnbr-hidden"> \
1971			    	' + fbhtml + ' \
1972			    	<div class="actnbr-popover tip tip-top-left actnbr-notice"> \
1973			    		<div class="tip-arrow"></div> \
1974			    		<div class="tip-inner actnbr-follow-bubble"></div> \
1975			    	</div> \
1976			    </li>';
1977	}
1978
1979	if ( ! fbd.canCustomizeSite ) {
1980		// Report Link
1981		reporthtml = '<li class="flb-report"><a href="http://en.wordpress.com/abuse/">' + fbd.i18n.report + '</a></li>';
1982	}
1983
1984	// Show shortlink on single posts
1985	if ( fbd.isSingular ) {
1986		slkhtml = '<li class="actnbr-shortlink"><a href="' + fbd.shortlink + '">' + fbd.i18n.shortlink + '</a></li>'
1987	}
1988
1989	// Set up fold/unfold menu item
1990	foldhtml = '<li class="actnbr-fold"><a href="">' + fbd.i18n.foldBar + '</a></li>'
1991	if ( fbd.isFolded ) {
1992		foldhtml = '<li class="actnbr-fold"><a href="">' + fbd.i18n.unfoldBar + '</a></li>'
1993	}
1994	if ( ! fbd.isLoggedIn && ! fbd.canFollow ) {
1995		foldhtml = '';
1996	}
1997
1998	if ( fbd.isLoggedIn ) {
1999		if ( '' != fbd.themeURL ) {
2000			themeHtml = '<li class="actnbr-theme"><a href="' + fbd.themeURL + '">' + fbd.i18n.themeInfo.replace( /{theme}/, fbd.themeName ) + '</a></li>';
2001		}
2002		if ( fbd.canFollow ) {
2003			if ( fbd.isSingular ) {
2004				viewReaderHtml = '<li class="actnbr-reader"><a href="https://wordpress.com/read/blogs/' + fbd.siteID + '/posts/' + fbd.postID +'">' + fbd.i18n.viewReadPost + '</a></li>';
2005			} else {
2006				viewReaderHtml = '<li class="actnbr-reader"><a href="https://wordpress.com/read/' + ( fbd.feedID ? 'feeds/' + fbd.feedID : 'blogs/' + fbd.siteID ) + '">' + fbd.i18n.viewReader + '</a></li>';
2007			}
2008		}
2009		editFollowsHtml = '<li class="actnbr-follows"><a href="https://wordpress.com/following/edit">' + fbd.i18n.editSubs + '</a></li>';
2010	} else {
2011		loginHtml += '<li class="actnbr-login"><a href="' + fbd.loginURL + '">' + fbd.i18n.login + '</a></li>';
2012		signupHtml = '<li class="actnbr-signup"><a href="' + fbd.signupURL + '">' + fbd.i18n.signup + '</a></li>';
2013		editSubsHtml = '<li class="actnbr-subs"><a href="https://subscribe.wordpress.com/">' + fbd.i18n.editSubs + '</a></li>';
2014	}
2015
2016	// Hide follow/unfollow completely for static sites, and sites not allowing followers (VIP, private, etc).
2017	if ( ! fbd.canFollow ) {
2018		fbHtmlLi = '';
2019	} else {
2020		fbHtmlLi = '<li class="actnbr-folded-follow">' + fbhtml + '</li>';
2021	}
2022
2023	// Ellipsis Menu
2024	fhtml += '<li class="actnbr-ellipsis actnbr-hidden"> \
2025			  <svg class="gridicon gridicon__ellipsis" height="24" width="24" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"><g><circle cx="5" cy="12" r="2"/><circle cx="19" cy="12" r="2"/><circle cx="12" cy="12" r="2"/></g></svg> \
2026			  <div class="actnbr-popover tip tip-top-left actnbr-more"> \
2027			  	<div class="tip-arrow"></div> \
2028			  	<div class="tip-inner"> \
2029				  <ul> \
2030				    <li class="actnbr-sitename"><a href="' + fbd.siteURL + '">' + fbd.icon + ' ' + actionBarEscapeHtml( fbd.siteName ) + '</a></li> \
2031				   	<li class="actnbr-folded-customize"><a href="'+ fbd.customizeLink +'">' + customizeIcon + '<span>' + fbd.i18n.customize + '<span></a></li> \
2032				    ' + fbHtmlLi + ' \
2033					' + signupHtml + ' \
2034				    ' + loginHtml + ' \
2035				    ' + themeHtml + ' \
2036				    ' + slkhtml + ' \
2037				    ' + reporthtml + ' \
2038				    ' + viewReaderHtml + ' \
2039				    ' + editFollowsHtml + ' \
2040				    ' + editSubsHtml + ' \
2041				    ' + foldhtml + ' \
2042			      </ul> \
2043			    </div> \
2044		      </div> \
2045		    </li> \
2046	      </ul>';
2047
2048	fbdf = d.createElement( 'div' );
2049	fbdf.id = 'actionbar';
2050	fbdf.innerHTML = fhtml;
2051	b.appendChild( fbdf );
2052
2053	$actionbar = $( '#actionbar' ).addClass( 'actnbr-' + fbd.themeSlug.replace( '/', '-' ) );
2054
2055	// Add classes based on contents
2056	if ( fbd.canCustomizeSite ) {
2057		$actionbar.addClass( 'actnbr-has-customize' );
2058	}
2059
2060	if ( fbd.canEditPost ) {
2061		$actionbar.addClass( 'actnbr-has-edit' );
2062	}
2063
2064	if ( ! fbd.canCustomizeSite ) {
2065		$actionbar.addClass( 'actnbr-has-follow' );
2066	}
2067
2068	if ( fbd.isFolded ) {
2069		$actionbar.addClass( 'actnbr-folded' );
2070	}
2071
2072	// Show status message if available
2073	if ( fbd.statusMessage ) {
2074		showActionBarStatusMessage( fbd.statusMessage );
2075	}
2076
2077	// *** Actions *****************
2078
2079	// Follow Site
2080	$actionbar.on(  'click', '.actnbr-actn-follow', function(e) {
2081		e.preventDefault();
2082
2083		if ( fbd.isLoggedIn ) {
2084			showActionBarStatusMessage( '<div class="actnbr-reader">' + fbd.i18n.followedText + '</div>' );
2085			bumpStat( 'followed' );
2086			var eventProps = {
2087				'follow_source': 'actionbar',
2088				'url': fbd.siteURL
2089			};
2090			recordTracksEvent( 'wpcom_actionbar_site_followed', eventProps );
2091
2092			request( 'ab_subscribe_to_blog' );
2093		} else {
2094			showActionBarFollowForm();
2095		}
2096	} )
2097
2098	// UnFollow Site
2099	.on(  'click', '.actnbr-actn-following', function(e) {
2100		e.preventDefault();
2101		$( '#actionbar .actnbr-actn-following' ).replaceWith( '<a class="actnbr-action actnbr-actn-follow" href="">' + followbtn + '<span>' + fbd.i18n.follow + '</span></a>' );
2102
2103		bumpStat( 'unfollowed' );
2104		var eventProps = {
2105			'follow_source': 'actionbar',
2106			'url': fbd.siteURL
2107		};
2108		recordTracksEvent( 'wpcom_actionbar_site_unfollowed', eventProps );
2109
2110		request( 'ab_unsubscribe_from_blog' );
2111	} )
2112
2113	// Show shortlink prompt
2114	.on( 'click', '.actnbr-shortlink a', function(e) {
2115		e.preventDefault();
2116		window.prompt( "Shortlink: ", fbd.shortlink );
2117	} )
2118
2119	// Toggle more menu
2120	.on( 'click', '.actnbr-ellipsis', function(e) {
2121		if ( $( e.target ).closest( 'a' ).hasClass( 'actnbr-action' ) ) {
2122			return false;
2123		}
2124
2125		var popoverLi = $( '#actionbar .actnbr-ellipsis' );
2126		popoverLi.toggleClass( 'actnbr-hidden' );
2127
2128		setTimeout( function() {
2129			if ( ! popoverLi.hasClass( 'actnbr-hidden' ) ) {
2130				bumpStat( 'show_more_menu' );
2131
2132				$( document ).on( 'click.actnbr-body-click', function() {
2133					popoverLi.addClass( 'actnbr-hidden' );
2134
2135					$( document ).off( 'click.actnbr-body-click' );
2136				} );
2137			}
2138		}, 10 );
2139	})
2140
2141	// Fold/Unfold
2142	.on( 'click', '.actnbr-fold', function(e) {
2143		e.preventDefault();
2144
2145		if ( $( '#actionbar' ).hasClass( 'actnbr-folded' ) ) {
2146			$( '.actnbr-fold a' ).html( fbd.i18n.foldBar );
2147			$( '#actionbar' ).removeClass( 'actnbr-folded' );
2148
2149			$.post( fbd.xhrURL, { 'action': 'unfold_actionbar' } );
2150		} else {
2151			$( '.actnbr-fold a' ).html( fbd.i18n.unfoldBar );
2152			$( '#actionbar' ).addClass( 'actnbr-folded' );
2153
2154			$.post( fbd.xhrURL, { 'action': 'fold_actionbar' } );
2155		}
2156	})
2157
2158	// Record stats for clicks
2159	.on( 'click', '.actnbr-sitename a', createStatsBumperEventHandler( 'clicked_site_title' ) )
2160	.on( 'click', '.actnbr-customize a', createStatsBumperEventHandler( 'customized' ) )
2161	.on( 'click', '.actnbr-folded-customize a', createStatsBumperEventHandler( 'customized' ) )
2162	.on( 'click', '.actnbr-theme a', createStatsBumperEventHandler( 'explored_theme' ) )
2163	.on( 'click', '.actnbr-edit a', createStatsBumperEventHandler( 'edited' ) )
2164	.on( 'click', '.actnbr-stats a', createStatsBumperEventHandler( 'clicked_stats' ) )
2165	.on( 'click', '.flb-report a', createStatsBumperEventHandler( 'reported_content' ) )
2166	.on( 'click', '.actnbr-follows a', createStatsBumperEventHandler( 'managed_following' ) )
2167	.on( 'click', '.actnbr-shortlink a', function() {
2168		bumpStat( 'copied_shortlink' );
2169	} )
2170	.on( 'click', '.actnbr-reader a', createStatsBumperEventHandler( 'view_reader' ) )
2171	.on( 'submit', '.actnbr-follow-bubble form', createStatsBumperEventHandler( 'submit_follow_form', function() {
2172		$( '#actionbar .actnbr-follow-bubble form button' ).attr( 'disabled', true );
2173	} ) )
2174	.on( 'click', '.actnbr-login-nudge a', createStatsBumperEventHandler( 'clicked_login_nudge' ) )
2175	.on( 'click', '.actnbr-signup a', createStatsBumperEventHandler( 'clicked_signup_link' ) )
2176	.on( 'click', '.actnbr-login a', createStatsBumperEventHandler( 'clicked_login_link' ) )
2177	.on( 'click', '.actnbr-subs a', createStatsBumperEventHandler( 'clicked_manage_subs_link' ) );
2178
2179	// Make Follow/Unfollow requests
2180	var request = function( action ) {
2181		$.post( fbd.xhrURL, {
2182			'action': action,
2183			'_wpnonce': fbd.nonce,
2184			'source': 'actionbar',
2185			'blog_id': fbd.siteID
2186		});
2187	};
2188
2189	// Show/Hide actionbar on scroll
2190	fb = $('#actionbar');
2191	toggleactionbar = function() {
2192		var st = $(window).scrollTop(),
2193			topOffset = 0;
2194
2195		if ( $(window).scrollTop() < 0 ) {
2196			return;
2197		}
2198
2199		// Still
2200		if ( lastScrollTop == 0 || ( ( st == lastScrollTop ) && lastScrollDir == 'up' ) ) {
2201			fb.removeClass( 'actnbr-hidden' );
2202
2203		// Moving
2204		} else {
2205			 // Scrolling Up
2206		    if ( st < lastScrollTop ){
2207				fb.removeClass( 'actnbr-hidden' );
2208				lastScrollDir = 'up';
2209
2210			// Scrolling Down
2211			} else {
2212				// check if there are any popovers open, and only hide action bar if not
2213				if ( $( '#actionbar > ul > li:not(.actnbr-hidden) > .actnbr-popover' ).length === 0 ) {
2214					fb.addClass( 'actnbr-hidden' );
2215					lastScrollDir = 'down';
2216
2217					// Hide any menus
2218					$( '#actionbar li' ).addClass( 'actnbr-hidden' );
2219				}
2220			}
2221		}
2222
2223		lastScrollTop = st;
2224	};
2225	setInterval( toggleactionbar, 100 );
2226
2227	var bumpStat = function( stat ) {
2228		return $.post( fbd.xhrURL, {
2229			'action': 'actionbar_stats',
2230			'stat': stat
2231		} );
2232	};
2233
2234	var recordTracksEvent =	function( eventName, eventProps ) {
2235		eventProps = eventProps || {};
2236		window._tkq = window._tkq || [];
2237		window._tkq.push( [ 'recordEvent', eventName, eventProps ] );
2238	};
2239
2240	/**
2241	 * A factory method for creating an event handler function that will bump a specific stat and ONLY THEN re-dispatch
2242	 * the event. This will ensure that the bumped stat is indeed recorded before navigating the page away, as otherwise
2243	 * some browsers may very well decide to cancel the stat request in that case.
2244	 *
2245	 * @param {String} stat the name of the stat to bump
2246	 * @param {Function} additionalEffect an additional function that should be called after the stat is bumped
2247	 */
2248	function createStatsBumperEventHandler( stat, additionalEffect ) {
2249		var completedEvents = {};
2250
2251		return function eventHandler( event ) {
2252			if ( completedEvents[ event.timeStamp ] ) {
2253				delete completedEvents[ event.timeStamp ];
2254
2255				// hack-around to submit forms, dispatching "submit" event is not enough for them
2256				if ( event.type === 'submit' ) {
2257					event.target.submit();
2258				}
2259
2260				if ( typeof additionalEffect === 'function' ) {
2261					return additionalEffect( event );
2262				}
2263
2264				return true;
2265			}
2266
2267			event.preventDefault();
2268			event.stopPropagation();
2269
2270			function dispatchOriginalEvent() {
2271				var newEvent;
2272
2273				// Retrieves the native event object created by the browser from the jQuery event object
2274				var originalEvent = event.originalEvent;
2275
2276				/**
2277				 * Handles Internet Explorer that doesn't support Event nor CustomEvent constructors
2278				 *
2279				 * @see https://developer.mozilla.org/en-US/docs/Web/API/Event/Event
2280				 * @see https://developer.mozilla.org/en-US/docs/Web/API/CustomEvent/CustomEvent
2281				 * @see https://stackoverflow.com/questions/26596123/internet-explorer-9-10-11-event-constructor-doesnt-work/
2282				 */
2283				if ( typeof window.CustomEvent !== 'function' ) {
2284					newEvent = document.createEvent( 'CustomEvent' );
2285					newEvent.initCustomEvent(
2286						originalEvent.type,
2287						originalEvent.bubbles,
2288						originalEvent.cancelable,
2289						originalEvent.detail
2290					);
2291				} else {
2292					newEvent = new originalEvent.constructor( originalEvent.type, originalEvent );
2293				}
2294
2295				completedEvents[ newEvent.timeStamp ] = true;
2296
2297				originalEvent.target.dispatchEvent( newEvent );
2298			}
2299
2300			bumpStat( stat ).then( dispatchOriginalEvent, dispatchOriginalEvent );
2301		}
2302	}
2303
2304	function actionBarEscapeHtml(string) {
2305		return String(string).replace(/[&<>
2305"'\/]/g, function (s) {
2306			var entityMap = {
2307				"&": "&amp;",
2308				"<": "&lt;",
2309				">": "&gt;",
2310				'"': '&quot;',
2311				"'": '&#39;',
2312				"/": '&#x2F;'
2313			};
2314			return entityMap[s];
2315		});
2316	}
2317
2318	function showActionBarStatusMessage( message ) {
2319		$( '#actionbar .actnbr-actn-follow' ).replaceWith( '<a class="actnbr-action actnbr-actn-following" href="">' + followingbtn + '<span>' + fbd.i18n.following + '</span></a>' );
2320		$( '#actionbar .actnbr-follow-bubble' ).html( ' \
2321			<ul> \
2322				<li class="actnbr-sitename"><a href="' + fbd.siteURL + '">' + fbd.icon + ' ' + actionBarEscapeHtml( fbd.siteName ) + '</a></li> \
2323				<li class="actnbr-message">' + message + '</li> \
2324			</ul> \
2325		');
2326
2327		var btn = $( '#actionbar .actnbr-btn' );
2328		btn.removeClass( 'actnbr-hidden' );
2329
2330		setTimeout( function() {
2331			if ( ! btn.hasClass( 'actnbr-hidden' ) ) {
2332				$( '#actionbar .actnbr-email-field' ).focus();
2333				$( document ).on( 'click.actnbr-body-click', function(e) {
2334					if ( $( e.target ).closest( '.actnbr-popover' )[0] ) {
2335						return;
2336					}
2337					btn.addClass( 'actnbr-hidden' );
2338					$( document ).off( 'click.actnbr-body-click' );
2339				} );
2340			}
2341		}, 10 );
2342	}
2343
2344	function showActionBarFollowForm() {
2345		var btn = $( '#actionbar .actnbr-btn' );
2346		btn.toggleClass( 'actnbr-hidden' );
2347
2348		var form = $('<form>');
2349
2350		if ( fbd.i18n.followers ) {
2351			form.append( $( '<div class="actnbr-follow-count">' ).html( fbd.i18n.followers ) );
2352		}
2353
2354		form.append($('<div>').append($('<input>').attr({"type": "email", "name": "email", "placeholder": fbd.i18n.enterEmail, "class": "actnbr-email-field"})));
2355		form.append($('<input type="hidden" name="action" value="subscribe"/>'));
2356		form.append($('<input type="hidden" name="blog_id">').attr('value', fbd.siteID));
2357		form.append($('<input type="hidden" name="source">').attr('value', fbd.referer));
2358		form.append($('<input type="hidden" name="sub-type" value="actionbar-follow"/>'));
2359		form.append($(fbd.subscribeNonce));
2360		form.append($('<div class="actnbr-button-wrap">').append($('<button type="submit">').attr('value', fbd.i18n.subscribe).html(fbd.i18n.subscribe)));
2361		form.attr('method', 'post');
2362		form.attr('action', 'https://subscribe.wordpress.com');
2363		form.attr('accept-charset', 'utf-8');
2364
2365		var html = $('<ul/>');
2366		html.append($('<li class="actnbr-sitename">').append($('<a>').attr('href', fbd.siteURL).append($(fbd.icon)).append(' ' + actionBarEscapeHtml( fbd.siteName ))))
2367		html.append($('<li>').append(form));
2368		html.append($('<li class="actnbr-login-nudge">').append($('<div>').html(fbd.i18n.alreadyUser)));
2369
2370		$( '#actionbar .actnbr-follow-bubble' ).empty().append(html);
2371
2372		setTimeout( function() {
2373			if ( ! btn.hasClass( 'actnbr-hidden' ) ) {
2374				bumpStat( 'show_follow_form' );
2375
2376				$( '#actionbar .actnbr-email-field' ).focus();
2377
2378				$( document ).on( 'click.actnbr-body-click', function( event ) {
2379					if ( $( event.target ).closest( '.actnbr-popover' )[ 0 ] ) {
2380						return;
2381					}
2382
2383					btn.addClass( 'actnbr-hidden' );
2384
2385					$( document ).off( 'click.actnbr-body-click' );
2386				} );
2387			}
2388		}, 10 );
2389	}
2390})(jQuery);
2391;
2392/* jshint sub: true, onevar: false, multistr: true, devel: true, smarttabs: true */
2393/* global jetpackCarouselStrings, DocumentTouch */
2394
2395// @start-hide-in-jetpack
2396if (typeof wpcom === 'undefined') {
2397	var wpcom = {};
2398}
2399wpcom.carousel = (function(/*$*/) {
2400	var prebuilt_widths = jetpackCarouselStrings.widths;
2401	var pageviews_stats_args = jetpackCarouselStrings.stats_query_args;
2402
2403	var findFirstLargeEnoughWidth = function(original_w, original_h, dest_w, dest_h) {
2404		var inverse_ratio = original_h / original_w;
2405
2406		for ( var i = 0; i < prebuilt_widths.length; ++i ) {
2407			if ( prebuilt_widths[i] >= dest_w || prebuilt_widths[i] * inverse_ratio >= dest_h ) {
2408				return prebuilt_widths[i];
2409			}
2410		}
2411
2412		return original_w;
2413	};
2414
2415	var addWidthToImageURL = function(url, width) {
2416		width = parseInt(width, 10);
2417		// Give devices with a higher devicePixelRatio higher-res images (Retina display = 2, Android phones = 1.5, etc)
2418		if ('undefined' !== typeof window.devicePixelRatio && window.devicePixelRatio > 1) {
2419			width = Math.round( width * window.devicePixelRatio );
2420		}
2421		url = addArgToURL(url, 'w', width);
2422		url = addArgToURL(url, 'h', '');
2423		return url;
2424	};
2425
2426	var addArgToURL = function(url, arg, value) {
2427		var re = new RegExp(arg+'=[^?&]+');
2428		if ( url.match(re) ) {
2429			return url.replace(re, arg + '=' + value);
2430		} else {
2431			var divider = url.indexOf('?') !== -1 ? '&' : '?';
2432			return url + divider + arg + '=' + value;
2433		}
2434	};
2435
2436	var stat = function ( names ) {
2437		if ( typeof names !== 'string' ) {
2438			names = names.join( ',' );
2439		}
2440
2441		new Image().src = window.location.protocol +
2442			'https://pixel.wp.com/g.gif?v=wpcom-no-pv' +
2443			'&x_carousel=' + names +
2444			'&baba=' + Math.random();
2445	};
2446
2447	var pageview = function ( post_id ) {
2448		new Image().src = window.location.protocol +
2449			'https://pixel.wp.com/g.gif?host=' + encodeURIComponent( window.location.host ) +
2450			'&ref=' + encodeURIComponent( document.referrer ) +
2451			'&rand=' + Math.random() +
2452			'&' + pageviews_stats_args +
2453			'&post=' + encodeURIComponent( post_id );
2454	};
2455
2456
2457	return {
2458		findFirstLargeEnoughWidth: findFirstLargeEnoughWidth,
2459		addWidthToImageURL: addWidthToImageURL,
2460		stat: stat,
2461		pageview: pageview
2462	};
2463
2464})(jQuery);
2465// @end-hide-in-jetpack
2466
2467jQuery(document).ready(function($) {
2468
2469	// gallery faded layer and container elements
2470	var overlay, comments, gallery, container, nextButton, previousButton, info, transitionBegin,
2471	caption, resizeTimeout, photo_info, close_hint, commentInterval, lastSelectedSlide,
2472	screenPadding = 110, originalOverflow = $('body').css('overflow'), originalHOverflow = $('html').css('overflow'), proportion = 85,
2473	last_known_location_hash = '', imageMeta, titleAndDescription, commentForm, leftColWrapper, scrollPos;
2474
2475	if ( window.innerWidth <= 760 ) {
2476		screenPadding = Math.round( ( window.innerWidth / 760 ) * 110 );
2477
2478		if ( screenPadding < 40 && ( ( 'ontouchstart' in window ) || window.DocumentTouch && document instanceof DocumentTouch ) ) {
2479			screenPadding = 0;
2480		}
2481	}
2482
2483	// Adding a polyfill for browsers that do not have Date.now
2484	if ( 'undefined' === typeof Date.now ) {
2485		Date.now = function now() {
2486			return new Date().getTime();
2487		};
2488	}
2489
2490	var keyListener = function(e){
2491		switch(e.which){
2492			case 38: // up
2493				e.preventDefault();
2494				container.scrollTop(container.scrollTop() - 100);
2495				break;
2496			case 40: // down
2497				e.preventDefault();
2498				container.scrollTop(container.scrollTop() + 100);
2499				break;
2500			case 39: // right
2501				e.preventDefault();
2502				gallery.jp_carousel('next');
2503				break;
2504			case 37: // left
2505			case 8: // backspace
2506				e.preventDefault();
2507				gallery.jp_carousel('previous');
2508				break;
2509			case 27: // escape
2510				e.preventDefault();
2511				container.jp_carousel('close');
2512				break;
2513			default:
2514				// making jslint happy
2515				break;
2516		}
2517	};
2518
2519	var resizeListener = function(/*e*/){
2520		clearTimeout(resizeTimeout);
2521		resizeTimeout = setTimeout(function(){
2522			gallery
2523				.jp_carousel('slides')
2524				.jp_carousel('fitSlide', true);
2525			gallery.jp_carousel('updateSlidePositions', true);
2526			gallery.jp_carousel('fitMeta', true);
2527		}, 200);
2528	};
2529
2530	var prepareGallery = function( /*dataCarouselExtra*/ ){
2531		if (!overlay) {
2532			overlay = $('<div></div>')
2533				.addClass('jp-carousel-overlay')
2534				.css({
2535					'position' : 'fixed',
2536					'top'      : 0,
2537					'right'    : 0,
2538					'bottom'   : 0,
2539					'left'     : 0
2540				});
2541
2542			var buttons  = '<a class="jp-carousel-commentlink" href="#">' + jetpackCarouselStrings.comment + '</a>';
2543			if ( 1 === Number( jetpackCarouselStrings.is_logged_in ) ) {
2544// @start-hide-in-jetpack
2545				if ( 1 === Number( jetpackCarouselStrings.is_public && 1 === Number( jetpackCarouselStrings.reblog_enabled ) ) ) {
2546					buttons += '<a class="jp-carousel-reblog" href="#">' + jetpackCarouselStrings.reblog + '</a>';
2547				}
2548// @end-hide-in-jetpack
2549			}
2550
2551			buttons  = $('<div class="jp-carousel-buttons">' + buttons + '</div>');
2552
2553			caption    = $('<h2 itemprop="caption description"></h2>');
2554			photo_info = $('<div class="jp-carousel-photo-info"></div>').append(caption);
2555
2556			imageMeta = $('<div></div>')
2557				.addClass('jp-carousel-image-meta')
2558				.css({
2559					'float'      : 'right',
2560					'margin-top' : '20px',
2561					'width'      :  '250px'
2562				});
2563
2564			imageMeta
2565				.append( buttons )
2566				.append( '<ul class=\'jp-carousel-image-exif\' style=\'display:none;\'></ul>' )
2567				.append( '<a class=\'jp-carousel-image-download\' style=\'display:none;\'></a>' )
2568				.append( '<div class=\'jp-carousel-image-map\' style=\'display:none;\'></div>' );
2569
2570			titleAndDescription = $('<div></div>')
2571				.addClass('jp-carousel-titleanddesc')
2572				.css({
2573					'width'      : '100%',
2574					'margin-top' : imageMeta.css('margin-top')
2575				});
2576
2577			var commentFormMarkup = '<div id="jp-carousel-comment-form-container">';
2578
2579			if ( jetpackCarouselStrings.local_comments_commenting_as && jetpackCarouselStrings.local_comments_commenting_as.length ) {
2580				// Comments not enabled, fallback to local comments
2581
2582				if ( 1 !== Number( jetpackCarouselStrings.is_logged_in ) && 1 === Number( jetpackCarouselStrings.comment_registration ) ) {
2583					commentFormMarkup += '<div id="jp-carousel-comment-form-commenting-as">' + jetpackCarouselStrings.local_comments_commenting_as + '</div>';
2584				} else {
2585					commentFormMarkup += '<form id="jp-carousel-comment-form">';
2586					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>';
2587					commentFormMarkup += '<div id="jp-carousel-comment-form-submit-and-info-wrapper">';
2588					commentFormMarkup += '<div id="jp-carousel-comment-form-commenting-as">' + jetpackCarouselStrings.local_comments_commenting_as + '</div>';
2589					commentFormMarkup += '<input type="submit" name="submit" class="jp-carousel-comment-form-button" id="jp-carousel-comment-form-button-submit" value="'+jetpackCarouselStrings.post_comment+'" />';
2590					commentFormMarkup += '<span id="jp-carousel-comment-form-spinner">&nbsp;</span>';
2591					commentFormMarkup += '<div id="jp-carousel-comment-post-results"></div>';
2592					commentFormMarkup += '</div>';
2593					commentFormMarkup += '</form>';
2594				}
2595			}
2596			commentFormMarkup += '</div>';
2597
2598			commentForm = $(commentFormMarkup)
2599				.css({
2600					'width'      : '100%',
2601					'margin-top' : '20px',
2602					'color'      : '#999'
2603				});
2604
2605			comments = $('<div></div>')
2606				.addClass('jp-carousel-comments')
2607				.css({
2608					'width'      : '100%',
2609					'bottom'     : '10px',
2610					'margin-top' : '20px'
2611				});
2612
2613			var commentsLoading = $('<div id="jp-carousel-comments-loading"><span>'+jetpackCarouselStrings.loading_comments+'</span></div>')
2614				.css({
2615					'width'      : '100%',
2616					'bottom'     : '10px',
2617					'margin-top' : '20px'
2618				});
2619
2620			var leftWidth = ( $(window).width() - ( screenPadding * 2 ) ) - (imageMeta.width() + 40);
2621			leftWidth += 'px';
2622
2623			leftColWrapper = $('<div></div>')
2624				.addClass('jp-carousel-left-column-wrapper')
2625				.css({
2626					'width' : Math.floor( leftWidth )
2627				})
2628				.append(titleAndDescription)
2629				.append(commentForm)
2630				.append(comments)
2631				.append(commentsLoading);
2632
2633			var fadeaway = $('<div></div>')
2634				.addClass('jp-carousel-fadeaway');
2635
2636			info = $('<div></div>')
2637				.addClass('jp-carousel-info')
2638				.css({
2639					'top'   : Math.floor( ($(window).height() / 100) * proportion ),
2640					'left'  : screenPadding,
2641					'right' : screenPadding
2642				})
2643				.append(photo_info)
2644				.append(imageMeta);
2645
2646			if ( window.innerWidth <= 760 ) {
2647				photo_info.remove().insertAfter( titleAndDescription );
2648				info.prepend( leftColWrapper );
2649			}
2650			else {
2651				info.append( leftColWrapper );
2652			}
2653
2654			var targetBottomPos = ( $(window).height() - parseInt( info.css('top'), 10 ) ) + 'px';
2655
2656			nextButton = $('<div><span></span></div>')
2657				.addClass('jp-carousel-next-button')
2658				.css({
2659					'right'    : '15px'
2660				})
2661				.hide();
2662
2663			previousButton = $('<div><span></span></div>')
2664				.addClass('jp-carousel-previous-button')
2665				.css({
2666					'left'     : 0
2667				})
2668				.hide();
2669
2670			nextButton.add( previousButton ).css( {
2671				'position' : 'fixed',
2672				'top' : '40px',
2673				'bottom' : targetBottomPos,
2674				'width' : screenPadding
2675			} );
2676
2677			gallery = $('<div></div>')
2678				.addClass('jp-carousel')
2679				.css({
2680					'position' : 'absolute',
2681					'top'      : 0,
2682					'bottom'   : targetBottomPos,
2683					'left'     : 0,
2684					'right'    : 0
2685				});
2686
2687			close_hint = $('<div class="jp-carousel-close-hint"><span>&times;</span></div>')
2688				.css({
2689					position : 'fixed'
2690				});
2691
2692			container = $('<div></div>')
2693				.addClass('jp-carousel-wrap')
2694				.addClass( 'jp-carousel-transitions' );
2695			if ( 'white' === jetpackCarouselStrings.background_color ) {
2696				 container.addClass('jp-carousel-light');
2697			}
2698
2699			container.attr('itemscope', '');
2700
2701			container.attr('itemtype', 'https://schema.org/ImageGallery');
2702
2703			container.css({
2704					'position'   : 'fixed',
2705					'top'        : 0,
2706					'right'      : 0,
2707					'bottom'     : 0,
2708					'left'       : 0,
2709					'z-index'    : 2147483647,
2710					'overflow-x' : 'hidden',
2711					'overflow-y' : 'auto',
2712					'direction'  : 'ltr'
2713				})
2714				.hide()
2715				.append(overlay)
2716				.append(gallery)
2717				.append(fadeaway)
2718				.append(info)
2719				.append(nextButton)
2720				.append(previousButton)
2721				.append(close_hint)
2722				.appendTo($('body'))
2723				.click(function(e){
2724					var target = $(e.target), wrap = target.parents('div.jp-carousel-wrap'), data = wrap.data('carousel-extra'),
2725						slide = wrap.find('div.selected'), attachment_id = slide.data('attachment-id');
2726					data = data || [];
2727
2728					if ( target.is(gallery) || target.parents().add(target).is(close_hint) ) {
2729						container.jp_carousel('close');
2730// @start-hide-in-jetpack
2731					} else if ( target.hasClass('jp-carousel-reblog') ) {
2732						e.preventDefault();
2733						e.stopPropagation();
2734						if ( !target.hasClass('reblogged') ) {
2735							target.jp_carousel('show_reblog_box');
2736							wpcom.carousel.stat('reblog_show_box');
2737						}
2738					} else if ( target.parents('#carousel-reblog-box').length ) {
2739						if ( target.is('a.cancel') ) {
2740							e.preventDefault();
2741							e.stopPropagation();
2742							target.jp_carousel('hide_reblog_box');
2743							wpcom.carousel.stat('reblog_cancel');
2744						} else if ( target.is( 'input[type="submit"]' ) ) {
2745							e.preventDefault();
2746							e.stopPropagation();
2747
2748							var note = $('#carousel-reblog-box textarea').val();
2749							if ( jetpackCarouselStrings.reblog_add_thoughts === note ) {
2750								note = '';
2751							}
2752
2753							$('#carousel-reblog-submit').val( jetpackCarouselStrings.reblogging );
2754							$('#carousel-reblog-submit').prop('disabled', true);
2755							$( '#carousel-reblog-box div.submit span.canceltext' ).spin( 'small' );
2756
2757							$.post( jetpackCarouselStrings.ajaxurl, {
2758								'action': 'post_reblog',
2759								'reblog_source': 'carousel',
2760								'original_blog_id': $('#carousel-reblog-box input#carousel-reblog-blog-id').val(),
2761								'original_post_id': $('.jp-carousel div.selected').data('attachment-id'),
2762								'blog_id': $('#carousel-reblog-box select').val(),
2763								'blog_url': $('#carousel-reblog-box input#carousel-reblog-blog-url').val(),
2764								'blog_title': $('#carousel-reblog-box input#carousel-reblog-blog-title').val(),
2765								'post_url': $('#carousel-reblog-box input#carousel-reblog-post-url').val(),
2766								'post_title': slide.data( 'caption' ) || $('#carousel-reblog-box input#carousel-reblog-post-title').val(),
2767								'note': note,
2768								'_wpnonce': $('#carousel-reblog-box #_wpnonce').val()
2769							},
2770							function(/*result*/) {
2771								$('#carousel-reblog-box').css({ 'height': $('#carousel-reblog-box').height() + 'px' }).slideUp('fast');
2772								$('a.jp-carousel-reblog').html( jetpackCarouselStrings.reblogged ).removeClass( 'reblog' ).addClass( 'reblogged' );
2773								$( '#carousel-reblog-box div.submit span.canceltext' ).spin( false );
2774								$('#carousel-reblog-submit').val( jetpackCarouselStrings.post_reblog );
2775								$('div.jp-carousel-info').children().not('#carousel-reblog-box').fadeIn('fast');
2776								slide.data('reblogged', 1);
2777								$('div.gallery').find('img[data-attachment-id="' + slide.data('attachment-id') + '"]').data('reblogged', 1);
2778
2779
2780							}, 'json' );
2781							wpcom.carousel.stat('reblog_submit');
2782						}
2783					} else if ( target.hasClass( 'jp-carousel-image-download' ) ) {
2784						wpcom.carousel.stat( 'download_original_click' );
2785// @end-hide-in-jetpack
2786					} else if ( target.hasClass('jp-carousel-commentlink') ) {
2787						e.preventDefault();
2788						e.stopPropagation();
2789						$(window).unbind('keydown', keyListener);
2790						container.animate({scrollTop: parseInt(info.position()['top'], 10)}, 'fast');
2791						$('#jp-carousel-comment-form-submit-and-info-wrapper').slideDown('fast');
2792						$('#jp-carousel-comment-form-comment-field').focus();
2793					} else if ( target.hasClass('jp-carousel-comment-login') ) {
2794						var url = jetpackCarouselStrings.login_url + '%23jp-carousel-' + attachment_id;
2795
2796						window.location.href = url;
2797					} else if ( target.parents('#jp-carousel-comment-form-container').length ) {
2798						var textarea = $('#jp-carousel-comment-form-comment-field')
2799							.blur(function(){
2800								$(window).bind('keydown', keyListener);
2801							})
2802							.focus(function(){
2803								$(window).unbind('keydown', keyListener);
2804							});
2805
2806						var emailField = $('#jp-carousel-comment-form-email-field')
2807							.blur(function(){
2808								$(window).bind('keydown', keyListener);
2809							})
2810							.focus(function(){
2811								$(window).unbind('keydown', keyListener);
2812							});
2813
2814						var authorField = $('#jp-carousel-comment-form-author-field')
2815							.blur(function(){
2816								$(window).bind('keydown', keyListener);
2817							})
2818							.focus(function(){
2819								$(window).unbind('keydown', keyListener);
2820							});
2821
2822						var urlField = $('#jp-carousel-comment-form-url-field')
2823							.blur(function(){
2824								$(window).bind('keydown', keyListener);
2825							})
2826							.focus(function(){
2827								$(window).unbind('keydown', keyListener);
2828							});
2829
2830						if ( textarea && textarea.attr('id') === target.attr('id')) {
2831							// For first page load
2832							$(window).unbind('keydown', keyListener);
2833							$('#jp-carousel-comment-form-submit-and-info-wrapper').slideDown('fast');
2834						} else if ( target.is( 'input[type="submit"]' ) ) {
2835							e.preventDefault();
2836							e.stopPropagation();
2837
2838							$('#jp-carousel-comment-form-spinner').spin('small', 'white');
2839
2840							var ajaxData = {
2841								action: 'post_attachment_comment',
2842								nonce:   jetpackCarouselStrings.nonce,
2843								blog_id: data['blog_id'],
2844								id:      attachment_id,
2845								comment: textarea.val()
2846							};
2847
2848							if ( ! ajaxData['comment'].length ) {
2849								gallery.jp_carousel('postCommentError', {'field': 'jp-carousel-comment-form-comment-field', 'error': jetpackCarouselStrings.no_comment_text});
2850								return;
2851							}
2852
2853							if ( 1 !== Number( jetpackCarouselStrings.is_logged_in ) ) {
2854								ajaxData['email']  = emailField.val();
2855								ajaxData['author'] = authorField.val();
2856								ajaxData['url']    = urlField.val();
2857
2858								if ( 1 === Number( jetpackCarouselStrings.require_name_email ) ) {
2859									if ( ! ajaxData['email'].length || ! ajaxData['email'].match('@') ) {
2860										gallery.jp_carousel('postCommentError', {'field': 'jp-carousel-comment-form-email-field', 'error': jetpackCarouselStrings.no_comment_email});
2861										return;
2862									} else if ( ! ajaxData['author'].length ) {
2863										gallery.jp_carousel('postCommentError', {'field': 'jp-carousel-comment-form-author-field', 'error': jetpackCarouselStrings.no_comment_author});
2864										return;
2865									}
2866								}
2867							}
2868
2869							$.ajax({
2870								type:       'POST',
2871								url:        jetpackCarouselStrings.ajaxurl,
2872								data:       ajaxData,
2873								dataType:   'json',
2874								success: function(response/*, status, xhr*/) {
2875									if ( 'approved' === response.comment_status ) {
2876										$('#jp-carousel-comment-post-results').slideUp('fast').html('<span class="jp-carousel-comment-post-success">' + jetpackCarouselStrings.comment_approved + '</span>').slideDown('fast');
2877									} else if ( 'unapproved' === response.comment_status ) {
2878										$('#jp-carousel-comment-post-results').slideUp('fast').html('<span class="jp-carousel-comment-post-success">' + jetpackCarouselStrings.comment_unapproved + '</span>').slideDown('fast');
2879									} else {
2880										// 'deleted', 'spam', false
2881										$('#jp-carousel-comment-post-results').slideUp('fast').html('<span class="jp-carousel-comment-post-error">' + jetpackCarouselStrings.comment_post_error + '</span>').slideDown('fast');
2882									}
2883									gallery.jp_carousel('clearCommentTextAreaValue');
2884									gallery.jp_carousel('getComments', {attachment_id: attachment_id, offset: 0, clear: true});
2885									$('#jp-carousel-comment-form-button-submit').val(jetpackCarouselStrings.post_comment);
2886									$('#jp-carousel-comment-form-spinner').spin(false);
2887								},
2888								error: function(/*xhr, status, error*/) {
2889									// TODO: Add error handling and display here
2890									gallery.jp_carousel('postCommentError', {'field': 'jp-carousel-comment-form-comment-field', 'error': jetpackCarouselStrings.comment_post_error});
2891									return;
2892								}
2893							});
2894						}
2895					} else if ( ! target.parents( '.jp-carousel-info' ).length ) {
2896						container.jp_carousel('next');
2897					}
2898				})
2899				.bind('jp_carousel.afterOpen', function(){
2900					$(window).bind('keydown', keyListener);
2901					$(window).bind('resize', resizeListener);
2902					gallery.opened = true;
2903
2904					resizeListener();
2905				})
2906				.bind('jp_carousel.beforeClose', function(){
2907					var scroll = $(window).scrollTop();
2908
2909					$(window).unbind('keydown', keyListener);
2910					$(window).unbind('resize', resizeListener);
2911					$(window).scrollTop(scroll);
2912					$( '.jp-carousel-previous-button' ).hide();
2913					$( '.jp-carousel-next-button' ).hide();
2914                    gallery.jp_carousel( 'hide_reblog_box' ); // @hide-in-jetpack
2915				})
2916				.bind('jp_carousel.afterClose', function(){
2917					if ( window.location.hash && history.back ) {
2918						history.back();
2919					}
2920					last_known_location_hash = '';
2921					gallery.opened = false;
2922				})
2923				.on( 'transitionend.jp-carousel ', '.jp-carousel-slide', function ( e ) {
2924					// If the movement transitions take more than twice the allotted time, disable them.
2925					// There is some wiggle room in the 2x, since some of that time is taken up in
2926					// JavaScript, setting up the transition and calling the events.
2927					if ( 'transform' === e.originalEvent.propertyName ) {
2928						var transitionMultiplier = ( ( Date.now() - transitionBegin ) / 1000 ) / e.originalEvent.elapsedTime;
2929
2930						container.off( 'transitionend.jp-carousel' );
2931
2932						if ( transitionMultiplier >= 2 ) {
2933							$( '.jp-carousel-transitions' ).removeClass( 'jp-carousel-transitions' );
2934						}
2935					}
2936				} );
2937
2938				$( '.jp-carousel-wrap' ).touchwipe( {
2939					wipeLeft : function ( e ) {
2940						e.preventDefault();
2941						gallery.jp_carousel( 'next' );
2942					},
2943					wipeRight : function ( e ) {
2944						e.preventDefault();
2945						gallery.jp_carousel( 'previous' );
2946					},
2947					preventDefaultEvents : false
2948				} );
2949
2950			nextButton.add(previousButton).click(function(e){
2951				e.preventDefault();
2952				e.stopPropagation();
2953				if ( nextButton.is(this) ) {
2954					gallery.jp_carousel('next');
2955				} else {
2956					gallery.jp_carousel('previous');
2957				}
2958			});
2959		}
2960	};
2961
2962	var processSingleImageGallery = function() {
2963		// process links that contain img tag with attribute data-attachment-id
2964		$( 'a img[data-attachment-id]' ).each(function() {
2965			var container = $( this ).parent();
2966
2967			// skip if image was already added to gallery by shortcode
2968			if( container.parent( '.gallery-icon' ).length ) {
2969				return;
2970			}
2971
2972			// skip if the container is not a link
2973			if ( 'undefined' === typeof( $( container ).attr( 'href' ) ) ) {
2974				return;
2975			}
2976
2977			var valid = false;
2978
2979			// if link points to 'Media File' (ignoring GET parameters) and flag is set allow it
2980			if ( $( container ).attr( 'href' ).split( '?' )[0] === $( this ).attr( 'data-orig-file' ).split( '?' )[0] &&
2981				1 === Number( jetpackCarouselStrings.single_image_gallery_media_file )
2982			) {
2983				valid = true;
2984			}
2985
2986			// if link points to 'Attachment Page' allow it
2987			if( $( container ).attr( 'href' ) === $( this ).attr( 'data-permalink' ) ) {
2988				valid = true;
2989			}
2990
2991			// links to 'Custom URL' or 'Media File' when flag not set are not valid
2992			if( ! valid ) {
2993				return;
2994			}
2995
2996			// make this node a gallery recognizable by event listener above
2997			$( container ).addClass( 'single-image-gallery' ) ;
2998			// blog_id is needed to allow posting comments to correct blog
2999			$( container ).data( 'carousel-extra', { blog_id: Number( jetpackCarouselStrings.blog_id ) } );
3000		});
3001	};
3002
3003	var methods = {
3004		testForData: function(gallery) {
3005			gallery = $( gallery ); // make sure we have it as a jQuery object.
3006			return !( ! gallery.length || ! gallery.data( 'carousel-extra' ) );
3007		},
3008
3009		testIfOpened: function() {
3010			return !!( 'undefined' !== typeof(gallery) && 'undefined' !== typeof(gallery.opened) && gallery.opened );
3011		},
3012
3013		openOrSelectSlide: function( index ) {
3014			// The `open` method triggers an asynchronous effect, so we will get an
3015			// error if we try to use `open` then `selectSlideAtIndex` immediately
3016			// after it. We can only use `selectSlideAtIndex` if the carousel is
3017			// already open.
3018			if ( ! $( this ).jp_carousel( 'testIfOpened' ) ) {
3019				// The `open` method selects the correct slide during the
3020				// initialization.
3021				$( this ).jp_carousel( 'open', { start_index: index } );
3022			} else {
3023				gallery.jp_carousel( 'selectSlideAtIndex', index );
3024			}
3025		},
3026
3027		open: function(options) {
3028			var settings = {
3029				'items_selector' : '.gallery-item [data-attachment-id], .tiled-gallery-item [data-attachment-id], img[data-attachment-id]',
3030				'start_index': 0
3031			},
3032			data = $(this).data('carousel-extra');
3033
3034			if ( !data ) {
3035				return; // don't run if the default gallery functions weren't used
3036			}
3037
3038			prepareGallery( data );
3039
3040			if ( gallery.jp_carousel( 'testIfOpened' ) ) {
3041				return; // don't open if already opened
3042			}
3043
3044			// make sure to stop the page from scrolling behind the carousel overlay, so we don't trigger
3045			// infiniscroll for it when enabled (Reader, theme infiniscroll, etc).
3046			originalOverflow = $('body').css('overflow');
3047			$('body').css('overflow', 'hidden');
3048			// prevent html from overflowing on some of the new themes.
3049			originalHOverflow = $('html').css('overflow');
3050			$('html').css('overflow', 'hidden');
3051			scrollPos = $( window ).scrollTop();
3052
3053			container.data('carousel-extra', data);
3054// @start-hide-in-jetpack
3055			wpcom.carousel.stat( ['open', 'view_image'] );
3056// @end-hide-in-jetpack
3057
3058			return this.each(function() {
3059				// If options exist, lets merge them
3060				// with our default settings
3061				var $this = $(this);
3062
3063				if ( options ) {
3064					$.extend( settings, options );
3065				}
3066				if ( -1 === settings.start_index ) {
3067					settings.start_index = 0; //-1 returned if can't find index, so start from beginning
3068				}
3069
3070				container.trigger('jp_carousel.beforeOpen').fadeIn('fast',function(){
3071					container.trigger('jp_carousel.afterOpen');
3072					gallery
3073						.jp_carousel('initSlides', $this.find(settings.items_selector), settings.start_index)
3074						.jp_carousel('selectSlideAtIndex', settings.start_index);
3075				});
3076				gallery.html('');
3077			});
3078		},
3079
3080		selectSlideAtIndex : function(index){
3081			var slides = this.jp_carousel('slides'), selected = slides.eq(index);
3082
3083			if ( 0 === selected.length ) {
3084				selected = slides.eq(0);
3085			}
3086
3087			gallery.jp_carousel('selectSlide', selected, false);
3088			return this;
3089		},
3090
3091		close : function(){
3092			// make sure to let the page scroll again
3093			$('body').css('overflow', originalOverflow);
3094			$('html').css('overflow', originalHOverflow);
3095			this.jp_carousel( 'clearCommentTextAreaValue' );
3096			return container
3097				.trigger('jp_carousel.beforeClose')
3098				.fadeOut('fast', function(){
3099					container.trigger('jp_carousel.afterClose');
3100					$( window ).scrollTop( scrollPos );
3101				});
3102
3103		},
3104
3105		next : function() {
3106			this.jp_carousel( 'previousOrNext', 'nextSlide' );
3107            gallery.jp_carousel( 'hide_reblog_box' ); // @hide-in-jetpack
3108		},
3109
3110		previous : function() {
3111			this.jp_carousel( 'previousOrNext', 'prevSlide' );
3112            gallery.jp_carousel( 'hide_reblog_box' ); // @hide-in-jetpack
3113		},
3114
3115		previousOrNext : function ( slideSelectionMethodName ) {
3116			if ( ! this.jp_carousel( 'hasMultipleImages' ) ) {
3117				return false;
3118			}
3119
3120			var slide = gallery.jp_carousel( slideSelectionMethodName );
3121
3122			if ( slide ) {
3123				container.animate( { scrollTop: 0 }, 'fast' );
3124				this.jp_carousel( 'clearCommentTextAreaValue' );
3125				this.jp_carousel( 'selectSlide', slide );
3126                wpcom.carousel.stat( ['previous', 'view_image'] ); // @hide-in-jetpack
3127			}
3128		},
3129
3130        // @start-hide-in-jetpack
3131       resetButtons : function(current) {
3132		   if ( current.data( 'reblogged' ) ) {
3133                $('.jp-carousel-buttons a.jp-carousel-reblog').addClass( 'reblogged' ).text( jetpackCarouselStrings.reblogged );
3134		   } else {
3135                $('.jp-carousel-buttons a.jp-carousel-reblog').removeClass( 'reblogged' ).text( jetpackCarouselStrings.reblog );
3136		   }
3137           // Must also take care of reblog/reblogged here
3138        },
3139        // @end-hide-in-jetpack
3140
3141
3142		selectedSlide : function(){
3143			return this.find('.selected');
3144		},
3145
3146		setSlidePosition : function(x) {
3147			transitionBegin = Date.now();
3148
3149			return this.css({
3150					'-webkit-transform':'translate3d(' + x + 'px,0,0)',
3151					'-moz-transform':'translate3d(' + x + 'px,0,0)',
3152					'-ms-transform':'translate(' + x + 'px,0)',
3153					'-o-transform':'translate(' + x + 'px,0)',
3154					'transform':'translate3d(' + x + 'px,0,0)'
3155			});
3156		},
3157
3158		updateSlidePositions : function(animate) {
3159			var current = this.jp_carousel( 'selectedSlide' ),
3160				galleryWidth = gallery.width(),
3161				currentWidth = current.width(),
3162				previous = gallery.jp_carousel( 'prevSlide' ),
3163				next = gallery.jp_carousel( 'nextSlide' ),
3164				previousPrevious = previous.prev(),
3165				nextNext = next.next(),
3166				left = Math.floor( ( galleryWidth - currentWidth ) * 0.5 );
3167
3168			current.jp_carousel( 'setSlidePosition', left ).show();
3169
3170			// minimum width
3171			gallery.jp_carousel( 'fitInfo', animate );
3172
3173			// prep the slides
3174			var direction = lastSelectedSlide.is( current.prevAll() ) ? 1 : -1;
3175
3176			// Since we preload the `previousPrevious` and `nextNext` slides, we need
3177			// to make sure they technically visible in the DOM, but invisible to the
3178			// user. To hide them from the user, we position them outside the edges
3179			// of the window.
3180			//
3181			// This section of code only applies when there are more than three
3182			// slides. Otherwise, the `previousPrevious` and `nextNext` slides will
3183			// overlap with the `previous` and `next` slides which must be visible
3184			// regardless.
3185			if ( 1 === direction ) {
3186				if ( ! nextNext.is( previous ) ) {
3187					nextNext.jp_carousel( 'setSlidePosition', galleryWidth + next.width() ).show();
3188				}
3189
3190				if ( ! previousPrevious.is( next ) ) {
3191					previousPrevious.jp_carousel( 'setSlidePosition', -previousPrevious.width() - currentWidth ).show();
3192				}
3193			} else {
3194				if ( ! nextNext.is( previous ) ) {
3195					nextNext.jp_carousel( 'setSlidePosition', galleryWidth + currentWidth ).show();
3196				}
3197			}
3198
3199			previous.jp_carousel( 'setSlidePosition', Math.floor( -previous.width() + ( screenPadding * 0.75 ) ) ).show();
3200			next.jp_carousel( 'setSlidePosition', Math.ceil( galleryWidth - ( screenPadding * 0.75 ) ) ).show();
3201		},
3202
3203		selectSlide : function(slide, animate){
3204			lastSelectedSlide = this.find( '.selected' ).removeClass( 'selected' );
3205
3206			var slides = gallery.jp_carousel( 'slides' ).css({ 'position': 'fixed' }),
3207				current = $( slide ).addClass( 'selected' ).css({ 'position': 'relative' }),
3208				attachmentId = current.data( 'attachment-id' ),
3209				previous = gallery.jp_carousel( 'prevSlide' ),
3210				next = gallery.jp_carousel( 'nextSlide' ),
3211				previousPrevious = previous.prev(),
3212				nextNext = next.next(),
3213				animated,
3214				captionHtml;
3215
3216			// center the main image
3217			gallery.jp_carousel( 'loadFullImage', current );
3218
3219			caption.hide();
3220
3221			if ( next.length === 0 && slides.length <= 2 ) {
3222				$( '.jp-carousel-next-button' ).hide();
3223			} else {
3224				$( '.jp-carousel-next-button' ).show();
3225			}
3226
3227			if ( previous.length === 0 && slides.length <= 2 ) {
3228				$( '.jp-carousel-previous-button' ).hide();
3229			} else {
3230				$( '.jp-carousel-previous-button' ).show();
3231			}
3232
3233			animated = current
3234				.add( previous )
3235				.add( previousPrevious )
3236				.add( next )
3237				.add( nextNext )
3238				.jp_carousel( 'loadSlide' );
3239
3240			// slide the whole view to the x we want
3241			slides.not( animated ).hide();
3242
3243			gallery.jp_carousel( 'updateSlidePositions', animate );
3244
3245			gallery.jp_carousel( 'resetButtons', current ); // @hide-in-jetpack
3246			container.trigger( 'jp_carousel.selectSlide', [current] );
3247
3248			gallery.jp_carousel( 'getTitleDesc', {
3249				title: current.data( 'title' ),
3250				desc: current.data( 'desc' )
3251			});
3252
3253			var imageMeta = current.data( 'image-meta' );
3254			gallery.jp_carousel( 'updateExif', imageMeta );
3255			gallery.jp_carousel( 'updateFullSizeLink', current );
3256			gallery.jp_carousel( 'updateMap', imageMeta );
3257			gallery.jp_carousel( 'testCommentsOpened', current.data( 'comments-opened' ) );
3258			gallery.jp_carousel( 'getComments', {
3259				'attachment_id': attachmentId,
3260				'offset': 0,
3261				'clear': true
3262			});
3263			$( '#jp-carousel-comment-post-results' ).slideUp();
3264
3265			// $('<div />').text(sometext).html() is a trick to go to HTML to plain
3266			// text (including HTML entities decode, etc)
3267			if ( current.data( 'caption' ) ) {
3268				captionHtml = $( '<div />' ).text( current.data( 'caption' ) ).html();
3269
3270				if ( captionHtml === $( '<div />' ).text( current.data( 'title' ) ).html() ) {
3271					$( '.jp-carousel-titleanddesc-title' ).fadeOut( 'fast' ).empty();
3272				}
3273
3274				if ( captionHtml === $( '<div />' ).text( current.data( 'desc' ) ).html() ) {
3275					$( '.jp-carousel-titleanddesc-desc' ).fadeOut( 'fast' ).empty();
3276				}
3277
3278				caption.html( current.data( 'caption' ) ).fadeIn( 'slow' );
3279			} else {
3280				caption.fadeOut( 'fast' ).empty();
3281			}
3282
3283			// Record pageview in WP Stats, for each new image loaded full-screen.
3284			if ( jetpackCarouselStrings.stats ) {
3285				new Image().src = document.location.protocol +
3286					'https://pixel.wp.com/g.gif?' +
3287					jetpackCarouselStrings.stats +
3288					'&post=' + encodeURIComponent( attachmentId ) +
3289					'&rand=' + Math.random();
3290			}
3291
3292            wpcom.carousel.pageview( attachmentId ); // @hide-in-jetpack
3293
3294			// Load the images for the next and previous slides.
3295			$( next ).add( previous ).each( function() {
3296				gallery.jp_carousel( 'loadFullImage', $( this ) );
3297			});
3298
3299			window.location.hash = last_known_location_hash = '#jp-carousel-' + attachmentId;
3300		},
3301
3302		slides : function(){
3303			return this.find('.jp-carousel-slide');
3304		},
3305
3306		slideDimensions : function(){
3307			return {
3308				width: $(window).width() - (screenPadding * 2),
3309				height: Math.floor( $(window).height() / 100 * proportion - 60 )
3310			};
3311		},
3312
3313		loadSlide : function() {
3314			return this.each(function(){
3315				var slide = $(this);
3316				slide.find('img')
3317					.one('load', function(){
3318						// set the width/height of the image if it's too big
3319						slide
3320							.jp_carousel('fitSlide',false);
3321					});
3322			});
3323		},
3324
3325		bestFit : function(){
3326			var max        = gallery.jp_carousel('slideDimensions'),
3327			    orig       = this.jp_carousel('originalDimensions'),
3328			    orig_ratio = orig.width / orig.height,
3329			    w_ratio    = 1,
3330			    h_ratio    = 1,
3331			    width, height;
3332
3333			if ( orig.width > max.width ) {
3334				w_ratio = max.width / orig.width;
3335			}
3336			if ( orig.height > max.height ) {
3337				h_ratio = max.height / orig.height;
3338			}
3339
3340			if ( w_ratio < h_ratio ) {
3341				width = max.width;
3342				height = Math.floor( width / orig_ratio );
3343			} else if ( h_ratio < w_ratio ) {
3344				height = max.height;
3345				width = Math.floor( height * orig_ratio );
3346			} else {
3347				width = orig.width;
3348				height = orig.height;
3349			}
3350
3351			return {
3352				width: width,
3353				height: height
3354			};
3355		},
3356
3357		fitInfo : function(/*animated*/){
3358			var current = this.jp_carousel('selectedSlide'),
3359				size = current.jp_carousel('bestFit');
3360
3361			photo_info.css({
3362				'left'  : Math.floor( (info.width() - size.width) * 0.5 ),
3363				'width' : Math.floor( size.width )
3364			});
3365
3366			return this;
3367		},
3368
3369		fitMeta : function(animated){
3370			var newInfoTop   = { top: Math.floor( $(window).height() / 100 * proportion + 5 ) + 'px' };
3371			var newLeftWidth = { width: ( info.width() - (imageMeta.width() + 80) ) + 'px' };
3372
3373			if (animated) {
3374				info.animate(newInfoTop);
3375				leftColWrapper.animate(newLeftWidth);
3376			} else {
3377				info.animate(newInfoTop);
3378				leftColWrapper.css(newLeftWidth);
3379			}
3380		},
3381
3382		fitSlide : function(/*animated*/){
3383			return this.each(function(){
3384				var $this      = $(this),
3385				    dimensions = $this.jp_carousel('bestFit'),
3386				    method     = 'css',
3387				    max        = gallery.jp_carousel('slideDimensions');
3388
3389				dimensions.left = 0;
3390				dimensions.top = Math.floor( (max.height - dimensions.height) * 0.5 ) + 40;
3391				$this[method](dimensions);
3392			});
3393		},
3394
3395		texturize : function(text) {
3396				text = '' + text; // make sure we get a string. Title "1" came in as int 1, for example, which did not support .replace().
3397				text = text.replace(/'/g, '&#8217;').replace(/&#039;/g, '&#8217;').replace(/[\u2019]/g, '&#8217;');
3398				text = text.replace(/"/g, '&#8221;').replace(/&#034;/g, '&#8221;').replace(/&quot;/g, '&#8221;').replace(/[\u201D]/g, '&#8221;');
3399				text = text.replace(/([\w]+)=&#[\d]+;(.+?)&#[\d]+;/g, '$1="$2"'); // untexturize allowed HTML tags params double-quotes
3400				return $.trim(text);
3401		},
3402
3403		initSlides : function(items, start_index){
3404			if ( items.length < 2 ) {
3405				$( '.jp-carousel-next-button, .jp-carousel-previous-button' ).hide();
3406			} else {
3407				$( '.jp-carousel-next-button, .jp-carousel-previous-button' ).show();
3408			}
3409
3410			// Calculate the new src.
3411			items.each(function(/*i*/){
3412				var src_item  = $(this),
3413					orig_size = src_item.data('orig-size') || '',
3414					max       = gallery.jp_carousel('slideDimensions'),
3415					parts     = orig_size.split(','),
3416					medium_file     = src_item.data('medium-file') || '',
3417					large_file      = src_item.data('large-file') || '',
3418					src;
3419				orig_size = {width: parseInt(parts[0], 10), height: parseInt(parts[1], 10)};
3420
3421// @start-hide-in-jetpack
3422				 if ( 'undefined' !== typeof wpcom ) {
3423					src = src_item.attr('src') || src_item.attr('original') || src_item.data('original') || src_item.data('lazy-src');
3424					if (src.indexOf('imgpress') !== -1) {
3425						src = src_item.data('orig-file');
3426					}
3427					src = wpcom.carousel.addWidthToImageURL( src, wpcom.carousel.findFirstLargeEnoughWidth( orig_size.width, orig_size.height, max.width, max.height ) );
3428				} else {
3429
3430// @end-hide-in-jetpack
3431					src = src_item.data('orig-file');
3432
3433					src = gallery.jp_carousel('selectBestImageSize', {
3434						orig_file   : src,
3435						orig_width  : orig_size.width,
3436						orig_height : orig_size.height,
3437						max_width   : max.width,
3438						max_height  : max.height,
3439						medium_file : medium_file,
3440						large_file  : large_file
3441					});
3442// @start-hide-in-jetpack
3443				 } // end else of if ( 'undefined' != typeof wpcom )
3444// @end-hide-in-jetpack
3445
3446				// Set the final src
3447				$(this).data( 'gallery-src', src );
3448			});
3449
3450			// If the start_index is not 0 then preload the clicked image first.
3451			if ( 0 !== start_index ) {
3452				$('<img/>')[0].src = $(items[start_index]).data('gallery-src');
3453			}
3454
3455			var useInPageThumbnails = items.first().closest( '.tiled-gallery.type-rectangular' ).length > 0;
3456
3457			// create the 'slide'
3458			items.each(function(i){
3459				var src_item        = $(this),
3460					reblogged       = src_item.data( 'reblogged' ) || 0, // @hide-in-jetpack
3461					attachment_id   = src_item.data('attachment-id') || 0,
3462					comments_opened = src_item.data('comments-opened') || 0,
3463					image_meta      = src_item.data('image-meta') || {},
3464					orig_size       = src_item.data('orig-size') || '',
3465					thumb_size      = { width : src_item[0].naturalWidth, height : src_item[0].naturalHeight },
3466					title           = src_item.data('image-title') || '',
3467					description     = src_item.data('image-description') || '',
3468					caption         = src_item.parents('.gallery-item').find('.gallery-caption').html() || '',
3469					src		= src_item.data('gallery-src') || '',
3470					medium_file     = src_item.data('medium-file') || '',
3471					large_file      = src_item.data('large-file') || '',
3472					orig_file	= src_item.data('orig-file') || '';
3473
3474				var tiledCaption = src_item.parents('div.tiled-gallery-item').find('div.tiled-gallery-caption').html();
3475				if ( tiledCaption ) {
3476					caption = tiledCaption;
3477				}
3478
3479				if ( attachment_id && orig_size.length ) {
3480					title       = gallery.jp_carousel('texturize', title);
3481					description = gallery.jp_carousel('texturize', description);
3482					caption     = gallery.jp_carousel('texturize', caption);
3483
3484					// Initially, the image is a 1x1 transparent gif.  The preview is shown as a background image on the slide itself.
3485					var image = $( '<img/>' )
3486						.attr( 'src', 'data:image/gif;base64,R0lGODlhAQABAIAAAAAAAP///yH5BAEAAAAALAAAAAABAAEAAAIBRAA7' )
3487						.css( 'width', '100%' )
3488						.css( 'height', '100%' );
3489
3490					var slide = $('<div class="jp-carousel-slide" itemprop="associatedMedia" itemscope itemtype="https://schema.org/ImageObject"></div>')
3491							.hide()
3492							.css({
3493								//'position' : 'fixed',
3494								'left'     : i < start_index ? -1000 : gallery.width()
3495							})
3496							.append( image )
3497							.appendTo(gallery)
3498							.data('src', src )
3499							.data('title', title)
3500							.data('desc', description)
3501							.data('caption', caption)
3502							.data('attachment-id', attachment_id)
3503							.data('permalink', src_item.parents('a').attr('href'))
3504							.data('orig-size', orig_size)
3505							.data('comments-opened', comments_opened)
3506							.data('image-meta', image_meta)
3507							.data('medium-file', medium_file)
3508							.data('large-file', large_file)
3509							.data('orig-file', orig_file)
3510							.data('thumb-size', thumb_size)
3511							.data( 'reblogged', reblogged ) // @hide-in-jetpack
3512							;
3513
3514						if ( useInPageThumbnails ) {
3515							// Use the image already loaded in the gallery as a preview.
3516							slide
3517								.data( 'preview-image', src_item.attr( 'src' ) )
3518								.css( {
3519									'background-image' : 'url("' + src_item.attr( 'src' ) + '")',
3520									'background-size' : '100% 100%',
3521									'background-position' : 'center center'
3522								} );
3523						}
3524
3525						slide.jp_carousel( 'fitSlide', false );
3526				}
3527			});
3528			return this;
3529		},
3530
3531		selectBestImageSize: function(args) {
3532			if ( 'object' !== typeof args ) {
3533				args = {};
3534			}
3535
3536			if ( 'undefined' === typeof args.orig_file ) {
3537				return '';
3538			}
3539
3540			if ( 'undefined' === typeof args.orig_width || 'undefined' === typeof args.max_width ) {
3541				return args.orig_file;
3542			}
3543
3544			if ( 'undefined' === typeof args.medium_file || 'undefined' === typeof args.large_file ) {
3545				return args.orig_file;
3546			}
3547
3548			// Check if the image is being served by Photon (using a regular expression on the hostname).
3549
3550			var imageLinkParser = document.createElement( 'a' );
3551			imageLinkParser.href = args.large_file;
3552
3553			var isPhotonUrl = ( imageLinkParser.hostname.match( /^i[\d]{1}.wp.com$/i ) != null );
3554
3555			var medium_size_parts	= gallery.jp_carousel( 'getImageSizeParts', args.medium_file, args.orig_width, isPhotonUrl );
3556			var large_size_parts	= gallery.jp_carousel( 'getImageSizeParts', args.large_file, args.orig_width, isPhotonUrl );
3557
3558			var large_width       = parseInt( large_size_parts[0], 10 ),
3559				large_height      = parseInt( large_size_parts[1], 10 ),
3560				medium_width      = parseInt( medium_size_parts[0], 10 ),
3561				medium_height     = parseInt( medium_size_parts[1], 10 );
3562
3563			// Assign max width and height.
3564			args.orig_max_width  = args.max_width;
3565			args.orig_max_height = args.max_height;
3566
3567			// Give devices with a higher devicePixelRatio higher-res images (Retina display = 2, Android phones = 1.5, etc)
3568			if ( 'undefined' !== typeof window.devicePixelRatio && window.devicePixelRatio > 1 ) {
3569				args.max_width  = args.max_width * window.devicePixelRatio;
3570				args.max_height = args.max_height * window.devicePixelRatio;
3571			}
3572
3573			if ( large_width >= args.max_width || large_height >= args.max_height ) {
3574				return args.large_file;
3575			}
3576
3577			if ( medium_width >= args.max_width || medium_height >= args.max_height ) {
3578				return args.medium_file;
3579			}
3580
3581			if ( isPhotonUrl ) {
3582				// args.orig_file doesn't point to a Photon url, so in this case we use args.large_file
3583				// to return the photon url of the original image.
3584				var largeFileIndex = args.large_file.lastIndexOf( '?' );
3585				var origPhotonUrl = args.large_file;
3586				if ( -1 !== largeFileIndex ) {
3587					origPhotonUrl = args.large_file.substring( 0, largeFileIndex );
3588					// If we have a really large image load a smaller version
3589					// that is closer to the viewable size
3590					if ( args.orig_width > args.max_width || args.orig_height > args.max_height ) {
3591						origPhotonUrl += '?fit=' + args.orig_max_width + '%2C' + args.orig_max_height;
3592					}
3593				}
3594				return origPhotonUrl;
3595			}
3596
3597			return args.orig_file;
3598		},
3599
3600		getImageSizeParts: function( file, orig_width, isPhotonUrl ) {
3601			var size		= isPhotonUrl ?
3602							file.replace( /.*=([\d]+%2C[\d]+).*$/, '$1' ) :
3603							file.replace( /.*-([\d]+x[\d]+)\..+$/, '$1' );
3604
3605			var size_parts  = ( size !== file ) ?
3606							( isPhotonUrl ? size.split( '%2C' ) : size.split( 'x' ) ) :
3607							[ orig_width, 0 ];
3608
3609			// If one of the dimensions is set to 9999, then the actual value of that dimension can't be retrieved from the url.
3610			// In that case, we set the value to 0.
3611			if ( '9999' === size_parts[0] ) {
3612				size_parts[0] = '0';
3613			}
3614
3615			if ( '9999' === size_parts[1] ) {
3616				size_parts[1] = '0';
3617			}
3618
3619			return size_parts;
3620		},
3621
3622// @start-hide-in-jetpack
3623        show_reblog_box: function() {
3624            $('#carousel-reblog-box textarea').val(jetpackCarouselStrings.reblog_add_thoughts);
3625            //t.addClass('selected');
3626            $('#carousel-reblog-box p.response').remove();
3627            $('#carousel-reblog-box div.submit, #carousel-reblog-box div.submit span.canceltext').show();
3628            $('#carousel-reblog-box div.submit input[type=submit]').prop('disabled', false);
3629
3630            var current = $('.jp-carousel div.selected');
3631            $('#carousel-reblog-box input#carousel-reblog-post-url').val( current.data('permalink') );
3632            $('#carousel-reblog-box input#carousel-reblog-post-title').val( $('div.jp-carousel-info').children('h2').text() );
3633
3634            $('div.jp-carousel-info').append( $('#carousel-reblog-box') ).children().fadeOut('fast');
3635            $('#carousel-reblog-box').fadeIn('fast');
3636        },
3637
3638        hide_reblog_box: function () {
3639            $( 'div.jp-carousel-info' ).children().not( '#carousel-reblog-box' ).fadeIn( 'fast' );
3640            $( '#carousel-reblog-box' ).fadeOut( 'fast' );
3641        },
3642// @end-hide-in-jetpack
3643
3644		originalDimensions: function() {
3645			var splitted = $(this).data('orig-size').split(',');
3646			return {width: parseInt(splitted[0], 10), height: parseInt(splitted[1], 10)};
3647		},
3648
3649		format: function( args ) {
3650			if ( 'object' !== typeof args ) {
3651				args = {};
3652			}
3653			if ( ! args.text || 'undefined' === typeof args.text ) {
3654				return;
3655			}
3656			if ( ! args.replacements || 'undefined' === typeof args.replacements ) {
3657				return args.text;
3658			}
3659			return args.text.replace(/{(\d+)}/g, function( match, number ) {
3660				return typeof args.replacements[number] !== 'undefined' ? args.replacements[number] : match;
3661			});
3662		},
3663
3664		/**
3665		 * Returns a number in a fraction format that represents the shutter speed.
3666		 * @param Number speed
3667		 * @return String
3668		 */
3669		shutterSpeed: function( speed ) {
3670			var denominator;
3671
3672			// round to one decimal if value >
3672 1s by multiplying it by 10, rounding, then dividing by 10 again
3673			if ( speed >= 1 ) {
3674				return Math.round( speed * 10 ) / 10 + 's';
3675			}
3676
3677			// If the speed is less than one, we find the denominator by inverting
3678			// the number. Since cameras usually use rational numbers as shutter
3679			// speeds, we should get a nice round number. Or close to one in cases
3680			// like 1/30. So we round it.
3681			denominator = Math.round( 1 / speed );
3682
3683			return '1/' + denominator + 's';
3684		},
3685
3686		parseTitleDesc: function( value ) {
3687			if ( !value.match(' ') && value.match('_') ) {
3688				return '';
3689			}
3690			// Prefix list originally based on http://commons.wikimedia.org/wiki/MediaWiki:Filename-prefix-blacklist
3691			$([
3692				'CIMG',                   // Casio
3693				'DSC_',                   // Nikon
3694				'DSCF',                   // Fuji
3695				'DSCN',                   // Nikon
3696				'DUW',                    // some mobile phones
3697				'GEDC',                   // GE
3698				'IMG',                    // generic
3699				'JD',                     // Jenoptik
3700				'MGP',                    // Pentax
3701				'PICT',                   // misc.
3702				'Imagen',                 // misc.
3703				'Foto',                   // misc.
3704				'DSC',                    // misc.
3705				'Scan',                   // Scanners
3706				'SANY',                   // Sanyo
3707				'SAM',                    // Samsung
3708				'Screen Shot [0-9]+'      // Mac screenshots
3709			])
3710			.each(function(key, val){
3711				var regex = new RegExp('^' + val);
3712				if ( regex.test(value) ) {
3713					value = '';
3714					return;
3715				}
3716			});
3717			return value;
3718		},
3719
3720		getTitleDesc: function( data ) {
3721			var title ='', desc = '', markup = '', target;
3722
3723			target = $( 'div.jp-carousel-titleanddesc', 'div.jp-carousel-wrap' );
3724			target.hide();
3725
3726			title = gallery.jp_carousel('parseTitleDesc', data.title) || '';
3727			desc  = gallery.jp_carousel('parseTitleDesc', data.desc)  || '';
3728
3729			if ( title.length || desc.length ) {
3730				// Convert from HTML to plain text (including HTML entities decode, etc)
3731				if ( $('<div />').html( title ).text() === $('<div />').html( desc ).text() ) {
3732					title = '';
3733				}
3734
3735				markup  = ( title.length ) ? '<div class="jp-carousel-titleanddesc-title">' + title + '</div>' : '';
3736				markup += ( desc.length )  ? '<div class="jp-carousel-titleanddesc-desc">' + desc + '</div>'   : '';
3737
3738				target.html( markup ).fadeIn('slow');
3739			}
3740
3741			$( 'div#jp-carousel-comment-form-container' ).css('margin-top', '20px');
3742			$( 'div#jp-carousel-comments-loading' ).css('margin-top', '20px');
3743		},
3744
3745		// updateExif updates the contents of the exif UL (.jp-carousel-image-exif)
3746		updateExif: function( meta ) {
3747			if ( !meta || 1 !== Number( jetpackCarouselStrings.display_exif ) ) {
3748				return false;
3749			}
3750
3751			var $ul = $( '<ul class=\'jp-carousel-image-exif\'></ul>' );
3752
3753			$.each( meta, function( key, val ) {
3754				if ( 0 === parseFloat(val) || !val.length || -1 === $.inArray( key, $.makeArray( jetpackCarouselStrings.meta_data ) ) ) {
3755					return;
3756				}
3757
3758				switch( key ) {
3759					case 'focal_length':
3760						val = val + 'mm';
3761						break;
3762					case 'shutter_speed':
3763						val = gallery.jp_carousel('shutterSpeed', val);
3764						break;
3765					case 'aperture':
3766						val = 'f/' + val;
3767						break;
3768				}
3769
3770				$ul.append( '<li><h5>' + jetpackCarouselStrings[key] + '</h5>' + val + '</li>' );
3771			});
3772
3773			// Update (replace) the content of the ul
3774			$( 'div.jp-carousel-image-meta ul.jp-carousel-image-exif' ).replaceWith( $ul );
3775		},
3776
3777		// updateFullSizeLink updates the contents of the jp-carousel-image-download link
3778		updateFullSizeLink: function(current) {
3779			if(!current || !current.data) {
3780				return false;
3781			}
3782			var original,
3783				origSize = current.data('orig-size').split(',' ),
3784				imageLinkParser = document.createElement( 'a' );
3785
3786			imageLinkParser.href = current.data( 'src' ).replace( /\?.+$/, '' );
3787
3788			// Is this a Photon URL?
3789			if ( imageLinkParser.hostname.match( /^i[\d]{1}.wp.com$/i ) !== null ) {
3790				original = imageLinkParser.href;
3791			} else {
3792				original = current.data('orig-file').replace(/\?.+$/, '');
3793			}
3794
3795			var permalink = $( '<a>'+gallery.jp_carousel('format', {'text': jetpackCarouselStrings.download_original, 'replacements': origSize})+'</a>' )
3796				.addClass( 'jp-carousel-image-download' )
3797				.attr( 'href', original )
3798				.attr( 'target', '_blank' );
3799
3800			// Update (replace) the content of the anchor
3801			$( 'div.jp-carousel-image-meta a.jp-carousel-image-download' ).replaceWith( permalink );
3802		},
3803
3804		updateMap: function( meta ) {
3805			if ( !meta.latitude || !meta.longitude || 1 !== Number( jetpackCarouselStrings.display_geo ) ) {
3806				return;
3807			}
3808
3809			var latitude  = meta.latitude,
3810				longitude = meta.longitude,
3811				$metabox  = $( 'div.jp-carousel-image-meta', 'div.jp-carousel-wrap' ),
3812				$mapbox   = $( '<div></div>' ),
3813				style     = '&scale=2&style=feature:all|element:all|invert_lightness:true|hue:0x0077FF|saturation:-50|lightness:-5|gamma:0.91';
3814
3815			$mapbox
3816				.addClass( 'jp-carousel-image-map' )
3817				.html( '<img width="154" height="154" src="https://maps.googleapis.com/maps/api/staticmap?\
3818							center=' + latitude + ',' + longitude + '&\
3819							zoom=8&\
3820							size=154x154&\
3821							sensor=false&\
3822							markers=size:medium%7Ccolor:blue%7C' + latitude + ',' + longitude + style +'" class="gmap-main" />\
3823							\
3824						<div class="gmap-topright"><div class="imgclip"><img width="175" height="154" src="https://maps.googleapis.com/maps/api/staticmap?\
3825							center=' + latitude + ',' + longitude + '&\
3826							zoom=3&\
3827							size=175x154&\
3828							sensor=false&\
3829							markers=size:small%7Ccolor:blue%7C' + latitude + ',' + longitude + style + '"c /></div></div>\
3830							\
3831						' )
3832				.prependTo( $metabox );
3833		},
3834
3835		testCommentsOpened: function( opened ) {
3836			if ( 1 === parseInt( opened, 10 ) ) {
3837// @start-hide-in-jetpack
3838				if ( 1 === Number( jetpackCarouselStrings.is_logged_in ) ) {
3839					$('.jp-carousel-commentlink').fadeIn('fast');
3840				} else {
3841// @end-hide-in-jetpack
3842					$('.jp-carousel-buttons').fadeIn('fast');
3843// @start-hide-in-jetpack
3844				}
3845// @end-hide-in-jetpack
3846				commentForm.fadeIn('fast');
3847			} else {
3848// @start-hide-in-jetpack
3849				if ( 1 === Number( jetpackCarouselStrings.is_logged_in ) ) {
3850					$('.jp-carousel-commentlink').fadeOut('fast');
3851				} else {
3852// @end-hide-in-jetpack
3853					$('.jp-carousel-buttons').fadeOut('fast');
3854// @start-hide-in-jetpack
3855				}
3856// @end-hide-in-jetpack
3857				commentForm.fadeOut('fast');
3858			}
3859		},
3860
3861		getComments: function( args ) {
3862			clearInterval( commentInterval );
3863
3864			if ( 'object' !== typeof args ) {
3865				return;
3866			}
3867
3868			if ( 'undefined' === typeof args.attachment_id || ! args.attachment_id ) {
3869				return;
3870			}
3871
3872			if ( ! args.offset || 'undefined' === typeof args.offset || args.offset < 1 ) {
3873				args.offset = 0;
3874			}
3875
3876			var comments        = $('.jp-carousel-comments'),
3877				commentsLoading = $('#jp-carousel-comments-loading').show();
3878
3879			if ( args.clear ) {
3880				comments.hide().empty();
3881			}
3882
3883			$.ajax({
3884				type:       'GET',
3885				url:        jetpackCarouselStrings.ajaxurl,
3886				dataType:   'json',
3887				data: {
3888					action: 'get_attachment_comments',
3889					nonce:  jetpackCarouselStrings.nonce,
3890					id:     args.attachment_id,
3891					offset: args.offset
3892				},
3893				success: function(data/*, status, xhr*/) {
3894					if ( args.clear ) {
3895						comments.fadeOut('fast').empty();
3896					}
3897
3898					$( data ).each(function(){
3899						var comment = $('<div></div>')
3900							.addClass('jp-carousel-comment')
3901							.attr('id', 'jp-carousel-comment-' + this['id'])
3902							.html(
3903								  '<div class="comment-gravatar">' +
3904								    this['gravatar_markup'] +
3905								  '</div>' +
3906								  '<div class="comment-author">' +
3907								    this['author_markup'] +
3908								  '</div>' +
3909								  '<div class="comment-date">' +
3910								    this['date_gmt'] +
3911								  '</div>' +
3912								  '<div class="comment-content">' +
3913								    this['content'] +
3914								  '</div>'
3915							);
3916						comments.append(comment);
3917
3918						// Set the interval to check for a new page of comments.
3919						clearInterval( commentInterval );
3920						commentInterval = setInterval( function() {
3921							if ( ( $('.jp-carousel-overlay').height() - 150 ) < $('.jp-carousel-wrap').scrollTop() + $(window).height() ) {
3922								gallery.jp_carousel('getComments',{ attachment_id: args.attachment_id, offset: args.offset + 10, clear: false });
3923								clearInterval( commentInterval );
3924							}
3925						}, 300 );
3926					});
3927
3928					// Verify (late) that the user didn't repeatldy click the arrows really fast, in which case the requested
3929					// attachment id might no longer match the current attachment id by the time we get the data back or a now
3930					// registered infiniscroll event kicks in, so we don't ever display comments for the wrong image by mistake.
3931					var current = $('.jp-carousel div.selected');
3932					if ( current && current.data && current.data('attachment-id') != args.attachment_id ) { // jshint ignore:line
3933						comments.fadeOut('fast');
3934						comments.empty();
3935						return;
3936					}
3937
3938					// Increase the height of the background, semi-transparent overlay to match the new length of the comments list.
3939					$('.jp-carousel-overlay').height( $(window).height() + titleAndDescription.height() + commentForm.height() + ( (comments.height() > 0) ? comments.height() : imageMeta.height() ) + 200 );
3940
3941					comments.show();
3942					commentsLoading.hide();
3943				},
3944				error: function(xhr, status, error) {
3945					// TODO: proper error handling
3946					console.log( 'Comment get fail...', xhr, status, error );
3947					comments.fadeIn('fast');
3948					commentsLoading.fadeOut('fast');
3949				}
3950			});
3951		},
3952
3953		postCommentError: function(args) {
3954			if ( 'object' !== typeof args ) {
3955				args = {};
3956			}
3957			if ( ! args.field || 'undefined' === typeof args.field ||  ! args.error || 'undefined' === typeof args.error ) {
3958				return;
3959			}
3960			$('#jp-carousel-comment-post-results').slideUp('fast').html('<span class="jp-carousel-comment-post-error">'+args.error+'</span>').slideDown('fast');
3961			$('#jp-carousel-comment-form-spinner').spin(false);
3962		},
3963
3964		setCommentIframeSrc: function(attachment_id) {
3965			var iframe = $('#jp-carousel-comment-iframe');
3966			// Set the proper irame src for the current attachment id
3967			if (iframe && iframe.length) {
3968				iframe.attr('src', iframe.attr('src').replace(/(postid=)\d+/, '$1'+attachment_id) );
3969				iframe.attr('src', iframe.attr('src').replace(/(%23.+)?$/, '%23jp-carousel-'+attachment_id) );
3970			}
3971		},
3972
3973		clearCommentTextAreaValue: function() {
3974			var commentTextArea = $('#jp-carousel-comment-form-comment-field');
3975			if ( commentTextArea ) {
3976				commentTextArea.val('');
3977			}
3978		},
3979
3980		nextSlide : function () {
3981			var slides = this.jp_carousel( 'slides' );
3982			var selected = this.jp_carousel( 'selectedSlide' );
3983
3984			if ( selected.length === 0 || ( slides.length > 2 && selected.is( slides.last() ) ) ) {
3985				return slides.first();
3986			}
3987
3988			return selected.next();
3989		},
3990
3991		prevSlide : function () {
3992			var slides = this.jp_carousel( 'slides' );
3993			var selected = this.jp_carousel( 'selectedSlide' );
3994
3995			if ( selected.length === 0 || ( slides.length > 2 && selected.is( slides.first() ) ) ) {
3996				return slides.last();
3997			}
3998
3999			return selected.prev();
4000		},
4001
4002		loadFullImage : function ( slide ) {
4003			var image = slide.find( 'img:first' );
4004
4005			if ( ! image.data( 'loaded' ) ) {
4006				// If the width of the slide is smaller than the width of the "thumbnail" we're already using,
4007				// don't load the full image.
4008
4009				image.on( 'load.jetpack', function () {
4010					image.off( 'load.jetpack' );
4011					$( this ).closest( '.jp-carousel-slide' ).css( 'background-image', '' );
4012				} );
4013
4014				if ( ! slide.data( 'preview-image' ) || ( slide.data( 'thumb-size' ) && slide.width() > slide.data( 'thumb-size' ).width ) ) {
4015					image.attr( 'src', image.closest( '.jp-carousel-slide' ).data( 'src' ) ).attr('itemprop', 'image');
4016				} else {
4017					image.attr( 'src', slide.data( 'preview-image' ) ).attr('itemprop', 'image');
4018				}
4019
4020				image.data( 'loaded', 1 );
4021			}
4022		},
4023
4024		hasMultipleImages : function () {
4025			return gallery.jp_carousel('slides').length > 1;
4026		}
4027	};
4028
4029	$.fn.jp_carousel = function(method){
4030		// ask for the HTML of the gallery
4031		// Method calling logic
4032		if ( methods[method] ) {
4033			return methods[ method ].apply( this, Array.prototype.slice.call( arguments, 1 ));
4034		} else if ( typeof method === 'object' || ! method ) {
4035			return methods.open.apply( this, arguments );
4036		} else {
4037			$.error( 'Method ' +	method + ' does not exist on jQuery.jp_carousel' );
4038		}
4039
4040	};
4041
4042	// register the event listener for starting the gallery
4043	$(document.body).on('click.jp-carousel', 'div.gallery, div.tiled-gallery, ul.wp-block-gallery, div.wp-block-jetpack-tiled-gallery, a.single-image-gallery', function( e ) {
4044		if ( ! $(this).jp_carousel( 'testForData', e.currentTarget ) ) {
4045			return;
4046		}
4047
4048		// Do not open the modal if we are looking at a gallery caption from before WP5, which may contain a link.
4049		if ( $(e.target).parent().hasClass('gallery-caption') ) {
4050			return;
4051		}
4052
4053		// Do not open the modal if we are looking at a caption of a gallery block, which may contain a link.
4054		if ( $(e.target).parent().is('figcaption') ) {
4055			return;
4056		}
4057
4058		e.preventDefault();
4059
4060		// Stopping propagation in case there are parent elements
4061		// with .gallery or .tiled-gallery class
4062		e.stopPropagation();
4063		$(this).jp_carousel('open', { start_index: $(this).find('.gallery-item, .tiled-gallery-item, .blocks-gallery-item, .tiled-gallery__item').index($(e.target).parents('.gallery-item, .tiled-gallery-item, .blocks-gallery-item, .tiled-gallery__item'))});
4064	});
4065
4066	// handle lightbox (single image gallery) for images linking to 'Attachment Page'
4067	if ( 1 === Number( jetpackCarouselStrings.single_image_gallery ) ) {
4068		processSingleImageGallery();
4069		$( document.body ).on( 'post-load', function() {
4070			processSingleImageGallery();
4071		} );
4072	}
4073
4074	// Makes carousel work on page load and when back button leads to same URL with carousel hash (ie: no actual document.ready trigger)
4075	$( window ).on( 'hashchange.jp-carousel', function () {
4076
4077		var hashRegExp = /jp-carousel-(\d+)/,
4078			matches, attachmentId, galleries, selectedThumbnail;
4079
4080		if ( ! window.location.hash || ! hashRegExp.test( window.location.hash ) ) {
4081			if ( gallery && gallery.opened ) {
4082				container.jp_carousel( 'close' );
4083			}
4084
4085			return;
4086		}
4087
4088		if ( ( window.location.hash === last_known_location_hash ) && gallery.opened ) {
4089			return;
4090		}
4091
4092		if ( window.location.hash && gallery && !gallery.opened && history.back) {
4093			history.back();
4094			return;
4095		}
4096
4097		last_known_location_hash = window.location.hash;
4098		matches = window.location.hash.match( hashRegExp );
4099		attachmentId = parseInt( matches[1], 10 );
4100		galleries = $( 'div.gallery, div.tiled-gallery, a.single-image-gallery, ul.wp-block-gallery, div.wp-block-jetpack-tiled-gallery' );
4101
4102		// Find the first thumbnail that matches the attachment ID in the location
4103		// hash, then open the gallery that contains it.
4104		galleries.each( function( _, galleryEl ) {
4105			$( galleryEl ).find('img').each( function( imageIndex, imageEl ) {
4106				if ( $( imageEl ).data( 'attachment-id' ) === parseInt( attachmentId, 10 ) ) {
4107					selectedThumbnail = { index: imageIndex, gallery: galleryEl };
4108					return false;
4109				}
4110			});
4111
4112			if ( selectedThumbnail ) {
4113				$( selectedThumbnail.gallery )
4114					.jp_carousel( 'openOrSelectSlide', selectedThumbnail.index );
4115				return false;
4116			}
4117		});
4118	});
4119
4120	if ( window.location.hash ) {
4121		$( window ).trigger( 'hashchange' );
4122	}
4123});
4124
4125/**
4126 * jQuery Plugin to obtain touch gestures from iPhone, iPod Touch and iPad, should also work with Android mobile phones (not tested yet!)
4127 * Common usage: wipe images (left and right to show the previous or next image)
4128 *
4129 * @author Andreas Waltl, netCU Internetagentur (http://www.netcu.de)
4130 * Version 1.1.1, modified to pass the touchmove event to the callbacks.
4131 */
4132(function($) {
4133$.fn.touchwipe = function(settings) {
4134	var config = {
4135			min_move_x: 20,
4136			min_move_y: 20,
4137			wipeLeft: function(/*e*/) { },
4138			wipeRight: function(/*e*/) { },
4139			wipeUp: function(/*e*/) { },
4140			wipeDown: function(/*e*/) { },
4141			preventDefaultEvents: true
4142	};
4143
4144	if (settings) {
4145		$.extend(config, settings);
4146	}
4147
4148	this.each(function() {
4149		var startX;
4150		var startY;
4151		var isMoving = false;
4152
4153		function cancelTouch() {
4154			this.removeEventListener('touchmove', onTouchMove);
4155			startX = null;
4156			isMoving = false;
4157		}
4158
4159		function onTouchMove(e) {
4160			if(config.preventDefaultEvents) {
4161				e.preventDefault();
4162			}
4163			if(isMoving) {
4164				var x = e.touches[0].pageX;
4165				var y = e.touches[0].pageY;
4166				var dx = startX - x;
4167				var dy = startY - y;
4168				if(Math.abs(dx) >= config.min_move_x) {
4169					cancelTouch();
4170					if(dx > 0) {
4171						config.wipeLeft(e);
4172					} else {
4173						config.wipeRight(e);
4174					}
4175				}
4176				else if(Math.abs(dy) >= config.min_move_y) {
4177						cancelTouch();
4178						if(dy > 0) {
4179							config.wipeDown(e);
4180						} else {
4181							config.wipeUp(e);
4182						}
4183					}
4184			}
4185		}
4186
4187		function onTouchStart(e)
4188		{
4189			if (e.touches.length === 1) {
4190				startX = e.touches[0].pageX;
4191				startY = e.touches[0].pageY;
4192				isMoving = true;
4193				this.addEventListener('touchmove', onTouchMove, false);
4194			}
4195		}
4196		if ('ontouchstart' in document.documentElement) {
4197			this.addEventListener('touchstart', onTouchStart, false);
4198		}
4199	});
4200
4201	return this;
4202};
4203})(jQuery);
4204;
4205/*	SWFObject v2.2 <http://code.google.com/p/swfobject/> 
4206	is released under the MIT License <http://www.opensource.org/licenses/mit-license.php> 
4207*/
4208var 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)}}
4208else{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}}}}();;
4209/*!
4210 * VideoPress JavaScript Loader 1.09
4211 *
4212 * Copyright 2011 Automattic Inc.
4213 * Licensed under GNU General Public License (GPL) Version 2 or later
4214 * http://www.gnu.org/licenses/gpl-2.0.html
4215 */
4216
4217// jQuery.type() was introduced in 1.4.3, WP 3.0 has 1.4.2 
4218// To maintain backwards compatibility, insert our own version if it doesn't exist 
4219if ( typeof jQuery.type != 'function' ) { 
4220	jQuery.extend({
4221		class2type: {},
4222	
4223		type: function( obj ) { 
4224	 		return obj == null ? 
4225	 	    	String( obj ) : 
4226	 	    	jQuery.class2type[ toString.call(obj) ] || "object"; 
4227		}
4228	});
4229	
4230	jQuery.each("Boolean Number String Function Array Date RegExp Object".split(" "), function(i, name) { 
4231	 	jQuery.class2type[ "[object " + name + "]" ] = name.toLowerCase(); 
4232	}); 
4233	
4234} 
4235
4236jQuery.extend({VideoPress: {
4237	error:{
4238		messages:{
4239			age:"You are of insufficient age to view this video.",
4240			error:"Unable to download and play video",
4241			flash:"Error loading Flash on your system",
4242			freedom:'You do not have sufficient <a href="http://www.gnu.org/philosophy/free-sw.html">Freedom levels</a> to view this video.',
4243			incompatible:'VideoPress requires either HTML5 video or <a href="http://www.adobe.com/go/getflashplayer">
4243Adobe Flash Player 10</a> or above for playback.',
4244			incapable:"Your device is incapable of playing the requested video.",
4245			network:"A network error prevented video download and playback"
4246		}
4247	},
4248	support:{
4249		flash: function() {
4250			if ( typeof swfobject !== "undefined" && swfobject.hasFlashPlayerVersion( jQuery.VideoPress.video.flash.min_version ) ) {
4251				return true;
4252			} else {
4253				return false;
4254			}
4255		},
4256		html5Video: function(sourcetype) {
4257			var v = document.createElement("video");
4258			if ( !!v.canPlayType ) {
4259				if ( jQuery.type(sourcetype)==="string" ) {
4260					return !!v.canPlayType(sourcetype).replace(/no/,"");
4261				}
4262				return true;
4263			} else {
4264				return false;
4265			}
4266		}
4267	},
4268	data:[],
4269	analytics:{
4270		wpcom:{
4271			base_uri:"https://pixel.wp.com/v.gif?",
4272			params:function(guid, filetype) {
4273				var data = jQuery.VideoPress.data[guid];
4274				if ( !jQuery.isPlainObject(data) ) {
4275					return;
4276				}
4277
4278				var params = {blog:data.blog,post:data.post,page_url:top.document.URL};
4279				if ( filetype==="mp4" && data.mp4 && data.mp4.size ) {
4280					params.video_fmt = "fmt_" + data.mp4.size;
4281				} else {
4282					params.video_fmt = "fmt_std";
4283				}
4284				return params;
4285			},
4286			send:function(params) {
4287				var beacon = new Image();
4288				beacon.src = jQuery.VideoPress.analytics.wpcom.base_uri + jQuery.param(params);
4289				beacon=null;
4290			}
4291		},
4292		ga:{
4293			is_active:function(){
4294				if ( (typeof _gaq !== "undefined") && jQuery.type( _gaq )==="array" ) {
4295					return true;
4296				} else {
4297					return false;
4298				}
4299			}
4300		},
4301		/*comscore:{
4302			is_active()function(){
4303				if ( typeof COMSCORE !== "undefined" )
4304					return true;
4305			}
4306		}*/
4307		impression:function(guid) {
4308			var params = jQuery.VideoPress.analytics.wpcom.params( guid, "mp4" );
4309			if ( !jQuery.isPlainObject( params ) ) {
4310				return;
4311			}
4312			params.video_impression = 1;
4313			jQuery.VideoPress.analytics.wpcom.send( params );
4314		},
4315		played:function(guid, filetype) {
4316			var params = jQuery.VideoPress.analytics.wpcom.params( guid, filetype );
4317			if ( !jQuery.isPlainObject( params ) ) {
4318				return;
4319			}
4320			params.video_play = 1;
4321			jQuery.VideoPress.analytics.wpcom.send( params );
4322
4323			if ( jQuery.VideoPress.analytics.ga.is_active() ) {
4324				/* Google Analytics tracks unique events per visit.
4325				 * Unique video value omitted since two videos on the same page that are both played would only be recorded as one play event.
4326				 */
4327				_gaq.push( ["_trackEvent", "Videos", "Play" ] );
4328			}
4329		},
4330		watched:function( guid, filetype, current_time, initial_time ) {
4331			if ( jQuery.VideoPress.data[guid].playback === undefined ) {
4332				var start = 0;
4333				if ( initial_time !== undefined && jQuery.type(initial_time)==="number" && initial_time > 0 ) {
4334					if ( current_time > initial_time ) {
4335						start = current_time - initial_time;
4336					}
4337				}
4338				jQuery.VideoPress.data[guid].playback = { last_observed:current_time, time_elapsed:start };
4339			}
4340			var time_watched = current_time - jQuery.VideoPress.data[guid].playback.last_observed;
4341			if ( time_watched > 0 ) {
4342				jQuery.VideoPress.data[guid].playback.time_elapsed += time_watched;
4343				jQuery.VideoPress.data[guid].playback.last_observed = current_time;
4344				if ( jQuery.VideoPress.data[guid].playback.time_elapsed > 15 ) {
4345					var params = jQuery.VideoPress.analytics.wpcom.params( guid, filetype );
4346					if ( !jQuery.isPlainObject( params ) ) {
4347			  			return;
4348					}
4349					params.t = 15;
4350					jQuery.VideoPress.analytics.wpcom.send( params );
4351					jQuery.VideoPress.data[guid].playback.time_elapsed -= 15;
4352				} else if ( current_time === jQuery.VideoPress.data[guid].duration ) {
4353					jQuery.VideoPress.analytics.wpcom.send( params );
4354					jQuery.VideoPress.data[guid].playback.time_elapsed = 0;
4355					delete jQuery.VideoPress.data[guid].playback;
4356				}
4357			}
4358		}
4359	},
4360	requirements:{
4361		isSufficientAge: function( container_el, min_age ) {
4362			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() ) );
4363			var allowed_age = new Date( Date.now() - (1000*60*60*24*365*min_age) );
4364			if ( birthday > allowed_age ) {
4365				container_el.html( '<p class="error" style="color:#fff;font-weight:bold">' + jQuery.VideoPress.error.messages.age + "</p>");
4366			} else {
4367				jQuery.VideoPress.video.play(container_el);
4368			}
4369		},
4370		allowedDomain: function (allowed_domains) {
4371			if ( jQuery.type(allowed_domains)==="array" ) {
4372				if ( jQuery.inArray( top.document.location.hostname, allowed_domains )===-1 ) {
4373					return false;
4374				}
4375			}
4376			return true;
4377		}
4378	},
4379	video:{
4380		flash:{
4381			// Protocol and domain for player_uri and expressinstall set in video.play()
4382			player_uri: ( 'https:' == location.protocol ? 'https://v0.wordpress.com' : 'http://s0.videopress.com' ) + "/player.swf?v=1.04",
4383			min_version:"10.0.0",
4384			params:{wmode:"direct",quality:"autohigh",seamlesstabbing:"true",allowfullscreen:"true",allowscriptaccess:"always",overstretch:"true"},
4385			expressinstall: ( 'https:' == location.protocol ? 'https://v0.wordpress.com' : 'http://s0.videopress.com' ) + "/playerProductInstall.swf",
4386			embedCallback: function(event) {
4387				if ( event.success===false ) {
4388					jQuery("#" + event.id).html("<p>" + jQuery.VideoPress.error.messages.flash + "</p>");
4389				}
4390			}
4391		},
4392		types:{mp4:'video/mp4; codecs="avc1.64001E, mp4a.40.2"',ogv:'video/ogg; codecs="theora, vorbis"'},
4393		canPlay:function () {
4394			if ( jQuery.VideoPress.support.flash() ) {
4395				jQuery.VideoPress.video.playerSupport = "flash";
4396			} else if ( jQuery.VideoPress.support.html5Video() ) {
4397				if ( jQuery.VideoPress.support.html5Video( jQuery.VideoPress.video.types.mp4 ) ) {
4398					jQuery.VideoPress.video.playerSupport = "mp4";
4399				} else if ( jQuery.VideoPress.support.html5Video( jQuery.VideoPress.video.types.ogv ) ) {
4400					jQuery.VideoPress.video.playerSupport = "ogv";
4401				} else {
4402					jQuery.VideoPress.video.playerSupport = "html5";
4403				}
4404			} else {
4405				jQuery.VideoPress.video.playerSupport = "";
4406			}
4407		},
4408		prepare: function ( guid, config, count ) {
4409			var video = jQuery.VideoPress.data[guid][count];
4410			if ( config.container === undefined || jQuery.type(video)!=="object" ) {
4411				return;
4412			}
4413			
4414			var width = 0;
4415			if ( config.width !== undefined ) {
4416				width = config.width;
4417			} else {
4418				config.container.width();
4419			}
4420
4421
4422			var height = 0;
4423			if ( config.height !== undefined ) {
4424				height = config.height;
4425			} else {
4426				config.container.height();
4427			}
4428			
4429			var div_id = "#v-" + guid + '-' + count;
4430			
4431			var parent_width = jQuery( div_id ).parent().width();
4432			var diffw = 0;
4433			var diffh = 0;
4434			var ratio = 0;
4435			if ( width > parent_width ) {
4436				diffw = width - parent_width + 11;
4437				ratio = ( width * 1.0 ) / ( height * 1.0 );
4438				diffh = diffw / ratio;
4439				
4440				width -= diffw;
4441				height -= Math.round( diffh );
4442			}
4443			
4444			if ( width < 60 || height < 60 ) {
4445				width = 400;
4446				height = 300;
4447			}
4448			
4449			jQuery.VideoPress.data[guid][count].dimensions = {};
4450			
4451			if( 0 == ratio ) {
4452				jQuery.VideoPress.data[guid][count].dimensions.width = width;
4453				jQuery.VideoPress.data[guid][count].dimensions.height = height;
4454			}
4455			else {
4456				jQuery.VideoPress.data[guid][count].dimensions.width = width - 7;
4457				jQuery.VideoPress.data[guid][count].dimensions.height = height - Math.round( 7 / ratio );
4458				
4459				jQuery( div_id ).width( width );
4460				jQuery( div_id ).height( height + 50 );
4461	
4462				jQuery( div_id + "-placeholder" ).width( jQuery.VideoPress.data[guid][count].dimensions.width );
4463				jQuery( div_id + "-placeholder" ).height( jQuery.VideoPress.data[guid][count].dimensions.height );
4464	
4465				jQuery( div_id + "-placeholder img.videopress-poster" ).width( jQuery.VideoPress.data[guid][count].dimensions.width );
4466				jQuery( div_id + "-placeholder img.videopress-poster" ).height( jQuery.VideoPress.data[guid][count].dimensions.height );
4467			}
4468
4469			config.container.data( "guid", guid );
4470			config.container.data( "count", count );
4471			if ( jQuery.VideoPress.video.playerSupport === undefined ) {
4472				jQuery.VideoPress.video.canPlay();
4473			}
4474
4475			if ( config.freedom===true && jQuery.type(video.ogv)==="string" ) {
4476				jQuery.VideoPress.video.insert( config.container, guid, count, video, "ogv", jQuery.VideoPress.data[guid][count].dimensions.width, jQuery.VideoPress.data[guid].dimensions.height );
4477				config.container.data( "player", "ogv" );
4478			} else if ( jQuery.VideoPress.video.playerSupport === "flash" ) {
4479				config.container.data( "player", "flash" );
4480				config.container.append( '<div id="v-' + guid + "-" + count + '-video">' );
4481			} else if ( jQuery.inArray( jQuery.VideoPress.video.playerSupport, ["html5","mp4","ogv"] ) && ( jQuery.type(video.mp4.uri)==="str
4481ing" || jQuery.type(video.ogv.uri)==="string" ) ) {
4482				var load_type = "html5";
4483				if ( jQuery.VideoPress.video.playerSupport==="mp4" && video.mp4!==undefined && jQuery.type(video.mp4.uri)==="string" ) {
4484					load_type = "mp4";
4485				} else if ( jQuery.VideoPress.video.playerSupport==="ogv" && video.ogv!==undefined && jQuery.type(video.ogv.uri)==="string" ) {
4486					load_type = "ogv";
4487				}
4488				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 );
4489				config.container.data( "player", load_type );
4490				load_type=null;
4491			} else {
4492				config.container.html('<p class="videopress-error">' + jQuery.VideoPress.error.messages.incompatible + '</p>');
4493				return false;
4494			}
4495			return true;
4496		},
4497		insert: function( container_el, guid, count, video_data, video_type, width, height ) {
4498			var video_id = "v-" + guid + "-" + count + "-video";
4499			var video_el = jQuery("<video />");
4500			video_el.attr( "id", video_id );
4501			video_el.attr( "width", width );
4502			video_el.attr( "height", height );
4503			video_el.attr( "poster", video_data.poster );
4504			if ( video_type==="ogv" ) {
4505				video_el.attr( "preload", "metadata" );
4506			} else {
4507				video_el.attr( "preload", "none" );
4508			}
4509			video_el.attr( "controls", "true" );
4510			video_el.attr( "x-webkit-airplay", "allow" );
4511			if ( video_type==="mp4" && video_data.mp4!==undefined && jQuery.type(video_data.mp4.uri)==="string" ) {
4512				video_el.attr( "src", video_data.mp4.uri );
4513			} else if ( video_type==="ogv" && video_data.ogv!==undefined && jQuery.type(video_data.ogv.uri)==="string" ) {
4514				video_el.attr( "src", video_data.ogv.uri );
4515			} else {
4516				// Purposely omit source type attribute since the browser does not seem to support specifics such as canPlayType
4517				if ( video_data.mp4!==undefined && jQuery.type(video_data.mp4.uri)==="string" ) {
4518					video_el.append( '<source src="' - video_data.mp4.uri + '" />' );
4519				}
4520				if ( video_data.ogv!==undefined && jQuery.type(video_data.ogv.uri)==="string" ) {
4521					video_el.append( '<source src="' + video_data.ogv.uri + '" />' );
4522				}
4523			}
4524			
4525			if ( video_data.tracks !== undefined ) {
4526				video_el.append( video_data.tracks );
4527			}
4528			video_el.append( '<p class="videopress-error">' + jQuery.VideoPress.error.messages.incompatible + "</p>" );
4529			video_el.hide();
4530			container_el.append( video_el );
4531			video_el=null;
4532			video_id=null;
4533		},
4534		play: function( container_el ) {
4535			var player = container_el.data( "player" );
4536			if ( player===undefined ) {
4537				player="flash";
4538			}
4539
4540			var guid = container_el.data( "guid" );
4541			var count = container_el.data( "count" );
4542
4543			if ( player === "flash" ) {
4544				jQuery( "#" + container_el.attr("id") + "-placeholder", container_el ).remove();
4545
4546				var player_uri = jQuery.VideoPress.video.flash.player_uri;
4547				var expressinstall = jQuery.VideoPress.video.flash.expressinstall;
4548
4549				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 );
4550			} else if ( jQuery.inArray( player, ["html5", "mp4", "ogv"] ) ) {
4551				var video_el = jQuery("video", container_el);
4552				if ( video_el ) {
4553					jQuery( "#" + container_el.attr("id") + "-placeholder", container_el ).remove();
4554					if ( player==="html5" ) {
4555						player = "mp4";
4556					}
4557					jQuery.VideoPress.video.playHTML5( video_el, guid, player );
4558				}
4559			} else {
4560				jQuery( "#" + container_el.attr("id") + "-placeholder", container_el ).remove();
4561				container_el.append( '<p class="videopress-error">Unable to play video. No suitable player.</p>' );
4562			}
4563
4564			var play_event = new CustomEvent( 'videopress_play_video', { 'detail': { 'video_id': guid } } );
4565			window.dispatchEvent( play_event );
4566		},
4567		playHTML5: function( video_el, guid, filetype ) {
4568			video_el.show();
4569			video_el[0].load();
4570
4571			/* It seems load() sometimes does not work, but play() will trigger load.
4572			 * Tried attaching play() to a data event but data might not load
4573			 * So we trigger play() even if there is not enough data loaded to begin playback
4574			 */
4575			video_el[0].play();
4576			jQuery.VideoPress.analytics.played(guid, filetype);
4577			video_el.bind( "error stalled", function(e) {
4578				var message = jQuery.VideoPress.error.messages.error;
4579				try {
4580					// provide a more detailed error message if a failure reason is communicated
4581					switch (e.target.error.code) {
4582						case e.target.error.MEDIA_ERR_NETWORK:
4583							message = jQuery.VideoPress.error.messages.network;
4584							break;
4585						case e.target.error.MEDIA_ERR_DECODE:
4586						case e.target.error.MEDIA_ERR_SRC_NOT_SUPPORTED:
4587							message = jQuery.VideoPress.error.messages.incapable + " " + filetype.toUpperCase() + ".";
4588							break;
4589						default:
4590							break;
4591					}
4592				} catch( err ){}
4593				// provide an opportunity to silence an error with an empty string
4594				if ( message.length > 0 ) {
4595					video_el.html( '<p class="videopress-error">' + message + "</p>" );
4596				}
4597				message=null;
4598			} );
4599			video_el.bind( "durationchange", {guid:guid}, function( event ) {
4600				var duration = jQuery(event.target).attr("duration");
4601				if ( jQuery.type(duration)==="number" ) {
4602					jQuery.VideoPress.data[event.data.guid].duration = duration;
4603				}
4604				duration=null;
4605			} );
4606
4607			/* Only record stats after video data has loaded
4608			 * 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.
4609			 */
4610			video_el.one( "loadeddata", {guid:guid, filetype:filetype}, function( event ){
4611				var filetype = event.data.filetype;
4612				var loaded_file = jQuery(event.target).attr("currentSrc");
4613				if ( jQuery.type(loaded_file)==="string" && loaded_file.length > 3 ) {
4614					var ext = loaded_file.substr( loaded_file.lastIndexOf(".") + 1 ).toLowerCase();
4615					if ( jQuery.inArray( ext, ["mp4","ogv"] ) ) {
4616						filetype = ext;
4617					}
4618					ext=null;
4619				}
4620				video_el.bind( "play", {guid:event.data.guid,filetype:filetype}, function( event ) {
4621					jQuery.VideoPress.analytics.played(event.data.guid, event.data.filetype);
4622				} );
4623				video_el.bind( "timeupdate", {guid:event.data.guid,filetype:filetype}, function( event ) {
4624					var target = jQuery(event.target);
4625					jQuery.VideoPress.analytics.watched( event.data.guid, event.data.filetype, target.attr("currentTime"), target.attr("initialTime") );
4626					target=null;
4627				} );
4628				video_el.bind( "ended", {guid:event.data.guid,filetype:filetype}, function( event ) {
4629					jQuery.VideoPress.analytics.watched( event.data.guid, event.data.filetype, jQuery.VideoPress.data[guid].duration, jQuery(event.target).attr("initialTime") );
4630				} );
4631			} );
4632		}
4633	}
4634}});
4635;

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.