import Cookies from '../vendor/jscookie';
import ImageContainer from './ImageContainer';
import customEvent from '../utils/customEvent';
import SophiTagWrapper from './SophiTagWrapper';
import '../polyfills/Fetch';
/**
* Sets up region selection
*
* @example
* <section data-region-picker>{{region menu here}}</section>
*
* @module RegionPicker
* @prop {object} selectors - DOM query selectors
* @prop {object} states - CSS classes
*/
const RegionPicker = {
selectors: {
main: '[data-region-picker]',
posts: '.c-posts',
postItem: '.c-posts__item:not(.c-posts__ad)',
regionLinks: '[data-region-link]',
linkLabel: '.c-link__label',
},
states: {
shimmer: 'c-shimmer',
skeleton: 'c-posts--skeleton',
animateIn: 'animate-fadeIn',
hidden: 'is-hidden',
},
cookieNames: {
geo: '_wpcom_geo',
geoExp: '_wpcom_geo_exp',
},
currentRegion: false,
cookieCheck: 0,
/**
* Search for any region selection modules and bind click events
* @method init
*/
init() {
const $regionPickers = document.querySelectorAll( this.selectors.main );
let cookiedRegion = Cookies.get( this.cookieNames.geo );
if ( cookiedRegion ) {
cookiedRegion = cookiedRegion.replace( 'gnca-', '' );
}
/* global gnca_settings */
/* eslint-disable camelcase */
const detectedRegion = gnca_settings && gnca_settings.user_region ? gnca_settings.user_region.replace( 'gnca-', '' ) : 'national';
/* eslint-enable camelcase */
this.currentRegion = detectedRegion || cookiedRegion;
this.saveRegion( this.currentRegion );
[].forEach.call( $regionPickers, ( $regionPicker ) => {
const targetSelector = $regionPicker.dataset.regionPickerTarget;
const $target = document.querySelector( targetSelector );
const clickListener = this.handleClick.bind( this, $target, $regionPicker.getAttribute( 'id' ) );
$regionPicker.addEventListener( 'click', clickListener );
});
},
/**
* Verify that the user has clicked on a link within the region selection module
* Then pass along the region name that was clicked on
* @method handleClick
* @param {element} $changeTarget - update content in $changeTarget based on new region
* @param {string} pickerId - picker id
* @param {event} event
*/
handleClick( $changeTarget, pickerId, event ) {
let $link = false;
if ( 'a' === event.target.nodeName.toLowerCase() ) {
$link = event.target;
} else if ( 'a' === event.target.parentNode.nodeName.toLowerCase() ) {
$link = event.target.parentNode;
}
if ( $link && 'false' !== $link.dataset.regionChange ) {
// don't follow anchor tag, we will do this via JS after resetting the cookie
event.preventDefault();
let region = $link.innerText;
region = this.parseRegion( region );
this.changeRegion( region );
this.trackRegionChange( region, event );
if ( $changeTarget ) {
const $toggleButton = document.querySelector( `[data-expand="#${pickerId}"]` );
$toggleButton.click();
this.changeRegionContent( region, $changeTarget );
} else {
this.postRegion( region, $link.getAttribute( 'href' ) );
}
}
},
/**
* Slugify the region name
* @method parseRegion
*/
parseRegion( region ) {
return region.trim().toLowerCase().replace( ' ', '-' );
},
/**
* Update the user's region cookie
* @method changeRegion
*/
changeRegion( region ) {
this.saveRegion( region );
customEvent.fire( window, customEvent.REGION_CHANGE, {
region,
});
},
/**
* Save region cookie - save two cookies in staggered expiry to keep region cookie alive
* to prevent infinite geo detection loop.
*
* @method saveRegion
*/
saveRegion( region ) {
Cookies.set( this.cookieNames.geo, `gnca-${region}`, { expires: 90, path: '/' });
Cookies.set( this.cookieNames.geoExp, `gnca-${region}`, { expires: 60, path: '/' });
},
/**
* Send region as a POST request for vary cache
*
* @method postRegion
* @param {string} region - region to remember
* @param {string} redirectUrl - optional URL to redirect to when post completed
*/
postRegion( region, redirectUrl = '' ) {
if ( ! redirectUrl ) {
const xhttp = new XMLHttpRequest();
xhttp.open( 'POST', redirectUrl, true );
xhttp.setRequestHeader( 'Content-type', 'application/x-www-form-urlencoded' );
xhttp.send( `gnca-region=gnca-${region}` );
} else {
// Create a temporary form for redirecting to the target URL as a POST request
const template = `
<form id="regionPickerForm" class="is-hidden" action="${redirectUrl}" method="POST">
<input type="hidden" name="gnca-region" value="gnca-${region}"/>
</form>
`;
const $form = document.createElement( 'div' );
$form.innerHTML = template;
document.querySelector( 'body' ).appendChild( $form );
this.redirectUser( region );
}
},
/**
* Redirect user to the target page once region cookie is set
*/
redirectUser( region ) {
const $form = document.querySelector( '#regionPickerForm' );
// Ensure cookie set before leaving the page
clearInterval( this.cookieCheck );
this.cookieCheck = setInterval( () => {
if ( `gnca-${region}` === Cookies.get( this.cookieNames.geo ) ) {
$form.submit();
clearInterval( this.cookieCheck );
}
}, 100 );
// Timeout when cookie still not set within 1 second.
setTimeout( () => {
$form.submit();
clearInterval( this.cookieCheck );
}, 1000 );
},
/**
* Make an ajax request to get content for selected region
*
* @method changeRegionContent
* @param {string} region - selected region
* @param {element} $target - target element where new content should be added
*/
changeRegionContent( region, $target ) {
const $posts = $target.querySelector( this.selectors.posts );
const keys = Object.keys( $posts.dataset );
const params = {};
[].forEach.call( keys, ( k ) => {
if ( /\{.+\}/.test( $posts.dataset[k]) ) {
params[k] = JSON.parse( $posts.dataset[k]);
} else {
params[k] = $posts.dataset[k];
}
});
params.region = `gnca-${region}`;
// Set loading style
$posts.classList.add( this.states.skeleton );
$posts.classList.add( this.states.shimmer );
$posts.classList.remove( this.states.animateIn );
$posts.dataset.sophiFeature = `local-news-${region}`;
// Region name
let regionName = region.replace( '-', ' ' );
regionName = regionName.length > 2 ? regionName.replace( /(^|\s)\S/g, t => t.toUpperCase() ) : regionName.toUpperCase();
const $regionLinks = document.querySelectorAll( this.selectors.regionLinks );
const link = `/${region}`;
let overrideLink = link;
/* global gnca_settings */
/* eslint-disable camelcase */
// check for override link
if ( gnca_settings && gnca_settings.override_links ) {
if ( gnca_settings.override_links[link]) {
overrideLink = gnca_settings.override_links[link];
}
}
/* eslint-enable camelcase */
[].forEach.call( $regionLinks, ( $link ) => {
$link.setAttribute( 'href', $link.dataset.regionLinkOverride ? overrideLink : link );
if ( Object.keys( $link.dataset ).indexOf( 'regionLabel' ) >= 0 ) {
const $label = $link.querySelector( this.selectors.linkLabel );
$label.textContent = regionName; /* eslint-disable-line no-param-reassign */
$link.setAttribute( 'title', regionName );
}
});
this.postRegion( region );
fetch( `/gnca-ajax-redesign/${params.action}/${encodeURI( JSON.stringify( params ) )}` )
.then( response => response.text() )
.then( ( content ) => {
const $htmlContent = new DOMParser().parseFromString( content, 'text/html' );
const $listItems = $htmlContent.querySelectorAll( this.selectors.postItem );
const $oldListItems = $posts.querySelectorAll( this.selectors.postItem );
const numOldItems = $oldListItems.length;
const numNewItems = $listItems.length;
if ( numNewItems > numOldItems ) {
[].forEach.call( $listItems, ( $newItem, index ) => {
const $oldItem = $oldListItems[index];
if ( $oldItem ) {
// Only replace the item if it isn't a hidden ad fallback item
if ( ! $oldItem.dataset.adFallback
|| ! $oldItem.classList.contains( this.states.hidden ) ) {
$posts.replaceChild( $newItem, $oldItem );
}
} else {
$posts.appendChild( $newItem );
}
});
} else {
[].forEach.call( $oldListItems, ( $oldItem, index ) => {
if ( $listItems[index]) {
// Only replace the item if it isn't a hidden ad fallback item
if ( ! $oldItem.dataset.adFallback
|| ! $oldItem.classList.contains( this.states.hidden ) ) {
$posts.replaceChild( $listItems[index], $oldItem );
}
} else {
$oldItem.classList.add( this.states.hidden );
}
});
}
ImageContainer.init( $posts.querySelectorAll( ImageContainer.selector ) );
$posts.classList.remove( this.states.skeleton );
$posts.classList.remove( this.states.shimmer );
$posts.classList.add( this.states.animateIn );
SophiTagWrapper.refreshTracking();
});
},
/**
* Track region change in Google and Adobe analytics
* @method trackRegionChange
*/
trackRegionChange( region, event ) {
/* global gn_analytics */
/* eslint-disable camelcase */
if ( 'undefined' !== typeof ( gn_analytics ) && 'undefined' !== typeof ( gn_analytics.Analytics ) ) {
const trackAction = `${this.currentRegion} > ${region}`;
gn_analytics.Analytics.track(['ga', 'adobe'], {
eventType: 'Region Change',
action: trackAction,
target: event.currentTarget,
});
}
/* eslint-enable camelcase */
},
};
export default RegionPicker;