2019-09-21 23:11:09 +02:00
|
|
|
// Here we are making iframes responsive. Particularly useful for inline Youtube videos.
|
2019-09-21 11:34:11 +02:00
|
|
|
function wrapFrames() {
|
|
|
|
document.querySelectorAll("iframe").forEach(element => {
|
2020-07-02 11:32:58 +02:00
|
|
|
if (element.height > 0 || parseInt(element.style.height) > 0)
|
|
|
|
return;
|
2019-09-21 11:34:11 +02:00
|
|
|
var wrapper = document.createElement("div");
|
|
|
|
wrapper.classList.add("iframeWrap");
|
|
|
|
element.parentNode.insertBefore(wrapper, element);
|
|
|
|
wrapper.appendChild(element);
|
|
|
|
});
|
|
|
|
}
|
|
|
|
|
2019-12-31 01:30:10 +01:00
|
|
|
// Strip out color and font styling
|
|
|
|
|
|
|
|
function stripStylesFromElement(element, propertiesToStrip) {
|
|
|
|
for (name of propertiesToStrip) {
|
|
|
|
element.style.removeProperty(name);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-11-20 18:02:30 +01:00
|
|
|
// Strip inline styles that could harm readability.
|
2019-09-21 11:34:11 +02:00
|
|
|
function stripStyles() {
|
|
|
|
document.getElementsByTagName("body")[0].querySelectorAll("style, link[rel=stylesheet]").forEach(element => element.remove());
|
2019-12-31 01:30:10 +01:00
|
|
|
// Removing "background" and "font" will also remove properties that would be reflected in them, e.g., "background-color" and "font-family"
|
2020-11-20 18:02:30 +01:00
|
|
|
document.getElementsByTagName("body")[0].querySelectorAll("[style]").forEach(element => stripStylesFromElement(element, ["color", "background", "font", "max-width", "max-height", "position"]));
|
2019-09-21 11:34:11 +02:00
|
|
|
}
|
|
|
|
|
2020-11-20 18:26:15 +01:00
|
|
|
// Constrain the height of iframes whose heights are defined relative to the document body to be at most
|
2020-11-20 18:22:07 +01:00
|
|
|
// 50% of the viewport width.
|
|
|
|
function constrainBodyRelativeIframes() {
|
|
|
|
let iframes = document.getElementsByTagName("iframe");
|
|
|
|
|
|
|
|
for (iframe of iframes) {
|
|
|
|
if (iframe.offsetParent === document.body) {
|
|
|
|
let heightAttribute = iframe.style.height;
|
|
|
|
|
2020-11-20 18:28:59 +01:00
|
|
|
if (/%|vw|vh$/i.test(heightAttribute)) {
|
2020-11-20 18:22:07 +01:00
|
|
|
iframe.classList.add("nnw-constrained");
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-01-28 23:39:02 +01:00
|
|
|
// Convert all Feedbin proxy images to be used as src, otherwise change image locations to be absolute if not already
|
2019-11-10 00:37:25 +01:00
|
|
|
function convertImgSrc() {
|
|
|
|
document.querySelectorAll("img").forEach(element => {
|
2020-01-17 19:41:04 +01:00
|
|
|
if (element.hasAttribute("data-canonical-src")) {
|
|
|
|
element.src = element.getAttribute("data-canonical-src")
|
2020-11-20 18:30:32 +01:00
|
|
|
} else if (!/^[a-z]+\:\/\//i.test(element.src)) {
|
2020-01-17 01:14:38 +01:00
|
|
|
element.src = new URL(element.src, document.baseURI).href;
|
|
|
|
}
|
2019-11-10 00:37:25 +01:00
|
|
|
});
|
|
|
|
}
|
|
|
|
|
2019-12-28 07:05:16 +01:00
|
|
|
// Wrap tables in an overflow-x: auto; div
|
|
|
|
function wrapTables() {
|
2020-02-24 19:24:42 +01:00
|
|
|
var tables = document.querySelectorAll("div.articleBody table");
|
2019-12-28 07:05:16 +01:00
|
|
|
|
|
|
|
for (table of tables) {
|
|
|
|
var wrapper = document.createElement("div");
|
|
|
|
wrapper.className = "nnw-overflow";
|
2019-12-31 00:24:02 +01:00
|
|
|
table.parentNode.insertBefore(wrapper, table);
|
|
|
|
wrapper.appendChild(table);
|
2019-12-28 07:05:16 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-03-28 06:56:29 +01:00
|
|
|
// Add the playsinline attribute to any HTML5 videos that don"t have it.
|
|
|
|
// Without this attribute videos may autoplay and take over the whole screen
|
|
|
|
// on an iphone when viewing an article.
|
|
|
|
function inlineVideos() {
|
|
|
|
document.querySelectorAll("video").forEach(element => {
|
2020-10-30 23:25:25 +01:00
|
|
|
element.setAttribute("playsinline", true);
|
|
|
|
if (!element.classList.contains("nnwAnimatedGIF")) {
|
|
|
|
element.setAttribute("controls", true);
|
2021-04-08 16:08:19 +02:00
|
|
|
element.removeAttribute("autoplay");
|
2020-10-30 22:43:32 +01:00
|
|
|
}
|
2020-03-28 06:56:29 +01:00
|
|
|
});
|
|
|
|
}
|
|
|
|
|
2019-12-28 08:27:31 +01:00
|
|
|
// Remove some children (currently just spans) from pre elements to work around a strange clipping issue
|
|
|
|
var ElementUnwrapper = {
|
|
|
|
unwrapSelector: "span",
|
|
|
|
unwrapElement: function (element) {
|
|
|
|
var parent = element.parentNode;
|
|
|
|
var children = Array.from(element.childNodes);
|
|
|
|
|
|
|
|
for (child of children) {
|
|
|
|
parent.insertBefore(child, element);
|
|
|
|
}
|
|
|
|
|
|
|
|
parent.removeChild(element);
|
|
|
|
},
|
2019-12-31 00:35:47 +01:00
|
|
|
// `elements` can be a selector string, an element, or a list of elements
|
2019-12-28 08:27:31 +01:00
|
|
|
unwrapAppropriateChildren: function (elements) {
|
|
|
|
if (typeof elements[Symbol.iterator] !== 'function')
|
|
|
|
elements = [elements];
|
|
|
|
else if (typeof elements === "string")
|
|
|
|
elements = document.querySelectorAll(elements);
|
|
|
|
|
|
|
|
for (element of elements) {
|
|
|
|
for (unwrap of element.querySelectorAll(this.unwrapSelector)) {
|
|
|
|
this.unwrapElement(unwrap);
|
|
|
|
}
|
|
|
|
|
|
|
|
element.normalize()
|
|
|
|
}
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
|
|
|
function flattenPreElements() {
|
2019-12-31 00:19:05 +01:00
|
|
|
ElementUnwrapper.unwrapAppropriateChildren("div.articleBody td > pre");
|
2019-12-28 08:27:31 +01:00
|
|
|
}
|
|
|
|
|
2020-02-09 22:08:11 +01:00
|
|
|
function reloadArticleImage(imageSrc) {
|
2019-11-19 02:33:31 +01:00
|
|
|
var image = document.getElementById("nnwImageIcon");
|
2021-09-07 19:29:23 +02:00
|
|
|
image.src = imageSrc + "?" + new Date().getTime();
|
2019-11-19 02:33:31 +01:00
|
|
|
}
|
|
|
|
|
2020-03-02 01:32:31 +01:00
|
|
|
function stopMediaPlayback() {
|
|
|
|
document.querySelectorAll("iframe").forEach(element => {
|
|
|
|
var iframeSrc = element.src;
|
|
|
|
element.src = iframeSrc;
|
|
|
|
});
|
|
|
|
|
2020-10-31 01:16:15 +01:00
|
|
|
// We pause all videos that have controls. Video without controls shouldn't
|
|
|
|
// have sound and are actually converted gifs. Basically if the user can't
|
|
|
|
// start the video again, don't stop it.
|
2020-03-02 01:32:31 +01:00
|
|
|
document.querySelectorAll("video, audio").forEach(element => {
|
2020-10-31 01:16:15 +01:00
|
|
|
if (element.hasAttribute("controls")) {
|
|
|
|
element.pause();
|
|
|
|
}
|
2020-03-02 01:32:31 +01:00
|
|
|
});
|
|
|
|
}
|
|
|
|
|
2019-09-21 11:34:11 +02:00
|
|
|
function error() {
|
|
|
|
document.body.innerHTML = "error";
|
|
|
|
}
|
|
|
|
|
2020-02-24 19:14:44 +01:00
|
|
|
// Takes into account absoluting of URLs.
|
|
|
|
function isLocalFootnote(target) {
|
|
|
|
return target.hash.startsWith("#fn") && target.href.indexOf(document.baseURI) === 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
function styleLocalFootnotes() {
|
|
|
|
for (elem of document.querySelectorAll("sup > a[href*='#fn'], sup > div > a[href*='#fn']")) {
|
|
|
|
if (isLocalFootnote(elem)) {
|
2020-04-17 14:06:55 +02:00
|
|
|
elem.classList.add("footnote");
|
2020-02-24 19:14:44 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-03-12 04:47:34 +01:00
|
|
|
// convert <img alt="📰" src="[...]" class="wp-smiley"> to a text node containing 📰
|
|
|
|
function removeWpSmiley() {
|
|
|
|
for (const img of document.querySelectorAll("img.wp-smiley[alt]")) {
|
|
|
|
img.parentNode.replaceChild(document.createTextNode(img.alt), img);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-03-17 23:18:24 +01:00
|
|
|
function processPage() {
|
|
|
|
wrapFrames();
|
|
|
|
wrapTables();
|
2020-03-28 06:56:29 +01:00
|
|
|
inlineVideos();
|
2020-03-17 23:18:24 +01:00
|
|
|
stripStyles();
|
2020-11-20 18:22:07 +01:00
|
|
|
constrainBodyRelativeIframes();
|
2020-03-17 23:18:24 +01:00
|
|
|
convertImgSrc();
|
|
|
|
flattenPreElements();
|
|
|
|
styleLocalFootnotes();
|
2021-03-12 04:47:34 +01:00
|
|
|
removeWpSmiley()
|
2020-03-17 23:18:24 +01:00
|
|
|
postRenderProcessing();
|
2019-09-21 11:34:11 +02:00
|
|
|
}
|