PluginProbe
Jetpack – WP Security, Backup, Speed, & Growth / 16.3
Jetpack – WP Security, Backup, Speed, & Growth v16.3
16.3 16.3-beta 16.3-a.5 16.3-a.7 16.3-a.3 16.3-a.1 16.2 16.2-beta 12.0.3 12.1.3 12.2.3 12.3.2 12.4.2 12.5.2 12.6.4 12.7.3 12.8.3 12.9.5 13.0.2 13.1.5 13.2.4 13.3.3 13.4.5 13.5.2 13.6.2 All 508 releases
← All changes | modules/sharedaddy/sharing.js +723 -0 16.2-beta → 16.3 View file →
@@ -1,0 +1,723 @@
1 +/* global WPCOM_sharing_counts */
2 +
3 +// NOTE: This file intentionally does not make use of polyfills or libraries,
4 +// including jQuery. Please keep all code as IE11-compatible vanilla ES5, and
5 +// ensure everything is inside an IIFE to avoid global namespace pollution.
6 +// Code follows WordPress browser support guidelines. For an up to date list,
7 +// see https://make.wordpress.org/core/handbook/best-practices/browser-support/
8 +
9 +( function () {
10 + var currentScript = document.currentScript;
11 +
12 + // -------------------------- UTILITY FUNCTIONS -------------------------- //
13 +
14 + // Helper function to load an external script.
15 + function loadScript( url ) {
16 + var script = document.createElement( 'script' );
17 + var prev = currentScript || document.getElementsByTagName( 'script' )[ 0 ];
18 + script.setAttribute( 'async', true );
19 + script.setAttribute( 'src', url );
20 + prev.parentNode.insertBefore( script, prev );
21 + }
22 +
23 + // Helper matches function (not a polyfill), compatible with IE 11.
24 + function matches( el, sel ) {
25 + if ( Element.prototype.matches ) {
26 + return el.matches( sel );
27 + }
28 +
29 + if ( Element.prototype.msMatchesSelector ) {
30 + return el.msMatchesSelector( sel );
31 + }
32 + }
33 +
34 + // Helper closest parent node function (not a polyfill) based on
35 + // https://developer.mozilla.org/en-US/docs/Web/API/Element/closest#Polyfill
36 + function closest( el, sel ) {
37 + if ( el.closest ) {
38 + return el.closest( sel );
39 + }
40 +
41 + var current = el;
42 +
43 + do {
44 + if ( matches( current, sel ) ) {
45 + return current;
46 + }
47 + current = current.parentElement || current.parentNode;
48 + } while ( current !== null && current.nodeType === 1 );
49 +
50 + return null;
51 + }
52 +
53 + // Helper function to iterate over a NodeList
54 + // (since IE 11 doesn't have NodeList.prototype.forEach)
55 + function forEachNode( list, fn ) {
56 + for ( var i = 0; i < list.length; i++ ) {
57 + var node = list[ i ];
58 + fn( node, i, list );
59 + }
60 + }
61 +
62 + // Helper function to remove a node from the DOM.
63 + function removeNode( node ) {
64 + if ( node && node.parentNode ) {
65 + node.parentNode.removeChild( node );
66 + }
67 + }
68 +
69 + // Helper functions to show/hide a node, and check its status.
70 + function hideNode( node ) {
71 + if ( node ) {
72 + node.style.display = 'none';
73 + }
74 + }
75 +
76 + function showNode( node ) {
77 + if ( node ) {
78 + node.style.removeProperty( 'display' );
79 + }
80 + }
81 +
82 + function isNodeHidden( node ) {
83 + return ! node || node.style.display === 'none';
84 + }
85 +
86 + // ------------------------------- CLASSES ------------------------------- //
87 +
88 + var PANE_SELECTOR = '.sharing-hidden .inner';
89 + var PANE_DATA_ATTR = 'data-sharing-more-button-id';
90 +
91 + // Implements a MoreButton class, which controls the lifecycle and behavior
92 + // of a "more" button and its dialog.
93 + function MoreButton( buttonEl ) {
94 + this.button = buttonEl;
95 + this.pane = closest( buttonEl, 'div' ).querySelector( PANE_SELECTOR );
96 + this.openedBy = null;
97 + this.recentlyOpenedByHover = false;
98 +
99 + MoreButton.instances.push( this );
100 + this.pane.setAttribute( PANE_DATA_ATTR, MoreButton.instances.length - 1 );
101 +
102 + this.attachHandlers();
103 + }
104 +
105 + // Keep a reference to each instance, so we can get back to it from the DOM.
106 + MoreButton.instances = [];
107 +
108 + // Delay time configs.
109 + MoreButton.hoverOpenDelay = 200;
110 + MoreButton.recentOpenDelay = 400;
111 + MoreButton.hoverCloseDelay = 300;
112 +
113 + // Use this to avoid creating new instances for buttons which already have one.
114 + MoreButton.instantiateOrReuse = function ( buttonEl ) {
115 + var pane = closest( buttonEl, 'div' ).querySelector( PANE_SELECTOR );
116 + var paneId = pane && pane.getAttribute( PANE_DATA_ATTR );
117 +
118 + var existingInstance = MoreButton.instances[ paneId ];
119 + if ( existingInstance ) {
120 + return existingInstance;
121 + }
122 +
123 + return new MoreButton( buttonEl );
124 + };
125 +
126 + // Retrieve a button instance from the pane DOM element.
127 + MoreButton.getButtonInstanceFromPane = function ( paneEl ) {
128 + var paneId = paneEl && paneEl.getAttribute( PANE_DATA_ATTR );
129 + return MoreButton.instances[ paneId ];
130 + };
131 +
132 + // Close all open More Button dialogs.
133 + MoreButton.closeAll = function () {
134 + for ( var i = 0; i < MoreButton.instances.length; i++ ) {
135 + MoreButton.instances[ i ].close();
136 + }
137 + };
138 +
139 + MoreButton.prototype.open = function () {
140 + var offset;
141 + var offsetParent;
142 + var parentOffset = [ 0, 0 ];
143 +
144 + function getOffsets( el ) {
145 + var rect = el.getBoundingClientRect();
146 + return [
147 + rect.left + ( window.scrollX || window.pageXOffset || 0 ),
148 + rect.top + ( window.scrollY || window.pageYOffset || 0 ),
149 + ];
150 + }
151 +
152 + function getStyleValue( el, prop ) {
153 + return parseInt( getComputedStyle( el ).getPropertyValue( prop ) || 0 );
154 + }
155 +
156 + offset = getOffsets( this.button );
157 + offsetParent = this.button.offsetParent || document.documentElement;
158 +
159 + while (
160 + offsetParent &&
161 + ( offsetParent === document.body || offsetParent === document.documentElement ) &&
162 + getComputedStyle( offsetParent ).getPropertyValue( 'position' ) === 'static'
163 + ) {
164 + offsetParent = offsetParent.parentNode;
165 + }
166 +
167 + if ( offsetParent && offsetParent !== this.button && offsetParent.nodeType === 1 ) {
168 + parentOffset = getOffsets( offsetParent );
169 + parentOffset = [
170 + parentOffset[ 0 ] + getStyleValue( offsetParent, 'border-left-width' ),
171 + parentOffset[ 1 ] + getStyleValue( offsetParent, 'border-top-width' ),
172 + ];
173 + }
174 +
175 + var positionLeft =
176 + offset[ 0 ] - parentOffset[ 0 ] - getStyleValue( this.button, 'margin-left' );
177 + var positionTop = offset[ 1 ] - parentOffset[ 1 ] - getStyleValue( this.button, 'margin-top' );
178 +
179 + this.pane.style.left = positionLeft + 'px';
180 + this.pane.style.top = positionTop + this.button.offsetHeight + 3 + 'px';
181 +
182 + showNode( this.pane );
183 + };
184 +
185 + MoreButton.prototype.close = function () {
186 + hideNode( this.pane );
187 + this.openedBy = null;
188 + };
189 +
190 + MoreButton.prototype.toggle = function () {
191 + if ( isNodeHidden( this.pane ) ) {
192 + this.open();
193 + } else {
194 + this.close();
195 + }
196 + };
197 +
198 + MoreButton.prototype.nonHoverOpen = function () {
199 + clearTimeout( this.openTimer );
200 + clearTimeout( this.closeTimer );
201 +
202 + if ( this.recentlyOpenedByHover ) {
203 + this.recentlyOpenedByHover = false;
204 + clearTimeout( this.hoverOpenTimer );
205 + this.open();
206 + } else {
207 + this.toggle();
208 + }
209 + };
210 +
211 + MoreButton.prototype.resetCloseTimer = function () {
212 + clearTimeout( this.closeTimer );
213 + this.closeTimer = setTimeout( this.close.bind( this ), MoreButton.hoverCloseDelay );
214 + };
215 +
216 + MoreButton.prototype.attachHandlers = function () {
217 + this.buttonClick = function ( event ) {
218 + event.preventDefault();
219 + event.stopPropagation();
220 +
221 + this.openedBy = 'click';
222 + this.nonHoverOpen();
223 + }.bind( this );
224 +
225 + this.buttonKeydown = function ( event ) {
226 + if ( event.keyCode === 13 || event.keyCode === 32 ) {
227 + event.preventDefault();
228 + event.stopPropagation();
229 +
230 + this.openedBy = 'keydown';
231 + this.nonHoverOpen();
232 + }
233 + }.bind( this );
234 +
235 + this.buttonEnter = function () {
236 + if ( ! this.openedBy ) {
237 + this.openTimer = setTimeout(
238 + function () {
239 + this.open();
240 + this.openedBy = 'hover';
241 + this.recentlyOpenedByHover = true;
242 + this.hoverOpenTimer = setTimeout(
243 + function () {
244 + this.recentlyOpenedByHover = false;
245 + }.bind( this ),
246 + MoreButton.recentOpenDelay
247 + );
248 + }.bind( this ),
249 + MoreButton.hoverOpenDelay
250 + );
251 + }
252 + clearTimeout( this.closeTimer );
253 + }.bind( this );
254 +
255 + this.buttonLeave = function () {
256 + if ( this.openedBy === 'hover' ) {
257 + this.resetCloseTimer();
258 + }
259 + clearTimeout( this.openTimer );
260 + }.bind( this );
261 +
262 + this.paneEnter = function () {
263 + clearTimeout( this.closeTimer );
264 + }.bind( this );
265 +
266 + this.paneLeave = function () {
267 + if ( this.openedBy === 'hover' ) {
268 + this.resetCloseTimer();
269 + }
270 + }.bind( this );
271 +
272 + this.documentClick = function () {
273 + this.close();
274 + }.bind( this );
275 +
276 + this.button.addEventListener( 'click', this.buttonClick );
277 + this.button.addEventListener( 'keydown', this.buttonKeydown );
278 + document.addEventListener( 'click', this.documentClick );
279 +
280 + if ( document.ontouchstart === undefined ) {
281 + // Non-touchscreen device: use hover/mouseout with delay
282 + this.button.addEventListener( 'mouseenter', this.buttonEnter );
283 + this.button.addEventListener( 'mouseleave', this.buttonLeave );
284 + this.pane.addEventListener( 'mouseenter', this.paneEnter );
285 + this.pane.addEventListener( 'mouseleave', this.paneLeave );
286 + }
287 + };
288 +
289 + // ---------------------------- SHARE COUNTS ---------------------------- //
290 +
291 + if ( window.sharing_js_options && window.sharing_js_options.counts ) {
292 + var WPCOMSharing = {
293 + done_urls: [],
294 + get_counts: function () {
295 + var url, requests, id, service, service_request;
296 +
297 + if ( 'undefined' === typeof WPCOM_sharing_counts ) {
298 + return;
299 + }
300 +
301 + for ( url in WPCOM_sharing_counts ) {
302 + id = WPCOM_sharing_counts[ url ];
303 +
304 + if ( 'undefined' !== typeof WPCOMSharing.done_urls[ id ] ) {
305 + continue;
306 + }
307 +
308 + requests = {
309 + // Pinterest handles share counts for both http and https
310 + pinterest: [
311 + window.location.protocol +
312 + '//api.pinterest.com/v1/urls/count.json?callback=WPCOMSharing.update_pinterest_count&url=' +
313 + encodeURIComponent( url ),
314 + ],
315 + };
316 +
317 + for ( service in requests ) {
318 + if ( ! document.querySelector( 'a[data-shared=sharing-' + service + '-' + id + ']' ) ) {
319 + continue;
320 + }
321 +
322 + while ( ( service_request = requests[ service ].pop() ) ) {
323 + loadScript( service_request );
324 + }
325 +
326 + if ( window.sharing_js_options.is_stats_active ) {
327 + WPCOMSharing.bump_sharing_count_stat( service );
328 + }
329 + }
330 +
331 + WPCOMSharing.done_urls[ id ] = true;
332 + }
333 + },
334 + update_pinterest_count: function ( data ) {
335 + if ( 'undefined' !== typeof data.count && data.count * 1 > 0 ) {
336 + WPCOMSharing.inject_share_count(
337 + 'sharing-pinterest-' + WPCOM_sharing_counts[ data.url ],
338 + data.count
339 + );
340 + }
341 + },
342 + inject_share_count: function ( id, count ) {
343 + forEachNode(
344 + document.querySelectorAll( 'a[data-shared=' + id + '] > span' ),
345 + function ( span ) {
346 + var countNode = span.querySelector( '.share-count' );
347 + removeNode( countNode );
348 + var newNode = document.createElement( 'span' );
349 + newNode.className = 'share-count';
350 + newNode.textContent = WPCOMSharing.format_count( count );
351 + span.appendChild( newNode );
352 + }
353 + );
354 + },
355 + format_count: function ( count ) {
356 + if ( count < 1000 ) {
357 + return count;
358 + }
359 + if ( count >= 1000 && count < 10000 ) {
360 + return String( count ).substring( 0, 1 ) + 'K+';
361 + }
362 + return '10K+';
363 + },
364 + bump_sharing_count_stat: function ( service ) {
365 + new Image().src =
366 + document.location.protocol +
367 + '//pixel.wp.com/g.gif?v=wpcom-no-pv&x_sharing-count-request=' +
368 + service +
369 + '&r=' +
370 + Math.random();
371 + },
372 + };
373 + window.WPCOMSharing = WPCOMSharing;
374 + }
375 +
376 + // ------------------------ BUTTON FUNCTIONALITY ------------------------ //
377 + function isUrlForCurrentHost( url ) {
378 + var currentDomain = window.location.protocol + '//' + window.location.hostname + '/';
379 +
380 + return String( url ).indexOf( currentDomain ) === 0;
381 + }
382 +
383 + function getEncodedFormFieldForSubmit( name, value ) {
384 + // Encode the key and value into a URI-compatible string.
385 + var encoded = encodeURIComponent( name ) + '=' + encodeURIComponent( value );
386 +
387 + // In x-www-form-urlencoded, spaces should be `+`, not `%20`.
388 + return encoded.replace( /%20/g, '+' );
389 + }
390 +
391 + function trackButtonClick( button ) {
392 + var clickCount = getClickCountForButton( button );
393 +
394 + setClickCountForButton( button, clickCount + 1 );
395 + }
396 +
397 + function setClickCountForButton( button, clickCount ) {
398 + button.setAttribute( 'jetpack-share-click-count', clickCount );
399 + }
400 +
401 + function getClickCountForButton( button ) {
402 + var currentClickCount = button.getAttribute( 'jetpack-share-click-count' );
403 + if ( currentClickCount === null ) {
404 + return 0;
405 + }
406 +
407 + return parseInt( currentClickCount, 10 );
408 + }
409 +
410 + function showEmailShareError( emailShareButton, sdUlGroup ) {
411 + var sdContent = sdUlGroup.parentElement;
412 + if ( ! sdContent.classList.contains( 'sd-content' ) ) {
413 + return;
414 + }
415 +
416 + forEachNode( sdContent.querySelectorAll( '.share-email-error' ), function ( shareEmailError ) {
417 + shareEmailError.parentElement.removeChild( shareEmailError );
418 + } );
419 +
420 + var newShareEmailError = document.createElement( 'div' );
421 + newShareEmailError.className = 'share-email-error';
422 +
423 + var newShareEmailErrorTitle = document.createElement( 'h6' );
424 + newShareEmailErrorTitle.className = 'share-email-error-title';
425 + newShareEmailErrorTitle.innerText = emailShareButton.getAttribute(
426 + 'data-email-share-error-title'
427 + );
428 + newShareEmailError.appendChild( newShareEmailErrorTitle );
429 +
430 + var newShareEmailErrorText = document.createElement( 'p' );
431 + newShareEmailErrorText.className = 'share-email-error-text';
432 + newShareEmailErrorText.innerText = emailShareButton.getAttribute(
433 + 'data-email-share-error-text'
434 + );
435 + newShareEmailError.appendChild( newShareEmailErrorText );
436 +
437 + sdContent.appendChild( newShareEmailError );
438 + }
439 +
440 + function recordEmailShareClick( emailShareTrackerUrl, emailShareNonce ) {
441 + var request = new XMLHttpRequest();
442 + request.open( 'POST', emailShareTrackerUrl, true );
443 + request.setRequestHeader( 'Content-Type', 'application/x-www-form-urlencoded; charset=UTF-8' );
444 + request.setRequestHeader( 'x-requested-with', 'XMLHttpRequest' );
445 +
446 + request.send( getEncodedFormFieldForSubmit( 'email-share-nonce', emailShareNonce ) );
447 + }
448 +
449 + // Sharing initialization.
450 + // Will run immediately or on `DOMContentLoaded`, depending on current page status.
451 + function init() {
452 + WPCOMSharing_do();
453 + }
454 + if ( document.readyState !== 'loading' ) {
455 + init();
456 + } else {
457 + document.addEventListener( 'DOMContentLoaded', init );
458 + }
459 +
460 + // Set up sharing again whenever a new post loads, to pick up any new buttons.
461 + document.body.addEventListener( 'is.post-load', WPCOMSharing_do );
462 +
463 + // Set up sharing, updating counts and adding all button functionality.
464 + function WPCOMSharing_do() {
465 + if ( window.WPCOMSharing ) {
466 + window.WPCOMSharing.get_counts();
467 + }
468 +
469 + forEachNode( document.querySelectorAll( '.sharedaddy a' ), function ( anchor ) {
470 + var href = anchor.getAttribute( 'href' );
471 + if ( href && href.indexOf( 'share=' ) !== -1 && href.indexOf( '&nb=1' ) === -1 ) {
472 + anchor.setAttribute( 'href', href + '&nb=1' );
473 + }
474 + } );
475 +
476 + // Show hidden buttons
477 +
478 + // Touchscreen device: use click.
479 + // Non-touchscreen device: use click if not already appearing due to a hover event
480 +
481 + forEachNode(
482 + document.querySelectorAll( '.sharedaddy a.sharing-anchor' ),
483 + function ( buttonEl ) {
484 + MoreButton.instantiateOrReuse( buttonEl );
485 + }
486 + );
487 +
488 + if ( document.ontouchstart !== undefined ) {
489 + document.body.classList.add( 'jp-sharing-input-touch' );
490 + }
491 +
492 + // Add click functionality
493 + forEachNode( document.querySelectorAll( '.sharedaddy ul' ), function ( group ) {
494 + if ( group.getAttribute( 'data-sharing-events-added' ) === 'true' ) {
495 + return;
496 + }
497 + group.setAttribute( 'data-sharing-events-added', 'true' );
498 +
499 + var printUrl = function ( uniqueId, urlToPrint ) {
500 + var iframe = document.createElement( 'iframe' );
501 + iframe.setAttribute(
502 + 'style',
503 + 'position:fixed; top:100; left:100; height:1px; width:1px; border:none;'
504 + );
505 + iframe.setAttribute( 'id', 'printFrame-' + uniqueId );
506 + iframe.setAttribute( 'name', iframe.getAttribute( 'id' ) );
507 + iframe.setAttribute( 'src', urlToPrint );
508 + iframe.setAttribute(
509 + 'onload',
510 + 'frames["printFrame-' +
511 + uniqueId +
512 + '"].focus();frames["printFrame-' +
513 + uniqueId +
514 + '"].print();'
515 + );
516 + document.body.appendChild( iframe );
517 + };
518 +
519 + // Print button
520 + forEachNode( group.querySelectorAll( 'a.share-print' ), function ( printButton ) {
521 + printButton.addEventListener( 'click', function ( event ) {
522 + event.preventDefault();
523 + event.stopPropagation();
524 +
525 + var ref = printButton.getAttribute( 'href' ) || '';
526 + var doPrint = function () {
527 + if ( ref.indexOf( '#print' ) === -1 ) {
528 + var uid = new Date().getTime();
529 + printUrl( uid, ref );
530 + } else {
531 + window.print();
532 + }
533 + };
534 +
535 + // Is the button in a dropdown?
536 + var pane = closest( printButton, PANE_SELECTOR );
537 + if ( pane ) {
538 + var moreButton = MoreButton.getButtonInstanceFromPane( pane );
539 + if ( moreButton ) {
540 + moreButton.close();
541 + doPrint();
542 + }
543 + } else {
544 + doPrint();
545 + }
546 + } );
547 + } );
548 +
549 + // Press This button
550 + forEachNode( group.querySelectorAll( 'a.share-press-this' ), function ( pressThisButton ) {
551 + // Unpredictable, but stable per page load so repeat clicks reuse the same popup.
552 + var pressThisWindowName = 'wp-press-this-' + Math.random().toString( 36 ).slice( 2 );
553 + pressThisButton.addEventListener( 'click', function ( event ) {
554 + event.preventDefault();
555 + event.stopPropagation();
556 +
557 + var s = '';
558 +
559 + if ( window.getSelection ) {
560 + s = window.getSelection();
561 + } else if ( document.getSelection ) {
562 + s = document.getSelection();
563 + } else if ( document.selection ) {
564 + s = document.selection.createRange().text;
565 + }
566 +
567 + if ( s ) {
568 + var href = pressThisButton.getAttribute( 'href' );
569 + pressThisButton.setAttribute( 'href', href + '&sel=' + encodeURI( s ) );
570 + }
571 +
572 + if (
573 + ! window.open(
574 + pressThisButton.getAttribute( 'href' ),
575 + pressThisWindowName,
576 + 'toolbar=0,resizable=1,scrollbars=1,status=1,width=720,height=570'
577 + )
578 + ) {
579 + document.location.href = pressThisButton.getAttribute( 'href' );
580 + }
581 + } );
582 + } );
583 +
584 + // Email button
585 + forEachNode( group.querySelectorAll( 'a.share-email' ), function ( emailButton ) {
586 + setClickCountForButton( emailButton, 0 );
587 +
588 + var emailShareNonce = emailButton.getAttribute( 'data-email-share-nonce' );
589 + var emailShareTrackerUrl = emailButton.getAttribute( 'data-email-share-track-url' );
590 +
591 + if (
592 + emailShareNonce &&
593 + emailShareTrackerUrl &&
594 + isUrlForCurrentHost( emailShareTrackerUrl )
595 + ) {
596 + emailButton.addEventListener( 'click', function () {
597 + trackButtonClick( emailButton );
598 +
599 + if ( getClickCountForButton( emailButton ) > 2 ) {
600 + showEmailShareError( emailButton, group );
601 + }
602 +
603 + recordEmailShareClick( emailShareTrackerUrl, emailShareNonce );
604 + } );
605 + }
606 + } );
607 + } );
608 +
609 + forEachNode(
610 + document.querySelectorAll( 'li.share-email, li.share-custom a.sharing-anchor' ),
611 + function ( node ) {
612 + node.classList.add( 'share-service-visible' );
613 + }
614 + );
615 + }
616 +
617 + // ---------------------------- SHARE POPUPS ---------------------------- //
618 +
619 + // Services that open their share link in a popup publish the window features
620 + // they need in `window.WPCOM_sharing_popups`, keyed by the service name that
621 + // also appears in the link's `share-<service>` class. Sharing_Source::js_dialog()
622 + // prints that map in an inline script after this file, and the one delegated
623 + // listener below serves every service, with only one popup open at a time.
624 + //
625 + // The Sharing Buttons block runs a parallel popup handler in
626 + // extensions/blocks/sharing-buttons/view.js; a change here usually belongs there too.
627 + var sharePopup;
628 + var sharePopupNames = {};
629 +
630 + // Popup names are unpredictable, so another page cannot pre-register one and
631 + // claim the popup. They are stable per service per page load, so repeat clicks
632 + // on a service reuse its window.
633 + function getSharePopupName( service ) {
634 + if ( typeof sharePopupNames[ service ] !== 'string' ) {
635 + sharePopupNames[ service ] =
636 + 'wpcom' + service + '-' + Math.random().toString( 36 ).slice( 2 );
637 + }
638 +
639 + return sharePopupNames[ service ];
640 + }
641 +
642 + // Find the share link a click landed on. Sharing buttons nest at most one
643 + // element inside the anchor, and this stays as shallow as the per-service
644 + // handlers it replaces, so nothing that used to reach its own click handler
645 + // starts being claimed by this one instead.
646 + function getShareLink( target ) {
647 + if ( target.nodeName === 'A' ) {
648 + return target;
649 + }
650 +
651 + var parent = target.parentNode;
652 + return parent && parent.nodeName === 'A' ? parent : null;
653 + }
654 +
655 + // Find the popup service a share link belongs to, if it belongs to one.
656 + function getSharePopupService( link ) {
657 + var popups = window.WPCOM_sharing_popups;
658 + if ( ! popups ) {
659 + return null;
660 + }
661 +
662 + var prefix = 'share-';
663 + var classes = ( link.getAttribute( 'class' ) || '' ).split( /\s+/ );
664 + for ( var i = 0; i < classes.length; i++ ) {
665 + if ( classes[ i ].indexOf( prefix ) !== 0 ) {
666 + continue;
667 + }
668 +
669 + var service = classes[ i ].slice( prefix.length );
670 + if ( typeof popups[ service ] === 'string' ) {
671 + return service;
672 + }
673 + }
674 +
675 + return null;
676 + }
677 +
678 + function openSharePopup( event ) {
679 + var target = event.target;
680 + if ( ! target || target.nodeType !== 1 ) {
681 + return;
682 + }
683 +
684 + var link = getShareLink( target );
685 + if ( ! link ) {
686 + return;
687 + }
688 +
689 + var service = getSharePopupService( link );
690 + if ( ! service ) {
691 + return;
692 + }
693 +
694 + event.preventDefault();
695 +
696 + // If there's another sharing window open, close it.
697 + if ( sharePopup ) {
698 + sharePopup.close();
699 + }
700 +
701 + sharePopup = window.open(
702 + link.getAttribute( 'href' ),
703 + getSharePopupName( service ),
704 + window.WPCOM_sharing_popups[ service ]
705 + );
706 + }
707 +
708 + // Listen on `document.body`, where the per-service handlers this replaces listened,
709 + // so a page script that stops a click from propagating past the body keeps having
710 + // the same effect it always had. The bind flag has to be global: if this file is
711 + // enqueued twice under different handles, each copy gets its own closure, and two
712 + // listeners would open two popups per click. Compare against `true` so a page
713 + // element named after the flag cannot clobber it and leave every button dead.
714 + //
715 + // This has to stay below the `is.post-load` listener above: both dereference
716 + // `document.body`, and if this file is ever evaluated before the body exists it
717 + // should fail where it already failed, after `init()` has registered, rather than
718 + // take the rest of the file down with it.
719 + if ( window.WPCOM_sharing_popups_bound !== true ) {
720 + document.body.addEventListener( 'click', openSharePopup );
721 + window.WPCOM_sharing_popups_bound = true;
722 + }
723 +} )();