ArticleTemplates/assets/js/bootstraps/common.js (654 lines of code) (raw):
import { attach } from 'fastclick';
import { render } from '@guardian/fence';
import {
getClosestParentWithTag,
debounce,
getElementOffset,
signalDevice,
isElementPartiallyInViewport,
isDarkMode
} from 'modules/util';
import { init as initComments } from 'modules/comments';
import { init as initCards } from 'modules/cards';
import { init as initMoreTags } from 'modules/more-tags';
import { init as initRichLinks } from 'modules/rich-links';
import { init as initAB } from 'modules/experiments/ab';
import { initMpuPoller } from 'modules/ads';
import { init as initHttp } from 'modules/http';
import { init as ophanIframeInit } from 'modules/ophan-iframe';
import { init as myGuardianFollowInit } from 'modules/myGuardianFollowInit';
let trackCommentContainerView = true;
function init(liveBlog = false) {
if ((GU && GU.opts && GU.opts.platform === 'android') || instagramHeader()) {
// polyfill to remove click delays on browsers with touch
attach(document.body);
}
if (!liveBlog) {
formatImages();
}
figcaptionToggle();
articleContentType();
insertTags();
videoPositioning();
initComments();
initCards();
loadEmbeds();
loadInteractives();
setupOfflineSwitch();
setupAlertSwitch();
setupTagFollowSwitch();
setupFontSizing();
setupLineHeightSizing();
setupGetArticleHeight();
showTabs();
setGlobalObject(window);
fixSeries();
advertorialUpdates();
setupTracking();
initAB();
initRichLinks();
initHttp();
setupForms();
darkModeSetup();
ophanIframeInit();
myGuardianFollowInit();
if (!document.body.classList.contains('no-ready')) {
if (!liveBlog) {
signalDevice('ready');
}
}
}
function darkModeSetup() {
if (isDarkMode()) {
document.getElementsByTagName('html')[0].style.background = "transparent";
}
}
function formatImages(images) {
let figure;
const figures = [];
let i;
let image;
if (!images) {
images = document.querySelectorAll('.article img');
}
for (i = 0; i < images.length; i++) {
image = images[i];
figure = getClosestParentWithTag(image, 'figure');
if (figure) {
figures.push(figure);
}
}
formatFigures(figures);
if (GU.opts.isOffline) {
checkAvailabilityOfImages(images);
}
}
function formatFigures(figures) {
let i;
let figure;
for (i = 0; i < figures.length; i++) {
figure = figures[i];
if (figure.classList.contains('element-image')) {
formatElementImageFigure(figure);
}
}
}
function formatElementImageFigure(figure) {
const caption = figure.getElementsByClassName('element-image__caption')[0];
const captionIcon = figure.getElementsByClassName('figure__caption__icon')[0];
let desiredImageHeight;
const isThumbnail = figure.classList.contains('element--thumbnail');
const imageClass = isThumbnail && caption ? 'figure--thumbnail-with-caption' : (isThumbnail ? 'figure--thumbnail' : 'figure-wide');
const imageOrLinkedImage = figure.children[0];
let imageWrapper;
figure.classList.add(imageClass);
if (isThumbnail) {
formatElementThumbnailFigure(figure);
}
if (imageOrLinkedImage &&
!imageOrLinkedImage.classList.contains('figure__inner')) {
imageWrapper = document.createElement('div');
imageWrapper.classList.add('figure__inner');
imageWrapper.appendChild(imageOrLinkedImage);
figure.insertBefore(imageWrapper, figure.firstChild);
if (isThumbnail || imageClass === 'figure-wide') {
desiredImageHeight = getDesiredImageHeight(figure);
if (desiredImageHeight) {
imageWrapper.style.height = `${desiredImageHeight}px`;
}
window.addEventListener('resize', debounce(resizeImageWrapper.bind(null, imageWrapper, figure), 100));
}
}
if (caption && !captionIcon) {
caption.innerHTML = `<span data-icon="" class="figure__caption__icon" aria-hidden="true"></span>${caption.innerHTML}`;
}
}
function formatElementThumbnailFigure(figure) {
const thumbnailImage = figure.getElementsByTagName('img')[0];
const isPortrait = parseInt(thumbnailImage.getAttribute('height'), 10) > parseInt(thumbnailImage.getAttribute('width'), 10);
if (isPortrait) {
figure.classList.add('portrait-thumbnail');
} else {
figure.classList.add('landscape-thumbnail');
}
}
function checkAvailabilityOfImages(images) {
let i;
let dummyImage;
let image;
for (i = 0; i < images.length; i++) {
// if image doesn't load replace with placeholder or hide
image = images[i];
dummyImage = new Image();
dummyImage.onerror = hideImageOnError.bind(null, image);
dummyImage.src = image.getAttribute('src');
}
}
function hideImageOnError(image) {
let figure;
let innerElem;
if (image.parentNode.classList.contains('element-image-inner')) {
image.style.display = 'none';
} else {
figure = getClosestParentWithTag(image, 'figure');
innerElem = document.createElement('div');
innerElem.classList.add('element-image-inner');
innerElem.setAttribute('height', image.getAttribute('height'));
innerElem.setAttribute('width', image.getAttribute('width'));
image.parentNode.replaceChild(innerElem, image);
}
}
function getDesiredImageHeight(figure) {
const elem = figure.getElementsByTagName('img')[0] || figure.getElementsByClassName('element-image-inner')[0];
const imgWidth = elem.getAttribute('width');
const imgHeight = elem.getAttribute('height');
const figInner = figure.getElementsByClassName('figure__inner')[0];
const figInnerWidth = getElementOffset(figInner).width;
const scale = figInnerWidth / imgWidth;
const newHeight = imgHeight * scale;
return Math.round(newHeight);
}
function resizeImageWrapper(imageWrapper, figure) {
imageWrapper.style.height = `${getDesiredImageHeight(figure)}px`;
}
function figcaptionToggle() {
let toggleCaptionVisibilityBound;
const mainMediaCaption = document.getElementsByClassName('main-media__caption__icon')[0];
const mainMediaCaptionText = document.getElementsByClassName('main-media__caption__text')[0];
if (mainMediaCaption && mainMediaCaptionText) {
toggleCaptionVisibilityBound = toggleCaptionVisibility.bind(null, mainMediaCaptionText);
mainMediaCaption.addEventListener('click', toggleCaptionVisibilityBound);
}
}
function toggleCaptionVisibility(mainMediaCaptionText) {
const className = 'is-visible';
if (mainMediaCaptionText.classList.contains(className)) {
mainMediaCaptionText.classList.remove(className);
} else {
mainMediaCaptionText.classList.add(className);
}
}
function articleContentType() {
let i;
let proseElem;
const proseElems = document.querySelectorAll('.article__body > .prose');
for (i = 0; i < proseElems.length; i++) {
proseElem = proseElems[i];
if (proseElem.querySelectorAll('.figure--thumbnail:not(.figure--thumbnail-with-caption)').length) {
proseElem.classList.add('prose--is-panel');
}
}
}
function insertTags() {
window.articleTagInserter = articleTagInserter;
window.applyNativeFunctionCall('articleTagInserter');
}
function articleTagInserter(html) {
const tagsList = document.querySelector('.tags .inline-list');
if (tagsList) {
tagsList.innerHTML += html;
initMoreTags();
}
}
function videoPositioning() {
window.videoPositioning = reportVideoPositions;
window.applyNativeFunctionCall('videoPositioning');
}
function reportVideoPositions() {
let i;
let mainMediaElem;
let mainMediaElemOffset;
const mainMediaElems = document.getElementsByClassName('video-URL');
for (i = 0; i < mainMediaElems.length; i++) {
mainMediaElem = mainMediaElems[i];
mainMediaElemOffset = getElementOffset(mainMediaElem);
if (mainMediaElemOffset.height && mainMediaElemOffset.width) {
window.GuardianJSInterface.videoPosition(mainMediaElemOffset.left, mainMediaElemOffset.top, mainMediaElemOffset.width, mainMediaElem.getAttribute('href'));
}
}
setTimeout(videoPositioningPoller.bind(null, document.body.offsetHeight), 500);
}
function videoPositioningPoller(pageHeight) {
const newHeight = document.body.offsetHeight;
if (pageHeight !== newHeight) {
reportVideoPositions();
} else {
setTimeout(videoPositioningPoller.bind(null, newHeight), 500);
}
}
function loadEmbeds() {
let i;
let fenceElems = document.querySelectorAll('iframe.fenced');
fixVineWidth();
fixEmbedHeights();
listenForEmailEmbedIFrameResize();
for (i = 0; i < fenceElems.length; i++) {
render(fenceElems[i]);
}
}
function fixVineWidth() {
let i;
let iframe;
let iframes;
let size;
let srcdoc;
iframes = document.querySelectorAll('iframe[srcdoc*="https://vine.co"]:not([data-vine-fixed])');
for (i = 0; i < iframes.length; i++) {
iframe = iframes[i];
size = iframe.parentNode.clientWidth;
srcdoc = iframe.getAttribute('srcdoc');
srcdoc = srcdoc.replace(/width="[\d]+"/,`width="${size}"`);
srcdoc = srcdoc.replace(/height="[\d]+"/,`height="${size}"`);
iframe.setAttribute('srcdoc', srcdoc);
iframe.dataset.vineFixed = true;
}
}
function fixEmbedHeights() {
let i;
let iframe;
const iframes = document.querySelectorAll('.element-embed .email-sub__iframe');
for (i = 0; i < iframes.length; i++) {
iframe = iframes[i];
iframe.setAttribute('height', '50px');
iframe.style.marginTop = '12px';
}
}
function listenForEmailEmbedIFrameResize() {
const allowedOrigins = ['https://www.theguardian.com'];
const allIframes = [].slice.call(
document.querySelectorAll('.email-sub__iframe')
);
window.addEventListener('message', (event) => {
if (!allowedOrigins.includes(event.origin)) return;
const iframes = allIframes.filter((i) => {
try {
if (i.contentWindow && event.source) return i.contentWindow === event.source;
} catch (e) {
return false;
}
return false;
});
if (iframes.length !== 0) {
try {
const message = JSON.parse(event.data);
switch (message.type) {
case 'set-height': {
const value = parseInt(message.value);
if (!Number.isInteger(value)) return;
iframes.forEach((iframe) => {
iframe.height = `${value}`;
});
break;
}
default:
}
// eslint-disable-next-line no-empty
} catch (e) {}
}
});
}
function loadInteractives(force) {
let i;
let bootUrl;
let interactive;
let interactives;
if ((!GU.opts.isOffline || force) && navigator.onLine) {
interactives = document.querySelectorAll('figure.interactive');
for (i = 0; i < interactives.length; i++) {
interactive = interactives[i];
bootUrl = interactive.dataset.interactive;
interactive.classList.add('interactive--loading');
curl([bootUrl], startInteractive.bind(null, interactive), showOfflineInteractiveIcons);
}
} else {
showOfflineInteractiveIcons();
if (!bootUrl) {
const reload = document.getElementsByClassName("interactive--offline--icon--reload")[0];
if (reload) {
reload.style.display = "none";
}
}
}
}
function startInteractive(interactiveElem, interactiveObj) {
if (interactiveObj && interactiveObj.boot) {
interactiveElem.classList.remove('interactive--offline');
interactiveObj.boot(interactiveElem, document.body);
}
}
function showOfflineInteractiveIcons() {
let i;
let interactive;
let reloadElem;
let loadingElem;
let interactives;
interactives = document.querySelectorAll('figure.interactive:not(.interactive--offline)');
for (i = 0; i < interactives.length; i++) {
interactive = interactives[i];
interactive.classList.add('interactive--offline');
reloadElem = document.createElement('div');
reloadElem.classList.add('interactive--offline--icon');
reloadElem.classList.add('interactive--offline--icon--reload');
reloadElem.addEventListener('click', loadInteractives.bind(null, true));
interactive.appendChild(reloadElem);
loadingElem = document.createElement('div');
loadingElem.classList.add('interactive--offline--icon');
loadingElem.classList.add('interactive--offline--icon--loading');
interactive.appendChild(loadingElem);
}
interactives = document.querySelectorAll('figure.interactive.interactive--loading');
for (i = 0; i < interactives.length; i++) {
interactive = interactives[i];
interactive.classList.remove('interactive--loading');
}
}
function setupOfflineSwitch() {
// Called by native code
window.offlineSwitch = offlineSwitch;
}
function offlineSwitch() {
GU.opts.isOffline = true;
document.body.classList.add('offline');
}
function setupAlertSwitch() {
// Called by native code
window.alertSwitch = alertSwitch;
}
function setupTagFollowSwitch() {
if (GU.opts.myGuardianEnabled) {
window.followTagSwitch = followTagSwitch;
}
}
function followTagSwitch(following, followid) {
let i;
let followObject;
const followObjects = document.querySelectorAll(`[data-follow-tag-id="${followid}"]`);
for (i = 0; i < followObjects.length; i++) {
followObject = followObjects[i];
if (parseInt(following, 10) === 1) {
followObject.classList.add('followingTag');
} else {
followObject.classList.remove('followingTag');
}
}
}
function alertSwitch(following, followid) {
let i;
let followObject;
const followObjects = document.querySelectorAll(`[data-follow-alert-id="${followid}"]`);
for (i = 0; i < followObjects.length; i++) {
followObject = followObjects[i];
if (parseInt(following, 10) === 1) {
followObject.classList.add('following');
} else {
followObject.classList.remove('following');
}
}
}
function setupFontSizing() {
// Called by native code
window.fontResize = fontResize;
}
function setupLineHeightSizing() {
// Called by native code
window.lineHeightResize = lineHeightResize;
}
function fontResize(current, replacement) {
const replacementStr = replacement;
const replacementInt = replacementStr.split('-');
document.body.classList.remove(current);
document.body.classList.add(replacement);
document.body.dataset.fontSize = replacementInt[2];
}
function lineHeightResize(current, replacement) {
document.body.classList.remove(current);
document.body.classList.add(replacement);
}
function setupGetArticleHeight() {
// Called by native code
window.getArticleHeight = getArticleHeight;
window.applyNativeFunctionCall('getArticleHeight');
}
function getArticleHeight() {
articleHeight(getArticleHeightCallback);
}
function getArticleHeightCallback(height) {
window.GuardianJSInterface.getArticleHeightCallback(height);
}
function articleHeight(callback) {
let articleContainer;
const contentType = document.body.getAttribute('data-content-type');
let height = 0;
if (contentType === 'article') {
articleContainer = document.querySelector('div[id$=-article-container]');
if (articleContainer) {
height = articleContainer.offsetHeight;
}
}
return callback(height);
}
function showTabs() {
let i;
let href;
let hideElem;
let tab;
let tabs;
const tabContainer = document.getElementsByClassName('tabs')[0];
if (tabContainer) {
tabs = tabContainer.getElementsByTagName('a');
for (i = 0; i < tabs.length; i++) {
tab = tabs[i];
if (i > 0) {
href = tab.getAttribute('href');
if (href) {
hideElem = document.querySelector(href);
if (hideElem) {
hideElem.style.display = 'none';
}
}
}
tab.addEventListener('click', showTab.bind(null, tab, tabContainer));
}
}
}
function showTab(tab, tabContainer, evt) {
let showElem;
let hideElem;
let href;
let activeTab;
let tabId;
initMpuPoller();
evt.preventDefault();
if (tab.getAttribute('aria-selected') !== 'true') {
activeTab = tabContainer.querySelector('[aria-selected="true"]');
tabId = tab.getAttribute('id');
if (activeTab) {
href = activeTab.getAttribute('href');
if (href) {
hideElem = document.querySelector(href);
if (hideElem) {
hideElem.style.display = 'none';
activeTab.setAttribute('aria-selected', false);
}
}
}
href = tab.getAttribute('href');
if (href) {
showElem = document.querySelector(href);
if (showElem) {
showElem.style.display = 'block';
tab.setAttribute('aria-selected', true);
}
}
switch(tabId) {
case 'football__tab--article':
window.location.href = 'x-gu://football_tab_report';
break;
case 'football__tab--stats':
setPieChartSize();
window.location.href = 'x-gu://football_tab_stats';
break;
case 'football__tab--liveblog':
window.location.href = 'x-gu://football_tab_liveblog';
break;
case 'cricket__tab--liveblog':
if (GU.opts.platform === 'android') {
window.GuardianJSInterface.cricketTabChanged('overbyover');
}
break;
case 'cricket__tab--stats':
if (GU.opts.platform === 'android') {
window.GuardianJSInterface.cricketTabChanged('scorecard');
}
break;
default:
window.location.href = 'x-gu://football_tab_unknown';
}
}
}
function setPieChartSize() {
const piechart = document.getElementsByClassName('pie-chart')[0];
if (piechart && piechart.parentNode) {
piechart.style.width = `${piechart.parentNode.offsetWidth}px`;
piechart.style.height = `${piechart.parentNode.offsetWidth}px`;
}
}
function setGlobalObject(root) {
const pageId = document.body.dataset.pageId;
root.guardian = {
config: {
page: {
pageId: pageId === '__PAGE_ID__' ? null : pageId
}
}
};
return root.guardian;
}
function fixSeries() {
let i;
let lineBreak;
const series = document.querySelector('.content__series-label.content__labels a');
let spans;
let lineWidth = 0;
const minLastLineWidth = 80;
if (series) {
series.innerHTML = `<span>${series.innerText.split(/\s+/).join(' </span><span>')} </span>`;
spans = series.getElementsByTagName('span');
for (i = spans.length - 1; i >=0; i--) {
lineWidth = lineWidth + spans[i].offsetWidth;
if (lineWidth > minLastLineWidth) {
if (Math.abs(spans[i].getBoundingClientRect().top - spans[spans.length - 1].getBoundingClientRect().top) >= spans[i].offsetHeight) {
lineBreak = document.createElement('br');
spans[i].parentNode.insertBefore(lineBreak, spans[i]);
}
break;
}
}
}
}
function advertorialUpdates() {
if (GU.opts.isAdvertising) {
/**
* Remove element with class 'meta'
* if it's child element with class 'byline' is empty
* and it has no children with class 'sponsorship'
*/
let i;
const metaContainers = document.getElementsByClassName('meta');
for (i = 0; i < metaContainers.length; i++) {
const metaContainer = metaContainers[i];
const bylineElem = metaContainer.getElementsByClassName('byline')[0];
if (bylineElem &&
bylineElem.innerHTML === '' &&
!metaContainer.getElementsByClassName('sponsorship').length) {
const metaParent = metaContainer.parentNode;
if (metaParent) {
metaParent.removeChild(metaContainer);
}
}
}
}
}
function setupTracking() {
const commentCount = document.querySelector('.comment-count a');
const viewMore = document.getElementsByClassName('comments__viewmore')[0];
const commentContainer = document.getElementsByClassName('comments')[0];
if (commentCount) {
commentCount.addEventListener('click', handleCommentCountClick);
}
if (viewMore) {
viewMore.addEventListener('click', viewMoreCommentsClick);
}
if (commentContainer) {
window.addEventListener('scroll', debounce(isCommentContainerInView.bind(null, commentContainer), 100));
}
}
function handleCommentCountClick(evt) {
evt.preventDefault();
signalDevice('trackAction/comments:view more:comment count');
signalDevice('showcomments');
}
function viewMoreCommentsClick(evt) {
evt.preventDefault();
signalDevice('trackAction/comments:view more');
signalDevice('showcomments');
}
function isCommentContainerInView(commentContainer) {
if (trackCommentContainerView &&
isElementPartiallyInViewport(commentContainer)) {
signalDevice('trackAction/comments:seen');
trackCommentContainerView = false;
}
}
function setupForms() {
let fenceElems = document.querySelectorAll('iframe.fenced');
let checkForLinks;
let attempts = 0;
let replaced = false;
if (fenceElems) {
checkForLinks = setInterval(() => {
attempts += 1;
fenceElems.forEach(elem => {
elem.contentWindow.document.querySelectorAll('.fsBody link').forEach(link => {
replaced = true;
if (link.href.startsWith("file:")) {
link.href = link.href.replace('file:', 'http:');
} else if (link.href.startsWith("//")) {
link.href = link.href.replace('//', 'http://');
}
});
});
if (attempts >= 20 || replaced) {
clearInterval(checkForLinks);
}
}, 500);
}
}
function instagramHeader() {
return !!document.querySelector('.article__header #main-media .element-instagram');
}
export {
init,
formatImages,
loadEmbeds,
loadInteractives,
getDesiredImageHeight
};