Este script no debería instalarse directamente. Es una biblioteca que utilizan otros scripts mediante la meta-directiva de inclusión // @require https://update.greasyfork.org/scripts/447533/1214813/findAndReplaceDOMText%20v%20046.js
- /**
- * findAndReplaceDOMText v 0.4.6
- * @author James Padolsey http://james.padolsey.com
- * @license http://unlicense.org/UNLICENSE
- *
- * Matches the text of a DOM node against a regular expression
- * and replaces each match (or node-separated portions of the match)
- * in the specified element.
- */
- (function (root, factory) {
- if (typeof module === 'object' && module.exports) {
- // Node/CommonJS
- module.exports = factory();
- } else if (typeof define === 'function' && define.amd) {
- // AMD. Register as an anonymous module.
- define(factory);
- } else {
- // Browser globals
- root.findAndReplaceDOMText = factory();
- }
- }(this, function factory() {
-
- var PORTION_MODE_RETAIN = 'retain';
- var PORTION_MODE_FIRST = 'first';
-
- var doc = document;
- var hasOwn = {}.hasOwnProperty;
-
- function escapeRegExp(s) {
- return String(s).replace(/([.*+?^=!:${}()|[\]\/\\])/g, '\\$1');
- }
-
- function exposed() {
- // Try deprecated arg signature first:
- return deprecated.apply(null, arguments) || findAndReplaceDOMText.apply(null, arguments);
- }
-
- function deprecated(regex, node, replacement, captureGroup, elFilter) {
- if ((node && !node.nodeType) && arguments.length <= 2) {
- return false;
- }
- var isReplacementFunction = typeof replacement == 'function';
-
- if (isReplacementFunction) {
- replacement = (function(original) {
- return function(portion, match) {
- return original(portion.text, match.startIndex);
- };
- }(replacement));
- }
-
- // Awkward support for deprecated argument signature (<0.4.0)
- var instance = findAndReplaceDOMText(node, {
-
- find: regex,
-
- wrap: isReplacementFunction ? null : replacement,
- replace: isReplacementFunction ? replacement : '$' + (captureGroup || '&'),
-
- prepMatch: function(m, mi) {
-
- // Support captureGroup (a deprecated feature)
-
- if (!m[0]) throw 'findAndReplaceDOMText cannot handle zero-length matches';
-
- if (captureGroup > 0) {
- var cg = m[captureGroup];
- m.index += m[0].indexOf(cg);
- m[0] = cg;
- }
-
- m.endIndex = m.index + m[0].length;
- m.startIndex = m.index;
- m.index = mi;
-
- return m;
- },
- filterElements: elFilter
- });
-
- exposed.revert = function() {
- return instance.revert();
- };
-
- return true;
- }
-
- /**
- * findAndReplaceDOMText
- *
- * Locates matches and replaces with replacementNode
- *
- * @param {Node} node Element or Text node to search within
- * @param {RegExp} options.find The regular expression to match
- * @param {String|Element} [options.wrap] A NodeName, or a Node to clone
- * @param {String} [options.wrapClass] A classname to append to the wrapping element
- * @param {String|Function} [options.replace='$&'] What to replace each match with
- * @param {Function} [options.filterElements] A Function to be called to check whether to
- * process an element. (returning true = process element,
- * returning false = avoid element)
- */
- function findAndReplaceDOMText(node, options) {
- return new Finder(node, options);
- }
-
- exposed.NON_PROSE_ELEMENTS = {
- br:1, hr:1,
- // Media / Source elements:
- script:1, style:1, img:1, video:1, audio:1, canvas:1, svg:1, map:1, object:1,source:1,
- // Input elements
- input:1, textarea:1, select:1, option:1, optgroup: 1, button:1,
- // 自用添加
- savdiv:1, avdiv:1,savmagnet:1,
- // 添加的其他项目
- noscript:1,code:1,footer:1,head:1,nav:1,pre:1,ruby:1
- };
-
- exposed.NON_CONTIGUOUS_PROSE_ELEMENTS = {
-
- // Elements that will not contain prose or block elements where we don't
- // want prose to be matches across element borders:
-
- // Block Elements
- address:1, article:1, aside:1, blockquote:1, dd:1, div:1,
- dl:1, fieldset:1, figcaption:1, figure:1, footer:1, form:1, h1:1, h2:1, h3:1,
- h4:1, h5:1, h6:1, header:1, hgroup:1, hr:1, main:1, nav:1, noscript:1, ol:1,
- output:1, p:1, pre:1, section:1, ul:1,
- // Other misc. elements that are not part of continuous inline prose:
- br:1, li: 1, summary: 1, dt:1, details:1, rp:1, rt:1, rtc:1,
- // Media / Source elements:
- script:1, style:1, img:1, video:1, audio:1, canvas:1, svg:1, map:1, object:1,
- // Input elements
- input:1, textarea:1, select:1, option:1, optgroup:1, button:1,
- // Table related elements:
- table:1, tbody:1, thead:1, th:1, tr:1, td:1, caption:1, col:1, tfoot:1, colgroup:1,
- // 自用, > v0.10.4 2022-07-25 添加
- a:1
-
- };
-
- exposed.NON_INLINE_PROSE = function(el) {
- return hasOwn.call(exposed.NON_CONTIGUOUS_PROSE_ELEMENTS, el.nodeName.toLowerCase());
- };
-
- // Presets accessed via `options.preset` when calling findAndReplaceDOMText():
- exposed.PRESETS = {
- prose: {
- forceContext: exposed.NON_INLINE_PROSE,
- filterElements: function(el) {
- // 在链接内的番号进一步筛选
- if(el.nodeName.toUpperCase() == "A"){
- // 疑似是磁力链接, 略过 magnet:?
- if(el.href.match(/magnet:\?/)){
- // 如果允许复制, 且不含有特定title
- // console.log("链接内含有磁链")
- if(window.qxin.CopyMagnet && !el.title.match(/点击复制磁力链接/)){
- // var odiv = document.createElement('savmagnet');
- // el.outerHTML = "<savmagnet>" + el.outerHTML + "</savmagnet>";
- el.title = "点击复制磁力链接";
- el.style.textDecoration= "underline #D9B412";
- el.addEventListener("click",function(){
- GM_setClipboard(el.href);
- if(window.qxin.QBit){
- // console.log("开始下载")
- window.qxin.QBit(el.href);
- }
- });
- }
- return false
- }
- // 排除在链接内的番号, 视为用户名, 排除
- if(!window.qxin.includeIDinLinks && el.innerText.search(/^[a-z|A-Z]{2,6}-?\d{2,5}(\.torrent)?$/i)>-1){
- // if(el.innerHTML.indexOf("-")<0){ // 导致链接中的 fc2 也会无法识别
- // console.log("------------------ 链接内没有横杠: ",el.innerText)
- return false
- }
- if(el.innerText.search(/^[a-z|A-Z]{2,6}\d{2,5}(\.torrent)?$/i)>-1){
- // if(el.innerHTML.indexOf("-")<0){ // 导致链接中的 fc2 也会无法识别
- // console.log("------------------ 链接内没有横杠: ",el.innerText)
- return false
- }
- }
-
- // td是由于图书馆论坛界面的用户名在td中。 位于td内, 且没有横杠的, 排除
- if(window.qxin.javlibrary && el.nodeName.toUpperCase() == "TD"){
- if(el.innerHTML.search(/^[a-z|A-Z]{2,6}\d{2,5}$/i)>-1){
- // if(el.innerHTML.indexOf("-")<0){
- // console.log("位于td内, 排除掉: " + el.innerHTML);
- return false
- }
- }
-
- // 根据class排除
- if(el.classList && el.classList.length
- // 对于svg , classname 返回 SVGAnimatedString 的对象导致报错
- && typeof(el.className)=="string"
- && el.className.match(window.qxin.RE_Exclude_className)
- && el.innerText.match(/(?<!\w)[a-z|A-Z]{2,6}[-\s]?\d{2,5}(?!\w)/i)
- && el.innerHTML.search("magnet:?")<0){
- // console.log("------------------ 特殊class内没有横杠: ",el.className,el)
- // console.log(el.innerText)
- return false
-
- }
-
- return !hasOwn.call(exposed.NON_PROSE_ELEMENTS, el.nodeName.toLowerCase());
- }
- }
- };
-
- exposed.Finder = Finder;
-
- /**
- * Finder -- encapsulates logic to find and replace.
- */
- function Finder(node, options) {
-
- var preset = options.preset && exposed.PRESETS[options.preset];
-
- options.portionMode = options.portionMode || PORTION_MODE_RETAIN;
-
- if (preset) {
- for (var i in preset) {
- if (hasOwn.call(preset, i) && !hasOwn.call(options, i)) {
- options[i] = preset[i];
- }
- }
- }
-
- this.node = node;
- this.options = options;
-
- // Enable match-preparation method to be passed as option:
- this.prepMatch = options.prepMatch || this.prepMatch;
-
- this.reverts = [];
-
- this.matches = this.search();
-
- if (this.matches.length) {
- this.processMatches();
- }
-
- }
-
- Finder.prototype = {
-
- /**
- * Searches for all matches that comply with the instance's 'match' option
- */
- search: function() {
-
- var match;
- var matchIndex = 0;
- var offset = 0;
- var regex = this.options.find;
- var textAggregation = this.getAggregateText();
- var matches = [];
- var self = this;
-
- regex = typeof regex === 'string' ? RegExp(escapeRegExp(regex), 'g') : regex;
-
- matchAggregation(textAggregation);
-
- function matchAggregation(textAggregation) {
- for (var i = 0, l = textAggregation.length; i < l; ++i) {
-
- var text = textAggregation[i];
-
- if (typeof text !== 'string') {
- // Deal with nested contexts: (recursive)
- matchAggregation(text);
- continue;
- }
-
- if (regex.global) {
- while (match = regex.exec(text)) {
- matches.push(self.prepMatch(match, matchIndex++, offset));
- }
- } else {
- if (match = text.match(regex)) {
- matches.push(self.prepMatch(match, 0, offset));
- }
- }
-
- offset += text.length;
- }
- }
-
- return matches;
-
- },
-
- /**
- * Prepares a single match with useful meta info:
- */
- prepMatch: function(match, matchIndex, characterOffset) {
-
- if (!match[0]) {
- throw new Error('findAndReplaceDOMText cannot handle zero-length matches');
- }
-
- match.endIndex = characterOffset + match.index + match[0].length;
- match.startIndex = characterOffset + match.index;
- match.index = matchIndex;
-
- return match;
- },
-
- /**
- * Gets aggregate text within subject node
- */
- getAggregateText: function() {
-
- var elementFilter = this.options.filterElements;
- var forceContext = this.options.forceContext;
-
- return getText(this.node);
-
- /**
- * Gets aggregate text of a node without resorting
- * to broken innerText/textContent
- */
- function getText(node) {
-
- if (node.nodeType === Node.TEXT_NODE) {
- return [node.data];
- }
-
- if (elementFilter && !elementFilter(node)) {
- return [];
- }
-
- var txt = [''];
- var i = 0;
-
- if (node = node.firstChild) do {
-
- if (node.nodeType === Node.TEXT_NODE) {
- txt[i] += node.data;
- continue;
- }
-
- var innerText = getText(node);
-
- if (
- forceContext &&
- node.nodeType === Node.ELEMENT_NODE &&
- (forceContext === true || forceContext(node))
- ) {
- txt[++i] = innerText;
- txt[++i] = '';
- } else {
- if (typeof innerText[0] === 'string') {
- // Bridge nested text-node data so that they're
- // not considered their own contexts:
- // I.e. ['some', ['thing']] -> ['something']
- txt[i] += innerText.shift();
- }
- if (innerText.length) {
- txt[++i] = innerText;
- txt[++i] = '';
- }
- }
- } while (node = node.nextSibling);
-
- return txt;
-
- }
-
- },
-
- /**
- * Steps through the target node, looking for matches, and
- * calling replaceFn when a match is found.
- */
- processMatches: function() {
-
- var matches = this.matches;
- var node = this.node;
- var elementFilter = this.options.filterElements;
-
- var startPortion,
- endPortion,
- innerPortions = [],
- curNode = node,
- match = matches.shift(),
- atIndex = 0, // i.e. nodeAtIndex
- matchIndex = 0,
- portionIndex = 0,
- doAvoidNode,
- nodeStack = [node];
-
- out: while (true) {
-
- if (curNode.nodeType === Node.TEXT_NODE) {
-
- if (!endPortion && curNode.length + atIndex >= match.endIndex) {
- // We've found the ending
- // (Note that, in the case of a single portion, it'll be an
- // endPortion, not a startPortion.)
- endPortion = {
- node: curNode,
- index: portionIndex++,
- text: curNode.data.substring(match.startIndex - atIndex, match.endIndex - atIndex),
-
- // If it's the first match (atIndex==0) we should just return 0
- indexInMatch: atIndex === 0 ? 0 : atIndex - match.startIndex,
-
- indexInNode: match.startIndex - atIndex,
- endIndexInNode: match.endIndex - atIndex,
- isEnd: true
- };
-
- } else if (startPortion) {
- // Intersecting node
- innerPortions.push({
- node: curNode,
- index: portionIndex++,
- text: curNode.data,
- indexInMatch: atIndex - match.startIndex,
- indexInNode: 0 // always zero for inner-portions
- });
- }
-
- if (!startPortion && curNode.length + atIndex > match.startIndex) {
- // We've found the match start
- startPortion = {
- node: curNode,
- index: portionIndex++,
- indexInMatch: 0,
- indexInNode: match.startIndex - atIndex,
- endIndexInNode: match.endIndex - atIndex,
- text: curNode.data.substring(match.startIndex - atIndex, match.endIndex - atIndex)
- };
- }
-
- atIndex += curNode.data.length;
-
- }
-
- doAvoidNode = curNode.nodeType === Node.ELEMENT_NODE && elementFilter && !elementFilter(curNode);
-
- if (startPortion && endPortion) {
-
- curNode = this.replaceMatch(match, startPortion, innerPortions, endPortion);
-
- // processMatches has to return the node that replaced the endNode
- // and then we step back so we can continue from the end of the
- // match:
-
- atIndex -= (endPortion.node.data.length - endPortion.endIndexInNode);
-
- startPortion = null;
- endPortion = null;
- innerPortions = [];
- match = matches.shift();
- portionIndex = 0;
- matchIndex++;
-
- if (!match) {
- break; // no more matches
- }
-
- } else if (
- !doAvoidNode &&
- (curNode.firstChild || curNode.nextSibling)
- ) {
- // Move down or forward:
- if (curNode.firstChild) {
- nodeStack.push(curNode);
- curNode = curNode.firstChild;
- } else {
- curNode = curNode.nextSibling;
- }
- continue;
- }
-
- // Move forward or up:
- while (true) {
- if (curNode.nextSibling) {
- curNode = curNode.nextSibling;
- break;
- }
- curNode = nodeStack.pop();
- if (curNode === node) {
- break out;
- }
- }
-
- }
-
- },
-
- /**
- * Reverts ... TODO
- */
- revert: function() {
- // Reversion occurs backwards so as to avoid nodes subsequently
- // replaced during the matching phase (a forward process):
- for (var l = this.reverts.length; l--;) {
- this.reverts[l]();
- }
- this.reverts = [];
- },
-
- prepareReplacementString: function(string, portion, match) {
- var portionMode = this.options.portionMode;
- if (
- portionMode === PORTION_MODE_FIRST &&
- portion.indexInMatch > 0
- ) {
- return '';
- }
- string = string.replace(/\$(\d+|&|`|')/g, function($0, t) {
- var replacement;
- switch(t) {
- case '&':
- replacement = match[0];
- break;
- case '`':
- replacement = match.input.substring(0, match.startIndex);
- break;
- case '\'':
- replacement = match.input.substring(match.endIndex);
- break;
- default:
- replacement = match[+t] || '';
- }
- return replacement;
- });
-
- if (portionMode === PORTION_MODE_FIRST) {
- return string;
- }
-
- if (portion.isEnd) {
- return string.substring(portion.indexInMatch);
- }
-
- return string.substring(portion.indexInMatch, portion.indexInMatch + portion.text.length);
- },
-
- getPortionReplacementNode: function(portion, match) {
-
- var replacement = this.options.replace || '$&';
- var wrapper = this.options.wrap;
- var wrapperClass = this.options.wrapClass;
-
- if (wrapper && wrapper.nodeType) {
- // Wrapper has been provided as a stencil-node for us to clone:
- var clone = doc.createElement('div');
- clone.innerHTML = wrapper.outerHTML || new XMLSerializer().serializeToString(wrapper);
- wrapper = clone.firstChild;
- }
-
- if (typeof replacement == 'function') {
- replacement = replacement(portion, match);
- if (replacement && replacement.nodeType) {
- return replacement;
- }
- return doc.createTextNode(String(replacement));
- }
-
- var el = typeof wrapper == 'string' ? doc.createElement(wrapper) : wrapper;
-
- if (el && wrapperClass) {
- el.className = wrapperClass;
- }
-
- replacement = doc.createTextNode(
- this.prepareReplacementString(
- replacement, portion, match
- )
- );
-
- if (!replacement.data) {
- return replacement;
- }
-
- if (!el) {
- return replacement;
- }
-
- el.appendChild(replacement);
-
- return el;
- },
-
- replaceMatch: function(match, startPortion, innerPortions, endPortion) {
-
- var matchStartNode = startPortion.node;
- var matchEndNode = endPortion.node;
-
- var precedingTextNode;
- var followingTextNode;
-
- if (matchStartNode === matchEndNode) {
-
- var node = matchStartNode;
-
- if (startPortion.indexInNode > 0) {
- // Add `before` text node (before the match)
- precedingTextNode = doc.createTextNode(node.data.substring(0, startPortion.indexInNode));
- node.parentNode.insertBefore(precedingTextNode, node);
- }
-
- // Create the replacement node:
- var newNode = this.getPortionReplacementNode(
- endPortion,
- match
- );
-
- node.parentNode.insertBefore(newNode, node);
-
- if (endPortion.endIndexInNode < node.length) { // ?????
- // Add `after` text node (after the match)
- followingTextNode = doc.createTextNode(node.data.substring(endPortion.endIndexInNode));
- node.parentNode.insertBefore(followingTextNode, node);
- }
-
- node.parentNode.removeChild(node);
-
- this.reverts.push(function() {
- if (precedingTextNode === newNode.previousSibling) {
- precedingTextNode.parentNode.removeChild(precedingTextNode);
- }
- if (followingTextNode === newNode.nextSibling) {
- followingTextNode.parentNode.removeChild(followingTextNode);
- }
- newNode.parentNode.replaceChild(node, newNode);
- });
-
- return newNode;
-
- } else {
- // Replace matchStartNode -> [innerMatchNodes...] -> matchEndNode (in that order)
-
-
- precedingTextNode = doc.createTextNode(
- matchStartNode.data.substring(0, startPortion.indexInNode)
- );
-
- followingTextNode = doc.createTextNode(
- matchEndNode.data.substring(endPortion.endIndexInNode)
- );
-
- var firstNode = this.getPortionReplacementNode(
- startPortion,
- match
- );
-
- var innerNodes = [];
-
- for (var i = 0, l = innerPortions.length; i < l; ++i) {
- var portion = innerPortions[i];
- var innerNode = this.getPortionReplacementNode(
- portion,
- match
- );
- portion.node.parentNode.replaceChild(innerNode, portion.node);
- this.reverts.push((function(portion, innerNode) {
- return function() {
- innerNode.parentNode.replaceChild(portion.node, innerNode);
- };
- }(portion, innerNode)));
- innerNodes.push(innerNode);
- }
-
- var lastNode = this.getPortionReplacementNode(
- endPortion,
- match
- );
-
- matchStartNode.parentNode.insertBefore(precedingTextNode, matchStartNode);
- matchStartNode.parentNode.insertBefore(firstNode, matchStartNode);
- matchStartNode.parentNode.removeChild(matchStartNode);
-
- matchEndNode.parentNode.insertBefore(lastNode, matchEndNode);
- matchEndNode.parentNode.insertBefore(followingTextNode, matchEndNode);
- matchEndNode.parentNode.removeChild(matchEndNode);
-
- this.reverts.push(function() {
- precedingTextNode.parentNode.removeChild(precedingTextNode);
- firstNode.parentNode.replaceChild(matchStartNode, firstNode);
- followingTextNode.parentNode.removeChild(followingTextNode);
- lastNode.parentNode.replaceChild(matchEndNode, lastNode);
- });
-
- return lastNode;
- }
- }
-
- };
-
- return exposed;
-
- }));