Change strings to other strings

// ==UserScript== // @name       Changer // @namespace  bulrush15 // @description Convert 'men' and 'women' to different words // @homepage https://github.com/bulrush15/gmscripts/tree/master // @downloadURL https://github.com/bulrush15/gmscripts/blob/master/Changer.user.js // @updateURL https://github.com/bulrush15/gmscripts/blob/master/Changer.user.js

// Include these sites. www.domain.com/*, required! // @include    http://www.reddit.com/* // @include    https://www.reddit.com/* // Exclude these sites. www.domain.com/* required!

// @version    0.03 // @grant      none // ==/UserScript== // Conversion table

// HTML Elements to check: p, h1, h2, h3, a, div, span, td, li, blockquote, label, option // Attributes to check: title, alt

//console.log("Starting, eleArr.length="+eleArr.length);

// First loop through every element to check for string in fromArr[]. //for (i=0; i < eleArr.length; i++) // { //  test1(eleArr[i]); // Process one element at a time. // }

//Make dividing line in console. console.log("=============================================="); console.log('GM_info.script.version: '+GM_info.script.version); console.log('GM version: '+GM_info.version);

// From http://commons.oreilly.com/wiki/index.php/Greasemonkey_Hacks/Beautifying_the_Web#Straighten_Smart_Quotes var arReplacements = { "Woman's":"Pink's", "woman": "pink", "women": "pinks", "Woman":"Pink", "Women":"Pink",

"man's": "blue's", "Man's":"Blue's", "men's":"blue's", "Men's":"Blue's", "men": "blues", "\bman\b": "blue", "Men": "Blues", "Man":"Blue",

"Girl's":"Pink's", "girl's":"pink's", "girl": "pink", "Girl":"Pink", };

var arRegex = new Array; for (var sKey in arReplacements) {	  arRegex[sKey] = new RegExp('\\b'+sKey+'\\b', 'g'); } //changetext; // uses .snapShotItem and arRegex.

test2; // Uses recursion /* for (var sKey in arReplacements) {    findAndReplaceDOMText(skey, '', arReplacements[skey]); }

//end of program

//////////////////////////////////////////////////////////////// function changetext {

var snapTextNodes = document.evaluate("//text[" +	  "not(ancestor::script) and not(ancestor::style)]",	   document, null, XPathResult.UNORDERED_NODE_SNAPSHOT_TYPE, null);

for (var i = snapTextNodes.snapshotLength - 1; i >= 0; i--) { var elmTextNode = snapTextNodes.snapshotItem(i); var sText = elmTextNode.nodeValue; for (var sKey in arReplacements) {	     sText = sText.replace(arRegex[sKey], arReplacements[sKey]); }   elmTextNode.nodeValue = sText; //elmTextNode.style.bgcolor='yellow'; }

} // changetext //////////////////////////////////////////////////////////////// function test2 {

var eleArr=['a', 'p', 'h1', 'h2', 'blockquote', 'div', 'td', 'li']; var fromArr=['woman', 'men', 'man', 'Men', 'Man', 'guy', 'guys', 'girl', 'Girl', 'girls']; var toArr=['pink', 'blues', 'blue', 'Blues', 'Blue', 'blue', 'blues', 'pink', 'pink', 'pinks']; if (fromArr.length != toArr.length) {   alert('fromArr and toArr do not have the same length!'); exit; }

for(var i=0; i < eleArr.length; i++){ var elements = document.getElementsByTagName(eleArr[i]); for(var j=0; j < elements.length; j++) {   	replaceText(elements[j], fromArr, toArr) } }

