/* global browserApi XMLHttpRequest ACTIONS ENV_DOES_NOT_SUPPORT_BLOB_URL_ACCESS */ 'use strict'; (function () { const iframes = {}; browserApi.runtime.onMessage.addListener(({ action, payload }, sender, sendResponse) => { if (action !== ACTIONS.AddIframeContent) return; const { url, content } = payload; iframes[url] = content; sendResponse({}); }); async function grabPdfContent () { const fileExtension = window.location.pathname.slice(-4).toLowerCase(); const hasPdfExtension = fileExtension === '.pdf'; const pdfContentTypes = [ 'application/acrobat', 'application/pdf', 'application/x-pdf', 'applications/vnd.pdf', 'text/pdf', 'text/x-pdf' ]; const isPdfContent = pdfContentTypes.indexOf(document.contentType) !== -1; if (!hasPdfExtension && !isPdfContent) { return Promise.resolve(null); } const embedEl = document.querySelector('embed'); if (embedEl && embedEl.type !== 'application/pdf') { return Promise.resolve(null); } if (ENV_DOES_NOT_SUPPORT_BLOB_URL_ACCESS && embedEl.src) { return Promise.resolve({ type: 'url', uploadContentObjUrl: embedEl.src }) } return new Promise((resolve, reject) => { const xhr = new XMLHttpRequest(); // load `document` from `cache` xhr.open('GET', '', true); xhr.responseType = 'blob'; xhr.onload = function (e) { if (this.status === 200) { resolve({ type: 'pdf', uploadContentObjUrl: URL.createObjectURL(this.response) }) } else { reject(e); } }; xhr.send(); }); } function prepareContentPostItem (itemEl) { const lowerTagName = itemEl.tagName.toLowerCase(); if (lowerTagName === 'iframe') { const frameHtml = iframes[itemEl.src]; if (!frameHtml) return; const containerEl = document.createElement('div'); containerEl.className = 'omnivore-instagram-embed'; containerEl.innerHTML = frameHtml; const parentEl = itemEl.parentNode; if (!parentEl) return; parentEl.replaceChild(containerEl, itemEl); return; } if (lowerTagName === 'img' || lowerTagName === 'image') { // Removing blurred images since they are mostly the copies of lazy loaded ones const style = window.getComputedStyle(itemEl); const filter = style.getPropertyValue('filter'); if (filter.indexOf('blur(') === -1) return; itemEl.remove(); return; } const style = window.getComputedStyle(itemEl); const backgroundImage = style.getPropertyValue('background-image'); // convert all nodes with background image to img nodes const noBackgroundImage = !backgroundImage || backgroundImage === 'none'; if (!noBackgroundImage) return; const filter = style.getPropertyValue('filter'); // avoiding image nodes with a blur effect creation if (filter && filter.indexOf('blur(') !== -1) { itemEl.remove(); return; } // Replacing element only of there are no content inside, b/c might remove important div with content. // Article example: http://www.josiahzayner.com/2017/01/genetic-designer-part-i.html // DIV with class "content-inner" has `url("https://resources.blogblog.com/blogblog/data/1kt/travel/bg_container.png")` background image. if (itemEl.src) return; if (itemEl.innerHTML.length > 24) return; const BI_SRC_REGEXP = /url\("(.+?)"\)/gi; const matchedSRC = BI_SRC_REGEXP.exec(backgroundImage); // Using "g" flag with a regex we have to manually break down lastIndex to zero after every usage // More details here: https://stackoverflow.com/questions/1520800/why-does-a-regexp-with-global-flag-give-wrong-results BI_SRC_REGEXP.lastIndex = 0; const targetSrc = matchedSRC && matchedSRC[1]; if (!targetSrc) return; const imgEl = document.createElement('img'); imgEl.src = targetSrc; const parentEl = itemEl.parentNode; if (!parentEl) return; parentEl.replaceChild(imgEl, itemEl); } function prepareContentPostScroll () { const contentCopyEl = document.createElement('div'); contentCopyEl.style.position = 'absolute'; contentCopyEl.style.left = '-2000px'; contentCopyEl.style.zIndex = '-2000'; contentCopyEl.innerHTML = document.body.innerHTML; // Appending copy of the content to the DOM to enable computed styles capturing ability // Without adding that copy to the DOM the `window.getComputedStyle` method will always return undefined. document.documentElement.appendChild(contentCopyEl); Array.from(contentCopyEl.getElementsByTagName('*')).forEach(prepareContentPostItem); try { // check if create_time is defined if (typeof create_time !== 'undefined' && create_time) { // create_time is a global variable set by WeChat when rendering the page const date = new Date(create_time * 1000); const dateNode = document.createElement('div'); dateNode.className = 'omnivore-published-date'; dateNode.innerHTML = date.toLocaleString(); contentCopyEl.appendChild(dateNode); } } catch (e) { console.log('Error while trying to add published date to WeChat post', e); } /* * Grab head and body separately as using clone on entire document into a div * removes the head and body tags while grabbing html in them. Instead we * capture them separately and concatenate them here with head and body tags * preserved. */ const contentCopyHtml = `${document.head.innerHTML}${contentCopyEl.innerHTML}`; // Cleaning up the copy element contentCopyEl.remove(); return contentCopyHtml; } function createBackdrop () { const backdropEl = document.createElement('div'); backdropEl.className = 'webext-omnivore-backdrop'; backdropEl.style.cssText = `all: initial !important; position: fixed !important; top: 0 !important; right: 0 !important; bottom: 0 !important; left: 0 !important; z-index: 99999 !important; background: #fff !important; opacity: 0.8 !important; transition: opacity 0.3s !important; -webkit-backdrop-filter: blur(4px) !important; backdrop-filter: blur(4px) !important; `; return backdropEl; } function clearExistingBackdrops () { const backdropCol = document.querySelectorAll('.webext-omnivore-backdrop'); for (let i = 0; i < backdropCol.length; i++) { const backdropEl = backdropCol[i]; backdropEl.style.setProperty('opacity', '0', 'important'); } setTimeout(() => { for (let i = 0; i < backdropCol.length; i++) { backdropCol[i].remove(); } }, 0.5e3); } async function prepareContent () { const pdfContent = await grabPdfContent(); if (pdfContent) { return pdfContent } const url = window.location.href; try { if (handleBackendUrl(url)) { return { type: 'url' } } } catch { console.log('error checking url') } async function scrollPage (url) { const scrollingEl = (document.scrollingElement || document.body); const lastScrollPos = scrollingEl.scrollTop; const currentScrollHeight = scrollingEl.scrollHeight; /* add blurred overlay while scrolling */ clearExistingBackdrops(); const backdropEl = createBackdrop(); document.body.appendChild(backdropEl); /* * check below compares scrollTop against initial page height to handle * pages with infinite scroll else we shall be infinitely scrolling here. * stop scrolling if the url has changed in the meantime. */ while (scrollingEl.scrollTop <= (currentScrollHeight - 500) && window.location.href === url) { const prevScrollTop = scrollingEl.scrollTop; scrollingEl.scrollTop += 500; /* sleep upon scrolling position change for event loop to handle events from scroll */ await (new Promise((resolve) => { setTimeout(resolve, 10); })); if (scrollingEl.scrollTop === prevScrollTop) { /* break out scroll loop if we are not able to scroll for any reason */ // console.log('breaking out scroll loop', scrollingEl.scrollTop, currentScrollHeight); break; } } scrollingEl.scrollTop = lastScrollPos; /* sleep upon scrolling position change for event loop to handle events from scroll */ await (new Promise((resolve) => { setTimeout(resolve, 10); })); } await scrollPage(url); clearExistingBackdrops(); return { type: 'html', content: prepareContentPostScroll() }; } window.prepareContent = prepareContent; })();