ArticleTemplates/assets/js/bootstraps/liveblog.js (361 lines of code) (raw):

import { init as initRelativeDates } from 'modules/relativeDates'; import { init as initTwitter, checkForTweets, enhanceTweets } from 'modules/twitter'; import { init as initYoutube, checkForVideos, resetAndCheckForVideos } from 'modules/youtube'; import { formatImages, loadEmbeds, loadInteractives } from 'bootstraps/common'; import { getElemsFromHTML, signalDevice, getElementOffset, debounce, scrollToElement } from 'modules/util'; import { initMpuPoller } from 'modules/ads'; import { initPositionPoller } from 'modules/cards'; let newBlockHtml; let liveblogStartPos; function safeEnhanceTweets() { if (typeof twttr !== 'undefined' && 'widgets' in twttr && 'load' in twttr.widgets) { enhanceTweets(); } } function updateBlocksOnScroll() { if (liveblogStartPos.top > window.scrollY) { liveblogNewBlockDump(); } } function scrollToBlock(id) { const block = document.querySelector(`#${id}`); if (block) { scrollToElement(block) return true; } return false; } function addNewBlockToBlog(insertAfterElem, block) { let insertBeforeElem = insertAfterElem.nextSibling; block.classList.add('animated'); block.classList.add('slideinright'); while (insertBeforeElem && insertBeforeElem.nodeType !== 1) { insertBeforeElem = insertBeforeElem.nextSibling; } if (!insertBeforeElem) { insertAfterElem.parentNode.appendChild(block); } else { insertAfterElem.parentNode.insertBefore(block, insertBeforeElem); } } function checkInjectedComponents(newBlocksAdded) { // When a block has loaded check position of related cards placeholder initPositionPoller(); initMpuPoller(0); resetAndCheckForVideos(); // check for tweets checkForTweets(); if (newBlocksAdded) { /** If newBlocksAdded wait 700ms to check for youtube video atoms as blocks slides in from right over 600ms. **/ setTimeout(() => { checkForVideos(); }, 650); } else { checkForVideos(); } } function liveblogNewBlockDump() { const articleBody = document.getElementsByClassName('article__body')[0]; const images = []; let blocks; let counter = 0; const insertAfterElem = document.getElementsByClassName('article__body--liveblog__pinned')[0]; let newBlockElems; let i; if (newBlockHtml) { newBlockElems = getElemsFromHTML(newBlockHtml); for (i = newBlockElems.length; i > 0; i--) { addNewBlockToBlog(insertAfterElem, newBlockElems[i - 1]); } blocks = articleBody.getElementsByClassName('block'); while (counter !== newBlockElems.length) { images.push(...blocks[counter].getElementsByTagName('img')); counter++; } formatImages(images); loadEmbeds(); loadInteractives(); // Move mpu ads if (window.updateLiveblogAdPlaceholders) { window.updateLiveblogAdPlaceholders(true); } window.liveblogTime(); checkInjectedComponents(true); newBlockHtml = ''; } } function decideKicker() { if (document.getElementsByClassName('article-kicker__highlight')[0] !== undefined) { if (document.getElementsByClassName('article-kicker__highlight')[0].innerHTML === '') { document.getElementsByClassName('article-kicker__highlight')[0].innerHTML = document.getElementsByClassName('article-kicker__section')[0].innerHTML; } } } function liveMore() { const liveMoreElem = document.getElementsByClassName('more--live-blogs')[0]; if (liveMoreElem) { liveMoreElem.addEventListener('click', onLiveMoreClick.bind(null, liveMoreElem)); } } function onLiveMoreClick(liveMoreElem) { const loadingElem = document.getElementsByClassName('loading--liveblog')[0]; liveMoreElem.style.display = 'none'; if (loadingElem) { loadingElem.classList.add('loading--visible'); } signalDevice('showmore'); } function setupTryLive() { const elem = document.querySelector('.block--live-promo'); if (elem == null) return; let tryLiveButton = elem.getElementsByClassName('live-promo__button')[0]; tryLiveButton.addEventListener('touchstart', () => { tryLiveButton.classList.add('pressed'); }); tryLiveButton.addEventListener('touchend', () => { tryLiveButton.classList.remove('pressed'); }); tryLiveButton.addEventListener('click', () => { signalDevice('try-live'); // Wait a little bit before removing in case there is some // animation to open the Live tab. setTimeout(() => { elem.remove(); checkInjectedComponents(false); }, 1000); }); let closeButton = elem.getElementsByClassName('live-promo__close-button')[0]; closeButton.addEventListener('touchstart', () => { closeButton.classList.add('pressed'); }); closeButton.addEventListener('touchend', () => { closeButton.classList.remove('pressed'); }); closeButton.addEventListener('click', () => { elem.remove(); signalDevice('close-try-live'); checkInjectedComponents(false); }); } function liveblogDeleteBlock(blockID) { const block = document.getElementById(blockID); if (block) { block.parentNode.removeChild(block); } } function liveblogUpdateBlock(blockID, html) { const block = document.getElementById(blockID); const newBlock = getElemsFromHTML(html)[0]; if (block && newBlock) { block.parentNode.replaceChild(newBlock, block); } } function liveblogLoadMore(html) { let i; const images = []; let blocks; const articleBody = document.getElementsByClassName('article__body')[0]; const oldBlockCount = articleBody.getElementsByClassName('block').length; const newBlockElems = getElemsFromHTML(html); document.getElementsByClassName('loading--liveblog')[0].classList.remove('loading--visible'); for (i = 0; i < newBlockElems.length; i++) { articleBody.appendChild(newBlockElems[i]); } blocks = articleBody.getElementsByClassName('block'); for (i = blocks.length; i > oldBlockCount; i--) { images.push(...blocks[i-1].getElementsByTagName('img')); } formatImages(images); loadEmbeds(); loadInteractives(); window.liveblogTime(); checkInjectedComponents(false); safeEnhanceTweets(); } function liveblogTime() { let i; let blockTimes; const toneLiveBlogElems = document.getElementsByClassName('garnett--type-live'); if (toneLiveBlogElems.length && GU.opts.isLive) { initRelativeDates('.key-event__time, .block__time', 'title'); } else { blockTimes = document.getElementsByClassName('block__time'); for (i = 0; i < blockTimes.length; i++) { blockTimes[i].innerHTML = blockTimes[i].getAttribute('title'); } } } function showLiveMore(show) { const liveMoreElem = document.getElementsByClassName('more--live-blogs')[0]; if (liveMoreElem) { if (show) { liveMoreElem.style.display = 'block'; } else { liveMoreElem.style.display = 'none'; } } } function liveblogNewBlock(html) { newBlockHtml = html + newBlockHtml; if (liveblogStartPos.top > window.scrollY) { liveblogNewBlockDump(); } } function insertAfter(referenceNode, newNode) { referenceNode.parentNode.insertBefore(newNode, referenceNode.nextSibling); } function onGapClick(e, afterBlockId, paginationLink) { [].slice.call(document.getElementsByClassName(`after-${afterBlockId}`)).forEach(gap => { gap.parentNode.removeChild(gap); }); document.getElementById(`loading-${afterBlockId}`).style.display = "block"; } function liveblogInsertGap(afterBlockId, olderPagination, newerPagination) { let before = document.createElement('div'); let after = document.createElement('div'); let loading = document.createElement('div'); before.innerHTML = ` <div onclick="return onGapClick(event, '${afterBlockId}', '${olderPagination}')" class="more more--live-blogs-blocks after-${afterBlockId}"> <a href="${olderPagination}" class="more__button"> <span class="more__icon" data-icon="&#xe050;" aria-hidden="true"></span> <span class="more__text">Load more</span> </a> </div> `; after.innerHTML = ` <div onclick="return onGapClick(event, '${afterBlockId}', '${newerPagination}')" class="more more--live-blogs-blocks after-${afterBlockId}"> <a href="${newerPagination}" class="more__button"> <span class="more__icon" data-icon="&#xe050;" aria-hidden="true"></span> <span class="more__text">Load more</span> </a> </div> `; loading.innerHTML = `<div style="display: none" id="loading-${afterBlockId}" class="loading gap-loading" data-icon="&#xe00C;">` insertAfter(document.getElementById(afterBlockId), after); insertAfter(document.getElementById(afterBlockId), loading); insertAfter(document.getElementById(afterBlockId), before); } function liveblogInsertBlocks(afterBlockId, html) { let i; let images = []; let blocks; const articleBody = document.getElementsByClassName('article__body')[0]; const oldBlockCount = articleBody.getElementsByClassName('block').length; const newBlockElems = getElemsFromHTML(html); const icons = [].slice.call(document.getElementsByClassName('gap-loading')); icons.forEach(loadingIcon => loadingIcon.parentNode.removeChild(loadingIcon)) document.getElementsByClassName('loading--liveblog')[0].classList.remove('loading--visible'); for (i = 0; i < newBlockElems.length; i++) { if (afterBlockId) { addNewBlockToBlog(document.getElementById(afterBlockId), newBlockElems[i]) } else { newBlockElems[i].classList.add('animated'); newBlockElems[i].classList.add('slideinright'); articleBody.prepend(newBlockElems[i]); } } blocks = articleBody.getElementsByClassName('block'); for (i = 0; i < blocks.length; i++) { images.push(...blocks[i].getElementsByTagName('img')); } formatImages(images); loadEmbeds(); loadInteractives(); window.liveblogTime(); checkInjectedComponents(false); safeEnhanceTweets(); } function setupGlobals() { // Global function to handle liveblogs, called by native code window.liveblogDeleteBlock = liveblogDeleteBlock; window.liveblogUpdateBlock = liveblogUpdateBlock; window.liveblogLoadMore = liveblogLoadMore; window.liveblogTime = liveblogTime; window.showLiveMore = showLiveMore; window.liveblogNewBlock = liveblogNewBlock; window.liveblogInsertBlocks = liveblogInsertBlocks; window.liveblogInsertGap = liveblogInsertGap; window.liveblogNewKeyEvent = liveblogNewKeyEvent; window.scrollToBlock = scrollToBlock; window.onGapClick = onGapClick; window.applyNativeFunctionCall('liveblogNewBlock'); window.applyNativeFunctionCall('liveblogInsertBlocks'); window.applyNativeFunctionCall('liveblogInsertGap'); window.applyNativeFunctionCall('liveblogDeleteBlock'); window.applyNativeFunctionCall('liveblogUpdateBlock'); window.applyNativeFunctionCall('liveblogNewKeyEvent'); window.applyNativeFunctionCall('scrollToBlock'); } function keyEvents() { const keyEventsToggle = document.getElementsByClassName('key-events__toggle')[0]; const keyEventLinks = document.getElementsByClassName('key-event__link'); if (keyEventsToggle) { keyEventsToggle.addEventListener('click', showHideKeyEvents); } if (keyEventLinks.length === 1) { document.querySelector('.key-events__toggle').style.display = 'none'; } } function liveblogNewKeyEvent(html) { let i; let j; const keyEventsList = document.getElementsByClassName('key-events__list')[0]; let newKeyEventLinks; if (!keyEventsList) { return; } newKeyEventLinks = getElemsFromHTML(html); for (i = newKeyEventLinks.length; i > 0; i--) { newKeyEventLinks[i - 1].classList.add('key-event--highlighted'); for (j = 0; j < newKeyEventLinks[i - 1].children.length; j++) { newKeyEventLinks[i - 1].children[j].classList.add('flipInX'); newKeyEventLinks[i - 1].children[j].classList.add('animated'); } keyEventsList.insertBefore(newKeyEventLinks[i - 1], keyEventsList.firstChild); setTimeout(unhighlightKeyEventLink.bind(null, newKeyEventLinks[i - 1]), 15000); } captureKeyEventClicks(newKeyEventLinks); updateKeyEventCount(keyEventsList.children.length); window.liveblogTime(); } function unhighlightKeyEventLink(link) { link.classList.remove('key-event--highlighted'); } function updateKeyEventCount(count) { let i; const keyEventsCounter = document.getElementsByClassName('key-events__counter')[0]; const keyEvents = document.getElementsByClassName('key-events')[0]; keyEventsCounter.innerHTML = `(${count})`; for (i = keyEvents.classList.length; i > 0; i--) { if (keyEvents.classList[i - 1].match(/(key-events--)+[0-9]/g)) { keyEvents.classList.remove(keyEvents.classList[i - 1]); } } keyEvents.classList.add(`key-events--${count}`); } function showHideKeyEvents() { const keyEvents = document.getElementsByClassName('key-events')[0]; if (keyEvents.classList.contains('key-events--expanded')) { keyEvents.classList.remove('key-events--expanded'); } else { keyEvents.classList.add('key-events--expanded'); } } function init() { newBlockHtml = ''; liveblogStartPos = getElementOffset(document.getElementsByClassName('article__body--liveblog')[0]); setupGlobals(); keyEvents(); window.liveblogTime(); window.addEventListener('scroll', debounce(updateBlocksOnScroll, 100, true)); liveMore(); setupTryLive(); initYoutube(); setInterval(window.liveblogTime, 30000); decideKicker(); const articleBody = document.getElementsByClassName('article__body')[0]; if (articleBody) { let images = []; [].slice.call(articleBody.getElementsByClassName('block')).forEach(block => { images.push(...block.getElementsByTagName('img')); }) setTimeout(() => { formatImages(images) }, 0); } initTwitter(() => signalDevice('ready')); } export { init };