import delegateEvent from '../utils/delegateEvent';
import dynamicElement from '../utils/dynamicElement';
/**
* Sets social share elements in article, in main nav and in submenu popup
*
* @module SocialShare
* @prop {Object} socialShare - DOM reference to the navigation bar
* @prop {String} selector - DOM selector for social share attributes
* @prop {String} commentsSelector - DOM selector for comments section
* @prop {String} articleUrl - Article URL from meta tags
* @prop {String} articleUrlEncoded - encoded article URL from meta tags
* @prop {String} articleTitle - Article title from meta tags
* @prop {String} articleTitleEncoded - encoded article title from meta tags
* @prop {String} navbarLongformClass - DOM selector for navbar on longform template
* @prop {String} longformHeaderClass - DOM selector for header on longform template
* @prop {String} interactiveHeaderClass - DOM selector for header on interactive template
* @prop {Object} $notificationContainer - DOM reference to notification container
* @prop {String} copiedLinkSelector - DOM selector for 'Link Copied!' popup
*/
const SocialShare = {
selector: 'data-socialshare',
commentsSelector: '.l-main__comments',
articleUrl: '',
articleUrlEncoded: '',
articleTitle: '',
articleTitleEncoded: '',
navbarLongformClass: 'l-longform-navbar',
longformHeaderClass: '.l-longform-header',
interactiveHeaderClass: '.l-interactive-header',
$notificationContainer: document.querySelector( '#notification' ),
copiedLinkSelector: '.c-socialShare__copiedLink',
/* global gn_analytics */
/**
* Initializes social share elements on the page
*
* @method init
*/
init() {
const $elems = document.querySelectorAll( '.c-socialShare' );
// Set article url
const articleUrl = document.querySelector( 'link[rel="canonical"]' );
this.articleUrl = articleUrl ? articleUrl.getAttribute( 'href' ) : null;
this.articleUrlEncoded = encodeURIComponent( this.articleUrl );
// Set article title
this.articleTitle = '';
const $title = document.querySelector( 'meta[property="og:title"]' );
if ( $title && $title.getAttribute( 'content' ) ) {
// Use openGraph Title
this.articleTitle = $title.getAttribute( 'content' );
} else {
// Use page title
this.articleTitle = document.title;
}
this.articleTitleEncoded = encodeURIComponent( this.articleTitle );
// If there is no 'Social Share' elements on the page, don't run
if ( 0 === $elems.length ) {
return;
}
// Create each social share block
[].forEach.call( $elems, ( $item, $index ) => {
this.setup( $item );
this.setId( $item, $index );
});
// watch for dynamically added social share
delegateEvent( dynamicElement.selector, 'added', '.c-socialShare', ( event, $socials ) => {
event.preventDefault();
event.stopPropagation();
[].forEach.call( $socials, ( $social ) => {
this.setup( $social );
this.setId( $social, document.querySelectorAll( '.c-socialShare' ).length );
});
}, false, true );
},
/**
* Sets up functionality for each social share container.
* Assigns 'title' and 'url' attributes to container, bind click event
*
* @method setup
* @param {Element} $item - Social share container element
*/
setup( $item ) {
// Loop through all 'Social Share' containers
const $providerButtons = $item.querySelectorAll( '.c-socialShare__item' );
[].forEach.call( $providerButtons, ( $button ) => {
const provider = $button.getAttribute( `${this.selector}-provider` );
// Add href attribute to <a> elements, skip <button> elements
const $buttonLink = $button.querySelector( 'a' );
if ( $buttonLink ) {
const url = this.getLink( provider );
$buttonLink.setAttribute( 'href', url );
// Add target attribute
const target = 'print' === provider || 'more' === provider ? '_self' : '_blank';
if ( 'comments' !== provider && 'copy' !== provider ) {
$buttonLink.setAttribute( 'target', target );
}
if ( 'comments' !== provider && 'copy' !== provider && 'print' !== provider && 'more' !== provider ) {
$buttonLink.setAttribute( 'rel', 'noreferrer' );
}
}
// Add event listener for clicking the buttons
$button.addEventListener( 'click', ( event ) => {
SocialShare.handleClick( event );
});
});
},
/**
* Sets id attribute for each social share container
*
* @method setId
* @param {Element} $item - Social share container element
* @param {Integer} index - Element index
*/
setId( $item, index ) {
$item.setAttribute( `${this.selector}-id`, index );
},
/**
* Handles click event when social share button is clicked
*
* @method handleClick
* @param {Object} ev - Social share button click event
*/
handleClick( ev ) {
const $target = ev.currentTarget;
const provider = $target.getAttribute( `${this.selector}-provider` );
// Always blur :)
$target.blur();
if ( '' === $target.getAttribute( 'href' ) ) {
ev.preventDefault();
}
// Some providers need you to do some stuff
if ( 'print' === provider ) { // 'Print' button is clicked
window.print();
} else if ( 'copy' === provider ) { // 'Copy' button is clicked
this.handleCopyClick( ev );
} else if ( 'linkedin' === provider ) { // 'Linkedin' button is clicked
this.handleLinkedinClick( ev, this.articleUrl );
}
this.trackClick( $target );
},
/**
* Handles click event for 'Copy' button in social share
*
* @method handleCopyClick
* @param {Object} ev - Social share 'Comments' button click event
*/
handleCopyClick( ev ) {
ev.preventDefault();
// create textarea and add article url as value
const hiddenTextArea = document.createElement( 'textarea' );
hiddenTextArea.value = this.articleUrl;
// hide textarea (note: can't use display:none, otherwise copying does not work)
hiddenTextArea.style.height = '1px';
hiddenTextArea.style.width = '1px';
hiddenTextArea.style.opacity = 0;
hiddenTextArea.setAttribute( 'readonly', '' );
document.body.appendChild( hiddenTextArea );
// copy textarea value
hiddenTextArea.select();
document.execCommand( 'copy' );
// remove textarea after link is copied
document.body.removeChild( hiddenTextArea );
// Only add "Link copied!" notification when one is not already in place
if ( ! document.querySelector( this.copiedLinkSelector ) ) {
// Add 'Link copied!' popup briefly below sticky notification bar
let $textCopiedDiv = document.createElement( 'div' );
const copiedText = document.createTextNode( 'Link copied!' );
$textCopiedDiv.appendChild( copiedText );
$textCopiedDiv.classList.add( 'c-socialShare__copiedLink' );
// Create top offset for 'Link Copied!' based on admin bar, navbar and notification height
let anchorElement = null;
const templateType = ev.currentTarget.parentNode.parentNode.dataset.socialshareTemplateType;
switch ( templateType ) {
case 'longform':
anchorElement = document.querySelector( this.longformHeaderClass );
break;
case 'interactive':
anchorElement = document.querySelector( this.interactiveHeaderClass );
break;
case 'gallery':
anchorElement = document.querySelector( '.c-gallery' );
break;
default:
anchorElement = this.$notificationContainer;
break;
}
$textCopiedDiv.classList.add( `c-socialShare__copiedLink--${templateType}` );
if ( anchorElement && $textCopiedDiv ) {
anchorElement.insertAdjacentElement( 'afterend', $textCopiedDiv );
$textCopiedDiv = document.querySelector( this.copiedLinkSelector );
$textCopiedDiv.classList.add( 'is-faded-in' );
$textCopiedDiv.classList.add( 'is-animated' );
// After 4 seconds start fading out the popup (transition lasts .3s)
setTimeout( () => {
$textCopiedDiv.classList.remove( 'is-faded-in' );
$textCopiedDiv.classList.remove( 'is-animated' );
$textCopiedDiv.classList.add( 'is-faded-out' );
$textCopiedDiv.classList.add( 'is-animated' );
}, 4000 );
// Then after 4.3 seconds remove it from the page
setTimeout( () => {
$textCopiedDiv.remove();
}, 4300 );
}
}
},
handleLinkedinClick( ev, url ) {
ev.preventDefault();
window.open( `https://www.linkedin.com/shareArticle?mini=true&url=${encodeURIComponent( url )}`, '_blank', 'toolbar=no,scrollbars=0,resizable=no,fullscreen=no,top=50,left=50,width=550,height=600' );
},
/**
* Gets href attribute for each social share button
*
* @method getLink
* @param {String} provider - Social share provider name
*/
getLink( provider ) {
const keyEncode = this.articleUrlEncoded.replace( '.', '%2E' );
const media = this.getMedia();
let link = '';
switch ( provider ) {
case 'facebook':
link = `https://www.facebook.com/sharer/sharer.php?u=${keyEncode}`;
break;
case 'print':
link = keyEncode.replace( '%2Fnews%2F', '%2Fprint%2F' );
break;
case 'twitter':
link = `https://twitter.com/intent/tweet?text=${this.articleTitleEncoded}%20${keyEncode}`;
break;
case 'email':
link = `mailto:?Subject=${this.articleTitleEncoded}&body=%0D%0A${this.articleTitleEncoded}%0D%0A${keyEncode}%0D%0A%0D%0A%0D%0A`;
break;
case 'whatsapp':
link = `whatsapp://send?text=${this.articleTitleEncoded}%20-%20${keyEncode}`;
break;
case 'reddit':
link = `https://www.reddit.com/submit?url=${keyEncode}&title=${this.articleTitleEncoded}`;
break;
case 'pinterest':
link = `https://pinterest.com/pin/create/button/?url=${keyEncode}&media=${media}&description=${this.articleTitleEncoded}`;
break;
case 'flipboard':
link = `https://share.flipboard.com/bookmarklet/popout?v=2&title=%22${this.articleTitleEncoded}%22&url=${keyEncode}`;
break;
default:
link = '';
break;
}
return link;
},
/**
* Gets social media image from <meta property="og:image">
*
* @method getMedia
* @return {String} - URL of the og:image.
*/
getMedia() {
const $ogImg = document.querySelector( 'meta[property="og:image"]' );
if ( ! $ogImg ) {
return '';
}
return encodeURIComponent( $ogImg.getAttribute( 'content' ) );
},
/**
* Analytics tracking for social share.
*
* @param {HTMLElement} $elem - HTML Element that triggered the click.
*
* @method trackClick
*/
trackClick( $elem ) {
/* eslint-disable camelcase */
if ( 'undefined' !== typeof ( gn_analytics ) ) {
// Set a small timeout to make sure the correct aria-expand state is read.
setTimeout( () => {
let action = $elem.dataset.socialshareProvider;
if ( 'more' === action && 'false' === $elem.getAttribute( 'aria-expanded' ) ) {
action = 'close';
}
const trackingData = {
'content.share': `share|news|${action}`,
};
gn_analytics.Analytics.track(['adobe'], {
eventType: 'click',
action: `social share | ${action}`,
data: trackingData,
target: $elem,
});
}, 500 );
}
/* eslint-enable camelcase */
},
};
export default SocialShare;