PageSourceSearch

https://elojodeuk.com/script/yPLwvnli-LGZKrxQx8VMsMzdYtvzvFlITeWBw…wlvvP79HlvEU-VrHa3w5Pz49nO-fu66bvwDF6ceI.js

js elojodeuk.com collected 2026-09-26 17:52:28 UTC 75,005 bytes, 2,181 lines download raw bytes

1var _____WB$wombat$assign$function_____=function(name){return (self._wb_wombat && self._wb_wombat.local_init && self._wb_wombat.local_init(name))||self[name];};if(!self.__WB_pmw){self.__WB_pmw=function(obj){this.__WB_source=obj;return this;}}{
2let window = _____WB$wombat$assign$function_____("window");
3let self = _____WB$wombat$assign$function_____("self");
4let document = _____WB$wombat$assign$function_____("document");
5let location = _____WB$wombat$assign$function_____("location");
6let top = _____WB$wombat$assign$function_____("top");
7let parent = _____WB$wombat$assign$function_____("parent");
8let frames = _____WB$wombat$assign$function_____("frames");
9let opens = _____WB$wombat$assign$function_____("opens");
10/* Detect-zoom
11 * -----------
12 * Cross Browser Zoom and Pixel Ratio Detector
13 * Version 1.0.4 | Apr 1 2013
14 * dual-licensed under the WTFPL and MIT license
15 * Maintained by https://github/tombigel
16 * Original developer https://github.com/yonran
17 */
18
19//AMD and CommonJS initialization copied from https://github.com/zohararad/audio5js
20(function (root, ns, factory) {
21    "use strict";
22
23    if (typeof (module) !== 'undefined' && module.exports) { // CommonJS
24        module.exports = factory(ns, root);
25    } else if (typeof (define) === 'function' && define.amd) { // AMD
26        define("factory", function () {
27            return factory(ns, root);
28        });
29    } else {
30        root[ns] = factory(ns, root);
31    }
32
33}(window, 'detectZoom', function () {
34
35    /**
36     * Use devicePixelRatio if supported by the browser
37     * @return {Number}
38     * @private
39     */
40    var devicePixelRatio = function () {
41        return window.devicePixelRatio || 1;
42    };
43
44    /**
45     * Fallback function to set default values
46     * @return {Object}
47     * @private
48     */
49    var fallback = function () {
50        return {
51            zoom: 1,
52            devicePxPerCssPx: 1
53        };
54    };
55    /**
56     * IE 8 and 9: no trick needed!
57     * TODO: Test on IE10 and Windows 8 RT
58     * @return {Object}
59     * @private
60     **/
61    var ie8 = function () {
62        var zoom = Math.round((screen.deviceXDPI / screen.logicalXDPI) * 100) / 100;
63        return {
64            zoom: zoom,
65            devicePxPerCssPx: zoom * devicePixelRatio()
66        };
67    };
68
69    /**
70     * For IE10 we need to change our technique again...
71     * thanks https://github.com/stefanvanburen
72     * @return {Object}
73     * @private
74     */
75    var ie10 = function () {
76        var zoom = Math.round((document.documentElement.offsetHeight / window.innerHeight) * 100) / 100;
77        return {
78            zoom: zoom,
79            devicePxPerCssPx: zoom * devicePixelRatio()
80        };
81    };
82
83    /**
84     * Mobile WebKit
85     * the trick: window.innerWIdth is in CSS pixels, while
86     * screen.width and screen.height are in system pixels.
87     * And there are no scrollbars to mess up the measurement.
88     * @return {Object}
89     * @private
90     */
91    var webkitMobile = function () {
92        var deviceWidth = (Math.abs(window.orientation) == 90) ? screen.height : screen.width;
93        var zoom = deviceWidth / window.innerWidth;
94        return {
95            zoom: zoom,
96            devicePxPerCssPx: zoom * devicePixelRatio()
97        };
98    };
99
100    /**
101     * Desktop Webkit
102     * the trick: an element's clientHeight is in CSS pixels, while you can
103     * set its line-height in system pixels using font-size and
104     * -webkit-text-size-adjust:none.
105     * device-pixel-ratio: http://www.webkit.org/blog/55/high-dpi-web-sites/
106     *
107     * Previous trick (used before http://trac.webkit.org/changeset/100847):
108     * documentElement.scrollWidth is in CSS pixels, while
109     * document.width was in system pixels. Note that this is the
110     * layout width of the document, which is slightly different from viewport
111     * because document width does not include scrollbars and might be wider
112     * due to big elements.
113     * @return {Object}
114     * @private
115     */
116    var webkit = function () {
117        var important = function (str) {
118            return str.replace(/;/g, " !important;");
119        };
120
121        var div = document.createElement('div');
122        div.innerHTML = "1<br>2<br>3<br>4<br>5<br>6<br>7<br>8<br>9<br>0";
123        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;'));
124
125        // The container exists so that the div will be laid out in its own flow
126        // while not impacting the layout, viewport size, or display of the
127        // webpage as a whole.
128        // Add !important and relevant CSS rule resets
129        // so that other rules cannot affect the results.
130        var container = document.createElement('div');
131        container.setAttribute('style', important('width:0; height:0; overflow:hidden; visibility:hidden; position: absolute;'));
132        container.appendChild(div);
133
134        document.body.appendChild(container);
135        var zoom = 1000 / div.clientHeight;
136        zoom = Math.round(zoom * 100) / 100;
137        document.body.removeChild(container);
138
139        return{
140            zoom: zoom,
141            devicePxPerCssPx: zoom * devicePixelRatio()
142        };
143    };
144
145    /**
146     * no real trick; device-pixel-ratio is the ratio of device dpi / css dpi.
147     * (Note that this is a different interpretation than Webkit's device
148     * pixel ratio, which is the ratio device dpi / system dpi).
149     *
150     * Also, for Mozilla, there is no difference between the zoom factor and the device ratio.
151     *
152     * @return {Object}
153     * @private
154     */
155    var firefox4 = function () {
156        var zoom = mediaQueryBinarySearch('min--moz-device-pixel-ratio', '', 0, 10, 20, 0.0001);
157        zoom = Math.round(zoom * 100) / 100;
158        return {
159            zoom: zoom,
160            devicePxPerCssPx: zoom
161        };
162    };
163
164    /**
165     * Firefox 18.x
166     * Mozilla added support for devicePixelRatio to Firefox 18,
167     * but it is affected by the zoom level, so, like in older
168     * Firefox we can't tell if we are in zoom mode or in a device
169     * with a different pixel ratio
170     * @return {Object}
171     * @private
172     */
173    var firefox18 = function () {
174        return {
175            zoom: firefox4().zoom,
176            devicePxPerCssPx: devicePixelRatio()
177        };
178    };
179
180    /**
181     * works starting Opera 11.11
182     * the trick: outerWidth is the viewport width including scrollbars in
183     * system px, while innerWidth is the viewport width including scrollbars
184     * in CSS px
185     * @return {Object}
186     * @private
187     */
188    var opera11 = function () {
189        var zoom = window.top.outerWidth / window.top.innerWidth;
190        zoom = Math.round(zoom * 100) / 100;
191        return {
192            zoom: zoom,
193            devicePxPerCssPx: zoom * devicePixelRatio()
194        };
195    };
196
197    /**
198     * Use a binary search through media queries to find zoom level in Firefox
199     * @param property
200     * @param unit
201     * @param a
202     * @param b
203     * @param maxIter
204     * @param epsilon
205     * @return {Number}
206     */
207    var mediaQueryBinarySearch = function (property, unit, a, b, maxIter, epsilon) {
208        var matchMedia;
209        var head, style, div;
210        if (window.matchMedia) {
211            matchMedia = window.matchMedia;
212        } else {
213            head = document.getElementsByTagName('head')[0];
214            style = document.createElement('style');
215            head.appendChild(style);
216
217            div = document.createElement('div');
218            div.className = 'mediaQueryBinarySearch';
219            div.style.display = 'none';
220            document.body.appendChild(div);
221
222            matchMedia = function (query) {
223                style.sheet.insertRule('@media ' + query + '{.mediaQueryBinarySearch ' + '{text-decoration: underline} }', 0);
224                var matched = getComputedStyle(div, null).textDecoration == 'underline';
225                style.sheet.deleteRule(0);
226                return {matches: matched};
227            };
228        }
229        var ratio = binarySearch(a, b, maxIter);
230        if (div) {
231            head.removeChild(style);
232            document.body.removeChild(div);
233        }
234        return ratio;
235
236        function binarySearch(a, b, maxIter) {
237            var mid = (a + b) / 2;
238            if (maxIter <= 0 || b - a < epsilon) {
239                return mid;
240            }
241            var query = "(" + property + ":" + mid + unit + ")";
242            if (matchMedia(query).matches) {
243                return binarySearch(mid, b, maxIter - 1);
244            } else {
245                return binarySearch(a, mid, maxIter - 1);
246            }
247        }
248    };
249
250    /**
251     * Generate detection function
252     * @private
253     */
254    var detectFunction = (function () {
255        var func = fallback;
256        //IE8+
257        if (!isNaN(screen.logicalXDPI) && !isNaN(screen.systemXDPI)) {
258            func = ie8;
259        }
260        // IE10+ / Touch
261        else if (window.navigator.msMaxTouchPoints) {
262            func = ie10;
263        }
264        //Mobile Webkit
265        else if ('orientation' in window && typeof document.body.style.webkitMarquee === 'string') {
266            func = webkitMobile;
267        }
268        //WebKit
269        else if (typeof document.body.style.webkitMarquee === 'string') {
270            func = webkit;
271        }
272        //Opera
273        else if (navigator.userAgent.indexOf('Opera') >= 0) {
274            func = opera11;
275        }
276        //Last one is Firefox
277        //FF 18.x
278        else if (window.devicePixelRatio) {
279            func = firefox18;
280        }
281        //FF 4.0 - 17.x
282        else if (firefox4().zoom > 0.001) {
283            func = firefox4;
284        }
285
286        return func;
287    }());
288
289
290    return ({
291
292        /**
293         * Ratios.zoom shorthand
294         * @return {Number} Zoom level
295         */
296        zoom: function () {
297            return detectFunction().zoom;
298        },
299
300        /**
301         * Ratios.devicePxPerCssPx shorthand
302         * @return {Number} devicePxPerCssPx level
303         */
304        device: function () {
305            return detectFunction().devicePxPerCssPx;
306        }
307    });
308}));
309
310var wpcom_img_zoomer = {
311        clientHintSupport: {
312                gravatar: false,
313                files: false,
314                photon: false,
315                mshots: false,
316                staticAssets: false,
317                latex: false,
318                imgpress: false,
319        },
320	useHints: false,
321	zoomed: false,
322	timer: null,
323	interval: 1000, // zoom polling interval in millisecond
324
325	// Should we apply width/height attributes to control the image size?
326	imgNeedsSizeAtts: function( img ) {
327		// Do not overwrite existing width/height attributes.
328		if ( img.getAttribute('width') !== null || img.getAttribute('height') !== null )
329			return false;
330		// Do not apply the attributes if the image is already constrained by a parent element.
331		if ( img.width < img.naturalWidth || img.height < img.naturalHeight )
332			return false;
333		return true;
334	},
335
336        hintsFor: function( service ) {
337                if ( this.useHints === false ) {
338                        return false;
339                }
340                if ( this.hints() === false ) {
341                        return false;
342                }
343                if ( typeof this.clientHintSupport[service] === "undefined" ) {
344                        return false;
345                }
346                if ( this.clientHintSupport[service] === true ) {
347                        return true;
348                }
349                return false;
350        },
351
352	hints: function() {
353		try {
354			var chrome = window.navigator.userAgent.match(/\sChrome\/([0-9]+)\.[.0-9]+\s/)
355			if (chrome !== null) {
356				var version = parseInt(chrome[1], 10)
357				if (isNaN(version) === false && version >= 46) {
358					return true
359				}
360			}
361		} catch (e) {
362			return false
363		}
364		return false
365	},
366
367	init: function() {
368		var t = this;
369		try{
370			t.zoomImages();
371			t.timer = setInterval( function() { t.zoomImages(); }, t.interval );
372		}
373		catch(e){
374		}
375	},
376
377	stop: function() {
378		if ( this.timer )
379			clearInterval( this.timer );
380	},
381
382	getScale: function() {
383		var scale = detectZoom.device();
384		// Round up to 1.5 or the next integer below the cap.
385		if      ( scale <= 1.0 ) scale = 1.0;
386		else if ( scale <= 1.5 ) scale = 1.5;
387		else if ( scale <= 2.0 ) scale = 2.0;
388		else if ( scale <= 3.0 ) scale = 3.0;
389		else if ( scale <= 4.0 ) scale = 4.0;
390		else                     scale = 5.0;
391		return scale;
392	},
393
394	shouldZoom: function( scale ) {
395		var t = this;
396		// Do not operate on hidden frames.
397		if ( "innerWidth" in window && !window.innerWidth )
398			return false;
399		// Don't do anything until scale > 1
400		if ( scale == 1.0 && t.zoomed == false )
401			return false;
402		return true;
403	},
404
405	zoomImages: function() {
406		var t = this;
407		var scale = t.getScale();
408		if ( ! t.shouldZoom( scale ) ){
409			return;
410		}
411		t.zoomed = true;
412		// Loop through all the <img> elements on the page.
413		var imgs = document.getElementsByTagName("img");
414
415		for ( var i = 0; i < imgs.length; i++ ) {
416			// Wait for original images to load
417			if ( "complete" in imgs[i] && ! imgs[i].complete )
418				continue;
419
420			// Skip images that have srcset attributes.
421			if ( imgs[i].hasAttribute('srcset') ) {
422				continue;
423			}
424
425			// Skip images that don't need processing.
426			var imgScale = imgs[i].getAttribute("scale");
427			if ( imgScale == scale || imgScale == "0" )
428				continue;
429
430			// Skip images that have already failed at this scale
431			var scaleFail = imgs[i].getAttribute("scale-fail");
432			if ( scaleFail && scaleFail <= scale )
433				continue;
434
435			// Skip images that have no dimensions yet.
436			if ( ! ( imgs[i].width && imgs[i].height ) )
437				continue;
438
439			// Skip images from Lazy Load plugins
440			if ( ! imgScale && imgs[i].getAttribute("data-lazy-src") && (imgs[i].getAttribute("data-lazy-src") !== imgs[i].getAttribute("src")))
441				continue;
442
443			if ( t.scaleImage( imgs[i], scale ) ) {
444				// Mark the img as having been processed at this scale.
445				imgs[i].setAttribute("scale", scale);
446			}
447			else {
448				// Set the flag to skip this image.
449				imgs[i].setAttribute("scale", "0");
450			}
451		}
452	},
453
454	scaleImage: function( img, scale ) {
455		var t = this;
456		var newSrc = img.src;
457
458                var isFiles = false;
459                var isLatex = false;
460                var isPhoton = false;
461
462		// Skip slideshow images
463		if ( img.parentNode.className.match(/slideshow-slide/) )
464			return false;
465
466		// Skip CoBlocks Lightbox images
467		if ( img.parentNode.className.match(/coblocks-lightbox__image/) )
468			return false;
469
470		// Scale gravatars that have ?s= or ?size=
471		if ( img.src.match( /^https?:\/\/([^\/]*\.)?gravatar\.com\/.+[?&](s|size)=/ ) ) {
472                        if ( this.hintsFor( "gravatar" ) === true ) {
473                                return false;
474                        }
475			newSrc = img.src.replace( /([?&](s|size)=)(\d+)/, function( $0, $1, $2, $3 ) {
476				// Stash the original size
477				var originalAtt = "originals",
478				originalSize = img.getAttribute(originalAtt);
479				if ( originalSize === null ) {
480					originalSize = $3;
481					img.setAttribute(originalAtt, originalSize);
482					if ( t.imgNeedsSizeAtts( img ) ) {
483						// Fix width and height attributes to rendered dimensions.
484						img.width = img.width;
485						img.height = img.height;
486					}
487				}
488				// Get the width/height of the image in CSS pixels
489				var size = img.clientWidth;
490				// Convert CSS pixels to device pixels
491				var targetSize = Math.ceil(img.clientWidth * scale);
492				// Don't go smaller than the original size
493				targetSize = Math.max( targetSize, originalSize );
494				// Don't go larger than the service supports
495				targetSize = Math.min( targetSize, 512 );
496				return $1 + targetSize;
497			});
498		}
499
500		// Scale mshots that have width
501		else if ( img.src.match(/^https?:\/\/([^\/]+\.)*(wordpress|wp)\.com\/mshots\/.+[?&]w=\d+/) ) {
502                        if ( this.hintsFor( "mshots" ) === true ) {
503                                return false;
504                        }
505			newSrc = img.src.replace( /([?&]w=)(\d+)/, function($0, $1, $2) {
506				// Stash the original size
507				var originalAtt = 'originalw', originalSize = img.getAttribute(originalAtt);
508				if ( originalSize === null ) {
509					originalSize = $2;
510					img.setAttribute(originalAtt, originalSize);
511					if ( t.imgNeedsSizeAtts( img ) ) {
512						// Fix width and height attributes to rendered dimensions.
513						img.width = img.width;
514						img.height = img.height;
515					}
516				}
517				// Get the width of the image in CSS pixels
518				var size = img.clientWidth;
519				// Convert CSS pixels to device pixels
520				var targetSize = Math.ceil(size * scale);
521				// Don't go smaller than the original size
522				targetSize = Math.max( targetSize, originalSize );
523				// Don't go bigger unless the current one is actually lacking
524				if ( scale > img.getAttribute("scale") && targetSize <= img.naturalWidth )
525					targetSize = $2;
526				if ( $2 != targetSize )
527					return $1 + targetSize;
528				return $0;
529			});
530
531			// Update height attribute to match width
532			newSrc = newSrc.replace( /([?&]h=)(\d+)/, function($0, $1, $2) {
533				if ( newSrc == img.src ) {
534					return $0;
535				}
536				// Stash the original size
537				var originalAtt = 'originalh', originalSize = img.getAttribute(originalAtt);
538				if ( originalSize === null ) {
539					originalSize = $2;
540					img.setAttribute(originalAtt, originalSize);
541				}
542				// Get the height of the image in CSS pixels
543				var size = img.clientHeight;
544				// Convert CSS pixels to device pixels
545				var targetSize = Math.ceil(size * scale);
546				// Don't go smaller than the original size
547				targetSize = Math.max( targetSize, originalSize );
548				// Don't go bigger unless the current one is actually lacking
549				if ( scale > img.getAttribute("scale") && targetSize <= img.naturalHeight )
550					targetSize = $2;
551				if ( $2 != targetSize )
552					return $1 + targetSize;
553				return $0;
554			});
555		}
556
557		// Scale simple imgpress queries (s0.wp.com) that only specify w/h/fit
558		else if ( img.src.match(/^https?:\/\/([^\/.]+\.)*(wp|wordpress)\.com\/imgpress\?(.+)/) ) {
559                        if ( this.hintsFor( "imgpress" ) === true ) {
560                                return false; 
561                        }
562			var imgpressSafeFunctions = ["zoom", "url", "h", "w", "fit", "filter", "brightness", "contrast", "colorize", "smooth", "unsharpmask"];
563			// Search the query string for unsupported functions.
564			var qs = RegExp.$3.split('&');
565			for ( var q in qs ) {
566				q = qs[q].split('=')[0];
567				if ( imgpressSafeFunctions.indexOf(q) == -1 ) {
568					return false;
569				}
570			}
571			// Fix width and height attributes to rendered dimensions.
572			img.width = img.width;
573			img.height = img.height;
574			// Compute new src
575			if ( scale == 1 )
576				newSrc = img.src.replace(/\?(zoom=[^&]+&)?/, '?');
577			else
578				newSrc = img.src.replace(/\?(zoom=[^&]+&)?/, '?zoom=' + scale + '&');
579		}
580
581		// Scale files.wordpress.com, LaTeX, or Photon images (i#.wp.com)
582		else if (
583			( isFiles = img.src.match(/^https?:\/\/([^\/]+)\.files\.wordpress\.com\/.+[?&][wh]=/) ) ||
584			( isLatex = img.src.match(/^https?:\/\/([^\/.]+\.)*(wp|wordpress)\.com\/latex\.php\?(latex|zoom)=(.+)/) ) ||
585			( isPhoton = img.src.match(/^https?:\/\/i[\d]{1}\.wp\.com\/(.+)/) )
586		) {
587                        if ( false !== isFiles && this.hintsFor( "files" ) === true ) {
588                                return false
589                        }
590                        if ( false !== isLatex && this.hintsFor( "latex" ) === true ) {
591                                return false
592                        }
593                        if ( false !== isPhoton && this.hintsFor( "photon" ) === true ) {
594                                return false
595                        }
596			// Fix width and height attributes to rendered dimensions.
597			img.width = img.width;
598			img.height = img.height;
599			// Compute new src
600			if ( scale == 1 ) {
601				newSrc = img.src.replace(/\?(zoom=[^&]+&)?/, '?');
602			} else {
603				newSrc = img.src;
604
605				var url_var = newSrc.match( /([?&]w=)(\d+)/ );
606				if ( url_var !== null && url_var[2] ) {
607					newSrc = newSrc.replace( url_var[0], url_var[1] + img.width );
608				}
609
610				url_var = newSrc.match( /([?&]h=)(\d+)/ );
611				if ( url_var !== null && url_var[2] ) {
612					newSrc = newSrc.replace( url_var[0], url_var[1] + img.height );
613				}
614
615				var zoom_arg = '&zoom=2';
616				if ( !newSrc.match( /\?/ ) ) {
617					zoom_arg = '?zoom=2';
618				}
619				img.setAttribute( 'srcset', newSrc + zoom_arg + ' ' + scale + 'x' );
620			}
621		}
622
623		// Scale static assets that have a name matching *-1x.png or *@1x.png
624		else if ( img.src.match(/^https?:\/\/[^\/]+\/.*[-@]([12])x\.(gif|jpeg|jpg|png)(\?|$)/) ) {
625                        if ( this.hintsFor( "staticAssets" ) === true ) {
626                                return false; 
627                        }
628			// Fix width and height attributes to rendered dimensions.
629			img.width = img.width;
630			img.height = img.height;
631			var currentSize = RegExp.$1, newSize = currentSize;
632			if ( scale <= 1 )
633				newSize = 1;
634			else
635				newSize = 2;
636			if ( currentSize != newSize )
637				newSrc = img.src.replace(/([-@])[12]x\.(gif|jpeg|jpg|png)(\?|$)/, '$1'+newSize+'x.$2$3');
638		}
639
640		else {
641			return false;
642		}
643
644		// Don't set img.src unless it has changed. This avoids unnecessary reloads.
645		if ( newSrc != img.src ) {
646			// Store the original img.src
647			var prevSrc, origSrc = img.getAttribute("src-orig");
648			if ( !origSrc ) {
649				origSrc = img.src;
650				img.setAttribute("src-orig", origSrc);
651			}
652			// In case of error, revert img.src
653			prevSrc = img.src;
654			img.onerror = function(){
655				img.src = prevSrc;
656				if ( img.getAttribute("scale-fail") < scale )
657					img.setAttribute("scale-fail", scale);
658				img.onerror = null;
659			};
660			// Finally load the new image
661			img.src = newSrc;
662		}
663
664		return true;
665	}
666};
667
668wpcom_img_zoomer.init();
669;
670( function() {
671	var cookieValue = document.cookie.replace( /(?:(?:^|.*;\s*)eucookielaw\s*\=\s*([^;]*).*$)|^.*$/, '$1' );
672	var overlay = document.querySelector( '#eu-cookie-law' );
673	var container = document.querySelector( '.widget_eu_cookie_law_widget' );
674	var initialScrollPosition, scrollFunction;
675
676	function remove( el ) {
677		return el && el.parentElement && el.parentElement.removeChild( el );
678	}
679
680	function triggerDismissEvent() {
681		try {
682			const dismissEvent = new Event( 'eucookielaw-dismissed' );
683			document.dispatchEvent( dismissEvent );
684		} catch ( err ) { }
685	}
686
687	function removeOverlay() {
688		remove( overlay );
689		triggerDismissEvent();
690	}
691
692	function fade( el, type, fn ) {
693		var duration = 400;
694
695		el.style.display = 'block';
696		el.style.transitionProperty = 'opacity';
697		el.style.transitionDuration = duration + 'ms';
698		el.style.opacity = type === 'in' ? 0 : 1;
699
700		// Double rAF to ensure styles are applied cross-browser.
701		requestAnimationFrame( function () {
702			requestAnimationFrame( function() {
703				el.style.opacity = type === 'in' ? 1 : 0;
704				// Wait for animation.
705				setTimeout( function () {
706					// Clean up.
707					el.style.removeProperty( 'opacity' );
708					el.style.removeProperty( 'transition-property' );
709					el.style.removeProperty( 'transition-duration' );
710
711					if ( type === 'out' ) {
712						el.style.display = 'none';
713					}
714
715					if ( typeof fn === 'function' ) {
716						fn();
717					}
718				}, duration + 50 );
719			} );
720		} );
721	}
722
723	function appendWidget() {
724		document.body.appendChild( container );
725		overlay.style.display = 'block';
726		fade( container, 'in' );
727	}
728
729	if ( typeof wp !== 'undefined' && !! wp.customize ) {
730		appendWidget();
731		return;
732	}
733
734	if ( ! overlay || ! container ) {
735		return;
736	}
737
738	if ( overlay.classList.contains( 'ads-active' ) ) {
739		var adsCookieValue = document.cookie.replace( /(?:(?:^|.*;\s*)personalized-ads-consent\s*\=\s*([^;]*).*$)|^.*$/, '$1' );
740		if ( cookieValue !== '' && adsCookieValue !== '' ) {
741			removeOverlay();
742		}
743	} else if ( cookieValue !== '' ) {
744		removeOverlay();
745	}
746
747	appendWidget();
748
749	overlay.querySelector( 'form' ).addEventListener( 'submit', accept );
750
751	if ( overlay.classList.contains( 'hide-on-scroll' ) ) {
752		initialScrollPosition = window.pageYOffset;
753
754		scrollFunction = function() {
755			if ( Math.abs( window.pageYOffset - initialScrollPosition ) > 50 ) {
756				accept();
757			}
758		};
759
760		window.addEventListener( 'scroll', scrollFunction );
761	} else if ( overlay.classList.contains( 'hide-on-time' ) ) {
762		var timeout = parseInt( overlay.getAttribute( 'data-hide-timeout' ), 10 ) || 0;
763		setTimeout( accept, timeout * 1000 );
764	}
765
766	var accepted = false;
767	function accept( event ) {
768		if ( accepted ) {
769			return;
770		}
771		accepted = true;
772
773		if ( event && event.preventDefault ) {
774			event.preventDefault();
775		}
776
777		if ( overlay.classList.contains( 'hide-on-scroll' ) ) {
778			window.removeEventListener( 'scroll', scrollFunction );
779		}
780
781		var expireTime = new Date();
782		var consentExpiration = parseInt( overlay.getAttribute( 'data-consent-expiration' ), 10 ) || 0;
783		expireTime.setTime( expireTime.getTime() + ( consentExpiration * 24 * 60 * 60 * 1000 ) );
784
785		document.cookie = 'eucookielaw=' + expireTime.getTime() + ';path=/;expires=' + expireTime.toGMTString();
786		if ( overlay.classList.contains( 'ads-active' ) && overlay.classList.contains( 'hide-on-button' ) ) {
787			document.cookie = 'personalized-ads-consent=' + expireTime.getTime() + ';path=/;expires=' + expireTime.toGMTString();
788		}
789
790		fade( overlay, 'out', function() {
791			removeOverlay();
792			remove( container );
793		} );
794	}
795} )();
796;
797/* global wpcom_reblog */
798
799var jetpackLikesWidgetBatch = [];
800var jetpackLikesMasterReady = false;
801
802// Due to performance problems on pages with a large number of widget iframes that need to be loaded,
803// we are limiting the processing at any instant to unloaded widgets that are currently in viewport,
804// plus this constant that will allow processing of widgets above and bellow the current fold.
805// This aim of it is to improve the UX and hide the transition from unloaded to loaded state from users.
806var jetpackLikesLookAhead = 2000; // pixels
807
808// Keeps track of loaded comment likes widget so we can unload them when they are scrolled out of view.
809var jetpackCommentLikesLoadedWidgets = [];
810
811var jetpackLikesDocReadyPromise = new Promise( resolve => {
812	if ( document.readyState !== 'loading' ) {
813		resolve();
814	} else {
815		window.addEventListener( 'DOMContentLoaded', () => resolve() );
816	}
817} );
818
819function JetpackLikesPostMessage( message, target ) {
820	if ( typeof message === 'string' ) {
821		try {
822			message = JSON.parse( message );
823		} catch ( e ) {
824			return;
825		}
826	}
827
828	if ( target && typeof target.postMessage === 'function' ) {
829		try {
830			target.postMessage(
831				JSON.stringify( {
832					type: 'likesMessage',
833					data: message,
834				} ),
835				'*'
836			);
837		} catch ( e ) {
838			return;
839		}
840	}
841}
842
843function JetpackLikesBatchHandler() {
844	const requests = [];
845	document.querySelectorAll( 'div.jetpack-likes-widget-unloaded' ).forEach( widget => {
846		if ( jetpackLikesWidgetBatch.indexOf( widget.id ) > -1 ) {
847			return;
848		}
849
850		if ( ! jetpackIsScrolledIntoView( widget ) ) {
851			return;
852		}
853
854		jetpackLikesWidgetBatch.push( widget.id );
855
856		var regex = /like-(post|comment)-wrapper-(\d+)-(\d+)-(\w+)/,
857			match = regex.exec( widget.id ),
858			info;
859
860		if ( ! match || match.length !== 5 ) {
861			return;
862		}
863
864		info = {
865			blog_id: match[ 2 ],
866			width: widget.width,
867		};
868
869		if ( 'post' === match[ 1 ] ) {
870			info.post_id = match[ 3 ];
871		} else if ( 'comment' === match[ 1 ] ) {
872			info.comment_id = match[ 3 ];
873		}
874
875		info.obj_id = match[ 4 ];
876
877		requests.push( info );
878	} );
879
880	if ( requests.length > 0 ) {
881		JetpackLikesPostMessage(
882			{ event: 'initialBatch', requests: requests },
883			window.frames[ 'likes-master' ]
884		);
885	}
886}
887
888function JetpackLikesMessageListener( event ) {
889	let message = event && event.data;
890	if ( typeof message === 'string' ) {
891		try {
892			message = JSON.parse( message );
893		} catch ( err ) {
894			return;
895		}
896	}
897
898	const type = message && message.type;
899	const data = message && message.data;
900
901	if ( type !== 'likesMessage' || typeof data.event === 'undefined' ) {
902		return;
903	}
904
905	// We only allow messages from one origin
906	const allowedOrigin = 'https://widgets.wp.com';
907	if ( allowedOrigin !== event.origin ) {
908		return;
909	}
910
911	switch ( data.event ) {
912		case 'masterReady':
913			jetpackLikesDocReadyPromise.then( () => {
914				jetpackLikesMasterReady = true;
915
916				const stylesData = {
917					event: 'injectStyles',
918				};
919				const sdTextColor = document.querySelector( '.sd-text-color' );
920				const sdLinkColor = document.querySelector( '.sd-link-color' );
921				const sdTextColorStyles = ( sdTextColor && getComputedStyle( sdTextColor ) ) || {};
922				const sdLinkColorStyles = ( sdLinkColor && getComputedStyle( sdLinkColor ) ) || {};
923
924				if ( document.querySelectorAll( 'iframe.admin-bar-likes-widget' ).length > 0 ) {
925					JetpackLikesPostMessage( { event: 'adminBarEnabled' }, window.frames[ 'likes-master' ] );
926
927					const bgSource = document.querySelector(
928						'#wpadminbar .quicklinks li#wp-admin-bar-wpl-like > a'
929					);
930
931					const wpAdminBar = document.querySelector( '#wpadminbar' );
932
933					stylesData.adminBarStyles = {
934						background: bgSource && getComputedStyle( bgSource ).background,
935						isRtl: wpAdminBar && getComputedStyle( wpAdminBar ).direction === 'rtl',
936					};
937				}
938
939				// enable reblogs if they are enabled for the page
940				if ( document.body.classList.contains( 'jetpack-reblog-enabled' ) ) {
941					JetpackLikesPostMessage( { event: 'reblogsEnabled' }, window.frames[ 'likes-master' ] );
942				}
943
944				stylesData.textStyles = {
945					color: sdTextColorStyles[ 'color' ],
946					fontFamily: sdTextColorStyles[ 'font-family' ],
947					fontSize: sdTextColorStyles[ 'font-size' ],
948					direction: sdTextColorStyles[ 'direction' ],
949					fontWeight: sdTextColorStyles[ 'font-weight' ],
950					fontStyle: sdTextColorStyles[ 'font-style' ],
951					textDecoration: sdTextColorStyles[ 'text-decoration' ],
952				};
953
954				stylesData.linkStyles = {
955					color: sdLinkColorStyles[ 'color' ],
956					fontFamily: sdLinkColorStyles[ 'font-family' ],
957					fontSize: sdLinkColorStyles[ 'font-size' ],
958					textDecoration: sdLinkColorStyles[ 'text-decoration' ],
959					fontWeight: sdLinkColorStyles[ 'font-weight' ],
960					fontStyle: sdLinkColorStyles[ 'font-style' ],
961				};
962
963				JetpackLikesPostMessage( stylesData, window.frames[ 'likes-master' ] );
964
965				JetpackLikesBatchHandler();
966			} );
967
968			break;
969
970		case 'showLikeWidget': {
971			const placeholder = document.querySelector( `#${ data.id } .likes-widget-placeholder` );
972			if ( placeholder ) {
973				placeholder.style.display = 'none';
974			}
975			break;
976		}
977
978		case 'showCommentLikeWidget': {
979			const placeholder = document.querySelector( `#${ data.id } .likes-widget-placeholder` );
980			if ( placeholder ) {
981				placeholder.style.display = 'none';
982			}
983			break;
984		}
985
986		case 'killCommentLikes':
987			// If kill switch for comment likes is enabled remove all widgets wrappers and `Loading...` placeholders.
988			document
989				.querySelectorAll( '.jetpack-comment-likes-widget-wrapper' )
990				.forEach( wrapper => wrapper.remove() );
991			break;
992
993		case 'clickReblogFlair':
994			if ( wpcom_reblog && typeof wpcom_reblog.toggle_reblog_box_flair === 'function' ) {
995				wpcom_reblog.toggle_reblog_box_flair( data.obj_id, data.post_id );
996			}
997			break;
998
999		case 'showOtherGravatars': {
1000			const container = document.querySelector( '#likes-other-gravatars' );
1001			if ( ! container ) {
1002				break;
1003			}
1004
1005			const list = container.querySelector( 'ul' );
1006
1007			container.style.display = 'none';
1008			list.innerHTML = '';
1009
1010			container
1011				.querySelectorAll( '.likes-text span' )
1012				.forEach( item => ( item.textContent = data.total ) );
1013
1014			( data.likers || [] ).forEach( async liker => {
1015				if ( liker.profile_URL.substr( 0, 4 ) !== 'http' ) {
1016					// We only display gravatars with http or https schema
1017					return;
1018				}
1019
1020				try {
1021					const response = await fetch( liker.avatar_URL, { method: 'HEAD' } );
1022					if ( !response.ok ) {
1023						// Image doesn't exist, don't create the element
1024						return;
1025					}
1026					} catch ( error ) {
1027						// Error occurred while checking image existence, don't create the element
1028					return;
1029				}
1030
1031				const element = document.createElement( 'li' );
1032				element.innerHTML = `
1033					<a href="${ encodeURI( liker.profile_URL ) }" rel="nofollow" target="_parent" class="wpl-liker">
1034						<img src="${ encodeURI( liker.avatar_URL ) }"
1035							alt=""
1036							style="width: 30px; height: 30px; padding-right: 3px;" />
1037					</a>
1038				`;
1039
1040				list.append( element );
1041
1042				// Add some extra attributes through native methods, to ensure strings are sanitized.
1043				element.classList.add( liker.css_class );
1044				element.querySelector( 'img' ).alt = liker.name;
1045			} );
1046
1047			const el = document.querySelector( `*[name='${ data.parent }']` );
1048			const rect = el.getBoundingClientRect();
1049			const win = el.ownerDocument.defaultView;
1050			const offset = {
1051				top: rect.top + win.pageYOffset,
1052				left: rect.left + win.pageXOffset,
1053			};
1054
1055			container.style.left = offset.left + data.position.left - 10 + 'px';
1056			container.style.top = offset.top + data.position.top - 33 + 'px';
1057
1058			// Container width - padding
1059			const initContainerWidth = data.width - 20;
1060			const rowLength = Math.floor( initContainerWidth / 37 );
1061			// # of rows + (avatar + avatar padding) + text above + container padding
1062			let height = Math.ceil( data.likers.length / rowLength ) * 37 + 17 + 22;
1063			if ( height > 204 ) {
1064				height = 204;
1065			}
1066
1067			// Avatars + padding
1068			const containerWidth = rowLength * 37 + 13;
1069			container.style.height = height + 'px';
1070			container.style.width = containerWidth + 'px';
1071
1072			const listWidth = rowLength * 37;
1073			list.style.width = listWidth + 'px';
1074
1075			container.style.display = 'block';
1076		}
1077	}
1078}
1079
1080window.addEventListener( 'message', JetpackLikesMessageListener );
1081
1082document.addEventListener( 'click', e => {
1083	const container = document.querySelector( '#likes-other-gravatars' );
1084
1085	if ( container && ! container.contains( e.target ) ) {
1086		container.style.display = 'none';
1087	}
1088} );
1089
1090function JetpackLikesWidgetQueueHandler() {
1091	var wrapperID;
1092
1093	if ( ! jetpackLikesMasterReady ) {
1094		setTimeout( JetpackLikesWidgetQueueHandler, 500 );
1095		return;
1096	}
1097
1098	// Restore widgets to initial unloaded state when they are scrolled out of view.
1099	jetpackUnloadScrolledOutWidgets();
1100
1101	var unloadedWidgetsInView = jetpackGetUnloadedWidgetsInView();
1102
1103	if ( unloadedWidgetsInView.length > 0 ) {
1104		// Grab any unloaded widgets for a batch request
1105		JetpackLikesBatchHandler();
1106	}
1107
1108	for ( var i = 0, length = unloadedWidgetsInView.length; i <= length - 1; i++ ) {
1109		wrapperID = unloadedWidgetsInView[ i ].id;
1110
1111		if ( ! wrapperID ) {
1112			continue;
1113		}
1114
1115		jetpackLoadLikeWidgetIframe( wrapperID );
1116	}
1117}
1118
1119function jetpackLoadLikeWidgetIframe( wrapperID ) {
1120	if ( typeof wrapperID === 'undefined' ) {
1121		return;
1122	}
1123
1124	const wrapper = document.querySelector( '#' + wrapperID );
1125	wrapper.querySelectorAll( 'iframe' ).forEach( iFrame => iFrame.remove() );
1126
1127	const placeholder = wrapper.querySelector( '.likes-widget-placeholder' );
1128
1129	// Post like iframe
1130	if ( placeholder && placeholder.classList.contains( 'post-likes-widget-placeholder' ) ) {
1131		const postLikesFrame = document.createElement( 'iframe' );
1132
1133		postLikesFrame.classList.add( 'post-likes-widget', 'jetpack-likes-widget' );
1134		postLikesFrame.name = wrapper.dataset.name;
1135		postLikesFrame.src = wrapper.dataset.src;
1136		postLikesFrame.height = '55px';
1137		postLikesFrame.width = '100%';
1138		postLikesFrame.frameBorder = '0';
1139		postLikesFrame.scrolling = 'no';
1140		postLikesFrame.title = wrapper.dataset.title;
1141
1142		placeholder.after( postLikesFrame );
1143	}
1144
1145	// Comment like iframe
1146	if ( placeholder.classList.contains( 'comment-likes-widget-placeholder' ) ) {
1147		const commentLikesFrame = document.createElement( 'iframe' );
1148
1149		commentLikesFrame.class = 'comment-likes-widget-frame jetpack-likes-widget-frame';
1150		commentLikesFrame.name = wrapper.dataset.name;
1151		commentLikesFrame.src = wrapper.dataset.src;
1152		commentLikesFrame.height = '18px';
1153		commentLikesFrame.width = '100%';
1154		commentLikesFrame.frameBorder = '0';
1155		commentLikesFrame.scrolling = 'no';
1156
1157		wrapper.querySelector( '.comment-like-feedback' ).after( commentLikesFrame );
1158
1159		jetpackCommentLikesLoadedWidgets.push( commentLikesFrame );
1160	}
1161
1162	wrapper.classList.remove( 'jetpack-likes-widget-unloaded' );
1163	wrapper.classList.add( 'jetpack-likes-widget-loading' );
1164
1165	wrapper.querySelector( 'iframe' ).addEventListener( 'load', e => {
1166		JetpackLikesPostMessage(
1167			{ event: 'loadLikeWidget', name: e.target.name, width: e.target.width },
1168			window.frames[ 'likes-master' ]
1169		);
1170
1171		wrapper.classList.remove( 'jetpack-likes-widget-loading' );
1172		wrapper.classList.add( 'jetpack-likes-widget-loaded' );
1173	} );
1174}
1175
1176function jetpackGetUnloadedWidgetsInView() {
1177	const unloadedWidgets = document.querySelectorAll( 'div.jetpack-likes-widget-unloaded' );
1178
1179	return [ ...unloadedWidgets ].filter( item => jetpackIsScrolledIntoView( item ) );
1180}
1181
1182function jetpackIsScrolledIntoView( element ) {
1183	const top = element.getBoundingClientRect().top;
1184	const bottom = element.getBoundingClientRect().bottom;
1185
1186	// Allow some slack above and bellow the fold with jetpackLikesLookAhead,
1187	// with the aim of hiding the transition from unloaded to loaded widget from users.
1188	return top + jetpackLikesLookAhead >= 0 && bottom <= window.innerHeight + jetpackLikesLookAhead;
1189}
1190
1191function jetpackUnloadScrolledOutWidgets() {
1192	for ( let i = jetpackCommentLikesLoadedWidgets.length - 1; i >= 0; i-- ) {
1193		const currentWidgetIframe = jetpackCommentLikesLoadedWidgets[ i ];
1194
1195		if ( ! jetpackIsScrolledIntoView( currentWidgetIframe ) ) {
1196			const widgetWrapper =
1197				currentWidgetIframe &&
1198				currentWidgetIframe.parentElement &&
1199				currentWidgetIframe.parentElement.parentElement;
1200
1201			// Restore parent class to 'unloaded' so this widget can be picked up by queue manager again if needed.
1202			widgetWrapper.classList.remove( 'jetpack-likes-widget-loaded' );
1203			widgetWrapper.classList.remove( 'jetpack-likes-widget-loading' );
1204			widgetWrapper.classList.add( 'jetpack-likes-widget-unloaded' );
1205
1206			// Bring back the loading placeholder into view.
1207			widgetWrapper
1208				.querySelectorAll( '.comment-likes-widget-placeholder' )
1209				.forEach( item => ( item.style.display = 'block' ) );
1210
1211			// Remove it from the list of loaded widgets.
1212			jetpackCommentLikesLoadedWidgets.splice( i, 1 );
1213
1214			// Remove comment like widget iFrame.
1215			currentWidgetIframe.remove();
1216		}
1217	}
1218}
1219
1220var jetpackWidgetsDelayedExec = function ( after, fn ) {
1221	var timer;
1222	return function () {
1223		clearTimeout( timer );
1224		timer = setTimeout( fn, after );
1225	};
1226};
1227
1228var jetpackOnScrollStopped = jetpackWidgetsDelayedExec( 250, JetpackLikesWidgetQueueHandler );
1229
1230// Load initial batch of widgets, prior to any scrolling events.
1231JetpackLikesWidgetQueueHandler();
1232
1233// Add event listener to execute queue handler after scroll.
1234window.addEventListener( 'scroll', jetpackOnScrollStopped, true );
1235;
1236/**
1237 * Comment Likes - JavaScript
1238 *
1239 * This handles liking and unliking comments, as well as viewing who has
1240 * liked a particular comment.
1241 *
1242 * @dependency  Swipe (dynamically loaded when needed)
1243 *
1244 * @package     Comment_Likes
1245 * @subpackage  JavaScript
1246 */
1247(function () {
1248	function init() {
1249		let extWin;
1250		let extWinCheck;
1251		let commentLikeEvent;
1252
1253		// Only run once.
1254		if (window.comment_likes_loaded) {
1255			return;
1256		}
1257		window.comment_likes_loaded = true;
1258
1259		// Client-side cache of who liked a particular comment to avoid
1260		// having to hit the server multiple times for the same data.
1261		const commentLikeCache = {};
1262
1263		let swipeLibPromise;
1264
1265		// Load the Swipe library, if it's not already loaded.
1266		function swipeLibLoader() {
1267			if (!swipeLibPromise) {
1268				swipeLibPromise = new Promise((resolve, reject) => {
1269					if (window.Swipe) {
1270						resolve(window.Swipe);
1271					} else {
1272						const swipeScript = document.createElement('script');
1273						swipeScript.src = comment_like_text.swipeUrl;
1274						swipeScript.async = true;
1275						document.body.appendChild(swipeScript);
1276						swipeScript.addEventListener('load', () => resolve(window.Swipe));
1277						swipeScript.addEventListener('error', error => reject(error));
1278					}
1279				});
1280			}
1281			return swipeLibPromise;
1282		}
1283
1284		/**
1285		 * Parse the comment ID from a comment like link.
1286		 */
1287		function getCommentId(link) {
1288			const commentId =
1289				link && link.getAttribute('href') && link.getAttribute('href').split('like_comment=');
1290			return commentId[1].split('&_wpnonce=')[0];
1291		}
1292
1293		/**
1294		 * Handle an ajax action on the comment like link.
1295		 */
1296		function handleLinkAction(link, action, commentId, callback) {
1297			const nonce =
1298				link && link.getAttribute('href') && link.getAttribute('href').split('_wpnonce=')[1];
1299
1300			fetch('/wp-admin/admin-ajax.php', {
1301				method: 'POST',
1302				body: new URLSearchParams({
1303					action: action,
1304					_wpnonce: nonce,
1305					like_comment: commentId,
1306					blog_id: Number(link.dataset.blog),
1307				}),
1308				headers: {
1309					'Content-Type': 'application/x-www-form-urlencoded; charset=UTF-8',
1310					'X-Requested-With': 'XMLHttpRequest',
1311					Accept: 'application/json',
1312					'cache-control': 'no-cache',
1313					pragma: 'no-cache',
1314				},
1315			})
1316				.then(response => response.json())
1317				.then(callback);
1318		}
1319
1320		function startPolling() {
1321			// Append cookie polling login iframe to this window to wait for user to finish logging in (or cancel)
1322			const loginIframe = document.createElement('iframe');
1323			loginIframe.id = 'wp-login-polling-iframe';
1324			loginIframe.src = 'https://wordpress.com/public.api/connect/?iframe=true';
1325			document.body.appendChild(loginIframe);
1326			loginIframe.style.display = 'none';
1327		}
1328
1329		function stopPolling() {
1330			const iframe = document.querySelector('#wp-login-polling-iframe');
1331			if (iframe) {
1332				iframe.remove();
1333			}
1334		}
1335
1336		function hide(el) {
1337			if (el && el.style) {
1338				el.style.display = 'none';
1339			}
1340		}
1341
1342		function show(el) {
1343			if (el && el.style) {
1344				el.style.removeProperty('display');
1345			}
1346		}
1347
1348		// Overlay used for displaying comment like info.
1349		class Overlay {
1350			constructor() {
1351				// Overlay element.
1352				this.el = document.createElement('div');
1353				this.el.classList.add('comment-likes-overlay');
1354				document.body.appendChild(this.el);
1355				hide(this.el);
1356
1357				this.el.addEventListener('mouseenter', () => {
1358					// Don't hide the overlay if the user is mousing over it.
1359					overlay.cancelHide();
1360				});
1361
1362				this.el.addEventListener('mouseleave', () => overlay.requestHide());
1363
1364				// Inner contents of overlay.
1365				this.innerEl = null;
1366
1367				// Instance of the Swipe library.
1368				this.swipe = null;
1369
1370				// Timeout used for hiding the overlay.
1371				this.hideTimeout = null;
1372			}
1373
1374			// Initialise the overlay for use, removing any old content.
1375			clear() {
1376				// Unload any previous instance of Swipe (to avoid leaking a global
1377				// event handler). This is done before clearing the contents of the
1378				// overlay because Swipe expects the slides to still be present.
1379				if (this.swipe) {
1380					this.swipe.kill();
1381					this.swipe = null;
1382				}
1383				this.el.innerHTML = '';
1384				this.innerEl = document.createElement('div');
1385				this.innerEl.classList.add('inner');
1386				this.el.appendChild(this.innerEl);
1387			}
1388
1389			/**
1390			 * Construct a list (<ul>) of user (gravatar, name) details.
1391			 *
1392			 * @param  data     liker data returned from the server
1393			 * @param  klass    CSS class to apply to the <ul> element
1394			 * @param  start    index of user to start at
1395			 * @param  length   number of users to include in the list
1396			 *
1397			 * @return          A container element with the list
1398			 */
1399			getUserBits(data, klass, start, length) {
1400				start = start || 0;
1401				let last = start + (length || data.length);
1402				last = last > data.length ? data.length : last;
1403				const container = document.createElement('div');
1404				container.classList.add('liker-list');
1405				let html = `<ul class="${klass || ''}">`;
1406				for (let i = start; i < last; ++i) {
1407					const user = data[i];
1408					html += `
1409						<li>
1410							<a rel="nofollow" title="${user.display_name_esc}" href="${user.profile_url_esc}">
1411								<img src="${user.avatar_url_esc}" alt="${user.display_name_esc}" />
1412								<span class="user-name">${user.display_name_esc}</span>
1413							</a>
1414						</li>
1415					`;
1416				}
1417				html += '</ul>';
1418				container.innerHTML = html;
1419				return container;
1420			}
1421
1422			/**
1423			 * Render the display of who has liked this comment. The type of
1424			 * display depends on how many people have liked the comment.
1425			 * If more than 10 people have liked the comment, this function
1426			 * renders navigation controls and sets up the Swipe library for
1427			 * changing between pages.
1428			 *
1429			 * @param link  the element over which the user is hovering
1430			 * @param data  the results retrieved from the server
1431			 */
1432			showLikes(link, data) {
1433				this.clear();
1434
1435				link.dataset.likeCount = data.length;
1436				if (data.length === 0) {
1437					// No likers after all.
1438					hide(this.el);
1439					return;
1440				}
1441
1442				this.innerEl.style.padding = '12px';
1443
1444				if (data.length < 6) {
1445					// Only one column needed.
1446					this.innerEl.style.maxWidth = '200px';
1447					this.innerEl.innerHTML = '';
1448					this.innerEl.appendChild(this.getUserBits(data, 'single'));
1449					this.setPosition(link);
1450				} else if (data.length < 11) {
1451					// Two columns, but only one page.
1452					this.innerEl.innerHTML = '';
1453					this.innerEl.appendChild(this.getUserBits(data, 'double'));
1454					this.setPosition(link);
1455				} else {
1456					// Multiple pages.
1457					this.renderLikesWithPagination(data, link);
1458				}
1459			}
1460
1461			/**
1462			 * Render multiple pages of likes with pagination controls.
1463			 * This function is intended to be called by `showLikes` above.
1464			 *
1465			 * @param data  the results retrieved from the server
1466			 */
1467			renderLikesWithPagination(data, link) {
1468				swipeLibLoader().then(() => {
1469					const page_count = Math.ceil(data.length / 10);
1470					// Swipe requires two nested containers.
1471					const swipe = document.createElement('div');
1472					swipe.classList.add('swipe');
1473					this.innerEl.appendChild(swipe);
1474
1475					const wrap = document.createElement('div');
1476					wrap.classList.add('swipe-wrap');
1477					swipe.appendChild(wrap);
1478
1479					for (let i = 0; i < page_count; ++i) {
1480						wrap.appendChild(this.getUserBits(data, 'double', i * 10, 10));
1481					}
1482
1483					/**
1484					 * Navigation controls.
1485					 * This is based on the Newdash controls found in
1486					 *    reader/recommendations-templates.php
1487					 */
1488					const nav = document.createElement('nav');
1489					nav.classList.add('slider-nav');
1490
1491					let navContents = `
1492						<a href="#" class="prev">
1493							<span class="noticon noticon-previous" title="Previous" alt="<"></span>
1494						</a>
1495						<span class="position">
1496					`;
1497					for (let i = 0; i < page_count; ++i) {
1498						navContents += `<em data-page="${i}" class="${i === 0 ? 'on' : ''}">&bull;</em>`;
1499					}
1500					navContents += `
1501						</span>
1502						<a href="#" class="next">
1503							<span class="noticon noticon-next" title="Next" alt=">"></span>
1504						</a>
1505					`;
1506					this.innerEl.appendChild(nav);
1507					nav.innerHTML = navContents;
1508
1509					/** Set up Swipe. **/
1510					// Swipe cannot be set up successfully unless its container
1511					// is visible, so we show it now.
1512					show(this.el);
1513					this.setPosition(link);
1514
1515					this.swipe = new Swipe(swipe, {
1516						callback: function (pos) {
1517							// Update the pagination indicators.
1518							//
1519							// If there are exactly two pages, Swipe has a weird
1520							// special case where it duplicates both pages and
1521							// can return index 2 and 3 even though those aren't
1522							// real pages (see swipe.js, line 47). To deal with
1523							// this, we use the expression `pos % page_count`.
1524							pos = pos % page_count;
1525							nav.querySelectorAll('em').forEach(em => {
1526								const page = Number(em.dataset.page);
1527								em.setAttribute('class', pos === page ? 'on' : '');
1528							});
1529						},
1530					});
1531
1532					nav.querySelectorAll('em').forEach(em => {
1533						em.addEventListener('click', e => {
1534							// Go to the page corresponding to the indicator clicked.
1535							this.swipe.slide(Number(em.dataset.page));
1536							e.preventDefault();
1537						});
1538					});
1539					// Previous and next buttons.
1540					nav.querySelector('.prev').addEventListener('click', e => {
1541						this.swipe.prev();
1542						e.preventDefault();
1543					});
1544					nav.querySelector('.next').addEventListener('click', e => {
1545						this.swipe.next();
1546						e.preventDefault();
1547					});
1548				});
1549			}
1550
1551			/**
1552			 * Open the overlay and show a loading message.
1553			 */
1554			showLoadingMessage(link) {
1555				this.clear();
1556				this.innerEl.textContent = comment_like_text.loading;
1557				this.setPosition(link);
1558			}
1559
1560			/**
1561			 * Position the overlay near the current comment.
1562			 *
1563			 * @param link  element near which to position the overlay
1564			 */
1565			setPosition(link) {
1566				// Prepare a down arrow icon for the bottom of the overlay.
1567				const icon = document.createElement('span');
1568				this.el.appendChild(icon);
1569				icon.classList.add('icon', 'noticon', 'noticon-downarrow');
1570				icon.style.textShadow = '0px 1px 1px rgb(223, 223, 223)';
1571
1572				const rect = link.getBoundingClientRect();
1573				const win = document.defaultView;
1574				const offset = {
1575					top: rect.top + win.scrollY,
1576					left: rect.left + win.scrollX,
1577				};
1578
1579				// Take measurements with the element fully visible.
1580				show(this.el);
1581				let left = offset.left - (this.el.offsetWidth - link.offsetWidth) / 2;
1582				left = left < 5 ? 5 : left;
1583				let top = offset.top - this.el.offsetHeight + 5;
1584				hide(this.el);
1585
1586				const adminBar = document.querySelector('#wpadminbar');
1587
1588				// Check if the overlay would appear off the screen.
1589				if (top < win.scrollY + ((adminBar && adminBar.offsetHeight) || 0)) {
1590					// We'll display the overlay beneath the link instead.
1591					top = offset.top + link.offsetHeight;
1592					// Instead of using the down arrow icon, use an up arrow.
1593					icon.remove();
1594					this.el.prepend(icon);
1595					icon.classList.remove('noticon-downarrow');
1596					icon.classList.add('noticon-uparrow');
1597					icon.style.textShadow = '0px -1px 1px rgb(223, 223, 223)';
1598					icon.style.verticalAlign = 'bottom';
1599				}
1600
1601				this.el.style.left = `${left}px`;
1602				this.el.style.top = `${top}px`;
1603				show(this.el);
1604
1605				// The height of the arrow icon differs slightly between browsers,
1606				// so we compute the margin here to make sure it isn't disjointed
1607				// from the overlay.
1608				icon.style.marginTop = `${icon.scrollHeight - 26}px`;
1609				icon.style.marginBottom = `${20 - icon.scrollHeight}px`;
1610
1611				// Position the arrow to be horizontally centred on the link.
1612				icon.style.paddingLeft = `${
1613					offset.left - left + (link.offsetWidth - icon.scrollWidth) / 2
1614				}px`;
1615			}
1616
1617			/**
1618			 * Return whether the overlay is visible.
1619			 */
1620			isVisible() {
1621				return this.el.style.getPropertyValue('display') !== 'none';
1622			}
1623
1624			/**
1625			 * Request that the overlay be hidden after a short delay.
1626			 */
1627			requestHide() {
1628				if (this.hideTimeout !== null) {
1629					return;
1630				}
1631				this.hideTimeout = setTimeout(() => {
1632					hide(this.el);
1633					this.clear();
1634				}, 300);
1635			}
1636
1637			/**
1638			 * Cancel a request to hide the overlay.
1639			 */
1640			cancelHide() {
1641				if (this.hideTimeout !== null) {
1642					clearTimeout(this.hideTimeout);
1643					this.hideTimeout = null;
1644				}
1645			}
1646		}
1647
1648		// Overlay used for displaying comment like info.
1649		const overlay = new Overlay();
1650
1651		// The most recent comment for which the user has requested to see
1652		// who liked it.
1653		var relevantComment;
1654
1655		// Precache after this timeout.
1656		var precacheTimeout = null;
1657
1658		/**
1659		 * Fetch the like data for a particular comment.
1660		 */
1661		function fetchLikeData(link, commentId) {
1662			commentLikeCache[commentId] = null;
1663
1664			const container = link && link.parentElement && link.parentElement.parentElement;
1665			const star = container.querySelector('a.comment-like-link');
1666			star &&
1667				handleLinkAction(star, 'view_comment_likes', commentId, data => {
1668					// Populate the cache.
1669					commentLikeCache[commentId] = data;
1670
1671					// Only show the overlay if the user is interested.
1672					if (overlay.isVisible() && relevantComment === commentId) {
1673						overlay.showLikes(link, data);
1674					}
1675				});
1676		}
1677
1678		function readCookie(c) {
1679			const nameEQ = c + '=';
1680			const cookieStrings = document.cookie.split(';');
1681
1682			for (let i = 0; i < cookieStrings.length; i++) {
1683				let cookieString = cookieStrings[i];
1684				while (cookieString.charAt(0) === ' ') {
1685					cookieString = cookieString.substring(1, cookieString.length);
1686				}
1687				if (cookieString.indexOf(nameEQ) === 0) {
1688					const chunk = cookieString.substring(nameEQ.length, cookieString.length);
1689					const pairs = chunk.split('&');
1690					const cookieData = {};
1691					for (let num = pairs.length - 1; num >= 0; num--) {
1692						const pair = pairs[num].split('=');
1693						cookieData[pair[0]] = decodeURIComponent(pair[1]);
1694					}
1695					return cookieData;
1696				}
1697			}
1698			return null;
1699		}
1700
1701		function getServiceData() {
1702			const data = readCookie('wpc_wpc');
1703			if (data === null || typeof data.access_token === 'undefined' || !data.access_token) {
1704				return false;
1705			}
1706			return data;
1707		}
1708
1709		function readMessage(msg) {
1710			const event = msg.data;
1711
1712			if (typeof event.event === 'undefined') {
1713				return;
1714			}
1715
1716			if (event.event === 'login' && event.success) {
1717				extWinCheck = setInterval(function () {
1718					if (!extWin || extWin.closed) {
1719						clearInterval(extWinCheck);
1720						if (getServiceData()) {
1721							// Load page in an iframe to get the current comment nonce
1722							const nonceIframe = document.createElement('iframe');
1723							nonceIframe.id = 'wp-login-comment-nonce-iframe';
1724							nonceIframe.style.display = 'none';
1725							nonceIframe.src = commentLikeEvent + '';
1726							document.body.appendChild(nonceIframe);
1727
1728							const commentLikeId = (commentLikeEvent + '')
1729								.split('like_comment=')[1]
1730								.split('&_wpnonce=')[0];
1731							let c;
1732
1733							// Set a 5 second timeout to redirect to the comment page without doing the Like as a fallback
1734							const commentLikeTimeout = setTimeout(() => {
1735								window.location = commentLikeEvent;
1736							}, 5000);
1737
1738							// Check for a new nonced redirect and use that if available before timing out
1739							const commentLikeCheck = setInterval(() => {
1740								const iframe = document.querySelector('#wp-login-comment-nonce-iframe');
1741								if (iframe) {
1742									c = iframe.querySelector(`#comment-like-${commentLikeId} .comment-like-link`);
1743								}
1744								if (c && typeof c.href !== 'undefined') {
1745									clearTimeout(commentLikeTimeout);
1746									clearInterval(commentLikeCheck);
1747									window.location = c.href;
1748								}
1749							}, 100);
1750						}
1751					}
1752				}, 100);
1753
1754				if (extWin) {
1755					if (!extWin.closed) {
1756						extWin.close();
1757					}
1758					extWin = false;
1759				}
1760
1761				stopPolling();
1762			}
1763		}
1764
1765		if (typeof window.postMessage !== 'undefined') {
1766			window.addEventListener('message', e => {
1767				let message = e && e.data;
1768				if (typeof message === 'string') {
1769					try {
1770						message = JSON.parse(message);
1771					} catch (err) {
1772						return;
1773					}
1774				}
1775
1776				const type = message && message.type;
1777				if (type === 'loginMessage') {
1778					readMessage(message);
1779				}
1780			});
1781		}
1782
1783		document.body.addEventListener('click', e => {
1784			let target = e.target;
1785
1786			// Don't do anything when clicking on the "X people" link.
1787			if (target.matches('p.comment-likes a.view-likers')) {
1788				e.preventDefault();
1789				return;
1790			}
1791
1792			// Retrieve the surrounding paragraph to the star, if it hasn't been liked.
1793			const notLikedPar = target.closest('p.comment-not-liked');
1794
1795			// Return if not clicking on star or surrounding paragraph.
1796			if (!target.matches('a.comment-like-link') && !notLikedPar) {
1797				return;
1798			}
1799
1800			// When a comment hasn't been liked, make the text clickable, too.
1801			if (notLikedPar) {
1802				target = notLikedPar.querySelector('a.comment-like-link');
1803				if (!target) {
1804					return;
1805				}
1806			}
1807
1808			if (target.classList.contains('needs-login')) {
1809				e.preventDefault();
1810				commentLikeEvent = target;
1811				if (extWin) {
1812					if (!extWin.closed) {
1813						extWin.close();
1814					}
1815					extWin = false;
1816				}
1817
1818				stopPolling();
1819
1820				const url = 'https://wordpress.com/public.api/connect/?action=request&service=wordpress';
1821				extWin = window.open(
1822					url,
1823					'likeconn',
1824					'status=0,toolbar=0,location=1,menubar=0,directories=0,resizable=1,scrollbars=1,height=560,width=500'
1825				);
1826
1827				startPolling();
1828
1829				return false;
1830			}
1831
1832			// Record that the user likes or does not like this comment.
1833			const commentId = getCommentId(target);
1834			target.classList.add('loading');
1835
1836			let commentEl = document.querySelector(`p#comment-like-${commentId}`);
1837			// Determine whether to like or unlike based on whether the comment is
1838			// currently liked.
1839			const action =
1840				commentEl && commentEl.dataset.liked === 'comment-liked'
1841					? 'unlike_comment'
1842					: 'like_comment';
1843			handleLinkAction(target, action, commentId, data => {
1844				// Invalidate the like cache for this comment.
1845				delete commentLikeCache[commentId];
1846
1847				const countEl = document.querySelector(`#comment-like-count-${data.context}`);
1848				if (countEl) {
1849					countEl.innerHTML = data.display;
1850				}
1851
1852				commentEl = document.querySelector(`p#comment-like-${data.context}`);
1853				if (action === 'like_comment') {
1854					commentEl.classList.remove('comment-not-liked');
1855					commentEl.classList.add('comment-liked');
1856					commentEl.dataset.liked = 'comment-liked';
1857				} else {
1858					commentEl.classList.remove('comment-liked');
1859					commentEl.classList.add('comment-not-liked');
1860					commentEl.dataset.liked = 'comment-not-liked';
1861				}
1862
1863				// Prefetch new data for this comment (if there are likers left).
1864				const parent = target.closest('.comment-likes');
1865				const link = parent && parent.querySelector('a.view-likers');
1866				if (link) {
1867					fetchLikeData(link, commentId);
1868				}
1869
1870				target.classList.remove('loading');
1871			});
1872			e.preventDefault();
1873			e.stopPropagation();
1874		});
1875
1876		document.body.addEventListener(
1877			'mouseenter',
1878			function (e) {
1879				if (!e.target.matches('p.comment-likes a.view-likers')) {
1880					return;
1881				}
1882				// Show the user a list of who has liked this comment.
1883
1884				const link = e.target;
1885				if (Number(link.dataset.likeCount || 0) === 0) {
1886					// No one has liked this comment.
1887					return;
1888				}
1889
1890				// Don't hide the overlay.
1891				overlay.cancelHide();
1892
1893				// Get the comment ID.
1894				const container = link.parentElement && link.parentElement.parentElement;
1895				const star = container && container.querySelector('a.comment-like-link');
1896				const commentId = star && getCommentId(star);
1897				relevantComment = commentId;
1898
1899				// Check if the list of likes for this comment is already in
1900				// the cache.
1901				if (commentId in commentLikeCache) {
1902					const entry = commentLikeCache[commentId];
1903					// Only display the likes if the ajax request is
1904					// actually done.
1905					if (entry !== null) {
1906						overlay.showLikes(link, entry);
1907					} else {
1908						// Make sure the overlay is visible (in case
1909						// the user moved the mouse away while loading
1910						// but then came back before it finished
1911						// loading).
1912						overlay.showLoadingMessage(link);
1913					}
1914					return;
1915				}
1916
1917				// Position the "Loading..." overlay.
1918				overlay.showLoadingMessage(link);
1919
1920				// Fetch the data.
1921				fetchLikeData(link, commentId);
1922			},
1923			true
1924		);
1925
1926		document.body.addEventListener(
1927			'mouseleave',
1928			e => {
1929				if (!e.target.matches('p.comment-likes a.view-likers')) {
1930					return;
1931				}
1932				// User has moved cursor away - hide the overlay.
1933				overlay.requestHide();
1934			},
1935			true
1936		);
1937
1938		document.body.addEventListener(
1939			'mouseenter',
1940			e => {
1941				if (!e.target.matches('.comment') || !e.target.querySelector('a.comment-like-link')) {
1942					return;
1943				}
1944				// User is moving over a comment - precache the comment like data.
1945				if (precacheTimeout !== null) {
1946					clearTimeout(precacheTimeout);
1947					precacheTimeout = null;
1948				}
1949
1950				const star = e.target.querySelector('a.comment-like-link');
1951				const parent = star.closest('.comment-likes');
1952				const link = parent && parent.querySelector('a.view-likers');
1953				if (!link || Number(link.dataset.likeCount || 0) === 0) {
1954					// No likes.
1955					return;
1956				}
1957				const commentId = getCommentId(star);
1958				if (commentId in commentLikeCache) {
1959					// Already in cache.
1960					return;
1961				}
1962
1963				precacheTimeout = setTimeout(() => {
1964					precacheTimeout = null;
1965					if (commentId in commentLikeCache) {
1966						// Was cached in the interim.
1967						return;
1968					}
1969					fetchLikeData(link, commentId);
1970				}, 1000);
1971			},
1972			true
1973		);
1974	}
1975
1976	if (document.readyState !== 'loading') {
1977		init();
1978	} else {
1979		document.addEventListener('DOMContentLoaded', init);
1980	}
1981})();
1982;
1983/**
1984 * navigation.js
1985 *
1986 * Handles toggling the navigation menu for small screens and enables tab
1987 * support for dropdown menus.
1988 */
1989( function() {
1990	var container, button, menu, links, subMenus;
1991
1992	container = document.getElementById( 'site-navigation' );
1993	if ( ! container ) {
1994		return;
1995	}
1996
1997	button = container.getElementsByTagName( 'button' )[0];
1998	if ( 'undefined' === typeof button ) {
1999		return;
2000	}
2001
2002	menu = container.getElementsByTagName( 'ul' )[0];
2003	socialMenu = container.getElementsByTagName( 'ul' )[1];
2004
2005	// Hide menu toggle button if both menus are empty and return early.
2006	if ( 'undefined' === typeof menu && 'undefined' === typeof socialMenu ) {
2007		button.style.display = 'none';
2008		return;
2009	}
2010
2011	menu.setAttribute( 'aria-expanded', 'false' );
2012	if ( -1 === menu.className.indexOf( 'nav-menu' ) ) {
2013		menu.className += ' nav-menu';
2014	}
2015
2016	button.onclick = function() {
2017		if ( -1 !== container.className.indexOf( 'toggled' ) ) {
2018			container.className = container.className.replace( ' toggled', '' );
2019			button.setAttribute( 'aria-expanded', 'false' );
2020			menu.setAttribute( 'aria-expanded', 'false' );
2021			socialMenu.setAttribute( 'aria-expanded', 'false' );
2022		} else {
2023			container.className += ' toggled';
2024			button.setAttribute( 'aria-expanded', 'true' );
2025			menu.setAttribute( 'aria-expanded', 'true' );
2026			socialMenu.setAttribute( 'aria-expanded', 'true' );
2027		}
2028	};
2029
2030	// Get all the link elements within the menu.
2031	links    = menu.getElementsByTagName( 'a' );
2032	subMenus = menu.getElementsByTagName( 'ul' );
2033
2034	// Set menu items with submenus to aria-haspopup="true".
2035	for ( var i = 0, len = subMenus.length; i < len; i++ ) {
2036		subMenus[i].parentNode.setAttribute( 'aria-haspopup', 'true' );
2037	}
2038
2039	// Each time a menu link is focused or blurred, toggle focus.
2040	for ( i = 0, len = links.length; i < len; i++ ) {
2041		links[i].addEventListener( 'focus', toggleFocus, true );
2042		links[i].addEventListener( 'blur', toggleFocus, true );
2043	}
2044
2045	/**
2046	 * Sets or removes .focus class on an element.
2047	 */
2048	function toggleFocus() {
2049		var self = this;
2050
2051		// Move up through the ancestors of the current link until we hit .nav-menu.
2052		while ( -1 === self.className.indexOf( 'nav-menu' ) ) {
2053
2054			// On li elements toggle the class .focus.
2055			if ( 'li' === self.tagName.toLowerCase() ) {
2056				if ( -1 !== self.className.indexOf( 'focus' ) ) {
2057					self.className = self.className.replace( ' focus', '' );
2058				} else {
2059					self.className += ' focus';
2060				}
2061			}
2062
2063			self = self.parentElement;
2064		}
2065	}
2066
2067	// Fix child menus for touch devices.
2068	function fixMenuTouchTaps( container ) {
2069		var touchStartFn,
2070		    parentLink = container.querySelectorAll( '.menu-item-has-children > a, .page_item_has_children > a' );
2071
2072		if ( 'ontouchstart' in window ) {
2073			touchStartFn = function( e ) {
2074				var menuItem = this.parentNode;
2075
2076				if ( ! menuItem.classList.contains( 'focus' ) ) {
2077					e.preventDefault();
2078					for( var i = 0; i < menuItem.parentNode.children.length; ++i ) {
2079						if ( menuItem === menuItem.parentNode.children[i] ) {
2080							continue;
2081						}
2082						menuItem.parentNode.children[i].classList.remove( 'focus' );
2083					}
2084					menuItem.classList.add( 'focus' );
2085				} else {
2086					menuItem.classList.remove( 'focus' );
2087				}
2088			};
2089
2090			for ( var i = 0; i < parentLink.length; ++i ) {
2091				parentLink[i].addEventListener( 'touchstart', touchStartFn, false )
2092			}
2093		}
2094	}
2095
2096	fixMenuTouchTaps( container );
2097} )();
2098;
2099( function() {
2100	var is_webkit = navigator.userAgent.toLowerCase().indexOf( 'webkit' ) > -1,
2101	    is_opera  = navigator.userAgent.toLowerCase().indexOf( 'opera' )  > -1,
2102	    is_ie     = navigator.userAgent.toLowerCase().indexOf( 'msie' )   > -1;
2103
2104	if ( ( is_webkit || is_opera || is_ie ) && document.getElementById && window.addEventListener ) {
2105		window.addEventListener( 'hashchange', function() {
2106			var id = location.hash.substring( 1 ),
2107				element;
2108
2109			if ( ! ( /^[A-z0-9_-]+$/.test( id ) ) ) {
2110				return;
2111			}
2112
2113			element = document.getElementById( id );
2114
2115			if ( element ) {
2116				if ( ! ( /^(?:a|select|input|button|textarea)$/i.test( element.tagName ) ) ) {
2117					element.tabIndex = -1;
2118				}
2119
2120				element.focus();
2121			}
2122		}, false );
2123	}
2124})();
2125;
2126/* global FB, jpfbembed */
2127( function ( window ) {
2128	var facebookEmbed = function () {
2129		var fbroot, src, newScript, firstScript;
2130
2131		if ( 'undefined' !== typeof FB && FB.XFBML ) {
2132			FB.XFBML.parse();
2133		} else {
2134			fbroot = document.createElement( 'div' );
2135			fbroot.id = 'fb-root';
2136			document.getElementsByTagName( 'body' )[ 0 ].appendChild( fbroot );
2137
2138			src = 'https://connect.facebook.net/' + jpfbembed.locale + '/sdk.js#xfbml=1';
2139			if ( jpfbembed.appid ) {
2140				src += '&appId=' + jpfbembed.appid;
2141			}
2142			src += '&version=v2.3';
2143
2144			newScript = document.createElement( 'script' );
2145			newScript.setAttribute( 'src', src );
2146			firstScript = document.querySelector( 'script' );
2147			firstScript.parentNode.insertBefore( newScript, firstScript );
2148		}
2149	};
2150
2151	window.fbAsyncInit = function () {
2152		FB.init( {
2153			appId: jpfbembed.appid,
2154			version: 'v2.3',
2155		} );
2156
2157		FB.XFBML.parse();
2158	};
2159
2160	if ( 'undefined' !== typeof infiniteScroll ) {
2161		document.body.addEventListener( 'post-load', facebookEmbed );
2162	}
2163
2164	facebookEmbed();
2165} )( window );
2166;
2167! ( function ( d, s, id ) {
2168	var js,
2169		fjs = d.getElementsByTagName( s )[ 0 ],
2170		p = /^http:/.test( d.location ) ? 'http' : 'https';
2171	if ( ! d.getElementById( id ) ) {
2172		js = d.createElement( s );
2173		js.id = id;
2174		js.src = p + '://platform.twitter.com/widgets.js';
2175		fjs.parentNode.insertBefore( js, fjs );
2176	}
2177} )( document, 'script', 'twitter-wjs' );
2178;
2179!function(){var e=document.currentScript;function t(t){var n=document.createElement("script"),o=e||document.getElementsByTagName("script")[0];n.setAttribute("async",!0),n.setAttribute("src",t),o.parentNode.insertBefore(n,o)}function n(e,t){return Element.prototype.matches?e.matches(t):Element.prototype.msMatchesSelector?e.msMatchesSelector(t):void 0}function o(e,t){if(e.closest)return e.closest(t);var o=e;do{if(n(o,t))return o;o=o.parentElement||o.parentNode}while(null!==o&&1===o.nodeType);return null}function i(e,t){for(var n=0;n<e.length;n++)t(e[n],n,e)}var r=".sharing-hidden .inner",s="data-sharing-more-button-id";function a(e){this.button=e,this.pane=o(e,"div").querySelector(r),this.openedBy=null,this.recentlyOpenedByHover=!1,a.instances.push(this),this.pane.setAttribute(s,a.instances.length-1),this.attachHandlers()}if(a.instances=[],a.hoverOpenDelay=200,a.recentOpenDelay=400,a.hoverCloseDelay=300,a.instantiateOrReuse=function(e){var t=o(e,"div").querySelector(r),n=t&&t.getAttribute(s),i=a.instances[n];return i||new a(e)},a.getButtonInstanceFromPane=function(e){var t=e&&e.getAttribute(s);return a.instances[t]},a.closeAll=function(){for(var e=0;e<a.instances.length;e++)a.instances[e].close()},a.prototype.open=function(){var e,t,n=[0,0];function o(e){var t=e.getBoundingClientRect();return[t.left+(window.scrollX||window.pageXOffset||0),t.top+(window.scrollY||window.pageYOffset||0)]}function i(e,t){return parseInt(getComputedStyle(e).getPropertyValue(t)||0)}for(e=o(this.button),t=this.button.offsetParent||document.documentElement;t&&(t===document.body||t===document.documentElement)&&"static"===getComputedStyle(t).getPropertyValue("position");)t=t.parentNode;t&&t!==this.button&&1===t.nodeType&&(n=[(n=o(t))[0]+i(t,"border-left-width"),n[1]+i(t,"border-top-width")]);var r,s=e[0]-n[0]-i(this.button,"margin-left"),a=e[1]-n[1]-i(this.button,"margin-top");this.pane.style.left=s+"px",this.pane.style.top=a+this.button.offsetHeight+3+"px",(r=this.pane)&&r.style.removeProperty("display")},a.prototype.close=function(){var e;(e=this.pane)&&(e.style.display="none"),this.openedBy=null},a.prototype.toggle=function(){var e;(e=this.pane)&&"none"!==e.style.display?this.close():this.open()},a.prototype.nonHoverOpen=function(){clearTimeout(this.openTimer),clearTimeout(this.closeTimer),this.recentlyOpenedByHover?(this.recentlyOpenedByHover=!1,clearTimeout(this.hoverOpenTimer),this.open()):this.toggle()},a.prototype.resetCloseTimer=function(){clearTimeout(this.closeTimer),this.closeTimer=setTimeout(this.close.bind(this),a.hoverCloseDelay)},a.prototype.attachHandlers=function(){this.buttonClick=function(e){e.preventDefault(),e.stopPropagation(),this.openedBy="click",this.nonHoverOpen()}.bind(this),this.buttonKeydown=function(e){13!==e.keyCode&&32!==e.keyCode||(e.preventDefault(),e.stopPropagation(),this.openedBy="keydown",this.nonHoverOpen())}.bind(this),this.buttonEnter=function(){this.openedBy||(this.openTimer=setTimeout(function(){this.open(),this.openedBy="hover",this.recentlyOpenedByHover=!0,this.hoverOpenTimer=setTimeout(function(){this.recentlyOpenedByHover=!1}.bind(this),a.recentOpenDelay)}.bind(this),a.hoverOpenDelay)),clearTimeout(this.closeTimer)}.bind(this),this.buttonLeave=function(){"hover"===this.openedBy&&this.resetCloseTimer(),clearTimeout(this.openTimer)}.bind(this),this.paneEnter=function(){clearTimeout(this.closeTimer)}.bind(this),this.paneLeave=function(){"hover"===this.openedBy&&this.resetCloseTimer()}.bind(this),this.documentClick=function(){this.close()}.bind(this),this.button.addEventListener("click",this.buttonClick),this.button.addEventListener("keydown",this.buttonKeydown),document.addEventListener("click",this.documentClick),void 0===document.ontouchstart&&(this.button.addEventListener("mouseenter",this.buttonEnter),this.button.addEventListener("mouseleave",this.buttonLeave),this.pane.addEventListener("mouseenter",this.paneEnter),this.pane.addEventListener("mouseleave",this.paneLeave))},window.sharing_js_options&&window.sharing_js_options.counts){var c={done_urls:[],get_counts:function(){var e,n,o,i,r;if("undefined"!=typeof WPCOM_sharing_counts)for(e in WPCOM_sharing_counts)if(o=WPCOM_sharing_counts[e],void 0===c.done_urls[o]){for(i in n={pinterest:[window.location.protocol+"https://api.pinterest.com/v1/urls/count.json?callback=WPCOMSharing.update_pinterest_count&url="+encodeURIComponent(e)]})if(document.querySelector("a[data-shared=sharing-"+i+"-"+o+"]")){for(;r=n[i].pop();)t(r);window.sharing_js_options.is_stats_active&&c.bump_sharing_count_stat(i)}c.done_urls[o]=!0}},update_pinterest_count:function(e){void 0!==e.count&&1*e.count>0&&c.inject_share_count("sharing-pinterest-"+WPCOM_sharing_counts[e.url],e.count)},inject_share_count:function(e,t){i(document.querySelectorAll("a[data-shared="+e+"] > span"),(function(e){var n,o=e.querySelector(".share-count");(n=o)&&n.parentNode&&n.parentNode.removeChild(n);var i=document.createElement("span");
2179i.className="share-count",i.textContent=c.format_count(t),e.appendChild(i)}))},format_count:function(e){return e<1e3?e:e>=1e3&&e<1e4?String(e).substring(0,1)+"K+":"10K+"},bump_sharing_count_stat:function(e){(new Image).src=document.location.protocol+"https://pixel.wp.com/g.gif?v=wpcom-no-pv&x_sharing-count-request="+e+"&r="+Math.random()}};window.WPCOMSharing=c}function u(e,t){e.setAttribute("jetpack-share-click-count",t)}function d(e){var t=e.getAttribute("jetpack-share-click-count");return null===t?0:parseInt(t,10)}function l(e,t){var n,o=new XMLHttpRequest;o.open("POST",e,!0),o.setRequestHeader("Content-Type","application/x-www-form-urlencoded; charset=UTF-8"),o.setRequestHeader("x-requested-with","XMLHttpRequest"),o.send((n=t,(encodeURIComponent("email-share-nonce")+"="+encodeURIComponent(n)).replace(/%20/g,"+")))}function h(){p()}function p(){window.WPCOMSharing&&window.WPCOMSharing.get_counts(),i(document.querySelectorAll(".sharedaddy a"),(function(e){var t=e.getAttribute("href");t&&-1!==t.indexOf("share=")&&-1===t.indexOf("&nb=1")&&e.setAttribute("href",t+"&nb=1")})),i(document.querySelectorAll(".sharedaddy a.sharing-anchor"),(function(e){a.instantiateOrReuse(e)})),void 0!==document.ontouchstart&&document.body.classList.add("jp-sharing-input-touch"),i(document.querySelectorAll(".sharedaddy ul"),(function(e){"true"!==e.getAttribute("data-sharing-events-added")&&(e.setAttribute("data-sharing-events-added","true"),i(e.querySelectorAll("a.share-print"),(function(e){e.addEventListener("click",(function(t){t.preventDefault(),t.stopPropagation();var n=e.getAttribute("href")||"",i=function(){if(-1===n.indexOf("#print")){var e=(new Date).getTime();t=e,o=n,(i=document.createElement("iframe")).setAttribute("style","position:fixed; top:100; left:100; height:1px; width:1px; border:none;"),i.setAttribute("id","printFrame-"+t),i.setAttribute("name",i.getAttribute("id")),i.setAttribute("src",o),i.setAttribute("onload",'frames["printFrame-'+t+'"].focus();frames["printFrame-'+t+'"].print();'),document.body.appendChild(i)}else window.print();var t,o,i},s=o(e,r);if(s){var c=a.getButtonInstanceFromPane(s);c&&(c.close(),i())}else i()}))})),i(e.querySelectorAll("a.share-press-this"),(function(e){e.addEventListener("click",(function(t){t.preventDefault(),t.stopPropagation();var n="";if(window.getSelection?n=window.getSelection():document.getSelection?n=document.getSelection():document.selection&&(n=document.selection.createRange().text),n){var o=e.getAttribute("href");e.setAttribute("href",o+"&sel="+encodeURI(n))}window.open(e.getAttribute("href"),"t","toolbar=0,resizable=1,scrollbars=1,status=1,width=720,height=570")||(document.location.href=e.getAttribute("href"))}))})),i(e.querySelectorAll("a.share-email"),(function(t){u(t,0);var n,o,r=t.getAttribute("data-email-share-nonce"),s=t.getAttribute("data-email-share-track-url");r&&s&&(n=s,o=window.location.protocol+"//"+window.location.hostname+"/",0===String(n).indexOf(o))&&t.addEventListener("click",(function(){var n;u(n=t,d(n)+1),d(t)>2&&function(e,t){var n=t.parentElement;if(n.classList.contains("sd-content")){i(n.querySelectorAll(".share-email-error"),(function(e){e.parentElement.removeChild(e)}));var o=document.createElement("div");o.className="share-email-error";var r=document.createElement("h6");r.className="share-email-error-title",r.innerText=e.getAttribute("data-email-share-error-title"),o.appendChild(r);var s=document.createElement("p");s.className="share-email-error-text",s.innerText=e.getAttribute("data-email-share-error-text"),o.appendChild(s),n.appendChild(o)}}(t,e),l(s,r)}))})))})),i(document.querySelectorAll("li.share-email, li.share-custom a.sharing-anchor"),(function(e){e.classList.add("share-service-visible")}))}"loading"!==document.readyState?h():document.addEventListener("DOMContentLoaded",h),document.body.addEventListener("is.post-load",p)}();;
2180
2181}

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.