rebuilding site Thu Jun 30 11:43:08 AM CST 2022

This commit is contained in:
James Feng Cao 2022-06-30 11:43:09 +08:00
parent ce465b53e3
commit d5e26f165a
228 changed files with 1786 additions and 26411 deletions

View file

@ -0,0 +1,197 @@
/**
* Try to color the cells of comparison tables based on their contents.
*
* @title Compare cells
*/
(function comparecells() {
/* Create a new IFRAME to get a "clean" Window object, so we can use its
* console. Sometimes sites (e.g. Twitter) override console.log and even
* the entire console object. "delete console.log" or "delete console"
* does not always work, and messing with the prototype seemed more
* brittle than this. */
let console = (function () {
let iframe = document.getElementById('xxxJanConsole');
if (!iframe) {
iframe = document.createElementNS('http://www.w3.org/1999/xhtml', 'iframe');
iframe.id = 'xxxJanConsole';
iframe.style.display = 'none';
(document.body || document.documentElement).appendChild(iframe);
}
return iframe && iframe.contentWindow && iframe.contentWindow.console || {
log: function () {}
};
})();
/**
* Get the text content for the given element.
*/
function getTextFromElement(element) {
/* TODO: take IMG@alt, BUTTON@value etc. into account */
return element.textContent.trim().toLowerCase();
}
/**
* Get a Uint8Array of the SHA-256 bytes for the given string.
*/
async function getSha256Bytes(string) {
try {
if (
typeof crypto === 'object' && typeof crypto.subtle === 'object' && typeof crypto.subtle.digest === 'function'
&& typeof Uint8Array === 'function'
&& typeof TextEncoder === 'function'
) {
return new Uint8Array(await crypto.subtle.digest('SHA-256', new TextEncoder('utf-8').encode(string)));
}
} catch (e) {
return null;
}
}
async function getColorsForValue(value) {
/* Cache the calculated values. */
getColorsForValue.cellValuesToRgb = getColorsForValue.cellValuesToRgb || {};
if (!getColorsForValue.cellValuesToRgb[value]) {
let normalizedValue = value.trim().toLowerCase();
let hash;
let yesValues = [
'✔',
'yes',
'ja',
'oui',
'si',
'sí'
];
let noValues = [
'x',
'no',
'nee',
'neen',
'nein',
'non',
'no'
];
if (yesValues.indexOf(normalizedValue) > -1) {
/* Make "Yes" cells green. */
getColorsForValue.cellValuesToRgb[value] = [ 150, 255, 32 ];
} else if (noValues.indexOf(normalizedValue) > -1) {
/* Make "No" cells green. */
getColorsForValue.cellValuesToRgb[value] = [ 238, 32, 32 ];
} else if ((shaBytes = await getSha256Bytes(normalizedValue))) {
/* Give other cells a color based on their contents SHA
* hash to ensure consistent random colors every time. */
getColorsForValue.cellValuesToRgb[value] = [
shaBytes[0],
shaBytes[1],
shaBytes[2]
];
} else {
/* If the SHA hash could not be calculated, just use random
* values. These will change on every execution. */
getColorsForValue.cellValuesToRgb[value] = [
Math.random() * 255,
Math.random() * 255,
Math.random() * 255
];
}
}
/* Calculate/approximate the lightness (tweaked from RGB to HSL) to
* determine whether black or white text is best suited. */
let isLight = 150 < (
getColorsForValue.cellValuesToRgb[value][0] * 0.299
+ getColorsForValue.cellValuesToRgb[value][1] * 0.587
+ getColorsForValue.cellValuesToRgb[value][2] * 0.114
);
return {
backgroundColor: 'rgb(' + getColorsForValue.cellValuesToRgb[value].join(', ') + ')',
color: isLight
? 'black'
: 'white',
textShadow: isLight
? '1px 1px 3px white'
: '1px 1px 3px black'
};
}
/* The main function. */
(function execute(document) {
Array.from(document.querySelectorAll('table')).forEach(table => {
Array.from(table.tBodies).forEach(tBody => {
if (tBody.rows.length < 3) {
console.log('Compare cells: skipping table body ', tBody, ' because it only has ', tBody.rows.length, ' rows');
return;
}
Array.from(tBody.rows).forEach(tr => {
/* Determine the values. */
let cellValues = [];
let uniqueCellValues = new Set();
Array.from(tr.cells).forEach((cell, i) => {
/* Don't take the header cells into account. */
if (cell.tagName.toUpperCase() === 'TH') {
return;
}
/* Assume the first cell is a header cell, even if it is not a TH. */
if (i === 0) {
return;
}
cellValues[i] = getTextFromElement(cell);
uniqueCellValues.add(cellValues[i]);
});
/* Color (or not) the cells based on the values. */
let isFirstValue = true;
let firstValue;
cellValues.forEach(async function(cellValue, i) {
let hasTwoUniqueValues = uniqueCellValues.size == 2;
if (isFirstValue) {
firstValue = cellValue;
isFirstValue = false;
}
let backgroundColor;
let color;
let textShadow;
if (
uniqueCellValues.size == 1 ||
(hasTwoUniqueValues && cellValue === firstValue) ||
cellValue.trim() === ''
) {
backgroundColor = 'inherit';
color = 'inherit';
textShadow = 'inherit';
} else {
backgroundColor = (await getColorsForValue(cellValue)).backgroundColor;
color = (await getColorsForValue(cellValue)).color;
textShadow = (await getColorsForValue(cellValue)).textShadow;
}
tr.cells[i].style.setProperty('background-color', backgroundColor, 'important');
tr.cells[i].style.setProperty('color', color, 'important');
tr.cells[i].style.setProperty('text-shadow', textShadow, 'important');
});
});
});
});
/* Recurse for frames and iframes. */
try {
Array.from(document.querySelectorAll('frame, iframe, object[type^="text/html"], object[type^="application/xhtml+xml"]')).forEach(function (elem) {
execute(elem.contentDocument);
});
} catch (e) {
/* Catch exceptions for out-of-domain access, but do not do anything with them. */
}
})(document);
})();

