Ei kuvausta

sharing.js 23KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741
  1. /* global WPCOM_sharing_counts, grecaptcha */
  2. // NOTE: This file intentionally does not make use of polyfills or libraries,
  3. // including jQuery. Please keep all code as IE11-compatible vanilla ES5, and
  4. // ensure everything is inside an IIFE to avoid global namespace pollution.
  5. // Code follows WordPress browser support guidelines. For an up to date list,
  6. // see https://make.wordpress.org/core/handbook/best-practices/browser-support/
  7. ( function () {
  8. var currentScript = document.currentScript;
  9. var recaptchaScriptAdded = false;
  10. // -------------------------- UTILITY FUNCTIONS -------------------------- //
  11. // Helper function to load an external script.
  12. function loadScript( url ) {
  13. var script = document.createElement( 'script' );
  14. var prev = currentScript || document.getElementsByTagName( 'script' )[ 0 ];
  15. script.setAttribute( 'async', true );
  16. script.setAttribute( 'src', url );
  17. prev.parentNode.insertBefore( script, prev );
  18. }
  19. // Helper matches function (not a polyfill), compatible with IE 11.
  20. function matches( el, sel ) {
  21. if ( Element.prototype.matches ) {
  22. return el.matches( sel );
  23. }
  24. if ( Element.prototype.msMatchesSelector ) {
  25. return el.msMatchesSelector( sel );
  26. }
  27. }
  28. // Helper closest parent node function (not a polyfill) based on
  29. // https://developer.mozilla.org/en-US/docs/Web/API/Element/closest#Polyfill
  30. function closest( el, sel ) {
  31. if ( el.closest ) {
  32. return el.closest( sel );
  33. }
  34. var current = el;
  35. do {
  36. if ( matches( current, sel ) ) {
  37. return current;
  38. }
  39. current = current.parentElement || current.parentNode;
  40. } while ( current !== null && current.nodeType === 1 );
  41. return null;
  42. }
  43. // Helper function to iterate over a NodeList
  44. // (since IE 11 doesn't have NodeList.prototype.forEach)
  45. function forEachNode( list, fn ) {
  46. for ( var i = 0; i < list.length; i++ ) {
  47. var node = list[ i ];
  48. fn( node, i, list );
  49. }
  50. }
  51. // Helper function to remove a node from the DOM.
  52. function removeNode( node ) {
  53. if ( node && node.parentNode ) {
  54. node.parentNode.removeChild( node );
  55. }
  56. }
  57. // Helper functions to show/hide a node, and check its status.
  58. function hideNode( node ) {
  59. if ( node ) {
  60. node.style.display = 'none';
  61. }
  62. }
  63. function showNode( node ) {
  64. if ( node ) {
  65. node.style.removeProperty( 'display' );
  66. }
  67. }
  68. function isNodeHidden( node ) {
  69. return ! node || node.style.display === 'none';
  70. }
  71. // ------------------------------- CLASSES ------------------------------- //
  72. var PANE_SELECTOR = '.sharing-hidden .inner';
  73. var PANE_DATA_ATTR = 'data-sharing-more-button-id';
  74. // Implements a MoreButton class, which controls the lifecycle and behavior
  75. // of a "more" button and its dialog.
  76. function MoreButton( buttonEl ) {
  77. this.button = buttonEl;
  78. this.pane = closest( buttonEl, 'div' ).querySelector( PANE_SELECTOR );
  79. this.openedBy = null;
  80. this.recentlyOpenedByHover = false;
  81. MoreButton.instances.push( this );
  82. this.pane.setAttribute( PANE_DATA_ATTR, MoreButton.instances.length - 1 );
  83. this.attachHandlers();
  84. }
  85. // Keep a reference to each instance, so we can get back to it from the DOM.
  86. MoreButton.instances = [];
  87. // Delay time configs.
  88. MoreButton.hoverOpenDelay = 200;
  89. MoreButton.recentOpenDelay = 400;
  90. MoreButton.hoverCloseDelay = 300;
  91. // Use this to avoid creating new instances for buttons which already have one.
  92. MoreButton.instantiateOrReuse = function ( buttonEl ) {
  93. var pane = closest( buttonEl, 'div' ).querySelector( PANE_SELECTOR );
  94. var paneId = pane && pane.getAttribute( PANE_DATA_ATTR );
  95. var existingInstance = MoreButton.instances[ paneId ];
  96. if ( existingInstance ) {
  97. return existingInstance;
  98. }
  99. return new MoreButton( buttonEl );
  100. };
  101. // Retrieve a button instance from the pane DOM element.
  102. MoreButton.getButtonInstanceFromPane = function ( paneEl ) {
  103. var paneId = paneEl && paneEl.getAttribute( PANE_DATA_ATTR );
  104. return MoreButton.instances[ paneId ];
  105. };
  106. // Close all open More Button dialogs.
  107. MoreButton.closeAll = function () {
  108. for ( var i = 0; i < MoreButton.instances.length; i++ ) {
  109. MoreButton.instances[ i ].close();
  110. }
  111. };
  112. MoreButton.prototype.open = function () {
  113. var offset;
  114. var offsetParent;
  115. var parentOffset = [ 0, 0 ];
  116. function getOffsets( el ) {
  117. var rect = el.getBoundingClientRect();
  118. return [
  119. rect.left + ( window.scrollX || window.pageXOffset || 0 ),
  120. rect.top + ( window.scrollY || window.pageYOffset || 0 ),
  121. ];
  122. }
  123. function getStyleValue( el, prop ) {
  124. return parseInt( getComputedStyle( el ).getPropertyValue( prop ) || 0 );
  125. }
  126. offset = getOffsets( this.button );
  127. offsetParent = this.button.offsetParent || document.documentElement;
  128. while (
  129. offsetParent &&
  130. ( offsetParent === document.body || offsetParent === document.documentElement ) &&
  131. getComputedStyle( offsetParent ).getPropertyValue( 'position' ) === 'static'
  132. ) {
  133. offsetParent = offsetParent.parentNode;
  134. }
  135. if ( offsetParent && offsetParent !== this.button && offsetParent.nodeType === 1 ) {
  136. parentOffset = getOffsets( offsetParent );
  137. parentOffset = [
  138. parentOffset[ 0 ] + getStyleValue( offsetParent, 'border-left-width' ),
  139. parentOffset[ 1 ] + getStyleValue( offsetParent, 'border-top-width' ),
  140. ];
  141. }
  142. var positionLeft =
  143. offset[ 0 ] - parentOffset[ 0 ] - getStyleValue( this.button, 'margin-left' );
  144. var positionTop = offset[ 1 ] - parentOffset[ 1 ] - getStyleValue( this.button, 'margin-top' );
  145. this.pane.style.left = positionLeft + 'px';
  146. this.pane.style.top = positionTop + this.button.offsetHeight + 3 + 'px';
  147. showNode( this.pane );
  148. };
  149. MoreButton.prototype.close = function () {
  150. hideNode( this.pane );
  151. this.openedBy = null;
  152. };
  153. MoreButton.prototype.toggle = function () {
  154. if ( isNodeHidden( this.pane ) ) {
  155. this.open();
  156. } else {
  157. this.close();
  158. }
  159. };
  160. MoreButton.prototype.resetCloseTimer = function () {
  161. clearTimeout( this.closeTimer );
  162. this.closeTimer = setTimeout( this.close.bind( this ), MoreButton.hoverCloseDelay );
  163. };
  164. MoreButton.prototype.attachHandlers = function () {
  165. this.buttonClick = function ( event ) {
  166. event.preventDefault();
  167. event.stopPropagation();
  168. this.openedBy = 'click';
  169. clearTimeout( this.openTimer );
  170. clearTimeout( this.closeTimer );
  171. closeEmailDialog();
  172. if ( this.recentlyOpenedByHover ) {
  173. this.recentlyOpenedByHover = false;
  174. clearTimeout( this.hoverOpenTimer );
  175. this.open();
  176. } else {
  177. this.toggle();
  178. }
  179. }.bind( this );
  180. this.buttonEnter = function () {
  181. if ( ! this.openedBy ) {
  182. this.openTimer = setTimeout(
  183. function () {
  184. closeEmailDialog();
  185. this.open();
  186. this.openedBy = 'hover';
  187. this.recentlyOpenedByHover = true;
  188. this.hoverOpenTimer = setTimeout(
  189. function () {
  190. this.recentlyOpenedByHover = false;
  191. }.bind( this ),
  192. MoreButton.recentOpenDelay
  193. );
  194. }.bind( this ),
  195. MoreButton.hoverOpenDelay
  196. );
  197. }
  198. clearTimeout( this.closeTimer );
  199. }.bind( this );
  200. this.buttonLeave = function () {
  201. if ( this.openedBy === 'hover' ) {
  202. this.resetCloseTimer();
  203. }
  204. clearTimeout( this.openTimer );
  205. }.bind( this );
  206. this.paneEnter = function () {
  207. clearTimeout( this.closeTimer );
  208. }.bind( this );
  209. this.paneLeave = function () {
  210. if ( this.openedBy === 'hover' ) {
  211. this.resetCloseTimer();
  212. }
  213. }.bind( this );
  214. this.documentClick = function () {
  215. this.close();
  216. }.bind( this );
  217. this.button.addEventListener( 'click', this.buttonClick );
  218. document.addEventListener( 'click', this.documentClick );
  219. if ( document.ontouchstart === undefined ) {
  220. // Non-touchscreen device: use hover/mouseout with delay
  221. this.button.addEventListener( 'mouseenter', this.buttonEnter );
  222. this.button.addEventListener( 'mouseleave', this.buttonLeave );
  223. this.pane.addEventListener( 'mouseenter', this.paneEnter );
  224. this.pane.addEventListener( 'mouseleave', this.paneLeave );
  225. }
  226. };
  227. // ---------------------------- SHARE COUNTS ---------------------------- //
  228. if ( window.sharing_js_options && window.sharing_js_options.counts ) {
  229. var WPCOMSharing = {
  230. done_urls: [],
  231. get_counts: function () {
  232. var url, requests, id, service, service_request;
  233. if ( 'undefined' === typeof WPCOM_sharing_counts ) {
  234. return;
  235. }
  236. for ( url in WPCOM_sharing_counts ) {
  237. id = WPCOM_sharing_counts[ url ];
  238. if ( 'undefined' !== typeof WPCOMSharing.done_urls[ id ] ) {
  239. continue;
  240. }
  241. requests = {
  242. // Pinterest handles share counts for both http and https
  243. pinterest: [
  244. window.location.protocol +
  245. '//api.pinterest.com/v1/urls/count.json?callback=WPCOMSharing.update_pinterest_count&url=' +
  246. encodeURIComponent( url ),
  247. ],
  248. // Facebook protocol summing has been shown to falsely double counts, so we only request the current URL
  249. facebook: [
  250. window.location.protocol +
  251. '//graph.facebook.com/?callback=WPCOMSharing.update_facebook_count&ids=' +
  252. encodeURIComponent( url ),
  253. ],
  254. };
  255. for ( service in requests ) {
  256. if ( ! document.querySelector( 'a[data-shared=sharing-' + service + '-' + id + ']' ) ) {
  257. continue;
  258. }
  259. while ( ( service_request = requests[ service ].pop() ) ) {
  260. loadScript( service_request );
  261. }
  262. if ( window.sharing_js_options.is_stats_active ) {
  263. WPCOMSharing.bump_sharing_count_stat( service );
  264. }
  265. }
  266. WPCOMSharing.done_urls[ id ] = true;
  267. }
  268. },
  269. // get the version of the url that was stored in the dom
  270. get_permalink: function ( url ) {
  271. if ( 'https:' === window.location.protocol ) {
  272. url = url.replace( /^http:\/\//i, 'https://' );
  273. } else {
  274. url = url.replace( /^https:\/\//i, 'http://' );
  275. }
  276. return url;
  277. },
  278. update_facebook_count: function ( data ) {
  279. var url, permalink;
  280. if ( ! data ) {
  281. return;
  282. }
  283. for ( url in data ) {
  284. if (
  285. ! Object.prototype.hasOwnProperty.call( data, url ) ||
  286. ! data[ url ].share ||
  287. ! data[ url ].share.share_count
  288. ) {
  289. continue;
  290. }
  291. permalink = WPCOMSharing.get_permalink( url );
  292. if ( ! ( permalink in WPCOM_sharing_counts ) ) {
  293. continue;
  294. }
  295. WPCOMSharing.inject_share_count(
  296. 'sharing-facebook-' + WPCOM_sharing_counts[ permalink ],
  297. data[ url ].share.share_count
  298. );
  299. }
  300. },
  301. update_pinterest_count: function ( data ) {
  302. if ( 'undefined' !== typeof data.count && data.count * 1 > 0 ) {
  303. WPCOMSharing.inject_share_count(
  304. 'sharing-pinterest-' + WPCOM_sharing_counts[ data.url ],
  305. data.count
  306. );
  307. }
  308. },
  309. inject_share_count: function ( id, count ) {
  310. forEachNode( document.querySelectorAll( 'a[data-shared=' + id + '] > span' ), function (
  311. span
  312. ) {
  313. var countNode = span.querySelector( '.share-count' );
  314. removeNode( countNode );
  315. var newNode = document.createElement( 'span' );
  316. newNode.className = 'share-count';
  317. newNode.textContent = WPCOMSharing.format_count( count );
  318. span.appendChild( newNode );
  319. } );
  320. },
  321. format_count: function ( count ) {
  322. if ( count < 1000 ) {
  323. return count;
  324. }
  325. if ( count >= 1000 && count < 10000 ) {
  326. return String( count ).substring( 0, 1 ) + 'K+';
  327. }
  328. return '10K+';
  329. },
  330. bump_sharing_count_stat: function ( service ) {
  331. new Image().src =
  332. document.location.protocol +
  333. '//pixel.wp.com/g.gif?v=wpcom-no-pv&x_sharing-count-request=' +
  334. service +
  335. '&r=' +
  336. Math.random();
  337. },
  338. };
  339. window.WPCOMSharing = WPCOMSharing;
  340. }
  341. // ------------------------ BUTTON FUNCTIONALITY ------------------------ //
  342. function shareIsEmail( val ) {
  343. return /^((([a-z]|\d|[!#\$%&'\*\+\-\/=\?\^_`{\|}~]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])+(\.([a-z]|\d|[!#\$%&'\*\+\-\/=\?\^_`{\|}~]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])+)*)|((\x22)((((\x20|\x09)*(\x0d\x0a))?(\x20|\x09)+)?(([\x01-\x08\x0b\x0c\x0e-\x1f\x7f]|\x21|[\x23-\x5b]|[\x5d-\x7e]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])|(\\([\x01-\x09\x0b\x0c\x0d-\x7f]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF]))))*(((\x20|\x09)*(\x0d\x0a))?(\x20|\x09)+)?(\x22)))@((([a-z]|\d|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])|(([a-z]|\d|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])([a-z]|\d|-|\.|_|~|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])*([a-z]|\d|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])))\.)+(([a-z]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])|(([a-z]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])([a-z]|\d|-|\.|_|~|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])*([a-z]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])))\.?$/i.test(
  344. val
  345. );
  346. }
  347. function closeEmailDialog() {
  348. var dialog = document.querySelector( '#sharing_email' );
  349. hideNode( dialog );
  350. }
  351. // Sharing initialization.
  352. // Will run immediately or on `DOMContentLoaded`, depending on current page status.
  353. function init() {
  354. // Move email dialog to end of body.
  355. var emailDialog = document.querySelector( '#sharing_email' );
  356. if ( emailDialog ) {
  357. document.body.appendChild( emailDialog );
  358. }
  359. WPCOMSharing_do();
  360. }
  361. if ( document.readyState !== 'loading' ) {
  362. init();
  363. } else {
  364. document.addEventListener( 'DOMContentLoaded', init );
  365. }
  366. // Set up sharing again whenever a new post loads, to pick up any new buttons.
  367. document.body.addEventListener( 'is.post-load', WPCOMSharing_do );
  368. // Set up sharing, updating counts and adding all button functionality.
  369. function WPCOMSharing_do() {
  370. if ( window.WPCOMSharing ) {
  371. window.WPCOMSharing.get_counts();
  372. }
  373. forEachNode( document.querySelectorAll( '.sharedaddy a' ), function ( anchor ) {
  374. var href = anchor.getAttribute( 'href' );
  375. if ( href && href.indexOf( 'share=' ) !== -1 && href.indexOf( '&nb=1' ) === -1 ) {
  376. anchor.setAttribute( 'href', href + '&nb=1' );
  377. }
  378. } );
  379. // Show hidden buttons
  380. // Touchscreen device: use click.
  381. // Non-touchscreen device: use click if not already appearing due to a hover event
  382. forEachNode( document.querySelectorAll( '.sharedaddy a.sharing-anchor' ), function (
  383. buttonEl
  384. ) {
  385. MoreButton.instantiateOrReuse( buttonEl );
  386. } );
  387. if ( document.ontouchstart !== undefined ) {
  388. document.body.classList.add( 'jp-sharing-input-touch' );
  389. }
  390. // Add click functionality
  391. forEachNode( document.querySelectorAll( '.sharedaddy ul' ), function ( group ) {
  392. if ( group.getAttribute( 'data-sharing-events-added' ) === 'true' ) {
  393. return;
  394. }
  395. group.setAttribute( 'data-sharing-events-added', 'true' );
  396. var printUrl = function ( uniqueId, urlToPrint ) {
  397. var iframe = document.createElement( 'iframe' );
  398. iframe.setAttribute(
  399. 'style',
  400. 'position:fixed; top:100; left:100; height:1px; width:1px; border:none;'
  401. );
  402. iframe.setAttribute( 'id', 'printFrame-' + uniqueId );
  403. iframe.setAttribute( 'name', iframe.getAttribute( 'id' ) );
  404. iframe.setAttribute( 'src', urlToPrint );
  405. iframe.setAttribute(
  406. 'onload',
  407. 'frames["printFrame-' +
  408. uniqueId +
  409. '"].focus();frames["printFrame-' +
  410. uniqueId +
  411. '"].print();'
  412. );
  413. document.body.appendChild( iframe );
  414. };
  415. // Print button
  416. forEachNode( group.querySelectorAll( 'a.share-print' ), function ( printButton ) {
  417. printButton.addEventListener( 'click', function ( event ) {
  418. event.preventDefault();
  419. event.stopPropagation();
  420. var ref = printButton.getAttribute( 'href' ) || '';
  421. var doPrint = function () {
  422. if ( ref.indexOf( '#print' ) === -1 ) {
  423. var uid = new Date().getTime();
  424. printUrl( uid, ref );
  425. } else {
  426. window.print();
  427. }
  428. };
  429. // Is the button in a dropdown?
  430. var pane = closest( printButton, PANE_SELECTOR );
  431. if ( pane ) {
  432. var moreButton = MoreButton.getButtonInstanceFromPane( pane );
  433. if ( moreButton ) {
  434. moreButton.close();
  435. doPrint();
  436. }
  437. } else {
  438. doPrint();
  439. }
  440. } );
  441. } );
  442. // Press This button
  443. forEachNode( group.querySelectorAll( 'a.share-press-this' ), function ( pressThisButton ) {
  444. pressThisButton.addEventListener( 'click', function ( event ) {
  445. event.preventDefault();
  446. event.stopPropagation();
  447. var s = '';
  448. if ( window.getSelection ) {
  449. s = window.getSelection();
  450. } else if ( document.getSelection ) {
  451. s = document.getSelection();
  452. } else if ( document.selection ) {
  453. s = document.selection.createRange().text;
  454. }
  455. if ( s ) {
  456. var href = pressThisButton.getAttribute( 'href' );
  457. pressThisButton.setAttribute( 'href', href + '&sel=' + encodeURI( s ) );
  458. }
  459. if (
  460. ! window.open(
  461. pressThisButton.getAttribute( 'href' ),
  462. 't',
  463. 'toolbar=0,resizable=1,scrollbars=1,status=1,width=720,height=570'
  464. )
  465. ) {
  466. document.location.href = pressThisButton.getAttribute( 'href' );
  467. }
  468. } );
  469. } );
  470. // Email button
  471. forEachNode( group.querySelectorAll( 'a.share-email' ), function ( emailButton ) {
  472. var dialog = document.querySelector( '#sharing_email' );
  473. emailButton.addEventListener( 'click', function ( event ) {
  474. event.preventDefault();
  475. event.stopPropagation();
  476. // Load reCAPTCHA if needed.
  477. if ( typeof grecaptcha !== 'object' && ! recaptchaScriptAdded ) {
  478. var configEl = document.querySelector( '.g-recaptcha' );
  479. if ( configEl && configEl.getAttribute( 'data-lazy' ) === 'true' ) {
  480. recaptchaScriptAdded = true;
  481. loadScript( decodeURI( configEl.getAttribute( 'data-url' ) ) );
  482. }
  483. }
  484. var url = emailButton.getAttribute( 'href' );
  485. var currentDomain = window.location.protocol + '//' + window.location.hostname + '/';
  486. if ( url.indexOf( currentDomain ) !== 0 ) {
  487. return true;
  488. }
  489. if ( ! isNodeHidden( dialog ) ) {
  490. closeEmailDialog();
  491. return;
  492. }
  493. removeNode( document.querySelector( '#sharing_email .response' ) );
  494. var form = document.querySelector( '#sharing_email form' );
  495. showNode( form );
  496. form.querySelector( 'input[type=submit]' ).removeAttribute( 'disabled' );
  497. showNode( form.querySelector( 'a.sharing_cancel' ) );
  498. // Reset reCATPCHA if exists.
  499. if (
  500. 'object' === typeof grecaptcha &&
  501. 'function' === typeof grecaptcha.reset &&
  502. window.___grecaptcha_cfg.count
  503. ) {
  504. grecaptcha.reset();
  505. }
  506. // Show dialog
  507. var rect = emailButton.getBoundingClientRect();
  508. var scrollLeft = window.pageXOffset || document.documentElement.scrollLeft || 0;
  509. var scrollTop = window.pageYOffset || document.documentElement.scrollTop || 0;
  510. dialog.style.left = scrollLeft + rect.left + 'px';
  511. dialog.style.top = scrollTop + rect.top + rect.height + 'px';
  512. showNode( dialog );
  513. // Close all open More Button dialogs.
  514. MoreButton.closeAll();
  515. } );
  516. // Hook up other buttons
  517. dialog.querySelector( 'a.sharing_cancel' ).addEventListener( 'click', function ( event ) {
  518. event.preventDefault();
  519. event.stopPropagation();
  520. hideNode( dialog.querySelector( '.errors' ) );
  521. hideNode( dialog );
  522. hideNode( document.querySelector( '#sharing_background' ) );
  523. } );
  524. var submitButton = dialog.querySelector( 'input[type=submit]' );
  525. submitButton.addEventListener( 'click', function ( event ) {
  526. event.preventDefault();
  527. event.stopPropagation();
  528. var form = closest( submitButton, 'form' );
  529. var source_email_input = form.querySelector( 'input[name=source_email]' );
  530. var target_email_input = form.querySelector( 'input[name=target_email]' );
  531. // Disable buttons + enable loading icon
  532. submitButton.setAttribute( 'disabled', true );
  533. hideNode( form.querySelector( 'a.sharing_cancel' ) );
  534. forEachNode( form.querySelectorAll( 'img.loading' ), function ( img ) {
  535. showNode( img );
  536. } );
  537. hideNode( form.querySelector( '.errors' ) );
  538. forEachNode( form.querySelectorAll( '.error' ), function ( node ) {
  539. node.classList.remove( 'error' );
  540. } );
  541. if ( ! shareIsEmail( source_email_input.value ) ) {
  542. source_email_input.classList.add( 'error' );
  543. }
  544. if ( ! shareIsEmail( target_email_input.value ) ) {
  545. target_email_input.classList.add( 'error' );
  546. }
  547. if ( ! form.querySelector( '.error' ) ) {
  548. // Encode form data. This would be much easier if we could rely on URLSearchParams...
  549. var params = [];
  550. for ( var i = 0; i < form.elements.length; i++ ) {
  551. if ( form.elements[ i ].name ) {
  552. // Encode each form element into a URI-compatible string.
  553. var encoded =
  554. encodeURIComponent( form.elements[ i ].name ) +
  555. '=' +
  556. encodeURIComponent( form.elements[ i ].value );
  557. // In x-www-form-urlencoded, spaces should be `+`, not `%20`.
  558. params.push( encoded.replace( '%20', '+' ) );
  559. }
  560. }
  561. var data = params.join( '&' );
  562. // AJAX send the form
  563. var request = new XMLHttpRequest();
  564. request.open( 'POST', emailButton.getAttribute( 'href' ), true );
  565. request.setRequestHeader(
  566. 'Content-Type',
  567. 'application/x-www-form-urlencoded; charset=UTF-8'
  568. );
  569. request.setRequestHeader( 'x-requested-with', 'XMLHttpRequest' );
  570. request.onreadystatechange = function () {
  571. if ( this.readyState === XMLHttpRequest.DONE && this.status === 200 ) {
  572. forEachNode( form.querySelectorAll( 'img.loading' ), function ( img ) {
  573. hideNode( img );
  574. } );
  575. if ( this.response === '1' || this.response === '2' || this.response === '3' ) {
  576. showNode( dialog.querySelector( '.errors-' + this.response ) );
  577. dialog.querySelector( 'input[type=submit]' ).removeAttribute( 'disabled' );
  578. showNode( dialog.querySelector( 'a.sharing_cancel' ) );
  579. if ( typeof grecaptcha === 'object' && typeof grecaptcha.reset === 'function' ) {
  580. grecaptcha.reset();
  581. }
  582. } else {
  583. hideNode( form );
  584. var temp = document.createElement( 'div' );
  585. temp.innerHTML = this.response;
  586. dialog.appendChild( temp.firstChild );
  587. showNode( dialog.querySelector( 'a.sharing_cancel' ) );
  588. var closeButton = dialog.querySelector( '.response a.sharing_cancel' );
  589. if ( closeButton ) {
  590. closeButton.addEventListener( 'click', function ( event ) {
  591. event.preventDefault();
  592. event.stopPropagation();
  593. closeEmailDialog();
  594. hideNode( document.querySelector( '#sharing_background' ) );
  595. } );
  596. }
  597. }
  598. }
  599. };
  600. request.send( data );
  601. return;
  602. }
  603. forEachNode( dialog.querySelectorAll( 'img.loading' ), function ( img ) {
  604. hideNode( img );
  605. } );
  606. submitButton.removeAttribute( 'disabled' );
  607. showNode( dialog.querySelector( 'a.sharing_cancel' ) );
  608. forEachNode( dialog.querySelectorAll( '.errors-1' ), function ( error ) {
  609. showNode( error );
  610. } );
  611. } );
  612. } );
  613. } );
  614. forEachNode(
  615. document.querySelectorAll( 'li.share-email, li.share-custom a.sharing-anchor' ),
  616. function ( node ) {
  617. node.classList.add( 'share-service-visible' );
  618. }
  619. );
  620. }
  621. } )();