PageSourceSearch

https://theelephantsdebt.com/script/Ovj-fx0fD69nI7DN7yID/M-.js

js theelephantsdebt.com collected 2026-09-27 07:39:05 UTC 153,988 bytes, 4,571 lines download raw bytes

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

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.