mirror of
https://github.com/torappinfo/uweb.git
synced 2024-08-14 23:54:59 +00:00
rebuilding site Thu Jun 30 11:43:08 AM CST 2022
This commit is contained in:
parent
ce465b53e3
commit
d5e26f165a
228 changed files with 1786 additions and 26411 deletions
197
zh/searchurl/bml/content/comparecells.js
Normal file
197
zh/searchurl/bml/content/comparecells.js
Normal 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 content’s 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);
|
||||
})();
|
1
zh/searchurl/bml/content/designmode.js
Normal file
1
zh/searchurl/bml/content/designmode.js
Normal file
|
@ -0,0 +1 @@
|
|||
(function(){if (document.designMode == 'off'){document.designMode = 'on';}else{document.designMode = 'off';}})()
|
54
zh/searchurl/bml/content/freeviddy.js
Normal file
54
zh/searchurl/bml/content/freeviddy.js
Normal 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 don’t 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);
|
||||
})();
|
497
zh/searchurl/bml/content/fullimg.js
Normal file
497
zh/searchurl/bml/content/fullimg.js
Normal 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;
|
||||
}
|
||||
})();
|
1447
zh/searchurl/bml/content/read.js
Normal file
1447
zh/searchurl/bml/content/read.js
Normal file
File diff suppressed because it is too large
Load diff
79
zh/searchurl/bml/content/rmi.js
Normal file
79
zh/searchurl/bml/content/rmi.js
Normal 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);
|
||||
})();
|
94
zh/searchurl/bml/content/rmo.js
Normal file
94
zh/searchurl/bml/content/rmo.js
Normal 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);
|
||||
})();
|
1
zh/searchurl/bml/content/vidspeed.js
Normal file
1
zh/searchurl/bml/content/vidspeed.js
Normal 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){}
|
Loading…
Add table
Add a link
Reference in a new issue