function replaceText(element, from, to) {   function recursiveReplace(elems) { // we need to traverse all descendants var procname='recurseiveReplace'; if(elems) {       	for(var k=0; k<elems.length; k++) { // for each element if((elems[k].nodeType == 3) && (elems[k].nodeValue)) { //if its a text node for(var j=0; j<from.length; j++) { // replace text //console.log(procname+': Looking for findstr: '+from[j]); var re = new RegExp('\\b' + from[j] + '\\b', 'g'); if (re.test(elems[k].nodeValue)==true) {                           //var s1='Replacing ' + from[j] + ' with '+to[j]; //console.log(s1); elems[k].nodeValue = elems[k].nodeValue.replace(re, to[j]); //elems[k].nodeValue=replaceEle(elems[k].nodeValue, re, to[j]); }                       } // for j                } recursiveReplace(elems[k].childNodes); // call same function with current node children } // for k       } }   recursiveReplace(element.childNodes) // start recursive function with passed element }

} // test2 //////////////////////////////////////////////////////////////// // In: s: string object. //    findStr: string to find //    replStr: string to replace with // Out: possibly changed string object. function replaceEle(s, findStr, replStr) { var i; var s2; var procname='replaceEle'; var arr=s.split(' ');

for (i=0; i<arr.length; i++) {   if (arr[i] = findStr) {       console.log(procname+': Replacing "'+findStr+'" with "'+replStr+'"'); arr[i]=replStr; }   } // for i

s2=arr.join(' '); return s2; } // replaceEle //////////////////////////////////////////////////////////////// /** * findAndReplaceDOMText v 0.4.3 * @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 toString = {}.toString; var hasOwn = {}.hasOwnProperty;

function isArray(a) { return toString.call(a) == '[object Array]'; }

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|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, // Input elements input:1, textarea:1, select:1, option:1, optgroup: 1, button: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

};

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) { 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, txt) {

if (node.nodeType === 3) { return [node.data]; }

if (elementFilter && !elementFilter(node)) { return []; }

var txt = ['']; var i = 0;

if (node = node.firstChild) do {

if (node.nodeType === 3) { txt[i] += node.data; continue; }

var innerText = getText(node);

if (						forceContext &&						node.nodeType === 1 &&						(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 === 3) {

if (!endPortion && curNode.length + atIndex >= match.endIndex) {

// We've found the ending endPortion = { node: curNode, index: portionIndex++, text: curNode.data.substring(match.startIndex - atIndex, match.endIndex - atIndex), indexInMatch: atIndex - match.startIndex, indexInNode: match.startIndex - atIndex, // always zero for end-portions 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 === 1 && 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, matchIndex) { 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, matchIndex) {

var replacement = this.options.replace || '$&'; var wrapper = this.options.wrap;

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, matchIndex); if (replacement && replacement.nodeType) { return replacement; }				return doc.createTextNode(String(replacement)); }

var el = typeof wrapper == 'string' ? doc.createElement(wrapper) : wrapper;

replacement = doc.createTextNode(				this.prepareReplacementString( replacement, portion, match, matchIndex )			);

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 preceedingTextNode; var followingTextNode;

if (matchStartNode === matchEndNode) {

var node = matchStartNode;

if (startPortion.indexInNode > 0) { // Add `before` text node (before the match) preceedingTextNode = doc.createTextNode(node.data.substring(0, startPortion.indexInNode)); node.parentNode.insertBefore(preceedingTextNode, 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 (preceedingTextNode === newNode.previousSibling) {						preceedingTextNode.parentNode.removeChild(preceedingTextNode);					}					if (followingTextNode === newNode.nextSibling) {						followingTextNode.parentNode.removeChild(followingTextNode);					}					newNode.parentNode.replaceChild(node, newNode);				});

return newNode;

} else { // Replace matchStartNode -> [innerMatchNodes...] -> matchEndNode (in that order)

preceedingTextNode = 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(preceedingTextNode, 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 {					preceedingTextNode.parentNode.removeChild(preceedingTextNode);					firstNode.parentNode.replaceChild(matchStartNode, firstNode);					followingTextNode.parentNode.removeChild(followingTextNode);					lastNode.parentNode.replaceChild(matchEndNode, lastNode);				});

return lastNode; }		}

};

return exposed;

}));

//////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////