PageSourceSearch

https://catchup.co.in/Catch_Up_site_files/a_002.js

js catchup.co.in collected 2026-10-02 01:55:36 UTC 99,842 bytes, 3,030 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		// Skip CoBlocks Lightbox images
458		if ( img.parentNode.className.match(/coblocks-lightbox__image/) )
459			return false;
460
461		// Scale gravatars that have ?s= or ?size=
462		if ( img.src.match( /^https?:\/\/([^\/]*\.)?gravatar\.com\/.+[?&](s|size)=/ ) ) {
463                        if ( this.hintsFor( "gravatar" ) === true ) {
464                                return false;
465                        }
466			newSrc = img.src.replace( /([?&](s|size)=)(\d+)/, function( $0, $1, $2, $3 ) {
467				// Stash the original size
468				var originalAtt = "originals",
469				originalSize = img.getAttribute(originalAtt);
470				if ( originalSize === null ) {
471					originalSize = $3;
472					img.setAttribute(originalAtt, originalSize);
473					if ( t.imgNeedsSizeAtts( img ) ) {
474						// Fix width and height attributes to rendered dimensions.
475						img.width = img.width;
476						img.height = img.height;
477					}
478				}
479				// Get the width/height of the image in CSS pixels
480				var size = img.clientWidth;
481				// Convert CSS pixels to device pixels
482				var targetSize = Math.ceil(img.clientWidth * scale);
483				// Don't go smaller than the original size
484				targetSize = Math.max( targetSize, originalSize );
485				// Don't go larger than the service supports
486				targetSize = Math.min( targetSize, 512 );
487				return $1 + targetSize;
488			});
489		}
490
491		// Scale mshots that have width
492		else if ( img.src.match(/^https?:\/\/([^\/]+\.)*(wordpress|wp)\.com\/mshots\/.+[?&]w=\d+/) ) {
493                        if ( this.hintsFor( "mshots" ) === true ) {
494                                return false;
495                        }
496			newSrc = img.src.replace( /([?&]w=)(\d+)/, function($0, $1, $2) {
497				// Stash the original size
498				var originalAtt = 'originalw', originalSize = img.getAttribute(originalAtt);
499				if ( originalSize === null ) {
500					originalSize = $2;
501					img.setAttribute(originalAtt, originalSize);
502					if ( t.imgNeedsSizeAtts( img ) ) {
503						// Fix width and height attributes to rendered dimensions.
504						img.width = img.width;
505						img.height = img.height;
506					}
507				}
508				// Get the width of the image in CSS pixels
509				var size = img.clientWidth;
510				// Convert CSS pixels to device pixels
511				var targetSize = Math.ceil(size * scale);
512				// Don't go smaller than the original size
513				targetSize = Math.max( targetSize, originalSize );
514				// Don't go bigger unless the current one is actually lacking
515				if ( scale > img.getAttribute("scale") && targetSize <= img.naturalWidth )
516					targetSize = $2;
517				if ( $2 != targetSize )
518					return $1 + targetSize;
519				return $0;
520			});
521
522			// Update height attribute to match width
523			newSrc = newSrc.replace( /([?&]h=)(\d+)/, function($0, $1, $2) {
524				if ( newSrc == img.src ) {
525					return $0;
526				}
527				// Stash the original size
528				var originalAtt = 'originalh', originalSize = img.getAttribute(originalAtt);
529				if ( originalSize === null ) {
530					originalSize = $2;
531					img.setAttribute(originalAtt, originalSize);
532				}
533				// Get the height of the image in CSS pixels
534				var size = img.clientHeight;
535				// Convert CSS pixels to device pixels
536				var targetSize = Math.ceil(size * scale);
537				// Don't go smaller than the original size
538				targetSize = Math.max( targetSize, originalSize );
539				// Don't go bigger unless the current one is actually lacking
540				if ( scale > img.getAttribute("scale") && targetSize <= img.naturalHeight )
541					targetSize = $2;
542				if ( $2 != targetSize )
543					return $1 + targetSize;
544				return $0;
545			});
546		}
547
548		// Scale simple imgpress queries (s0.wp.com) that only specify w/h/fit
549		else if ( img.src.match(/^https?:\/\/([^\/.]+\.)*(wp|wordpress)\.com\/imgpress\?(.+)/) ) {
550                        if ( this.hintsFor( "imgpress" ) === true ) {
551                                return false; 
552                        }
553			var imgpressSafeFunctions = ["zoom", "url", "h", "w", "fit", "filter", "brightness", "contrast", "colorize", "smooth", "unsharpmask"];
554			// Search the query string for unsupported functions.
555			var qs = RegExp.$3.split('&');
556			for ( var q in qs ) {
557				q = qs[q].split('=')[0];
558				if ( imgpressSafeFunctions.indexOf(q) == -1 ) {
559					return false;
560				}
561			}
562			// Fix width and height attributes to rendered dimensions.
563			img.width = img.width;
564			img.height = img.height;
565			// Compute new src
566			if ( scale == 1 )
567				newSrc = img.src.replace(/\?(zoom=[^&]+&)?/, '?');
568			else
569				newSrc = img.src.replace(/\?(zoom=[^&]+&)?/, '?zoom=' + scale + '&');
570		}
571
572		// Scale files.wordpress.com, LaTeX, or Photon images (i#.wp.com)
573		else if (
574			( isFiles = img.src.match(/^https?:\/\/([^\/]+)\.files\.wordpress\.com\/.+[?&][wh]=/) ) ||
575			( isLatex = img.src.match(/^https?:\/\/([^\/.]+\.)*(wp|wordpress)\.com\/latex\.php\?(latex|zoom)=(.+)/) ) ||
576			( isPhoton = img.src.match(/^https?:\/\/i[\d]{1}\.wp\.com\/(.+)/) )
577		) {
578                        if ( false !== isFiles && this.hintsFor( "files" ) === true ) {
579                                return false
580                        }
581                        if ( false !== isLatex && this.hintsFor( "latex" ) === true ) {
582                                return false
583                        }
584                        if ( false !== isPhoton && this.hintsFor( "photon" ) === true ) {
585                                return false
586                        }
587			// Fix width and height attributes to rendered dimensions.
588			img.width = img.width;
589			img.height = img.height;
590			// Compute new src
591			if ( scale == 1 ) {
592				newSrc = img.src.replace(/\?(zoom=[^&]+&)?/, '?');
593			} else {
594				newSrc = img.src;
595
596				var url_var = newSrc.match( /([?&]w=)(\d+)/ );
597				if ( url_var !== null && url_var[2] ) {
598					newSrc = newSrc.replace( url_var[0], url_var[1] + img.width );
599				}
600
601				url_var = newSrc.match( /([?&]h=)(\d+)/ );
602				if ( url_var !== null && url_var[2] ) {
603					newSrc = newSrc.replace( url_var[0], url_var[1] + img.height );
604				}
605
606				var zoom_arg = '&zoom=2';
607				if ( !newSrc.match( /\?/ ) ) {
608					zoom_arg = '?zoom=2';
609				}
610				img.setAttribute( 'srcset', newSrc + zoom_arg + ' ' + scale + 'x' );
611			}
612		}
613
614		// Scale static assets that have a name matching *-1x.png or *@1x.png
615		else if ( img.src.match(/^https?:\/\/[^\/]+\/.*[-@]([12])x\.(gif|jpeg|jpg|png)(\?|$)/) ) {
616                        if ( this.hintsFor( "staticAssets" ) === true ) {
617                                return false; 
618                        }
619			// Fix width and height attributes to rendered dimensions.
620			img.width = img.width;
621			img.height = img.height;
622			var currentSize = RegExp.$1, newSize = currentSize;
623			if ( scale <= 1 )
624				newSize = 1;
625			else
626				newSize = 2;
627			if ( currentSize != newSize )
628				newSrc = img.src.replace(/([-@])[12]x\.(gif|jpeg|jpg|png)(\?|$)/, '$1'+newSize+'x.$2$3');
629		}
630
631		else {
632			return false;
633		}
634
635		// Don't set img.src unless it has changed. This avoids unnecessary reloads.
636		if ( newSrc != img.src ) {
637			// Store the original img.src
638			var prevSrc, origSrc = img.getAttribute("src-orig");
639			if ( !origSrc ) {
640				origSrc = img.src;
641				img.setAttribute("src-orig", origSrc);
642			}
643			// In case of error, revert img.src
644			prevSrc = img.src;
645			img.onerror = function(){
646				img.src = prevSrc;
647				if ( img.getAttribute("scale-fail") < scale )
648					img.setAttribute("scale-fail", scale);
649				img.onerror = null;
650			};
651			// Finally load the new image
652			img.src = newSrc;
653		}
654
655		return true;
656	}
657};
658
659wpcom_img_zoomer.init();
660;
661!function(e){var t={};function n(o){if(t[o])return t[o].exports;var c=t[o]={i:o,l:!1,exports:{}};return e[o].call(c.exports,c,c.exports,n),c.l=!0,c.exports}n.m=e,n.c=t,n.d=function(e,t,o){n.o(e,t)||Object.defineProperty(e,t,{enumerable:!0,get:o})},n.r=function(e){"undefined"!=typeof Symbol&&Symbol.toStringTag&&Object.defineProperty(e,Symbol.toStringTag,{value:"Module"}),Object.defineProperty(e,"__esModule",{value:!0})},n.t=function(e,t){if(1&t&&(e=n(e)),8&t)return e;if(4&t&&"object"==typeof e&&e&&e.__esModule)return e;var o=Object.create(null);if(n.r(o),Object.defineProperty(o,"default",{enumerable:!0,value:e}),2&t&&"string"!=typeof e)for(var c in e)n.d(o,c,function(t){return e[t]}.bind(null,c));return o},n.n=function(e){var t=e&&e.__esModule?function(){return e.default}:function(){return e};return n.d(t,"a",t),t},n.o=function(e,t){return Object.prototype.hasOwnProperty.call(e,t)},n.p="",n(n.s=287)}({287:function(e,t){!function(){"use strict";var e=document.getElementsByClassName("has-lightbox");Array.from(e).forEach((function(e,t){e.className+=" lightbox-"+t+" ",function(e){var t=document.createElement("div");t.setAttribute("class","coblocks-lightbox");var n=document.createElement("div");n.setAttribute("class","coblocks-lightbox__background");var o=document.createElement("div");o.setAttribute("class","coblocks-lightbox__heading");var c=document.createElement("button");c.setAttribute("class","coblocks-lightbox__close");var r=document.createElement("span");r.setAttribute("class","coblocks-lightbox__count");var a=document.createElement("div");a.setAttribute("class","coblocks-lightbox__image");var i=document.createElement("img"),l=document.createElement("figcaption");l.setAttribute("class","coblocks-lightbox__caption");var s=document.createElement("button");s.setAttribute("class","coblocks-lightbox__arrow coblocks-lightbox__arrow--left");var u=document.createElement("button");u.setAttribute("class","coblocks-lightbox__arrow coblocks-lightbox__arrow--right");var d=document.createElement("div");d.setAttribute("class","arrow-right");var b=document.createElement("div");b.setAttribute("class","arrow-left");var m,f=document.querySelectorAll(".has-lightbox.lightbox-".concat(e," > :not(.carousel-nav) figure img, figure.has-lightbox.lightbox-").concat(e," > img")),g=document.querySelectorAll(".has-lightbox.lightbox-".concat(e," > :not(.carousel-nav) figure figcaption"));o.append(r,c),a.append(i,l),s.append(b),u.append(d),t.append(n,o,a,s,u),f.length>0&&(document.getElementsByTagName("BODY")[0].append(t),1===f.length&&(u.remove(),s.remove()));g.length>0&&Array.from(g).forEach((function(e,t){e.addEventListener("click",(function(){p(t)}))}));Array.from(f).forEach((function(e,t){e.closest("figure").addEventListener("click",(function(){p(t)}))})),s.addEventListener("click",(function(){p(m=0===m?f.length-1:m-1)})),u.addEventListener("click",(function(){p(m=m===f.length-1?0:m+1)})),n.addEventListener("click",(function(){t.style.display="none"})),c.addEventListener("click",(function(){t.style.display="none"}));var v={preloaded:!1,setPreloadImages:function(){v.preloaded||(v.preloaded=!0,Array.from(f).forEach((function(e,t){v["img-".concat(t)]=new window.Image,v["img-".concat(t)].src=e.attributes.src.value,v["img-".concat(t)]["data-caption"]=f[t]&&f[t].nextElementSibling?function(e){for(var t=e.nextElementSibling;t;){if(t.matches("figcaption"))return t.innerHTML;t=t.nextElementSibling}return""}(f[t]):""})),document.onkeydown=function(e){if(void 0!==t&&"none"!==t)switch((e=e||window.event).keyCode){case 27:c.click();break;
661case 37:case 65:s.click();break;case 39:case 68:u.click()}})}};function p(e){v.setPreloadImages(),m=e,t.style.display="flex",n.style.backgroundImage="url(".concat(v["img-".concat(m)].src,")"),i.src=v["img-".concat(m)].src,l.innerHTML=v["img-".concat(m)]["data-caption"],r.textContent="".concat(m+1," / ").concat(f.length)}}(t)}))}()}});;
662( function( $ ) {
663	var cookieValue = document.cookie.replace( /(?:(?:^|.*;\s*)eucookielaw\s*\=\s*([^;]*).*$)|^.*$/, '$1' ),
664		overlay = $( '#eu-cookie-law' ),
665		container = $( '.widget_eu_cookie_law_widget' ),
666		initialScrollPosition,
667		scrollFunction;
668
669	if ( overlay.hasClass( 'ads-active' ) ) {
670		var adsCookieValue = document.cookie.replace( /(?:(?:^|.*;\s*)personalized-ads-consent\s*\=\s*([^;]*).*$)|^.*$/, '$1' );
671		if ( '' !== cookieValue && '' !== adsCookieValue ) {
672			overlay.remove();
673		}
674	} else if ( '' !== cookieValue ) {
675		overlay.remove();
676	}
677
678	$( '.widget_eu_cookie_law_widget' ).appendTo( 'body' ).fadeIn();
679
680	overlay.find( 'form' ).on( 'submit', accept );
681
682	if ( overlay.hasClass( 'hide-on-scroll' ) ) {
683		initialScrollPosition = $( window ).scrollTop();
684		scrollFunction = function() {
685			if ( Math.abs( $( window ).scrollTop() - initialScrollPosition ) > 50 ) {
686				accept();
687			}
688		};
689		$( window ).on( 'scroll', scrollFunction );
690	} else if ( overlay.hasClass( 'hide-on-time' ) ) {
691		setTimeout( accept, overlay.data( 'hide-timeout' ) * 1000 );
692	}
693
694	var accepted = false;
695	function accept( event ) {
696		if ( accepted ) {
697			return;
698		}
699		accepted = true;
700
701		if ( event && event.preventDefault ) {
702			event.preventDefault();
703		}
704
705		if ( overlay.hasClass( 'hide-on-scroll' ) ) {
706			$( window ).off( 'scroll', scrollFunction );
707		}
708
709		var expireTime = new Date();
710		expireTime.setTime( expireTime.getTime() + ( overlay.data( 'consent-expiration' ) * 24 * 60 * 60 * 1000 ) );
711
712		document.cookie = 'eucookielaw=' + expireTime.getTime() + ';path=/;expires=' + expireTime.toGMTString();
713		if ( overlay.hasClass( 'ads-active' ) && overlay.hasClass( 'hide-on-button' ) ) {
714			document.cookie = 'personalized-ads-consent=' + expireTime.getTime() + ';path=/;expires=' + expireTime.toGMTString();
715		}
716
717		overlay.fadeOut( 400, function() {
718			overlay.remove();
719			container.remove();
720		} );
721	}
722} )( jQuery );
723;
724(function() {
725
726	/**
727	 * Debounce
728	 *
729	 * @param {Function} func
730	 * @param {number} wait
731	 * @param {boolean} immediate
732	 */
733	function debounce(func, wait, immediate) {
734		'use strict';
735
736		var timeout;
737		wait      = (typeof wait !== 'undefined') ? wait : 20;
738		immediate = (typeof immediate !== 'undefined') ? immediate : true;
739
740		return function() {
741
742			var context = this, args = arguments;
743			var later = function() {
744				timeout = null;
745
746				if (!immediate) {
747					func.apply(context, args);
748				}
749			};
750
751			var callNow = immediate && !timeout;
752
753			clearTimeout(timeout);
754			timeout = setTimeout(later, wait);
755
756			if (callNow) {
757				func.apply(context, args);
758			}
759		};
760	}
761
762	/**
763	 * Get page header height and use it for top-margin on
764	 * site-content when above mobile breakpoint
765	 */
766	function pageHeaderHeight() {
767		var body = document.body;
768
769		if ( document.documentElement.clientWidth < 560 ) {
770			document.getElementById( "primary" ).style.marginTop = 0;
771			body.classList.remove( 'wp-sticky-header' );
772		} else {
773			var header  = document.getElementById( 'masthead' );
774			var content = document.getElementById( 'primary' );
775
776			content.style.marginTop = header.offsetHeight + "px";
777
778			if ( document.documentElement.clientHeight / 5 - header.offsetHeight >= 0 ) {
779				body.classList.add( 'wp-sticky-header' );
780			} else {
781				body.classList.remove( 'wp-sticky-header' );
782			}
783		}
784	};
785
786	/**
787	 * Run our function every time the window resizes
788	 */
789	var isResizing = false;
790	window.addEventListener( 'resize',
791		debounce( function() {
792			if ( isResizing ) {
793				return;
794			}
795
796			isResizing = true;
797			setTimeout( function() {
798				pageHeaderHeight();
799				isResizing = false;
800			}, 150 );
801		} )
802	);
803
804	/**
805	 * Run our page header height function
806	 */
807	pageHeaderHeight();
808})();
809;
810/* globals JSON */
811( function () {
812	var eventName = 'wpcom_masterbar_click';
813
814	var linksTracksEvents = {
815		// top level items
816		'wp-admin-bar-blog'                        : 'my_sites',
817		'wp-admin-bar-newdash'                     : 'reader',
818		'wp-admin-bar-ab-new-post'                 : 'write_button',
819		'wp-admin-bar-my-account'                  : 'my_account',
820		'wp-admin-bar-notes'                       : 'notifications',
821		// my sites - top items
822		'wp-admin-bar-switch-site'                 : 'my_sites_switch_site',
823		'wp-admin-bar-blog-info'                   : 'my_sites_site_info',
824		'wp-admin-bar-site-view'                   : 'my_sites_view_site',
825		'wp-admin-bar-blog-stats'                  : 'my_sites_site_stats',
826		'wp-admin-bar-plan'                        : 'my_sites_plan',
827		'wp-admin-bar-plan-badge'                  : 'my_sites_plan_badge',
828		// my sites - manage
829		'wp-admin-bar-edit-page'                   : 'my_sites_manage_site_pages',
830		'wp-admin-bar-new-page-badge'              : 'my_sites_manage_add_page',
831		'wp-admin-bar-edit-post'                   : 'my_sites_manage_blog_posts',
832		'wp-admin-bar-new-post-badge'              : 'my_sites_manage_add_post',
833		'wp-admin-bar-edit-attachment'             : 'my_sites_manage_media',
834		'wp-admin-bar-new-attachment-badge'        : 'my_sites_manage_add_media',
835		'wp-admin-bar-comments'                    : 'my_sites_manage_comments',
836		'wp-admin-bar-edit-jetpack-testimonial'    : 'my_sites_manage_testimonials',
837		'wp-admin-bar-new-jetpack-testimonial'     : 'my_sites_manage_add_testimonial',
838		'wp-admin-bar-edit-jetpack-portfolio'      : 'my_sites_manage_portfolio',
839		'wp-admin-bar-new-jetpack-portfolio'       : 'my_sites_manage_add_portfolio',
840		// my sites - personalize
841		'wp-admin-bar-themes'                      : 'my_sites_personalize_themes',
842		'wp-admin-bar-cmz'                         : 'my_sites_personalize_themes_customize',
843		// my sites - configure
844		'wp-admin-bar-sharing'                     : 'my_sites_configure_sharing',
845		'wp-admin-bar-people'                      : 'my_sites_configure_people',
846		'wp-admin-bar-people-add'                  : 'my_sites_configure_people_add_button',
847		'wp-admin-bar-plugins'                     : 'my_sites_configure_plugins',
848		'wp-admin-bar-domains'                     : 'my_sites_configure_domains',
849		'wp-admin-bar-domains-add'                 : 'my_sites_configure_add_domain',
850		'wp-admin-bar-blog-settings'               : 'my_sites_configure_settings',
851		'wp-admin-bar-legacy-dashboard'            : 'my_sites_configure_wp_admin',
852		// reader
853		'wp-admin-bar-followed-sites'              : 'reader_followed_sites',
854		'wp-admin-bar-reader-followed-sites-manage': 'reader_manage_followed_sites',
855		'wp-admin-bar-discover-discover'           : 'reader_discover',
856		'wp-admin-bar-discover-search'             : 'reader_search',
857		'wp-admin-bar-my-activity-my-likes'        : 'reader_my_likes',
858		// account
859		'wp-admin-bar-user-info'                   : 'my_account_user_name',
860		// account - profile
861		'wp-admin-bar-my-profile'                  : 'my_account_profile_my_profile',
862		'wp-admin-bar-account-settings'            : 'my_account_profile_account_settings',
863		'wp-admin-bar-billing'                     : 'my_account_profile_manage_purchases',
864		'wp-admin-bar-security'                    : 'my_account_profile_security',
865		'wp-admin-bar-notifications'               : 'my_account_profile_notifications',
866		// account - special
867		'wp-admin-bar-get-apps'                    : 'my_account_special_get_apps',
868		'wp-admin-bar-next-steps'                  : 'my_account_special_next_steps',
869		'wp-admin-bar-help'                        : 'my_account_special_help',
870	};
871
872	var notesTracksEvents = {
873		openSite: function ( data ) {
874			return {
875				clicked: 'masterbar_notifications_panel_site',
876				site_id: data.siteId
877			};
878		},
879		openPost: function ( data ) {
880			return {
881				clicked: 'masterbar_notifications_panel_post',
882				site_id: data.siteId,
883				post_id: data.postId
884			};
885		},
886		openComment: function ( data ) {
887			return {
888				clicked: 'masterbar_notifications_panel_comment',
889				site_id: data.siteId,
890				post_id: data.postId,
891				comment_id: data.commentId
892			};
893		}
894	};
895
896	// Element.prototype.matches as a standalone function, with old browser fallback
897	function matches( node, selector ) {
898		if ( ! node ) {
899			return undefined;
900		}
901
902		if ( ! Element.prototype.matches && ! Element.prototype.msMatchesSelector ) {
903			throw new Error( 'Unsupported browser' );
904		}
905
906		return Element.prototype.matches ? node.matches( selector ) : node.msMatchesSelector( selector );
907	}
908
909	// Element.prototype.closest as a standalone function, with old browser fallback
910	function closest( node, selector ) {
911		if ( ! node ) {
912			return undefined;
913		}
914
915		if ( Element.prototype.closest ) {
916			return node.closest( selector );
917		}
918
919		do {
920			if ( matches( node, selector ) ) {
921				return node;
922			}
923
924			node = node.parentElement || node.parentNode;
925		} while ( node !== null && node.nodeType === 1 );
926
927		return null;
928	}
929
930	function recordTracksEvent( eventProps ) {
931		eventProps = eventProps || {};
932		window._tkq = window._tkq || [];
933		window._tkq.push( [ 'recordEvent', eventName, eventProps ] );
934	}
935
936	function parseJson( s, defaultValue ) {
937		try {
938			return JSON.parse( s );
939		} catch ( e ) {
940			return defaultValue;
941		}
942	}
943
944	function createTrackableLinkEventHandler( link ) {
945		return function () {
946			var parent = closest( link, 'li' );
947
948			if ( ! parent ) {
949				return;
950			}
951
952			var trackingId = link.getAttribute( 'ID' ) || parent.getAttribute( 'ID' );
953
954			if ( ! linksTracksEvents.hasOwnProperty( trackingId ) ) {
955				return;
956			}
957
958			var eventProps = { 'clicked': linksTracksEvents[ trackingId ] };
959			recordTracksEvent( eventProps );
960		}
961	}
962
963	function init() {
964		var trackableLinkSelector = '.mb-trackable .ab-item:not(div),' +
965			'#wp-admin-bar-notes .ab-item,' +
966			'#wp-admin-bar-user-info .ab-item,' +
967			'.mb-trackable .ab-secondary';
968
969		var trackableLinks = document.querySelectorAll( trackableLinkSelector );
970
971		for ( var i = 0; i < trackableLinks.length; i++ ) {
972			var link = trackableLinks[ i ];
973			var handler = createTrackableLinkEventHandler( link );
974
975			link.addEventListener( 'click', handler );
976			link.addEventListener( 'touchstart', handler );
977		}
978	}
979
980	if ( document.readyState === 'loading' ) {
981		document.addEventListener( 'DOMContentLoaded', init );
982	} else {
983		init();
984	}
985
986	// listen for postMessage events from the notifications iframe
987	window.addEventListener( 'message', function ( event ) {
988		if ( event.origin !== 'https://widgets.wp.com' ) {
989			return;
990		}
991
992		var data = ( typeof event.data === 'string' ) ? parseJson( event.data, {} ) : event.data;
993		if ( data.type !== 'notesIframeMessage' ) {
994			return;
995		}
996
997		var eventData = notesTracksEvents[ data.action ];
998		if ( ! eventData ) {
999			return;
1000		}
1001
1002		recordTracksEvent( eventData( data ) );
1003	}, false );
1004
1005} )();
1006;
1007/* global jetpackCarouselStrings, DocumentTouch */
1008
1009// @start-hide-in-jetpack
1010if (typeof wpcom === 'undefined') {
1011	var wpcom = {};
1012}
1013wpcom.carousel = (function (/*$*/) {
1014	var prebuilt_widths = jetpackCarouselStrings.widths;
1015	var pageviews_stats_args = jetpackCarouselStrings.stats_query_args;
1016
1017	var findFirstLargeEnoughWidth = function (original_w, original_h, dest_w, dest_h) {
1018		var inverse_ratio = original_h / original_w;
1019
1020		for ( var i = 0; i < prebuilt_widths.length; ++i ) {
1021			if ( prebuilt_widths[i] >= dest_w || prebuilt_widths[i] * inverse_ratio >= dest_h ) {
1022				return prebuilt_widths[i];
1023			}
1024		}
1025
1026		return original_w;
1027	};
1028
1029	var removeResizeFromImageURL = function ( url ) {
1030		return removeArgFromURL( url, 'resize' );
1031	};
1032
1033	var removeArgFromURL = function ( url, arg ) {
1034		var re = new RegExp( '[\\?&]' + arg + '(=[^?&]+)?' );
1035		if ( url.match( re ) ) {
1036			return url.replace( re, '' );
1037		}
1038		return url;
1039	};
1040
1041	var addWidthToImageURL = function (url, width) {
1042		width = parseInt(width, 10);
1043		// Give devices with a higher devicePixelRatio higher-res images (Retina display = 2, Android phones = 1.5, etc)
1044		if ('undefined' !== typeof window.devicePixelRatio && window.devicePixelRatio > 1) {
1045			width = Math.round( width * window.devicePixelRatio );
1046		}
1047		url = addArgToURL(url, 'w', width);
1048		url = addArgToURL(url, 'h', '');
1049		return url;
1050	};
1051
1052	var addArgToURL = function (url, arg, value) {
1053		var re = new RegExp(arg+'=[^?&]+');
1054		if ( url.match(re) ) {
1055			return url.replace(re, arg + '=' + value);
1056		} else {
1057			var divider = url.indexOf('?') !== -1 ? '&' : '?';
1058			return url + divider + arg + '=' + value;
1059		}
1060	};
1061
1062	var stat = function ( names ) {
1063		if ( typeof names !== 'string' ) {
1064			names = names.join( ',' );
1065		}
1066
1067		new Image().src = window.location.protocol +
1068			'//pixel.wp.com/g.gif?v=wpcom-no-pv' +
1069			'&x_carousel=' + names +
1070			'&baba=' + Math.random();
1071	};
1072
1073	var pageview = function ( post_id ) {
1074		new Image().src = window.location.protocol +
1075			'//pixel.wp.com/g.gif?host=' + encodeURIComponent( window.location.host ) +
1076			'&ref=' + encodeURIComponent( document.referrer ) +
1077			'&rand=' + Math.random() +
1078			'&' + pageviews_stats_args +
1079			'&post=' + encodeURIComponent( post_id );
1080	};
1081
1082
1083	return {
1084		findFirstLargeEnoughWidth: findFirstLargeEnoughWidth,
1085		removeResizeFromImageURL: removeResizeFromImageURL,
1086		addWidthToImageURL: addWidthToImageURL,
1087		stat: stat,
1088		pageview: pageview
1089	};
1090
1091})(jQuery);
1092// @end-hide-in-jetpack
1093jQuery( document ).ready( function ( $ ) {
1094	// gallery faded layer and container elements
1095	var overlay,
1096		comments,
1097		gallery,
1098		container,
1099		nextButton,
1100		previousButton,
1101		info,
1102		transitionBegin,
1103		caption,
1104		resizeTimeout,
1105		photo_info,
1106		close_hint,
1107		commentInterval,
1108		lastSelectedSlide,
1109		screenPadding = 110,
1110		originalOverflow = $( 'body' ).css( 'overflow' ),
1111		originalHOverflow = $( 'html' ).css( 'overflow' ),
1112		proportion = 85,
1113		last_known_location_hash = '',
1114		imageMeta,
1115		titleAndDescription,
1116		commentForm,
1117		leftColWrapper,
1118		scrollPos;
1119
1120	if ( window.innerWidth <= 760 ) {
1121		screenPadding = Math.round( ( window.innerWidth / 760 ) * 110 );
1122
1123		if (
1124			screenPadding < 40 &&
1125			( 'ontouchstart' in window || ( window.DocumentTouch && document instanceof DocumentTouch ) )
1126		) {
1127			screenPadding = 0;
1128		}
1129	}
1130
1131	// Adding a polyfill for browsers that do not have Date.now
1132	if ( 'undefined' === typeof Date.now ) {
1133		Date.now = function now() {
1134			return new Date().getTime();
1135		};
1136	}
1137
1138	var keyListener = function ( e ) {
1139		switch ( e.which ) {
1140			case 38: // up
1141				e.preventDefault();
1142				container.scrollTop( container.scrollTop() - 100 );
1143				break;
1144			case 40: // down
1145				e.preventDefault();
1146				container.scrollTop( container.scrollTop() + 100 );
1147				break;
1148			case 39: // right
1149				e.preventDefault();
1150				gallery.jp_carousel( 'next' );
1151				break;
1152			case 37: // left
1153			case 8: // backspace
1154				e.preventDefault();
1155				gallery.jp_carousel( 'previous' );
1156				break;
1157			case 27: // escape
1158				e.preventDefault();
1159				container.jp_carousel( 'close' );
1160				break;
1161			default:
1162				// making jslint happy
1163				break;
1164		}
1165	};
1166
1167	var resizeListener = function (/*e*/) {
1168		clearTimeout( resizeTimeout );
1169		resizeTimeout = setTimeout( function () {
1170			gallery.jp_carousel( 'slides' ).jp_carousel( 'fitSlide', true );
1171			gallery.jp_carousel( 'updateSlidePositions', true );
1172			gallery.jp_carousel( 'fitMeta', true );
1173		}, 200 );
1174	};
1175
1176	var prepareGallery = function (/*dataCarouselExtra*/) {
1177		if ( ! overlay ) {
1178			overlay = $( '<div></div>' )
1179				.addClass( 'jp-carousel-overlay' )
1180				.css( {
1181					position: 'fixed',
1182					top: 0,
1183					right: 0,
1184					bottom: 0,
1185					left: 0,
1186				} );
1187
1188			var displayComments = 1 === +jetpackCarouselStrings.display_comments;
1189			var buttons = displayComments
1190				? '<a class="jp-carousel-commentlink" href="#">' + jetpackCarouselStrings.comment + '</a>'
1191				: '';
1192			if ( 1 === Number( jetpackCarouselStrings.is_logged_in ) ) {
1193// @start-hide-in-jetpack
1194				if ( 1 === Number( jetpackCarouselStrings.is_public && 1 === Number( jetpackCarouselStrings.reblog_enabled ) ) ) {
1195					buttons += '<a class="jp-carousel-reblog" href="#">' + jetpackCarouselStrings.reblog + '</a>';
1196				}
1197// @end-hide-in-jetpack
1198			}
1199
1200			buttons = $( '<div class="jp-carousel-buttons">' + buttons + '</div>' );
1201
1202			caption = $( '<h2 itemprop="caption description"></h2>' );
1203			photo_info = $( '<div class="jp-carousel-photo-info"></div>' ).append( caption );
1204
1205			imageMeta = $( '<div></div>' ).addClass( 'jp-carousel-image-meta' ).css( {
1206				float: 'right',
1207				'margin-top': '20px',
1208				width: '250px',
1209			} );
1210
1211			if ( 0 < buttons.children().length ) {
1212				imageMeta.append( buttons );
1213			}
1214
1215			imageMeta
1216				.append( "<ul class='jp-carousel-image-exif' style='display:none;'></ul>" )
1217				.append( "<a class='jp-carousel-image-download' style='display:none;'></a>" )
1218				.append( "<div class='jp-carousel-image-map' style='display:none;'></div>" );
1219
1220			titleAndDescription = $( '<div></div>' )
1221				.addClass( 'jp-carousel-titleanddesc' )
1222				.css( {
1223					width: '100%',
1224					'margin-top': imageMeta.css( 'margin-top' ),
1225				} );
1226
1227			var leftWidth = $( window ).width() - screenPadding * 2 - ( imageMeta.width() + 40 );
1228			leftWidth += 'px';
1229
1230			leftColWrapper = $( '<div></div>' )
1231				.addClass( 'jp-carousel-left-column-wrapper' )
1232				.css( {
1233					width: Math.floor( leftWidth ),
1234				} )
1235				.append( titleAndDescription );
1236
1237			if ( displayComments ) {
1238				var commentFormMarkup = '<div id="jp-carousel-comment-form-container">';
1239
1240				if (
1241					jetpackCarouselStrings.local_comments_commenting_as &&
1242					jetpackCarouselStrings.local_comments_commenting_as.length
1243				) {
1244					// Comments not enabled, fallback to local comments
1245
1246					if (
1247						1 !== Number( jetpackCarouselStrings.is_logged_in ) &&
1248						1 === Number( jetpackCarouselStrings.comment_registration )
1249					) {
1250						commentFormMarkup +=
1251							'<div id="jp-carousel-comment-form-commenting-as">' +
1252							jetpackCarouselStrings.local_comments_commenting_as +
1253							'</div>';
1254					} else {
1255						commentFormMarkup += '<form id="jp-carousel-comment-form">';
1256						commentFormMarkup +=
1257							'<textarea name="comment" class="jp-carousel-comment-form-field jp-carousel-comment-form-textarea" id="jp-carousel-comment-form-comment-field" placeholder="' +
1258							jetpackCarouselStrings.write_comment +
1259							'"></textarea>';
1260						commentFormMarkup += '<div id="jp-carousel-comment-form-submit-and-info-wrapper">';
1261						commentFormMarkup +=
1262							'<div id="jp-carousel-comment-form-commenting-as">' +
1263							jetpackCarouselStrings.local_comments_commenting_as +
1264							'</div>';
1265						commentFormMarkup +=
1266							'<input type="submit" name="submit" class="jp-carousel-comment-form-button" id="jp-carousel-comment-form-button-submit" value="' +
1267							jetpackCarouselStrings.post_comment +
1268							'" />';
1269						commentFormMarkup += '<span id="jp-carousel-comment-form-spinner">&nbsp;</span>';
1270						commentFormMarkup += '<div id="jp-carousel-comment-post-results"></div>';
1271						commentFormMarkup += '</div>';
1272						commentFormMarkup += '</form>';
1273					}
1274				}
1275				commentFormMarkup += '</div>';
1276
1277				commentForm = $( commentFormMarkup ).css( {
1278					width: '100%',
1279					'margin-top': '20px',
1280					color: '#999',
1281				} );
1282
1283				comments = $( '<div></div>' ).addClass( 'jp-carousel-comments' ).css( {
1284					width: '100%',
1285					bottom: '10px',
1286					'margin-top': '20px',
1287				} );
1288
1289				var commentsLoading = $(
1290					'<div id="jp-carousel-comments-loading"><span>' +
1291						jetpackCarouselStrings.loading_comments +
1292						'</span></div>'
1293				).css( {
1294					width: '100%',
1295					bottom: '10px',
1296					'margin-top': '20px',
1297				} );
1298
1299				leftColWrapper.append( commentForm ).append( comments ).append( commentsLoading );
1300			}
1301
1302			var fadeaway = $( '<div></div>' ).addClass( 'jp-carousel-fadeaway' );
1303
1304			info = $( '<div></div>' )
1305				.addClass( 'jp-carousel-info' )
1306				.css( {
1307					top: Math.floor( ( $( window ).height() / 100 ) * proportion ),
1308					left: screenPadding,
1309					right: screenPadding,
1310				} )
1311				.append( photo_info )
1312				.append( imageMeta );
1313
1314			if ( window.innerWidth <= 760 ) {
1315				photo_info.remove().insertAfter( titleAndDescription );
1316				info.prepend( leftColWrapper );
1317			} else {
1318				info.append( leftColWrapper );
1319			}
1320
1321			var targetBottomPos = $( window ).height() - parseInt( info.css( 'to
1321p' ), 10 ) + 'px';
1322
1323			nextButton = $( '<div><span></span></div>' )
1324				.addClass( 'jp-carousel-next-button' )
1325				.css( {
1326					right: '15px',
1327				} )
1328				.hide();
1329
1330			previousButton = $( '<div><span></span></div>' )
1331				.addClass( 'jp-carousel-previous-button' )
1332				.css( {
1333					left: 0,
1334				} )
1335				.hide();
1336
1337			nextButton.add( previousButton ).css( {
1338				position: 'fixed',
1339				top: '40px',
1340				bottom: targetBottomPos,
1341				width: screenPadding,
1342			} );
1343
1344			gallery = $( '<div></div>' ).addClass( 'jp-carousel' ).css( {
1345				position: 'absolute',
1346				top: 0,
1347				bottom: targetBottomPos,
1348				left: 0,
1349				right: 0,
1350			} );
1351
1352			close_hint = $( '<div class="jp-carousel-close-hint"><span>&times;</span></div>' ).css( {
1353				position: 'fixed',
1354			} );
1355
1356			container = $( '<div></div>' )
1357				.addClass( 'jp-carousel-wrap' )
1358				.addClass( 'jp-carousel-transitions' );
1359			if ( 'white' === jetpackCarouselStrings.background_color ) {
1360				container.addClass( 'jp-carousel-light' );
1361			}
1362
1363			container.attr( 'itemscope', '' );
1364
1365			container.attr( 'itemtype', 'https://schema.org/ImageGallery' );
1366
1367			container
1368				.css( {
1369					position: 'fixed',
1370					top: 0,
1371					right: 0,
1372					bottom: 0,
1373					left: 0,
1374					'z-index': 2147483647,
1375					'overflow-x': 'hidden',
1376					'overflow-y': 'auto',
1377					direction: 'ltr',
1378				} )
1379				.hide()
1380				.append( overlay )
1381				.append( gallery )
1382				.append( fadeaway )
1383				.append( info )
1384				.append( nextButton )
1385				.append( previousButton )
1386				.append( close_hint )
1387				.appendTo( $( 'body' ) )
1388				.click( function ( e ) {
1389					var target = $( e.target ),
1390						wrap = target.parents( 'div.jp-carousel-wrap' ),
1391						data = wrap.data( 'carousel-extra' ),
1392						slide = wrap.find( 'div.selected' ),
1393						attachment_id = slide.data( 'attachment-id' );
1394					data = data || [];
1395
1396					if ( target.is( gallery ) || target.parents().add( target ).is( close_hint ) ) {
1397						container.jp_carousel( 'close' );
1398// @start-hide-in-jetpack
1399					} else if ( target.hasClass('jp-carousel-reblog') ) {
1400						e.preventDefault();
1401						e.stopPropagation();
1402						if ( !target.hasClass('reblogged') ) {
1403							target.jp_carousel('show_reblog_box');
1404							wpcom.carousel.stat('reblog_show_box');
1405						}
1406					} else if ( target.parents('#carousel-reblog-box').length ) {
1407						if ( target.is('a.cancel') ) {
1408							e.preventDefault();
1409							e.stopPropagation();
1410							target.jp_carousel('hide_reblog_box');
1411							wpcom.carousel.stat('reblog_cancel');
1412						} else if ( target.is( 'input[type="submit"]' ) ) {
1413							e.preventDefault();
1414							e.stopPropagation();
1415
1416							var note = $('#carousel-reblog-box textarea').val();
1417							if ( jetpackCarouselStrings.reblog_add_thoughts === note ) {
1418								note = '';
1419							}
1420
1421							$('#carousel-reblog-submit').val( jetpackCarouselStrings.reblogging );
1422							$('#carousel-reblog-submit').prop('disabled', true);
1423							$( '#carousel-reblog-box div.submit span.canceltext' ).show();
1424
1425							$.post( jetpackCarouselStrings.ajaxurl, {
1426								'action': 'post_reblog',
1427								'reblog_source': 'carousel',
1428								'original_blog_id': $('#carousel-reblog-box input#carousel-reblog-blog-id').val(),
1429								'original_post_id': $('.jp-carousel div.selected').data('attachment-id'),
1430								'blog_id': $('#carousel-reblog-box select').val(),
1431								'blog_url': $('#carousel-reblog-box input#carousel-reblog-blog-url').val(),
1432								'blog_title': $('#carousel-reblog-box input#carousel-reblog-blog-title').val(),
1433								'post_url': $('#carousel-reblog-box input#carousel-reblog-post-url').val(),
1434								'post_title': slide.data( 'caption' ) || $('#carousel-reblog-box input#carousel-reblog-post-title').val(),
1435								'note': note,
1436								'_wpnonce': $('#carousel-reblog-box #_wpnonce').val()
1437							},
1438							function (/*result*/) {
1439								$('#carousel-reblog-box').css({ 'height': $('#carousel-reblog-box').height() + 'px' }).slideUp('fast');
1440								$('a.jp-carousel-reblog').html( jetpackCarouselStrings.reblogged ).removeClass( 'reblog' ).addClass( 'reblogged' );
1441								$( '#carousel-reblog-box div.submit span.canceltext' ).hide();
1442								$('#carousel-reblog-submit').val( jetpackCarouselStrings.post_reblog );
1443								$('div.jp-carousel-info').children().not('#carousel-reblog-box').fadeIn('fast');
1444								slide.data('reblogged', 1);
1445								$('div.gallery').find('img[data-attachment-id="' + slide.data('attachment-id') + '"]').data('reblogged', 1);
1446
1447
1448							}, 'json' );
1449							wpcom.carousel.stat('reblog_submit');
1450						}
1451					} else if ( target.hasClass( 'jp-carousel-image-download' ) ) {
1452						wpcom.carousel.stat( 'download_original_click' );
1453// @end-hide-in-jetpack
1454					} else if ( target.hasClass( 'jp-carousel-commentlink' ) ) {
1455						e.preventDefault();
1456						e.stopPropagation();
1457						$( window ).unbind( 'keydown', keyListener );
1458						container.animate( { scrollTop: parseInt( info.position()[ 'top' ], 10 ) }, 'fast' );
1459						$( '#jp-carousel-comment-form-submit-and-info-wrapper' ).slideDown( 'fast' );
1460						$( '#jp-carousel-comment-form-comment-field' ).focus();
1461					} else if ( target.hasClass( 'jp-carousel-comment-login' ) ) {
1462						var url = jetpackCarouselStrings.login_url + '%23jp-carousel-' + attachment_id;
1463
1464						window.location.href = url;
1465					} else if ( target.parents( '#jp-carousel-comment-form-container' ).length ) {
1466						var textarea = $( '#jp-carousel-comment-form-comment-field' )
1467							.blur( function () {
1468								$( window ).bind( 'keydown', keyListener );
1469							} )
1470							.focus( function () {
1471								$( window ).unbind( 'keydown', keyListener );
1472							} );
1473
1474						var emailField = $( '#jp-carousel-comment-form-email-field' )
1475							.blur( function () {
1476								$( window ).bind( 'keydown', keyListener );
1477							} )
1478							.focus( function () {
1479								$( window ).unbind( 'keydown', keyListener );
1480							} );
1481
1482						var authorField = $( '#jp-carousel-comment-form-author-field' )
1483							.blur( function () {
1484								$( window ).bind( 'keydown', keyListener );
1485							} )
1486							.focus( function () {
1487								$( window ).unbind( 'keydown', keyListener );
1488							} );
1489
1490						var urlField = $( '#jp-carousel-comment-form-url-field' )
1491							.blur( function () {
1492								$( window ).bind( 'keydown', keyListener );
1493							} )
1494							.focus( function () {
1495								$( window ).unbind( 'keydown', keyListener );
1496							} );
1497
1498						if ( textarea && textarea.attr( 'id' ) === target.attr( 'id' ) ) {
1499							// For first page load
1500							$( window ).unbind( 'keydown', keyListener );
1501							$( '#jp-carousel-comment-form-submit-and-info-wrapper' ).slideDown( 'fast' );
1502						} else if ( target.is( 'input[type="submit"]' ) ) {
1503							e.preventDefault();
1504							e.stopPropagation();
1505
1506							$( '#jp-carousel-comment-form-spinner' ).show();
1507
1508							var ajaxData = {
1509								action: 'post_attachment_comment',
1510								nonce: jetpackCarouselStrings.nonce,
1511								blog_id: data[ 'blog_id' ],
1512								id: attachment_id,
1513								comment: textarea.val(),
1514							};
1515
1516							if ( ! ajaxData[ 'comment' ].length ) {
1517								gallery.jp_carousel( 'postCommentError', {
1518									field: 'jp-carousel-comment-form-comment-field',
1519									error: jetpackCarouselStrings.no_comment_text,
1520								} );
1521								return;
1522							}
1523
1524							if ( 1 !== Number( jetpackCarouselStrings.is_logged_in ) ) {
1525								ajaxData[ 'email' ] = emailField.val();
1526								ajaxData[ 'author' ] = authorField.val();
1527								ajaxData[ 'url' ] = urlField.val();
1528
1529								if ( 1 === Number( jetpackCarouselStrings.require_name_email ) ) {
1530									if ( ! ajaxData[ 'email' ].length || ! ajaxData[ 'email' ].match( '@' ) ) {
1531										gallery.jp_carousel( 'postCommentError', {
1532											field: 'jp-carousel-comment-form-email-field',
1533											error: jetpackCarouselStrings.no_comment_email,
1534										} );
1535										return;
1536									} else if ( ! ajaxData[ 'author' ].length ) {
1537										gallery.jp_carousel( 'postCommentError', {
1538											field: 'jp-carousel-comment-form-author-field',
1539											error: jetpackCarouselStrings.no_comment_author,
1540										} );
1541										return;
1542									}
1543								}
1544							}
1545
1546							$.ajax( {
1547								type: 'POST',
1548								url: jetpackCarouselStrings.ajaxurl,
1549								data: ajaxData,
1550								dataType: 'json',
1551								success: function ( response /*, status, xhr*/ ) {
1552									if ( 'approved' === response.comment_status ) {
1553										$( '#jp-carousel-comment-post-results' )
1554											.slideUp( 'fast' )
1555											.html(
1556												'<span class="jp-carousel-comment-post-success">' +
1557													jetpackCarouselStrings.comment_approved +
1558													'</span>'
1559											)
1560											.slideDown( 'fast' );
1561									} else if ( 'unapproved' === response.comment_status ) {
1562										$( '#jp-carousel-comment-post-results' )
1563											.slideUp( 'fast' )
1564											.html(
1565												'<span class="jp-carousel-comment-post-success">' +
1566													jetpackCarouselStrings.comment_unapproved +
1567													'</span>'
1568											)
1569											.slideDown( 'fast' );
1570									} else {
1571										// 'deleted', 'spam', false
1572										$( '#jp-carousel-comment-post-results' )
1573											.slideUp( 'fast' )
1574											.html(
1575												'<span class="jp-carousel-comment-post-error">' +
1576													jetpackCarouselStrings.comment_post_error +
1577													'</span>'
1578											)
1579											.slideDown( 'fast' );
1580									}
1581									gallery.jp_carousel( 'clearCommentTextAreaValue' );
1582									gallery.jp_carousel( 'getComments', {
1583										attachment_id: attachment_id,
1584										offset: 0,
1585										clear: true,
1586									} );
1587									$( '#jp-carousel-comment-form-button-submit' ).val(
1588										jetpackCarouselStrings.post_comment
1589									);
1590									$( '#jp-carousel-comment-form-spinner' ).hide();
1591								},
1592								error: function (/*xhr, status, error*/) {
1593									// TODO: Add error handling and display here
1594									gallery.jp_carousel( 'postCommentError', {
1595										field: 'jp-carousel-comment-form-comment-field',
1596										error: jetpackCarouselStrings.comment_post_error,
1597									} );
1598									return;
1599								},
1600							} );
1601						}
1602					} else if ( ! target.parents( '.jp-carousel-info' ).length ) {
1603						container.jp_carousel( 'next' );
1604					}
1605				} )
1606				.bind( 'jp_carousel.afterOpen', function () {
1607					$( window ).bind( 'keydown', keyListener );
1608					$( window ).bind( 'resize', resizeListener );
1609					gallery.opened = true;
1610
1611					resizeListener();
1612				} )
1613				.bind( 'jp_carousel.beforeClose', function () {
1614					var scroll = $( window ).scrollTop();
1615
1616					$( window ).unbind( 'keydown', keyListener );
1617					$( window ).unbind( 'resize', resizeListener );
1618					$( window ).scrollTop( scroll );
1619					$( '.jp-carousel-previous-button' ).hide();
1620					$( '.jp-carousel-next-button' ).hide();
1621					// Set height to original value
1622					// Fix some themes where closing carousel brings view back to top
1623					$( 'html' ).css( 'height', '' );
1624					gallery.jp_carousel( 'hide_reblog_box' ); // @hide-in-jetpack
1625				} )
1626				.bind( 'jp_carousel.afterClose', function () {
1627					if ( window.location.hash && history.back ) {
1628						history.back();
1629					}
1630					last_known_location_hash = '';
1631					gallery.opened = false;
1632				} )
1633				.on( 'transitionend.jp-carousel ', '.jp-carousel-slide', function ( e ) {
1634					// If the movement transitions take more than twice the allotted time, disable them.
1635					// There is some wiggle room in the 2x, since some of that time is taken up in
1636					// JavaScript, setting up the transition and calling the events.
1637					if ( 'transform' === e.originalEvent.propertyName ) {
1638						var transitionMultiplier =
1639							( Date.now() - transitionBegin ) / 1000 / e.originalEvent.elapsedTime;
1640
1641						container.off( 'transitionend.jp-carousel' );
1642
1643						if ( transitionMultiplier >= 2 ) {
1644							$( '.jp-carousel-transitions' ).removeClass( 'jp-carousel-transitions' );
1645						}
1646					}
1647				} );
1648
1649			$( '.jp-carousel-wrap' ).touchwipe( {
1650				wipeLeft: function ( e ) {
1651					e.preventDefault();
1652					gallery.jp_carousel( 'next' );
1653				},
1654				wipeRight: function ( e ) {
1655					e.preventDefault();
1656					gallery.jp_carousel( 'previous' );
1657				},
1658				preventDefaultEvents: false,
1659			} );
1660
1661			nextButton.add( previousButton ).click( function ( e ) {
1662				e.preventDefault();
1663				e.stopPropagation();
1664				if ( nextButton.is( this ) ) {
1665					gallery.jp_carousel( 'next' );
1666				} else {
1667					gallery.jp_carousel( 'previous' );
1668				}
1669			} );
1670		}
1671	};
1672
1673	var processSingleImageGallery = function () {
1674		// process links that contain img tag with attribute data-attachment-id
1675		$( 'a img[data-attachment-id]' ).each( function () {
1676			var container = $( this ).parent();
1677
1678			// skip if image was already added to gallery by shortcode
1679			if ( container.parent( '.gallery-icon' ).length ) {
1680				return;
1681			}
1682
1683			// skip if the container is not a link
1684			if ( 'undefined' === typeof $( container ).attr( 'href' ) ) {
1685				return;
1686			}
1687
1688			var valid = false;
1689
1690			// if link points to 'Media File' (ignoring GET parameters) and flag is set allow it
1691			if (
1692				$( container ).attr( 'href' ).split( '?' )[ 0 ] ===
1693					$( this ).attr( 'data-orig-file' ).split( '?' )[ 0 ] &&
1694				1 === Number( jetpackCarouselStrings.single_image_gallery_media_file )
1695			) {
1696				valid = true;
1697			}
1698
1699			// if link points to 'Attachment Page' allow it
1700			if ( $( container ).attr( 'href' ) === $( this ).attr( 'data-permalink' ) ) {
1701				valid = true;
1702			}
1703
1704			// links to 'Custom URL' or 'Media File' when flag not set are not valid
1705			if ( ! valid ) {
1706				return;
1707			}
1708
1709			// make this node a gallery recognizable by event listener above
1710			$( container ).addClass( 'single-image-gallery' );
1711			// blog_id is needed to allow posting comments to correct blog
1712			$( container ).data( 'carousel-extra', {
1713				blog_id: Number( jetpackCarouselStrings.blog_id ),
1714			} );
1715		} );
1716	};
1717
1718	var methods = {
1719		testForData: function ( gallery ) {
1720			gallery = $( gallery ); // make sure we have it as a jQuery object.
1721			return ! ( ! gallery.length || ! gallery.data( 'carousel-extra' ) );
1722		},
1723
1724		testIfOpened: function () {
1725			return !! (
1726				'undefined' !== typeof gallery &&
1727				'undefined' !== typeof gallery.opened &&
1728				gallery.opened
1729			);
1730		},
1731
1732		openOrSelectSlide: function ( index ) {
1733			// The `open` method triggers an asynchronous effect, so we will get an
1734			// error if we try to use `open` then `selectSlideAtIndex` immediately
1735			// after it. We can only use `selectSlideAtIndex` if the carousel is
1736			// already open.
1737			if ( ! $( this ).jp_carousel( 'testIfOpened' ) ) {
1738				// The `open` method selects the correct slide during the
1739				// initialization.
1740				$( this ).jp_carousel( 'open', { start_index: index } );
1741			} else {
1742				gallery.jp_carousel( 'selectSlideAtIndex', index );
1743			}
1744		},
1745
1746		open: function ( options ) {
1747			var settings = {
1748					items_selector:
1749						'.gallery-item [data-attachment-id], .tiled-gallery-item [data-attachment-id], img[data-attachment-id]',
1750					start_index: 0,
1751				},
1752				data = $( this ).data( 'carousel-extra' );
1753
1754			if ( ! data ) {
1755				return; // don't run if the default gallery functions weren't used
1756			}
1757
1758			prepareGallery( data );
1759
1760			if ( gallery.jp_carousel( 'testIfOpened' ) ) {
1761				return; // don't open if already opened
1762			}
1763
1764			// make sure to stop the page from scrolling behind the carousel overlay, so we don't trigger
1765			// infiniscroll for it when enabled (Reader, theme infiniscroll, etc).
1766			originalOverflow = $( 'body' ).css( 'overflow' );
1767			$( 'body' ).css( 'overflow', 'hidden' );
1768			// prevent html from overflowing on some of the new themes.
1769			originalHOverflow = $( 'html' ).css( 'overflow' );
1770			$( 'html' ).css( 'overflow', 'hidden' );
1771			scrollPos = $( window ).scrollTop();
1772
1773			container.data( 'carousel-extra', data );
1774// @start-hide-in-jetpack
1775			wpcom.carousel.stat( ['open', 'view_image'] );
1776// @end-hide-in-jetpack
1777
1778			return this.each( function () {
1779				// If options exist, lets merge them
1780				// with our default settings
1781				var $this = $( this );
1782
1783				if ( options ) {
1784					$.extend( settings, options );
1785				}
1786				if ( -1 === settings.start_index ) {
1787					settings.start_index = 0; //-1 returned if can't find index, so start from beginning
1788				}
1789
1790				container.trigger( 'jp_carousel.beforeOpen' ).fadeIn( 'fast', function () {
1791					container.trigger( 'jp_carousel.afterOpen' );
1792					gallery
1793						.jp_carousel(
1794							'initSlides',
1795							$this.find( settings.items_selector ),
1796							settings.start_index
1797						)
1798						.jp_carousel( 'selectSlideAtIndex', settings.start_index );
1799				} );
1800				gallery.html( '' );
1801			} );
1802		},
1803
1804		selectSlideAtIndex: function ( index ) {
1805			var slides = this.jp_carousel( 'slides' ),
1806				selected = slides.eq( index );
1807
1808			if ( 0 === selected.length ) {
1809				selected = slides.eq( 0 );
1810			}
1811
1812			gallery.jp_carousel( 'selectSlide', selected, false );
1813			return this;
1814		},
1815
1816		close: function () {
1817			// make sure to let the page scroll again
1818			$( 'body' ).css( 'overflow', originalOverflow );
1819			$( 'html' ).css( 'overflow', originalHOverflow );
1820			this.jp_carousel( 'clearCommentTextAreaValue' );
1821			return container.trigger( 'jp_carousel.beforeClose' ).fadeOut( 'fast', function () {
1822				container.trigger( 'jp_carousel.afterClose' );
1823				$( window ).scrollTop( scrollPos );
1824			} );
1825		},
1826
1827		next: function () {
1828			this.jp_carousel( 'previousOrNext', 'nextSlide' );
1829            gallery.jp_carousel( 'hide_reblog_box' ); // @hide-in-jetpack
1830		},
1831
1832		previous: function () {
1833			this.jp_carousel( 'previousOrNext', 'prevSlide' );
1834            gallery.jp_carousel( 'hide_reblog_box' ); // @hide-in-jetpack
1835		},
1836
1837		previousOrNext: function ( slideSelectionMethodName ) {
1838			if ( ! this.jp_carousel( 'hasMultipleImages' ) ) {
1839				return false;
1840			}
1841
1842			var slide = gallery.jp_carousel( slideSelectionMethodName );
1843
1844			if ( slide ) {
1845				container.animate( { scrollTop: 0 }, 'fast' );
1846				this.jp_carousel( 'clearCommentTextAreaValue' );
1847				this.jp_carousel( 'selectSlide', slide );
1848                wpcom.carousel.stat( ['previous', 'view_image'] ); // @hide-in-jetpack
1849			}
1850		},
1851
1852        // @start-hide-in-jetpack
1853       resetButtons : function (current) {
1854		   if ( current.data( 'reblogged' ) ) {
1855                $('.jp-carousel-buttons a.jp-carousel-reblog').addClass( 'reblogged' ).text( jetpackCarouselStrings.reblogged );
1856		   } else {
1857                $('.jp-carousel-buttons a.jp-carousel-reblog').removeClass( 'reblogged' ).text( jetpackCarouselStrings.reblog );
1858		   }
1859           // Must also take care of reblog/reblogged here
1860        },
1861        // @end-hide-in-jetpack
1862		selectedSlide: function () {
1863			return this.find( '.selected' );
1864		},
1865
1866		setSlidePosition: function ( x ) {
1867			transitionBegin = Date.now();
1868
1869			return this.css( {
1870				'-webkit-transform': 'translate3d(' + x + 'px,0,0)',
1871				'-moz-transform': 'translate3d(' + x + 'px,0,0)',
1872				'-ms-transform': 'translate(' + x + 'px,0)',
1873				'-o-transform': 'translate(' + x + 'px,0)',
1874				transform: 'translate3d(' + x + 'px,0,0)',
1875			} );
1876		},
1877
1878		updateSlidePositions: function ( animate ) {
1879			var current = this.jp_carousel( 'selectedSlide' ),
1880				galleryWidth = gallery.width(),
1881				currentWidth = current.width(),
1882				previous = gallery.jp_carousel( 'prevSlide' ),
1883				next = gallery.jp_carousel( 'nextSlide' ),
1884				previousPrevious = previous.prev(),
1885				nextNext = next.next(),
1886				left = Math.floor( ( galleryWidth - currentWidth ) * 0.5 );
1887
1888			current.jp_carousel( 'setSlidePosition', left ).show();
1889
1890			// minimum width
1891			gallery.jp_carousel( 'fitInfo', animate );
1892
1893			// prep the slides
1894			var direction = lastSelectedSlide.is( current.prevAll() ) ? 1 : -1;
1895
1896			// Since we preload the `previousPrevious` and `nextNext` slides, we need
1897			// to make sure they technically visible in the DOM, but invisible to the
1898			// user. To hide them from the user, we position them outside the edges
1899			// of the window.
1900			//
1901			// This section of code only applies when there are more than three
1902			// slides. Otherwise, the `previousPrevious` and `nextNext` slides will
1903			// overlap with the `previous` and `next` slides which must be visible
1904			// regardless.
1905			if ( 1 === direction ) {
1906				if ( ! nextNext.is( previous ) ) {
1907					nextNext.jp_carousel( 'setSlidePosition', galleryWidth + next.width() ).show();
1908				}
1909
1910				if ( ! previousPrevious.is( next ) ) {
1911					previousPrevious
1912						.jp_carousel( 'setSlidePosition', -previousPrevious.width() - currentWidth )
1913						.show();
1914				}
1915			} else {
1916				if ( ! nextNext.is( previous ) ) {
1917					nextNext.jp_carousel( 'setSlidePosition', galleryWidth + currentWidth ).show();
1918				}
1919			}
1920
1921			previous
1922				.jp_carousel( 'setSlidePosition', Math.floor( -previous.width() + screenPadding * 0.75 ) )
1923				.show();
1924			next
1925				.jp_carousel( 'setSlidePosition', Math.ceil( galleryWidth - screenPadding * 0.75 ) )
1926				.show();
1927		},
1928
1929		selectSlide: function ( slide, animate ) {
1930			lastSelectedSlide = this.find( '.selected' ).removeClass( 'selected' );
1931
1932			var slides = gallery.jp_carousel( 'slides' ).css( { position: 'fixed' } ),
1933				current = $( slide ).addClass( 'selected' ).css( { position: 'relative' } ),
1934				attachmentId = current.data( 'attachment-id' ),
1935				previous = gallery.jp_carousel( 'prevSlide' ),
1936				next = gallery.jp_carousel( 'nextSlide' ),
1937				previousPrevious = previous.prev(),
1938				nextNext = next.next(),
1939				animated,
1940				captionHtml;
1941
1942			// center the main image
1943			gallery.jp_carousel( 'loadFullImage', current );
1944
1945			caption.hide();
1946
1947			if ( next.length === 0 && slides.length <= 2 ) {
1948				$( '.jp-carousel-next-button' ).hide();
1949			} else {
1950				$( '.jp-carousel-next-button' ).show();
1951			}
1952
1953			if ( previous.length === 0 && slides.length <= 2 ) {
1954				$( '.jp-carousel-previous-button' ).hide();
1955			} else {
1956				$( '.jp-carousel-previous-button' ).show();
1957			}
1958
1959			animated = current
1960				.add( previous )
1961				.add( previousPrevious )
1962				.add( next )
1963				.add( nextNext )
1964				.jp_carousel( 'loadSlide' );
1965
1966			// slide the whole view to the x we want
1967			slides.not( animated ).hide();
1968
1969			gallery.jp_carousel( 'updateSlidePositions', animate );
1970			gallery.jp_carousel( 'resetButtons', current ); // @hide-in-jetpack
1971
1972			container.trigger( 'jp_carousel.selectSlide', [ current ] );
1973
1974			gallery.jp_carousel( 'getTitleDesc', {
1975				title: current.data( 'title' ),
1976				desc: current.data( 'desc' ),
1977			} );
1978
1979			var imageMeta = current.data( 'image-meta' );
1980			gallery.jp_carousel( 'updateExif', imageMeta );
1981			gallery.jp_carousel( 'updateFullSizeLink', current );
1982			gallery.jp_carousel( 'updateMap', imageMeta );
1983
1984			if ( 1 === +jetpackCarouselStrings.display_comments ) {
1985				gallery.jp_carousel( 'testCommentsOpened', current.data( 'comments-opened' ) );
1986				gallery.jp_carousel( 'getComments', {
1987					attachment_id: attachmentId,
1988					offset: 0,
1989					clear: true,
1990				} );
1991				$( '#jp-carousel-comment-post-results' ).slideUp();
1992			}
1993
1994			// $('<div />').text(sometext).html() is a trick to go to HTML to plain
1995			// text (including HTML entities decode, etc)
1996			if ( current.data( 'caption' ) ) {
1997				captionHtml = $( '<div />' ).text( current.data( 'caption' ) ).html();
1998
1999				if ( captionHtml === $( '<div />' ).text( current.data( 'title' ) ).html() ) {
2000					$( '.jp-carousel-titleanddesc-title' ).fadeOut( 'fast' ).empty();
2001				}
2002
2003				if ( captionHtml === $( '<div />' ).text( current.data( 'desc' ) ).html() ) {
2004					$( '.jp-carousel-titleanddesc-desc' ).fadeOut( 'fast' ).empty();
2005				}
2006
2007				caption.html( current.data( 'caption' ) ).fadeIn( 'slow' );
2008			} else {
2009				caption.fadeOut( 'fast' ).empty();
2010			}
2011
2012			// Record pageview in WP Stats, for each new image loaded full-screen.
2013			if ( jetpackCarouselStrings.stats ) {
2014				new Image().src =
2015					document.location.protocol +
2016					'//pixel.wp.com/g.gif?' +
2017					jetpackCarouselStrings.stats +
2018					'&post=' +
2019					encodeURIComponent( attachmentId ) +
2020					'&rand=' +
2021					Math.random();
2022			}
2023
2024			wpcom.carousel.pageview( attachmentId ); // @hide-in-jetpack
2025			// Load the images for the next and previous slides.
2026			$( next )
2027				.add( previous )
2028				.each( function () {
2029					gallery.jp_carousel( 'loadFullImage', $( this ) );
2030				} );
2031
2032			window.location.hash = last_known_location_hash = '#jp-carousel-' + attachmentId;
2033		},
2034
2035		slides: function () {
2036			return this.find( '.jp-carousel-slide' );
2037		},
2038
2039		slideDimensions: function () {
2040			return {
2041				width: $( window ).width() - screenPadding * 2,
2042				height: Math.floor( ( $( window ).height() / 100 ) * proportion - 60 ),
2043			};
2044		},
2045
2046		loadSlide: function () {
2047			return this.each( function () {
2048				var slide = $( this );
2049				slide.find( 'img' ).one( 'load', function () {
2050					// set the width/height of the image if it's too big
2051					slide.jp_carousel( 'fitSlide', false );
2052				} );
2053			} );
2054		},
2055
2056		bestFit: function () {
2057			var max = gallery.jp_carousel( 'slideDimensions' ),
2058				orig = this.jp_carousel( 'originalDimensions' ),
2059				orig_ratio = orig.width / orig.height,
2060				w_ratio = 1,
2061				h_ratio = 1,
2062				width,
2063				height;
2064
2065			if ( orig.width > max.width ) {
2066				w_ratio = max.width / orig.width;
2067			}
2068			if ( orig.height > max.height ) {
2069				h_ratio = max.height / orig.height;
2070			}
2071
2072			if ( w_ratio < h_ratio ) {
2073				width = max.width;
2074				height = Math.floor( width / orig_ratio );
2075			} else if ( h_ratio < w_ratio ) {
2076				height = max.height;
2077				width = Math.floor( height * orig_ratio );
2078			} else {
2079				width = orig.width;
2080				height = orig.height;
2081			}
2082
2083			return {
2084				width: width,
2085				height: height,
2086			};
2087		},
2088
2089		fitInfo: function (/*animated*/) {
2090			var current = this.jp_carousel( 'selectedSlide' ),
2091				size = current.jp_carousel( 'bestFit' );
2092
2093			photo_info.css( {
2094				left: Math.floor( ( info.width() - size.width ) * 0.5 ),
2095				width: Math.floor( size.width ),
2096			} );
2097
2098			return this;
2099		},
2100
2101		fitMeta: function ( animated ) {
2102			var newInfoTop = {
2103				top: Math.floor( ( $( window ).height() / 100 ) * proportion + 5 ) + 'px',
2104			};
2105			var newLeftWidth = { width: info.width() - ( imageMeta.width() + 80 ) + 'px' };
2106
2107			if ( animated ) {
2108				info.animate( newInfoTop );
2109				leftColWrapper.animate( newLeftWidth );
2110			} else {
2111				info.animate( newInfoTop );
2112				leftColWrapper.css( newLeftWidth );
2113			}
2114		},
2115
2116		fitSlide: function (/*animated*/) {
2117			return this.each( function () {
2118				var $this = $( this ),
2119					dimensions = $this.jp_carousel( 'bestFit' ),
2120					method = 'css',
2121					max = gallery.jp_carousel( 'slideDimensions' );
2122
2123				dimensions.left = 0;
2124				dimensions.top = Math.floor( ( max.height - dimensions.height ) * 0.5 ) + 40;
2125				$this[ method ]( dimensions );
2126			} );
2127		},
2128
2129		texturize: function ( text ) {
2130			text = '' + text; // make sure we get a string. Title "1" came in as int 1, for example, which did not support .replace().
2131			text = text
2132				.replace( /'/g, '&#8217;' )
2133				.replace( /&#039;/g, '&#8217;' )
2134				.replace( /[\u2019]/g, '&#8217;' );
2135			text = text
2136				.replace( /"/g, '&#8221;' )
2137				.replace( /&#034;/g, '&#8221;' )
2138				.replace( /&quot;/g, '&#8221;' )
2139				.replace( /[\u201D]/g, '&#8221;' );
2140			text = text.replace( /([\w]+)=&#[\d]+;(.+?)&#[\d]+;/g, '$1="$2"' ); // untexturize allowed HTML tags params double-quotes
2141			return $.trim( text );
2142		},
2143
2144		initSlides: function ( items, start_index ) {
2145			if ( items.length < 2 ) {
2146				$( '.jp-carousel-next-button, .jp-carousel-previous-button' ).hide();
2147			} else {
2148				$( '.jp-carousel-next-button, .jp-carousel-previous-button' ).show();
2149			}
2150
2151			// Calculate the new src.
2152			items.each( function (/*i*/) {
2153				var src_item = $( this ),
2154					orig_size = src_item.data( 'orig-size' ) || '',
2155					max = gallery.jp_carousel( 'slideDimensions' ),
2156					parts = orig_size.split( ',' ),
2157					medium_file = src_item.data( 'medium-file' ) || '',
2158					large_file = src_item.data( 'large-file' ) || '',
2159					src;
2160				orig_size = { width: parseInt( parts[ 0 ], 10 ), height: parseInt( parts[ 1 ], 10 ) };
2161
2162// @start-hide-in-jetpack
2163				 if ( 'undefined' !== typeof wpcom ) {
2164					src = src_item.attr('src') || src_item.attr('original') || src_item.data('original') || src_item.data('lazy-src');
2165					if (src.indexOf('imgpress') !== -1) {
2166						src = src_item.data('orig-file');
2167					}
2168					// Square/Circle galleries use a resize param that needs to be removed.
2169					src = wpcom.carousel.removeResizeFromImageURL( src );
2170					src = wpcom.carousel.addWidthToImageURL( src, wpcom.carousel.findFirstLargeEnoughWidth( orig_size.width, orig_size.height, max.width, max.height ) );
2171				} else {
2172
2173// @end-hide-in-jetpack
2174				src = src_item.data( 'orig-file' );
2175
2176				src = gallery.jp_carousel( 'selectBestImageSize', {
2177					orig_file: src,
2178					orig_width: orig_size.width,
2179					orig_height: orig_size.height,
2180					max_width: max.width,
2181					max_height: max.height,
2182					medium_file: medium_file,
2183					large_file: large_file,
2184				} );
2185// @start-hide-in-jetpack
2186				 } // end else of if ( 'undefined' != typeof wpcom )
2187// @end-hide-in-jetpack
2188
2189				// Set the final src
2190				$( this ).data( 'gallery-src', src );
2191			} );
2192
2193			// If the start_index is not 0 then preload the clicked image first.
2194			if ( 0 !== start_index ) {
2195				$( '<img/>' )[ 0 ].src = $( items[ start_index ] ).data( 'gallery-src' );
2196			}
2197
2198			var useInPageThumbnails =
2199				items.first().closest( '.tiled-gallery.type-rectangular' ).length > 0;
2200
2201			// create the 'slide'
2202			items.each( function ( i ) {
2203				var src_item = $( this ),
2204					reblogged       = src_item.data( 'reblogged' ) || 0, // @hide-in-jetpack
2205					attachment_id = src_item.data( 'attachment-id' ) || 0,
2206					comments_opened = src_item.data( 'comments-opened' ) || 0,
2207					image_meta = src_item.data( 'image-meta' ) || {},
2208					orig_size = src_item.data( 'orig-size' ) || '',
2209					thumb_size = { width: src_item[ 0 ].naturalWidth, height: src_item[ 0 ].naturalHeight },
2210					title = src_item.data( 'image-title' ) || '',
2211					description = src_item.data( 'image-description' ) || '',
2212					caption = src_item.parents( '.gallery-item' ).find( '.gallery-caption' ).html() || '',
2213					src = src_item.data( 'gallery-src' ) || '',
2214					medium_file = src_item.data( 'medium-file' ) || '',
2215					large_file = src_item.data( 'large-file' ) || '',
2216					orig_file = src_item.data( 'orig-file' ) || '';
2217
2218				var tiledCaption = src_item
2219					.parents( 'div.tiled-gallery-item' )
2220					.find( 'div.tiled-gallery-caption' )
2221					.html();
2222				if ( tiledCaption ) {
2223					caption = tiledCaption;
2224				}
2225
2226				if ( attachment_id && orig_size.length ) {
2227					title = gallery.jp_carousel( 'texturize', title );
2228					description = gallery.jp_carousel( 'texturize', description );
2229					caption = gallery.jp_carousel( 'texturize', caption );
2230
2231					// Initially, the image is a 1x1 transparent gif.  The preview is shown as a background image on the slide itself.
2232					var image = $( '<img/>' )
2233						.attr(
2234							'src',
2235							'data:image/gif;base64,R0lGODlhAQABAIAAAAAAAP///yH5BAEAAAAALAAAAAABAAEAAAIBRAA7'
2236						)
2237						.css( 'width', '100%' )
2238						.css( 'height', '100%' );
2239
2240					var slide = $(
2241						'<div class="jp-carousel-slide" itemprop="associatedMedia" itemscope itemtype="https://schema.org/ImageObject"></div>'
2242					)
2243						.hide()
2244						.css( {
2245							//'position' : 'fixed',
2246							left: i < start_index ? -1000 : gallery.width(),
2247						} )
2248						.append( image )
2249						.appendTo( gallery )
2250						.data( 'src', src )
2251						.data( 'title', title )
2252						.data( 'desc', description )
2253						.data( 'caption', caption )
2254						.data( 'attachment-id', attachment_id )
2255						.data( 'permalink', src_item.parents( 'a' ).attr( 'href' ) )
2256						.data( 'orig-size', orig_size )
2257						.data( 'comments-opened', comments_opened )
2258						.data( 'image-meta', image_meta )
2259						.data( 'medium-file', medium_file )
2260						.data( 'large-file', large_file )
2261						.data( 'orig-file', orig_file )
2262						.data( 'reblogged', reblogged ) // @hide-in-jetpack
2263						.data( 'thumb-size', thumb_size );
2264					if ( useInPageThumbnails ) {
2265						// Use the image already loaded in the gallery as a preview.
2266						slide.data( 'preview-image', src_item.attr( 'src' ) ).css( {
2267							'background-image': 'url("' + src_item.attr( 'src' ) + '")',
2268							'background-size': '100% 100%',
2269							'background-position': 'center center',
2270						} );
2271					}
2272
2273					slide.jp_carousel( 'fitSlide', false );
2274				}
2275			} );
2276			return this;
2277		},
2278
2279		selectBestImageSize: function ( args ) {
2280			if ( 'object' !== typeof args ) {
2281				args = {};
2282			}
2283
2284			if ( 'undefined' === typeof args.orig_file ) {
2285				return '';
2286			}
2287
2288			if ( 'undefined' === typeof args.orig_width || 'undefined' === typeof args.max_width ) {
2289				return args.orig_file;
2290			}
2291
2292			if ( 'undefined' === typeof args.medium_file || 'undefined' === typeof args.large_file ) {
2293				return args.orig_file;
2294			}
2295
2296			// Check if the image is being served by Photon (using a regular expression on the hostname).
2297
2298			var imageLinkParser = document.createElement( 'a' );
2299			imageLinkParser.href = args.large_file;
2300
2301			var isPhotonUrl = /^i[0-2].wp.com$/i.test( imageLinkParser.hostname );
2302
2303			var medium_size_parts = gallery.jp_carousel(
2304				'getImageSizeParts',
2305				args.medium_file,
2306				args.orig_width,
2307				isPhotonUrl
2308			);
2309			var large_size_parts = gallery.jp_carousel(
2310				'getImageSizeParts',
2311				args.large_file,
2312				args.orig_width,
2313				isPhotonUrl
2314			);
2315
2316			var large_width = parseInt( large_size_parts[ 0 ], 10 ),
2317				large_height = parseInt( large_size_parts[ 1 ], 10 ),
2318				medium_width = parseInt( medium_size_parts[ 0 ], 10 ),
2319				medium_height = parseInt( medium_size_parts[ 1 ], 10 );
2320
2321			// Assign max width and height.
2322			args.orig_max_width = args.max_width;
2323			args.orig_max_height = args.max_height;
2324
2325			// Give devices with a higher devicePixelRatio higher-res images (Retina display = 2, Android phones = 1.5, etc)
2326			if ( 'undefined' !== typeof window.devicePixelRatio && window.devicePixelRatio > 1 ) {
2327				args.max_width = args.max_width * window.devicePixelRatio;
2328				args.max_height = args.max_height * window.devicePixelRatio;
2329			}
2330
2331			if ( large_width >= args.max_width || large_height >= args.max_height ) {
2332				return args.large_file;
2333			}
2334
2335			if ( medium_width >= args.max_width || medium_height >= args.max_height ) {
2336				return args.medium_file;
2337			}
2338
2339			if ( isPhotonUrl ) {
2340				// args.orig_file doesn't point to a Photon url, so in this case we use args.large_file
2341				// to return the photon url of the original image.
2342				var largeFileIndex = args.large_file.lastIndexOf( '?' );
2343				var origPhotonUrl = args.large_file;
2344				if ( -1 !== largeFileIndex ) {
2345					origPhotonUrl = args.large_file.substring( 0, largeFileIndex );
2346					// If we have a really large image load a smaller version
2347					// that is closer to the viewable size
2348					if ( args.orig_width > args.max_width || args.orig_height > args.max_height ) {
2349						origPhotonUrl += '?fit=' + args.orig_max_width + '%2C' + args.orig_max_height;
2350					}
2351				}
2352				return origPhotonUrl;
2353			}
2354
2355			return args.orig_file;
2356		},
2357
2358		getImageSizeParts: function ( file, orig_width, isPhotonUrl ) {
2359			var size = isPhotonUrl
2360				? file.replace( /.*=([\d]+%2C[\d]+).*$/, '$1' )
2361				: file.replace( /.*-([\d]+x[\d]+)\..+$/, '$1' );
2362
2363			var size_parts =
2364				size !== file
2365					? isPhotonUrl
2366						? size.split( '%2C' )
2367						: size.split( 'x' )
2368					: [ orig_width, 0 ];
2369
2370			// If one of the dimensions is set to 9999, then the actual value of that dimension can't be retrieved from the url.
2371			// In that case, we set the value to 0.
2372			if ( '9999' === size_parts[ 0 ] ) {
2373				size_parts[ 0 ] = '0';
2374			}
2375
2376			if ( '9999' === size_parts[ 1 ] ) {
2377				size_parts[ 1 ] = '0';
2378			}
2379
2380			return size_parts;
2381		},
2382
2383// @start-hide-in-jetpack
2384        show_reblog_box: function () {
2385            $('#carousel-reblog-box textarea').val(jetpackCarouselStrings.reblog_add_thoughts);
2386            //t.addClass('selected');
2387            $('#carousel-reblog-box p.response').remove();
2388            $('#carousel-reblog-box div.submit, #carousel-reblog-box div.submit span.canceltext').show();
2389            $('#carousel-reblog-box div.submit input[type=submit]').prop('disabled', false);
2390
2391            var current = $('.jp-carousel div.selected');
2392            $('#carousel-reblog-box input#carousel-reblog-post-url').val( current.data('permalink') );
2393            $('#carousel-reblog-box input#carousel-reblog-post-title').val( $('div.jp-carousel-info').children('h2').text() );
2394
2395            $('div.jp-carousel-info').append( $('#carousel-reblog-box') ).children().fadeOut('fast');
2396            $('#carousel-reblog-box').fadeIn('fast');
2397        },
2398
2399        hide_reblog_box: function () {
2400            $( 'div.jp-carousel-info' ).children().not( '#carousel-reblog-box' ).fadeIn( 'fast' );
2401            $( '#carousel-reblog-box' ).fadeOut( 'fast' );
2402        },
2403// @end-hide-in-jetpack
2404		originalDimensions: function () {
2405			var splitted = $( this ).data( 'orig-size' ).split( ',' );
2406			return { width: parseInt( splitted[ 0 ], 10 ), height: parseInt( splitted[ 1 ], 10 ) };
2407		},
2408
2409		format: function ( args ) {
2410			if ( 'object' !== typeof args ) {
2411				args = {};
2412			}
2413			if ( ! args.text || 'undefined' === typeof args.text ) {
2414				return;
2415			}
2416			if ( ! args.replacements || 'undefined' === typeof args.replacements ) {
2417				return args.text;
2418			}
2419			return args.text.replace( /{(\d+)}/g, function ( match, number ) {
2420				return typeof args.replacements[ number ] !== 'undefined'
2421					? args.replacements[ number ]
2422					: match;
2423			} );
2424		},
2425
2426		/**
2427		 * Returns a number in a fraction format that represents the shutter speed.
2428		 * @param Number speed
2429		 * @return String
2430		 */
2431		shutterSpeed: function ( speed ) {
2432			var denominator;
2433
2434			// round to one decimal if value >
2434 1s by multiplying it by 10, rounding, then dividing by 10 again
2435			if ( speed >= 1 ) {
2436				return Math.round( speed * 10 ) / 10 + 's';
2437			}
2438
2439			// If the speed is less than one, we find the denominator by inverting
2440			// the number. Since cameras usually use rational numbers as shutter
2441			// speeds, we should get a nice round number. Or close to one in cases
2442			// like 1/30. So we round it.
2443			denominator = Math.round( 1 / speed );
2444
2445			return '1/' + denominator + 's';
2446		},
2447
2448		parseTitleDesc: function ( value ) {
2449			if ( ! value.match( ' ' ) && value.match( '_' ) ) {
2450				return '';
2451			}
2452
2453			return value;
2454		},
2455
2456		getTitleDesc: function ( data ) {
2457			var title = '',
2458				desc = '',
2459				markup = '',
2460				target;
2461
2462			target = $( 'div.jp-carousel-titleanddesc', 'div.jp-carousel-wrap' );
2463			target.hide();
2464
2465			title = gallery.jp_carousel( 'parseTitleDesc', data.title ) || '';
2466			desc = gallery.jp_carousel( 'parseTitleDesc', data.desc ) || '';
2467
2468			if ( title.length || desc.length ) {
2469				// Convert from HTML to plain text (including HTML entities decode, etc)
2470				if ( $( '<div />' ).html( title ).text() === $( '<div />' ).html( desc ).text() ) {
2471					title = '';
2472				}
2473
2474				markup = title.length
2475					? '<div class="jp-carousel-titleanddesc-title">' + title + '</div>'
2476					: '';
2477				markup += desc.length
2478					? '<div class="jp-carousel-titleanddesc-desc">' + desc + '</div>'
2479					: '';
2480
2481				target.html( markup ).fadeIn( 'slow' );
2482			}
2483
2484			$( 'div#jp-carousel-comment-form-container' ).css( 'margin-top', '20px' );
2485			$( 'div#jp-carousel-comments-loading' ).css( 'margin-top', '20px' );
2486		},
2487
2488		// updateExif updates the contents of the exif UL (.jp-carousel-image-exif)
2489		updateExif: function ( meta ) {
2490			if ( ! meta || 1 !== Number( jetpackCarouselStrings.display_exif ) ) {
2491				return false;
2492			}
2493
2494			var $ul = $( "<ul class='jp-carousel-image-exif'></ul>" );
2495
2496			$.each( meta, function ( key, val ) {
2497				if (
2498					0 === parseFloat( val ) ||
2499					! val.length ||
2500					-1 === $.inArray( key, $.makeArray( jetpackCarouselStrings.meta_data ) )
2501				) {
2502					return;
2503				}
2504
2505				switch ( key ) {
2506					case 'focal_length':
2507						val = val + 'mm';
2508						break;
2509					case 'shutter_speed':
2510						val = gallery.jp_carousel( 'shutterSpeed', val );
2511						break;
2512					case 'aperture':
2513						val = 'f/' + val;
2514						break;
2515				}
2516
2517				$ul.append( '<li><h5>' + jetpackCarouselStrings[ key ] + '</h5>' + val + '</li>' );
2518			} );
2519
2520			// Update (replace) the content of the ul
2521			$( 'div.jp-carousel-image-meta ul.jp-carousel-image-exif' ).replaceWith( $ul );
2522		},
2523
2524		// updateFullSizeLink updates the contents of the jp-carousel-image-download link
2525		updateFullSizeLink: function ( current ) {
2526			if ( ! current || ! current.data ) {
2527				return false;
2528			}
2529			var original,
2530				origSize = current.data( 'orig-size' ).split( ',' ),
2531				imageLinkParser = document.createElement( 'a' );
2532
2533			imageLinkParser.href = current.data( 'src' ).replace( /\?.+$/, '' );
2534
2535			// Is this a Photon URL?
2536			if ( imageLinkParser.hostname.match( /^i[\d]{1}.wp.com$/i ) !== null ) {
2537				original = imageLinkParser.href;
2538			} else {
2539				original = current.data( 'orig-file' ).replace( /\?.+$/, '' );
2540			}
2541
2542			var permalink = $(
2543				'<a>' +
2544					gallery.jp_carousel( 'format', {
2545						text: jetpackCarouselStrings.download_original,
2546						replacements: origSize,
2547					} ) +
2548					'</a>'
2549			)
2550				.addClass( 'jp-carousel-image-download' )
2551				.attr( 'href', original )
2552				.attr( 'target', '_blank' );
2553
2554			// Update (replace) the content of the anchor
2555			$( 'div.jp-carousel-image-meta a.jp-carousel-image-download' ).replaceWith( permalink );
2556		},
2557
2558		updateMap: function ( meta ) {
2559			if (
2560				! meta.latitude ||
2561				! meta.longitude ||
2562				1 !== Number( jetpackCarouselStrings.display_geo )
2563			) {
2564				return;
2565			}
2566
2567			var latitude = meta.latitude,
2568				longitude = meta.longitude,
2569				$metabox = $( 'div.jp-carousel-image-meta', 'div.jp-carousel-wrap' ),
2570				$mapbox = $( '<div></div>' ),
2571				style =
2572					'&scale=2&style=feature:all|element:all|invert_lightness:true|hue:0x0077FF|saturation:-50|lightness:-5|gamma:0.91';
2573
2574			$mapbox
2575				.addClass( 'jp-carousel-image-map' )
2576				.html(
2577					'<img width="154" height="154" src="https://maps.googleapis.com/ma
2577ps/api/staticmap?\
2578							center=' +
2579						latitude +
2580						',' +
2581						longitude +
2582						'&\
2583							zoom=8&\
2584							size=154x154&\
2585							sensor=false&\
2586							markers=size:medium%7Ccolor:blue%7C' +
2587						latitude +
2588						',' +
2589						longitude +
2590						style +
2591						'" class="gmap-main" />\
2592							\
2593						<div class="gmap-topright"><div class="imgclip"><img width="175" height="154" src="https://maps.googleapis.com/maps/api/staticmap?\
2594							center=' +
2595						latitude +
2596						',' +
2597						longitude +
2598						'&\
2599							zoom=3&\
2600							size=175x154&\
2601							sensor=false&\
2602							markers=size:small%7Ccolor:blue%7C' +
2603						latitude +
2604						',' +
2605						longitude +
2606						style +
2607						'"c /></div></div>\
2608							\
2609						'
2610				)
2611				.prependTo( $metabox );
2612		},
2613
2614		testCommentsOpened: function ( opened ) {
2615			if ( 1 === parseInt( opened, 10 ) ) {
2616// @start-hide-in-jetpack
2617				if ( 1 === Number( jetpackCarouselStrings.is_logged_in ) ) {
2618					$('.jp-carousel-commentlink').fadeIn('fast');
2619				} else {
2620// @end-hide-in-jetpack
2621				$( '.jp-carousel-buttons' ).fadeIn( 'fast' );
2622// @start-hide-in-jetpack
2623				}
2624// @end-hide-in-jetpack
2625				commentForm.fadeIn( 'fast' );
2626			} else {
2627// @start-hide-in-jetpack
2628				if ( 1 === Number( jetpackCarouselStrings.is_logged_in ) ) {
2629					$('.jp-carousel-commentlink').fadeOut('fast');
2630				} else {
2631// @end-hide-in-jetpack
2632				$( '.jp-carousel-buttons' ).fadeOut( 'fast' );
2633// @start-hide-in-jetpack
2634				}
2635// @end-hide-in-jetpack
2636				commentForm.fadeOut( 'fast' );
2637			}
2638		},
2639
2640		getComments: function ( args ) {
2641			clearInterval( commentInterval );
2642
2643			if ( 'object' !== typeof args ) {
2644				return;
2645			}
2646
2647			if ( 'undefined' === typeof args.attachment_id || ! args.attachment_id ) {
2648				return;
2649			}
2650
2651			if ( ! args.offset || 'undefined' === typeof args.offset || args.offset < 1 ) {
2652				args.offset = 0;
2653			}
2654
2655			var comments = $( '.jp-carousel-comments' ),
2656				commentsLoading = $( '#jp-carousel-comments-loading' ).show();
2657
2658			if ( args.clear ) {
2659				comments.hide().empty();
2660			}
2661
2662			$.ajax( {
2663				type: 'GET',
2664				url: jetpackCarouselStrings.ajaxurl,
2665				dataType: 'json',
2666				data: {
2667					action: 'get_attachment_comments',
2668					nonce: jetpackCarouselStrings.nonce,
2669					id: args.attachment_id,
2670					offset: args.offset,
2671				},
2672				success: function ( data /*, status, xhr*/ ) {
2673					if ( args.clear ) {
2674						comments.fadeOut( 'fast' ).empty();
2675					}
2676
2677					$( data ).each( function () {
2678						var comment = $( '<div></div>' )
2679							.addClass( 'jp-carousel-comment' )
2680							.attr( 'id', 'jp-carousel-comment-' + this[ 'id' ] )
2681							.html(
2682								'<div class="comment-gravatar">' +
2683									this[ 'gravatar_markup' ] +
2684									'</div>' +
2685									'<div class="comment-author">' +
2686									this[ 'author_markup' ] +
2687									'</div>' +
2688									'<div class="comment-date">' +
2689									this[ 'date_gmt' ] +
2690									'</div>' +
2691									'<div class="comment-content">' +
2692									this[ 'content' ] +
2693									'</div>'
2694							);
2695						comments.append( comment );
2696
2697						// Set the interval to check for a new page of comments.
2698						clearInterval( commentInterval );
2699						commentInterval = setInterval( function () {
2700							if (
2701								$( '.jp-carousel-overlay' ).height() - 150 <
2702								$( '.jp-carousel-wrap' ).scrollTop() + $( window ).height()
2703							) {
2704								gallery.jp_carousel( 'getComments', {
2705									attachment_id: args.attachment_id,
2706									offset: args.offset + 10,
2707									clear: false,
2708								} );
2709								clearInterval( commentInterval );
2710							}
2711						}, 300 );
2712					} );
2713
2714					// Verify (late) that the user didn't repeatldy click the arrows really fast, in which case the requested
2715					// attachment id might no longer match the current attachment id by the time we get the data back or a now
2716					// registered infiniscroll event kicks in, so we don't ever display comments for the wrong image by mistake.
2717					var current = $( '.jp-carousel div.selected' );
2718					if ( current && current.data && current.data( 'attachment-id' ) != args.attachment_id ) {
2719						comments.fadeOut( 'fast' );
2720						comments.empty();
2721						return;
2722					}
2723
2724					// Increase the height of the background, semi-transparent overlay to match the new length of the comments list.
2725					$( '.jp-carousel-overlay' ).height(
2726						$( window ).height() +
2727							titleAndDescription.height() +
2728							commentForm.height() +
2729							( comments.height() > 0 ? comments.height() : imageMeta.height() ) +
2730							200
2731					);
2732
2733					comments.show();
2734					commentsLoading.hide();
2735				},
2736				error: function ( xhr, status, error ) {
2737					// TODO: proper error handling
2738					console.log( 'Comment get fail...', xhr, status, error );
2739					comments.fadeIn( 'fast' );
2740					commentsLoading.fadeOut( 'fast' );
2741				},
2742			} );
2743		},
2744
2745		postCommentError: function ( args ) {
2746			if ( 'object' !== typeof args ) {
2747				args = {};
2748			}
2749			if (
2750				! args.field ||
2751				'undefined' === typeof args.field ||
2752				! args.error ||
2753				'undefined' === typeof args.error
2754			) {
2755				return;
2756			}
2757			$( '#jp-carousel-comment-post-results' )
2758				.slideUp( 'fast' )
2759				.html( '<span class="jp-carousel-comment-post-error">' + args.error + '</span>' )
2760				.slideDown( 'fast' );
2761			$( '#jp-carousel-comment-form-spinner' ).hide();
2762		},
2763
2764		setCommentIframeSrc: function ( attachment_id ) {
2765			var iframe = $( '#jp-carousel-comment-iframe' );
2766			// Set the proper irame src for the current attachment id
2767			if ( iframe && iframe.length ) {
2768				iframe.attr( 'src', iframe.attr( 'src' ).replace( /(postid=)\d+/, '$1' + attachment_id ) );
2769				iframe.attr(
2770					'src',
2771					iframe.attr( 'src' ).replace( /(%23.+)?$/, '%23jp-carousel-' + attachment_id )
2772				);
2773			}
2774		},
2775
2776		clearCommentTextAreaValue: function () {
2777			var commentTextArea = $( '#jp-carousel-comment-form-comment-field' );
2778			if ( commentTextArea ) {
2779				commentTextArea.val( '' );
2780			}
2781		},
2782
2783		nextSlide: function () {
2784			var slides = this.jp_carousel( 'slides' );
2785			var selected = this.jp_carousel( 'selectedSlide' );
2786
2787			if ( selected.length === 0 || ( slides.length > 2 && selected.is( slides.last() ) ) ) {
2788				return slides.first();
2789			}
2790
2791			return selected.next();
2792		},
2793
2794		prevSlide: function () {
2795			var slides = this.jp_carousel( 'slides' );
2796			var selected = this.jp_carousel( 'selectedSlide' );
2797
2798			if ( selected.length === 0 || ( slides.length > 2 && selected.is( slides.first() ) ) ) {
2799				return slides.last();
2800			}
2801
2802			return selected.prev();
2803		},
2804
2805		loadFullImage: function ( slide ) {
2806			var image = slide.find( 'img:first' );
2807
2808			if ( ! image.data( 'loaded' ) ) {
2809				// If the width of the slide is smaller than the width of the "thumbnail" we're already using,
2810				// don't load the full image.
2811
2812				image.on( 'load.jetpack', function () {
2813					image.off( 'load.jetpack' );
2814					$( this ).closest( '.jp-carousel-slide' ).css( 'background-image', '' );
2815				} );
2816
2817				if (
2818					! slide.data( 'preview-image' ) ||
2819					( slide.data( 'thumb-size' ) && slide.width() > slide.data( 'thumb-size' ).width )
2820				) {
2821					image
2822						.attr( 'src', image.closest( '.jp-carousel-slide' ).data( 'src' ) )
2823						.attr( 'itemprop', 'image' );
2824				} else {
2825					image.attr( 'src', slide.data( 'preview-image' ) ).attr( 'itemprop', 'image' );
2826				}
2827
2828				image.data( 'loaded', 1 );
2829			}
2830		},
2831
2832		hasMultipleImages: function () {
2833			return gallery.jp_carousel( 'slides' ).length > 1;
2834		},
2835	};
2836
2837	$.fn.jp_carousel = function ( method ) {
2838		// ask for the HTML of the gallery
2839		// Method calling logic
2840		if ( methods[ method ] ) {
2841			return methods[ method ].apply( this, Array.prototype.slice.call( arguments, 1 ) );
2842		} else if ( typeof method === 'object' || ! method ) {
2843			return methods.open.apply( this, arguments );
2844		} else {
2845			$.error( 'Method ' + method + ' does not exist on jQuery.jp_carousel' );
2846		}
2847	};
2848
2849	// register the event listener for starting the gallery
2850	$( document.body ).on(
2851		'click.jp-carousel',
2852		'div.gallery, div.tiled-gallery, ul.wp-block-gallery, ul.blocks-gallery-grid, div.wp-block-jetpack-tiled-gallery, a.single-image-gallery',
2853		function ( e ) {
2854			if ( ! $( this ).jp_carousel( 'testForData', e.currentTarget ) ) {
2855				return;
2856			}
2857
2858			// Do not open the modal if we are looking at a gallery caption from before WP5, which may contain a link.
2859			if ( $( e.target ).parent().hasClass( 'gallery-caption' ) ) {
2860				return;
2861			}
2862
2863			// Do not open the modal if we are looking at a caption of a gallery block, which may contain a link.
2864			if ( $( e.target ).parent().is( 'figcaption' ) ) {
2865				return;
2866			}
2867
2868			// Set height to auto
2869			// Fix some themes where closing carousel brings view back to top
2870			$( 'html' ).css( 'height', 'auto' );
2871
2872			e.preventDefault();
2873
2874			// Stopping propagation in case there are parent elements
2875			// with .gallery or .tiled-gallery class
2876			e.stopPropagation();
2877			$( this ).jp_carousel( 'open', {
2878				start_index: $( this )
2879					.find( '.gallery-item, .tiled-gallery-item, .blocks-gallery-item, .tiled-gallery__item' )
2880					.index(
2881						$( e.target ).parents(
2882							'.gallery-item, .tiled-gallery-item, .blocks-gallery-item, .tiled-gallery__item'
2883						)
2884					),
2885			} );
2886		}
2887	);
2888
2889	// handle lightbox (single image gallery) for images linking to 'Attachment Page'
2890	if ( 1 === Number( jetpackCarouselStrings.single_image_gallery ) ) {
2891		processSingleImageGallery();
2892		$( document.body ).on( 'post-load', function () {
2893			processSingleImageGallery();
2894		} );
2895	}
2896
2897	// Makes carousel work on page load and when back button leads to same URL with carousel hash (ie: no actual document.ready trigger)
2898	$( window ).on( 'hashchange.jp-carousel', function () {
2899		var hashRegExp = /jp-carousel-(\d+)/,
2900			matches,
2901			attachmentId,
2902			galleries,
2903			selectedThumbnail;
2904
2905		if ( ! window.location.hash || ! hashRegExp.test( window.location.hash ) ) {
2906			if ( gallery && gallery.opened ) {
2907				container.jp_carousel( 'close' );
2908			}
2909
2910			return;
2911		}
2912
2913		if ( window.location.hash === last_known_location_hash && gallery.opened ) {
2914			return;
2915		}
2916
2917		if ( window.location.hash && gallery && ! gallery.opened && history.back ) {
2918			history.back();
2919			return;
2920		}
2921
2922		last_known_location_hash = window.location.hash;
2923		matches = window.location.hash.match( hashRegExp );
2924		attachmentId = parseInt( matches[ 1 ], 10 );
2925		galleries = $(
2926			'div.gallery, div.tiled-gallery, a.single-image-gallery, ul.wp-block-gallery, div.wp-block-jetpack-tiled-gallery'
2927		);
2928
2929		// Find the first thumbnail that matches the attachment ID in the location
2930		// hash, then open the gallery that contains it.
2931		galleries.each( function ( _, galleryEl ) {
2932			$( galleryEl )
2933				.find( 'img' )
2934				.each( function ( imageIndex, imageEl ) {
2935					if ( $( imageEl ).data( 'attachment-id' ) === parseInt( attachmentId, 10 ) ) {
2936						selectedThumbnail = { index: imageIndex, gallery: galleryEl };
2937						return false;
2938					}
2939				} );
2940
2941			if ( selectedThumbnail ) {
2942				$( selectedThumbnail.gallery ).jp_carousel( 'openOrSelectSlide', selectedThumbnail.index );
2943				return false;
2944			}
2945		} );
2946	} );
2947
2948	if ( window.location.hash ) {
2949		$( window ).trigger( 'hashchange' );
2950	}
2951} );
2952
2953/**
2954 * jQuery Plugin to obtain touch gestures from iPhone, iPod Touch and iPad, should also work with Android mobile phones (not tested yet!)
2955 * Common usage: wipe images (left and right to show the previous or next image)
2956 *
2957 * @author Andreas Waltl, netCU Internetagentur (http://www.netcu.de)
2958 * Version 1.1.1, modified to pass the touchmove event to the callbacks.
2959 */
2960( function ( $ ) {
2961	$.fn.touchwipe = function ( settings ) {
2962		var config = {
2963			min_move_x: 20,
2964			min_move_y: 20,
2965			wipeLeft: function (/*e*/) {},
2966			wipeRight: function (/*e*/) {},
2967			wipeUp: function (/*e*/) {},
2968			wipeDown: function (/*e*/) {},
2969			preventDefaultEvents: true,
2970		};
2971
2972		if ( settings ) {
2973			$.extend( config, settings );
2974		}
2975
2976		this.each( function () {
2977			var startX;
2978			var startY;
2979			var isMoving = false;
2980
2981			function cancelTouch() {
2982				this.removeEventListener( 'touchmove', onTouchMove );
2983				startX = null;
2984				isMoving = false;
2985			}
2986
2987			function onTouchMove( e ) {
2988				if ( config.preventDefaultEvents ) {
2989					e.preventDefault();
2990				}
2991				if ( isMoving ) {
2992					var x = e.touches[ 0 ].pageX;
2993					var y = e.touches[ 0 ].pageY;
2994					var dx = startX - x;
2995					var dy = startY - y;
2996					if ( Math.abs( dx ) >= config.min_move_x ) {
2997						cancelTouch();
2998						if ( dx > 0 ) {
2999							config.wipeLeft( e );
3000						} else {
3001							config.wipeRight( e );
3002						}
3003					} else if ( Math.abs( dy ) >= config.min_move_y ) {
3004						cancelTouch();
3005						if ( dy > 0 ) {
3006							config.wipeDown( e );
3007						} else {
3008							config.wipeUp( e );
3009						}
3010					}
3011				}
3012			}
3013
3014			function onTouchStart( e ) {
3015				if ( e.touches.length === 1 ) {
3016					startX = e.touches[ 0 ].pageX;
3017					startY = e.touches[ 0 ].pageY;
3018					isMoving = true;
3019					this.addEventListener( 'touchmove', onTouchMove, false );
3020				}
3021			}
3022			if ( 'ontouchstart' in document.documentElement ) {
3023				this.addEventListener( 'touchstart', onTouchStart, false );
3024			}
3025		} );
3026
3027		return this;
3028	};
3029} )( jQuery );
3030;

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.