View file

@ -0,0 +1 @@
(function(){if (document.designMode == 'off'){document.designMode = 'on';}else{document.designMode = 'off';}})()

View file

@ -0,0 +1,54 @@
/**
* Free the VIDEO elements: get rid of overlays, and enable the native controls.
*
* Useful on https://www.instagram.com/ where the stupid overlays prevent
* showing the controls and triggering the context menu, so you dont know how
* long the video will take and can't play the video in full screen mode.
*
* @title Free Viddy
*/
(function freeviddy() {
/* Recursively execute the main logic on the document and its sub-documents. */
function execute(document) {
document.addEventListener('touchmove mousemove', function debouncer(event) {
clearTimeout(debouncer.timeoutId);
debouncer.timeoutId = setTimeout(function () {
let elementsUnderPointer = document.elementsFromPoint(event.clientX, event.clientY);
let overlaysToRemove = [];
for (let i = 0; i < elementsUnderPointer.length; i++) {
if ((elementsUnderPointer[i].tagName.toUpperCase() === 'VIDEO' ||
elementsUnderPointer[i].tagName.toUpperCase() === 'IFRAME') && !elementsUnderPointer[i].xxxJanFreeViddyProcessed) {
let video = elementsUnderPointer[i];
video.controls = true;
video.xxxJanFreeViddyProcessed = true;
if (i === 0) {
} else {
overlaysToRemove = elementsUnderPointer.slice(0, i);
}
break;
}
}
if (overlaysToRemove.length) {
overlaysToRemove.forEach(element => element.remove());
}
}, 50);
});
/* Recurse for frames and iframes. */
try {
Array.from(
document.querySelectorAll('frame, iframe, object[type^="text/html"], object[type^="application/xhtml+xml"]')
).forEach(
elem => execute(elem.contentDocument)
);
} catch (e) {
/* Catch exceptions for out-of-domain access, but do not do anything with them. */
}
}
execute(document);
})();

View file

@ -0,0 +1,497 @@
/**
* Load the full-size versions of resized images based on their "src"
* attribute, or their containing link's "href" attribute. Also, make IFRAMEs
* take up the entire width of their offset parent (useful for embedded videos
* and whatnot). Same goes for the VIDEO elements.
*
* @title Load full images
*/
(function fullimg() {
/* Create a new IFRAME to get a "clean" Window object, so we can use its
* console. Sometimes sites (e.g. Twitter) override console.log and even
* the entire console object. "delete console.log" or "delete console"
* does not always work, and messing with the prototype seemed more
* brittle than this. */
var console = (function () {
var iframe = document.getElementById('xxxJanConsole');
if (!iframe) {
iframe = document.createElementNS('http://www.w3.org/1999/xhtml', 'iframe');
iframe.id = 'xxxJanConsole';
iframe.style.display = 'none';
(document.body || document.documentElement).appendChild(iframe);
}
return iframe && iframe.contentWindow && iframe.contentWindow.console || {
log: function () {}
};
})();
/* Get rid of "width=", "height=" etc. followed by numbers or number pairs
* in IMG@src query strings. */
var parameterNames = [
'width',
'Width',
'height',
'Height',
'maxwidth',
'maxWidth',
'MaxWidth',
'maxheight',
'maxHeight',
'MaxHeight',
'w',
'W',
'h',
'H',
'fit',
'Fit',
'resize',
'reSize',
'Resize',
'size',
'Size'
];
parameterNames.forEach(function (parameterName) {
var selector = 'img[src*="?' + parameterName + '="]'
+ ', img[src*="?"][src*="&' + parameterName + '="]';
/* Match query string parameters (?[&]name=value[&]) where the value is
* a number (e.g. "width=1200") or a pair of numbers (e.g. * "resize=640x480"). */
var parameterReplacementRegexp = new RegExp('(\\?[^#]*&)?' + parameterName + '=[1-9][0-9]+(?:(?:[xX,*:]|%2[CcAa]|%3[Aa])[1-9][0-9]+)?([^&#]*)');
[].forEach.call(document.querySelectorAll(selector), function (img) {
var newSrc = img.src
/* Remove the parameter "name=value" pair from the query string. */
.replace(parameterReplacementRegexp, '$1$2')
/* Remove trailing "&" from the query string. */
.replace(/(\?[^#]*)&(#.*)?$/, '$1$2')
/* Remove empty query strings ("?" not followed by
* anything) from the URL. */
.replace(/\?(#.*)?$/, '$1')
/* Remove empty fragment identifiers from the URL. */
.replace(/#$/, '')
;
changeSrc(img, newSrc, 'found image with parameter "' + parameterName + '" in query string');
});
});
/* Show the original image for Polopoly CMS "generated derivatives".
*
* Example:
* https://sporza.be/polopoly_fs/1.2671026!image/1706320883.jpg_gen/derivatives/landscape670/1706320883.jpg
* https://sporza.be/polopoly_fs/1.2671026!image/1706320883.jpg
*/
[].forEach.call(
document.querySelectorAll('img[src*="_gen/derivatives/"]'),
function (img) {
var matches = img.src.match(/(.*\.(jpe?g|png|gif))_gen.*\.\2(\?.*)?$/);
if (matches && matches[1]) {
changeSrc(img, matches[1], 'found image with Polopoly CMS "generated derivative" URL');
}
}
);
/* Try to load the originals for images whose source URLs look like
* thumbnail/resized versions with dimensions.
*/
[].forEach.call(
document.images,
function (img) {
var oldSrc = img.src;
/* Example:
* https://www.cycling-challenge.com/wp-content/uploads/2014/08/IMG_6197-150x150.jpg
* https://www.cycling-challenge.com/wp-content/uploads/2014/08/IMG_6197.jpg
*/
var matches = oldSrc.match(/(.*)[-_.@]\d+x\d+(\.[^\/.]+)/);
if (matches && matches[1] && matches[2]) {
var newSrc = matches[1] + matches[2];
return changeSrc(img, newSrc, 'found image whose URL looks like a thumbnail/resized version');
}
/* Example:
* https://upload.wikimedia.org/wikipedia/commons/thumb/8/83/Kowloon-Walled-City-1898.jpg/220px-Kowloon-Walled-City-1898.jpg
* https://upload.wikimedia.org/wikipedia/commons/8/83/Kowloon-Walled-City-1898.jpg
*/
matches = oldSrc.match(/(.*\/)thumb\/(.*)\/[^\/]+$/);
if (matches) {
var newSrc = matches[1] + matches[2];
return changeSrc(img, newSrc, 'found image whose URL looks like a MediaWiki thumbnail/resized version');
}
}
);
/* Try to load the originals for images whose source URLs look like
* thumbnail/resized versions with a text label.
*
* Example:
* https://www.crazyguyonabike.com/pics/docs/00/01/27/84/small/DSCF3555.JPG
* https://www.crazyguyonabike.com/pics/docs/00/01/27/84/large/DSCF3555.JPG
*/
var thumbnailPathRegexp = /(.*[/.-])(small|thumb|thumbnail|resized|preview|medium)([/.-].*)/;
var fullSizePathParts = [
'large',
'original',
'source',
'normal',
'xlarge',
];
[].forEach.call(
document.images,
function (img) {
var oldSrc = img.src;
var matches = oldSrc.match(thumbnailPathRegexp);
if (matches) {
var newSources = [];
fullSizePathParts.forEach(function (part) {
newSources.push(matches[1] + part + matches[3]);
});
changeSrc(img, newSources, 'found image whose URL looks like a thumbnail/resized version');
}
}
);
/* Change the IMG@src of linked images to their link's A@href if they look
* similar, assuming that the linked version is larger. */
[].forEach.call(
document.querySelectorAll('a img'),
function (img) {
if (!img.src) {
return;
}
var a = img.parentNode;
while (a && a.tagName && a.tagName.toLowerCase() !== 'a') {
a = a.parentNode;
}
if (!a) {
return;
}
var aHref = a.href;
if (a.hostname.match(/\.blogspot\.com$/)) {
/* Get rid of Blogspot's links to useless HTML wrappers. */
aHref = aHref.replace(/\/(s\d+)-h\/([^\/]+)$/, '/$1/$2');
}
if (aHref === img.src) {
return;
}
/* Simplify a URL for similarity calculation. */
function simplifyUrl(url) {
return ('' + url)
.replace(/\d+/g, '0')
.replace(/^https?:/, '');
}
var similarity = getSimilarity(simplifyUrl(img.src), simplifyUrl(a.href));
if (similarity > 0.66) {
changeSrc(img, aHref, 'found linked image with ' + Math.round(similarity * 100) + '% similarity');
}
}
);
/* Change all Blogspot images that have not been changed yet. */
Array.from(
document.querySelectorAll('img[src*="bp.blogspot.com/"]')
).forEach(img => {
let matches;
if ((matches = img.src.match(/^(.*\/)s(\d+)(\/[^/]+)$/)) && matches[2] < 9999) {
let newSrc = matches[1] + 's9999' + matches[3];
changeSrc(img, newSrc, 'found Blogspot image with restricted size (' + matches[2] + ')');
}
});
/* Use larger YouTube thumbnails. */
Array.from(
document.querySelectorAll('img[src*="//yt"][src*=".ggpht.com"]')
).forEach(img => {
let matches;
if ((matches = img.src.match(/^(.*\/)s(\d+)([^/]+\/photo\.[^/.]+)$/)) && matches[2] < 1024) {
let newSrc = matches[1] + 's1024' + matches[3];
changeSrc(img, newSrc, 'found YouTube avatar with restricted size (' + matches[2] + ')');
}
});
/* Get rid of all IMG@srcset attributes that have not been removed in the
* previous steps.
*/
[].forEach.call(
document.querySelectorAll('img[srcset]'),
function (img) {
console.log('Load full images: removing srcset attribute: ', img);
img.originalSrcset = img.getAttribute('srcset');
img.removeAttribute('srcset');
}
);
/* Make native VIDEO elements and video IFRAMEs take up the entire width
* of their offset parent. */
var elementsToEnlargeSelectors = [
'video',
'iframe.twitter-tweet-rendered',
'iframe[src*="embed"]',
'iframe[src*="video"]',
'iframe[src*="syndication"]',
'iframe[class*="altura"]',
'iframe[id*="altura"]',
'iframe[src*="altura"]',
'iframe[src*="//e.infogr.am/"]',
'iframe[src*="//www.kickstarter.com/projects/"]',
'iframe[src*="//media-service.vara.nl/player.php"]',
'iframe[src*="//player.vimeo.com/video/"]'
];
[].forEach.call(
document.querySelectorAll(elementsToEnlargeSelectors.join(', ')),
function (element) {
var scale = element.offsetParent.offsetWidth / element.offsetWidth;
var newWidth = Math.round(element.offsetWidth * scale);
var newHeight = Math.round(element.offsetHeight * scale);
console.log(
'Load full images: resizing element ', element,
' from ' + element.offsetWidth + 'x' + element.offsetHeight
+ ' to ' + newWidth + 'x' + newHeight
);
element.xxxJanReadableAllowStyle = true;
element.style.width = newWidth + 'px';
element.style.height = newHeight + 'px';
}
);
/* Show controls on AUDIO and VIDEO elements. */
[].forEach.call(
document.querySelectorAll('audio, video'),
function (element) {
element.controls = true;
}
);
/* Show controls on YouTube embeds. */
[].forEach.call(
document.querySelectorAll('iframe[src^="https://www.youtube.com/embed/"][src*="?"][src*="=0"]'),
function (iframe) {
var beforeAndAfterHash = iframe.src.split('#');
var beforeAndAfterQuery = beforeAndAfterHash[0].split('?');
var newPrefix = beforeAndAfterQuery[0];
var newQueryString = '';
if (beforeAndAfterQuery.length > 1) {
beforeAndAfterQuery.shift();
var newQueryParts = beforeAndAfterQuery
.join('?')
.split('&')
.filter(function (keyValuePair) {
return !keyValuePair.match(/^(controls|showinfo|rel)=0$/);
}
);
if (newQueryParts.length) {
newQueryString = '?' + newQueryParts.join('&');
}
}
var newHash = '';
if (beforeAndAfterHash.length > 1) {
beforeAndAfterHash.shift();
newHash = '#' + beforeAndAfterHash.join('#');
}
var newSrc = newPrefix + newQueryString + newHash;
if (newSrc !== iframe.src) {
iframe.src = newSrc;
}
}
);
/**
* Crudely calculate the similarity between two strings. Taken from
* https://stackoverflow.com/a/10473855. An alternative would be the
* Levenshtein distance, implemented in JavaScript here:
* https://andrew.hedges.name/experiments/levenshtein/
*/
function getSimilarity(strA, strB) {
var result = 0;
var i = Math.min(strA.length, strB.length);
if (i === 0) {
return;
}
while (--i) {
if (strA[i] === strB[i]) {
continue;
}
if (strA[i].toLowerCase() === strB[i].toLowerCase()) {
result++;
} else {
result += 4;
}
}
return 1 - (result + 4 * Math.abs(strA.length - strB.length)) / (2 * (strA.length + strB.length));
}
/**
* Change the IMG@src and fall back to the original source if the new
* source triggers an error. You can specify an array of new sources that
* will be tried in order. When all of the new sources fail, the original
* source will be used.
*/
function changeSrc(img, newSrc, reason)
{
var basename = img.src.replace(/[?#].*/, '').replace(/.*?([^\/]*)\/*$/, '$1');
console.log('[' + basename + '] Load full images: ' + reason + ': ', img);
if (img.hasNewSource) {
console.log('[' + basename + '] Image already has a new source: ', img);
return;
}
var newSources = Array.isArray(newSrc)
? newSrc
: [ newSrc ];
while ((newSrc = newSources.shift())) {
if (newSrc && img.src !== newSrc) {
break;
}
}
if (!newSrc) {
return;
}
console.log('[' + basename + '] → Old img.src: ' + img.src);
console.log('[' + basename + '] → Try img.src: ' + newSrc);
/* Save the original source. */
if (!img.originalSrc) {
img.originalSrc = img.src;
}
if (!img.originalNaturalWidth) {
img.originalNaturalWidth = img.naturalWidth;
}
if (!img.originalNaturalHeight) {
img.originalNaturalHeight = img.naturalHeight;
}
/* Save and disable the srcset on the IMG element. */
if (img.hasAttribute('srcset')) {
img.originalSrcset = img.getAttribute('srcset');
img.removeAttribute('srcset');
}
/* Save and disable the srcset in the container PICTURE element's SOURCE descendants. */
if (img.parentNode.tagName.toLowerCase() === 'picture') {
[].forEach.call(
img.parentNode.querySelectorAll('source[srcset]'),
function (source) {
source.originalSrcset = source.getAttribute('srcset');
source.removeAttribute('srcset');
}
);
}
/* When the new source has failed to load, load the next one from the
* list of possible new sources. If there are no more left, revert to
* the original source. */
var errorHandler;
if (newSources.length) {
console.log('[' + basename + '] Setting errorHandler to loadNextNewSrc for ', img, '; newSources: "' + newSources.join('", "') + '"; reason:', reason);
errorHandler = function loadNextNewSrc() {
img.removeEventListener('error', loadNextNewSrc);
changeSrc(img, newSources, reason);
};
} else {
console.log('[' + basename + '] Setting errorHandler to restoreOriginalSrc for ', img, '; originalSrc: "' + img.originalSrc + '"; reason:', reason);
errorHandler = function restoreOriginalSrc() {
console.log('[' + basename + '] Load full images: error while loading new source for image: ', img);
console.log('[' + basename + '] → Unable to load new img.src: ' + newSrc);
console.log('[' + basename + '] → Resetting to original img.src: ' + img.originalSrc);
img.removeEventListener('error', restoreOriginalSrc);
/* Restore the original source. */
img.src = img.originalSrc;
/* Re-enable the original srcset on the IMG element. */
if (img.originalSrcset) {
img.setAttribute('srcset', img.originalSrcset);
delete img.originalSrcset;
}
/* Re-enable the original srcset in the container PICTURE element's SOURCE descendants. */
if (img.parentNode.tagName.toLowerCase() === 'picture') {
[].forEach.call(
img.parentNode.querySelectorAll('source'),
function (source) {
if (source.originalSrcset) {
source.setAttribute('srcset', source.originalSrcset);
delete source.originalSrcset;
}
}
);
}
};
}
img.addEventListener('error', errorHandler);
/* When the new source image is smaller than the original image,
* treat that as an error, too. */
img.addEventListener('load', function () {
if (img.naturalWidth * img.naturalHeight < img.originalNaturalWidth * img.originalNaturalHeight) {
console.log('[' + basename + '] Load full images: new image (', img.naturalWidth, 'x', img.naturalHeight, ') is smaller than old image (', img.originalNaturalWidth, 'x', img.originalNaturalHeight, '): ', img);
return errorHandler();
}
if (img.src !== img.originalSrc) {
console.log('[' + basename + '] → Success: ' + img.src);
img.hasNewSource = true;
}
});
/* Finally, actually try to load the image. */
img.src = newSrc;
}
})();

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,79 @@
/**
* Remove all the IFRAMEs that are off-site or do not have a src attribute.
* These are typically used for ads and unwanted external content.
* navigation etc.
*
* IFRAMEs without a src attribute are also used for sandboxing untrusted
* content, e.g. on mailinator.com, but I have not yet found a way to
* distinguish between src-less IFRAMEs for ads and src-less IFRAMEs for
* regular content. Maybe try to guess based on the dimensions? Meh.
*
* @title rm IFRAMEs
*/
(function rmi() {
/* Create a new IFRAME to get a "clean" Window object, so we can use its
* console. Sometimes sites (e.g. Twitter) override console.log and even
* the entire console object. "delete console.log" or "delete console"
* does not always work, and messing with the prototype seemed more
* brittle than this. */
let console = (function () {
let iframe = document.getElementById('xxxJanConsole');
if (!iframe) {
iframe = document.createElementNS('http://www.w3.org/1999/xhtml', 'iframe');
iframe.id = 'xxxJanConsole';
iframe.style.display = 'none';
(document.body || document.documentElement).appendChild(iframe);
}
return iframe && iframe.contentWindow && iframe.contentWindow.console || {
log: function () {}
};
})();
/* Keep track of the HTMLDocument instances we have processed. */
let processed = new Set();
/**
* Is the given node empty-ish? I.e., does it lack child elements and
* non-whitespace text?
*/
function isEmpty(node) {
return !node || (!node.childElementCount && (typeof node.textContent !== 'string' || node.textContent.trim() === ''));
}
/* The main function. */
(function execute(document) {
if (!document || typeof document.querySelectorAll !== 'function' || processed.has(document)) {
return;
}
processed.add(document);
/* Process all IFRAMEs. */
Array.from(document.querySelectorAll('iframe:not(#xxxJanConsole)')).forEach(iframe => {
let shouldDelete = false;
try {
shouldDelete = iframe.contentDocument === null || iframe.src === '';
} catch (e) {
shouldDelete = true;
}
if (shouldDelete) {
console.log('rm IFRAMEs: found suspicious IFRAME to delete: ', iframe);
let parentNode = iframe.parentNode;
iframe.remove();
while (parentNode && isEmpty(parentNode)) {
console.log('rm IFRAMEs: found empty parent node to delete: ', parentNode);
let oldParentNode = parentNode;
parentNode = parentNode.parentNode;
oldParentNode.remove();
}
} else {
console.log('rm IFRAMEs: found non-suspicious IFRAME to recurse into: ', iframe);
execute(iframe.contentDocument);
}
});
})(document);
})();

View file

@ -0,0 +1,94 @@
/**
* Get rid of full-page overlays.
*
* @title rm overlays
*/
(function rmo() {
function getFirstZIndexedElement(elements) {
if (!Array.isArray(elements)) {
elements = Array.from(elements);
}
for (let i = 0; i < elements.length; i++) {
if (!isNaN(getComputedStyle(elements[i]).zIndex)) {
return elements[i];
}
}
return null;
}
/* Recursively execute the logic on the document and its sub-documents. */
function execute(document) {
/* Look for absolutely positioned (well, Z-indexed) elements that
* cover the entire width of the page. Look for them in the vertical
* center, to avoid cookie/GDPR/ banners that are typically at the
* top or bottom of the window, and slightly away from the edges, to
* avoid scrollbars/social sharing toolbars/ */
let leftX = 64;
let leftY = document.defaultView.innerHeight / 2;
let leftOverlay = getFirstZIndexedElement(document.elementsFromPoint(leftX, leftY));
if (!leftOverlay)
return;
let rightX = document.defaultView.innerWidth - 64;
let rightY = document.defaultView.innerHeight / 2;
let rightOverlay = getFirstZIndexedElement(document.elementsFromPoint(rightX, rightY));
if (!rightOverlay)
return;
if (leftOverlay !== rightOverlay)
return;
let centerX = document.defaultView.innerWidth / 2;
let centerY = document.defaultView.innerHeight / 2;
let centerElements = document.elementsFromPoint(centerX, centerY);
if (!centerElements.indexOf(leftOverlay) === -1)
return;//leftOverlay not in center
/* Hide the overlay and its visual descendants (i.e., the elements
* on top of the overlay). */
for (let i = 0; i < centerElements.length; i++) {
centerElements[i].style.display = 'none';
if (centerElements[i] === leftOverlay) {
break;
}
}
/* Re-enable scrolling on the BODY element. */
let currentBodyStyle = document.body.hasAttribute('style')
? document.body.getAttribute('style')
: '';
let newBodyStyle = currentBodyStyle +
'; overflow: auto !important' +
'; position: static !important';
document.body.setAttribute('style', newBodyStyle);
/* Re-enable scrolling on Quora.com. */
document.body.classList.remove('login_no_scroll');
/* Re-enable scrolling disabled by inline styles. */
[].forEach.call(
document.querySelectorAll('[style*="overflow"][style*="hidden"]'),
function (elem) {
elem.setAttribute('style', elem.getAttribute('style').replace(/overflow\s*:\s*hidden\s*;?/, ''));
}
);
/* Recurse for frames and IFRAMEs. */
try {
Array.from(
document.querySelectorAll('frame, iframe, object[type^="text/html"], object[type^="application/xhtml+xml"]')
).forEach(
elem => execute(elem.contentDocument)
);
} catch (e) {
/* Catch and ignore exceptions for out-of-domain access. */
}
}
execute(document);
})();

View file

@ -0,0 +1 @@
try{var s = prompt("Enter video speed: "); if(s !="" && s!=null && s!=0){document.querySelector('video').playbackRate = s; void 0}}catch(err){}