├── Source.crx ├── Source ├── javascript.vim ├── images │ ├── icon128.png │ ├── icon16.png │ ├── icon48.png │ ├── icon19-off.png │ └── icon19-on.png ├── background.html ├── options.html ├── manifest.json ├── options.js ├── background.js ├── content_script.js └── dictionaries │ └── original.js ├── images ├── lake.png └── article.png ├── firefox ├── addon.xpi ├── lib │ └── main.js ├── package.json ├── test │ └── test-main.js └── data │ └── content.js └── README.md /Source.crx: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/snipe/downworthy/HEAD/Source.crx -------------------------------------------------------------------------------- /Source/javascript.vim: -------------------------------------------------------------------------------- 1 | setlocal shiftwidth=4 2 | setlocal tabstop=4 3 | -------------------------------------------------------------------------------- /images/lake.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/snipe/downworthy/HEAD/images/lake.png -------------------------------------------------------------------------------- /firefox/addon.xpi: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/snipe/downworthy/HEAD/firefox/addon.xpi -------------------------------------------------------------------------------- /images/article.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/snipe/downworthy/HEAD/images/article.png -------------------------------------------------------------------------------- /Source/images/icon128.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/snipe/downworthy/HEAD/Source/images/icon128.png -------------------------------------------------------------------------------- /Source/images/icon16.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/snipe/downworthy/HEAD/Source/images/icon16.png -------------------------------------------------------------------------------- /Source/images/icon48.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/snipe/downworthy/HEAD/Source/images/icon48.png -------------------------------------------------------------------------------- /Source/images/icon19-off.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/snipe/downworthy/HEAD/Source/images/icon19-off.png -------------------------------------------------------------------------------- /Source/images/icon19-on.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/snipe/downworthy/HEAD/Source/images/icon19-on.png -------------------------------------------------------------------------------- /firefox/lib/main.js: -------------------------------------------------------------------------------- 1 | var data = require("sdk/self").data; 2 | var pageMod = require("sdk/page-mod"); 3 | 4 | pageMod.PageMod({ 5 | include: "*", 6 | contentScriptFile: data.url("content.js") 7 | }); 8 | 9 | -------------------------------------------------------------------------------- /firefox/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "addon", 3 | "title": "addon", 4 | "id": "jid1-AjIZRnAiLrX5mA", 5 | "description": "a basic add-on", 6 | "author": "", 7 | "license": "MPL 2.0", 8 | "version": "0.1" 9 | } 10 | -------------------------------------------------------------------------------- /Source/background.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Background Page 5 | 6 | 7 | 8 | 9 | 10 | -------------------------------------------------------------------------------- /firefox/test/test-main.js: -------------------------------------------------------------------------------- 1 | var main = require("./main"); 2 | 3 | exports["test main"] = function(assert) { 4 | assert.pass("Unit test running!"); 5 | }; 6 | 7 | exports["test main async"] = function(assert, done) { 8 | assert.pass("async Unit test running!"); 9 | done(); 10 | }; 11 | 12 | require("sdk/test").run(exports); 13 | -------------------------------------------------------------------------------- /Source/options.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | Downworthy Options 4 | 5 | 6 |

7 |

8 | 9 | 10 | 11 | 12 | 13 | -------------------------------------------------------------------------------- /Source/manifest.json: -------------------------------------------------------------------------------- 1 | { 2 | "manifest_version": 2, 3 | "name": "Downworthy", 4 | "version": "0.0.8", 5 | "description": "Replaces hyberbolic headlines from bombastic viral websites with a slightly more realistic version.", 6 | "permissions": [ 7 | "storage" 8 | ], 9 | 10 | "background": 11 | { 12 | "page": "background.html" 13 | }, 14 | 15 | "content_scripts": 16 | [ 17 | { 18 | "matches": ["*://*/*"], 19 | "js": ["content_script.js"], 20 | "run_at": "document_end" 21 | } 22 | ], 23 | 24 | "icons": 25 | { 26 | "16": "images/icon16.png", 27 | "48": "images/icon48.png", 28 | "128": "images/icon128.png" 29 | }, 30 | 31 | "browser_action": 32 | { 33 | "default_icon": "images/icon19-on.png", 34 | "default_title": "Toggle Downworthy" 35 | }, 36 | "content_security_policy": "default-src 'none'; script-src 'self'", 37 | "options_page": "options.html" 38 | 39 | } 40 | 41 | -------------------------------------------------------------------------------- /Source/options.js: -------------------------------------------------------------------------------- 1 | var startingOptions = JSON.parse(localStorage.getItem("options")); 2 | var start = !!startingOptions; 3 | 4 | function splitTrim(string, delim) { 5 | split = string.split(delim); 6 | for (var i=0; i 0.5; // Flip a coin! 25 | lastChangedAt = setPaused(pause); 26 | } 27 | 28 | // Set up the next check. 29 | if(!_alreadyQueued) { 30 | pollTimeout = (lastChangedAt + ONE_DAY) - now(); 31 | 32 | setTimeout(function() { 33 | _alreadyQueued = false; 34 | checkForRandomSwap(); 35 | }, pollTimeout); 36 | 37 | _alreadyQueued = true; 38 | } 39 | } 40 | } 41 | 42 | function updateBadge(paused) { 43 | var badgeText = paused ? "OFF" : ""; 44 | chrome.browserAction.setBadgeText( { text: badgeText } ); 45 | } 46 | 47 | function isPaused() { 48 | return (localStorage.getItem(KEY_PAUSED) == 'true'); 49 | } 50 | 51 | function setPaused(paused) { 52 | var lastChangedAt = now(); 53 | 54 | localStorage.setItem(KEY_PAUSED, paused); 55 | chrome.storage.sync.set( { 'paused': paused } ); 56 | updateBadge(paused); 57 | 58 | localStorage.setItem(KEY_LAST_CHANGED_AT, lastChangedAt); 59 | return lastChangedAt; 60 | } 61 | 62 | function togglePause(tab) { 63 | setPaused(!isPaused()); 64 | 65 | // Reload the current tab. 66 | chrome.tabs.update(tab.id, {url: tab.url}); 67 | } 68 | 69 | function getExcluded() { 70 | var opts = JSON.parse(localStorage.getItem(KEY_OPTIONS)); 71 | return opts ? opts['excluded'] : []; 72 | } 73 | 74 | function onMessage(request, sender, sendResponse) { 75 | var requestId = request.id; 76 | 77 | if(requestId == 'isPaused?') { 78 | // TODO: Convert to boolean. 79 | sendResponse({value: isPaused()}); 80 | } 81 | else if(requestId == 'getExcluded') { 82 | sendResponse({value: getExcluded()}); 83 | } 84 | else if(requestId == 'setOptions') { 85 | localStorage.setItem(KEY_OPTIONS, request.options); 86 | } 87 | else if(requestId == 'getDictionary') { 88 | sendResponse(dictionary); 89 | } 90 | } 91 | 92 | chrome.browserAction.onClicked.addListener(togglePause); 93 | chrome.extension.onRequest.addListener(onMessage); 94 | 95 | // TODO: Have an option where you can select a specific replacement set, such as "Standard", "Cynical Millenial", etc. 96 | // TODO: The option value would then be passed into loadDictionary for appropriate dictionary file selection. 97 | 98 | updateBadge(isPaused()); 99 | 100 | checkForRandomSwap(); 101 | 102 | })(); 103 | -------------------------------------------------------------------------------- /Source/content_script.js: -------------------------------------------------------------------------------- 1 | (function() { 2 | var _self = this; 3 | var _dictionary; 4 | 5 | function getDictionary(callback) { 6 | chrome.extension.sendRequest({id: "getDictionary"}, function(response) { 7 | _dictionary = response; // Store the dictionary for later use. 8 | callback.apply(_self, arguments); 9 | }); 10 | } 11 | 12 | function handleText(textNode) { 13 | var replacements = _dictionary.replacements; 14 | var expressions = _dictionary.expressions; 15 | var v = textNode.nodeValue; 16 | var matchFound = false; 17 | 18 | var regex, original; 19 | 20 | //text replacements 21 | for(original in replacements) { 22 | original_escaped = original; 23 | 24 | regex_for_question_mark = /\?/g 25 | regex_for_period = /\./g 26 | 27 | original_escaped = original_escaped.replace(regex_for_question_mark, "\\?"); 28 | original_escaped = original_escaped.replace(regex_for_period, "\\."); 29 | 30 | regex = new RegExp('\\b' + original_escaped + '\\b', "gi"); 31 | if (v.match(regex)) { 32 | v = v.replace(regex, replacements[original]); 33 | matchFound = true; 34 | } 35 | 36 | } 37 | 38 | // regex replacements 39 | for(original in expressions) { 40 | regex = new RegExp(original, "g"); 41 | if (v.match(regex)) { 42 | v = v.replace(regex, expressions[original]); 43 | matchFound = true; 44 | } 45 | 46 | } 47 | 48 | // Only change the node if there was any actual text change 49 | if (matchFound) { 50 | textNode.nodeValue = v; 51 | } 52 | 53 | } 54 | 55 | function walk(node) { 56 | 57 | // I stole this function from here: - ZW 58 | // And I stole it from ZW - AG 59 | // http://is.gd/mwZp7E 60 | 61 | var child, next; 62 | 63 | switch(node.nodeType) { 64 | case 1: // Element 65 | case 9: // Document 66 | case 11: // Document fragment 67 | child = node.firstChild; 68 | while(child) { 69 | next = child.nextSibling; 70 | walk(child); 71 | child = next; 72 | } 73 | break; 74 | case 3: // Text node 75 | handleText(node); 76 | break; 77 | } 78 | } 79 | 80 | 81 | 82 | // Flag to prevent multiple triggering of DOMSubtreeModified 83 | // set it to true initially so that the DOMSubtreeModified event 84 | // does not trigger work until the two chrome.extension requests 85 | // have been handled 86 | var running = true; 87 | 88 | 89 | // Function that calls walk() but makes sure that it only is called once 90 | // the first call has finished. Any changes that we make to the DOM in walk() 91 | // will trigget DOMSubtreeModified, so we handle this by using the running flag 92 | function work() { 93 | // Set running to true to prevent more calls until the first one is done 94 | running = true; 95 | 96 | // Go through the DOM 97 | walk(document.body); 98 | 99 | // Set running to false to allow additional calls 100 | running = false; 101 | } 102 | 103 | 104 | 105 | chrome.extension.sendRequest({id: 'isPaused?'}, function(response) { 106 | var isPaused = response.value; 107 | 108 | // If the extension is paused, no need to try to call getExcluded 109 | if(isPaused) { 110 | return; 111 | } 112 | 113 | chrome.extension.sendRequest({id: 'getExcluded'}, function (r2) { 114 | 115 | var ex = r2.value; 116 | for (x in ex) { 117 | if (window.location.href.indexOf(ex[x]) != -1) { 118 | return; 119 | } 120 | } 121 | 122 | getDictionary(function() { 123 | work(); 124 | }); 125 | }); 126 | 127 | }); 128 | 129 | 130 | 131 | 132 | /** 133 | The below solution to handle dynamically added content 134 | is borrowed from http://stackoverflow.com/a/7326468 135 | */ 136 | 137 | // Add a timer to prevent instant triggering on each DOM change 138 | var timeout = null; 139 | 140 | // Add an eventlistener for changes to the DOM, e.g. new content has been loaded via AJAX or similar 141 | // Any changes that we do to the DOM will trigger this event, so we need to prevent infinite looping 142 | // by checking the running flag first. 143 | document.addEventListener('DOMSubtreeModified', function(){ 144 | if (running) { 145 | return; 146 | } 147 | 148 | if (timeout) { 149 | clearTimeout(timeout); 150 | } 151 | 152 | timeout = setTimeout(work, 500); 153 | }, false); 154 | 155 | })(); 156 | -------------------------------------------------------------------------------- /Source/dictionaries/original.js: -------------------------------------------------------------------------------- 1 | var dictionary={ 2 | "replacements": { 3 | "A Single" : "A", 4 | "Absolutely" : "Moderately", 5 | "Amazing" : "Barely Noticeable", 6 | "Awesome" : "Probably Slightly Less Boring Than Working", 7 | "Best" : "Most Unexceptional", 8 | "Breathtaking" : "Fleetingly Inspirational", 9 | "But what happened next" : "And As You Expect It", 10 | "Can change your life" : "Will Not Change Your Life in ANY Meaningful Way", 11 | "Can't Even Handle" : "Can Totally Handle Without Any Significant Issue", 12 | "Can't Handle" : "Can Totally Handle Without Any Significant Issue", 13 | "Cannot Even Handle" : "Can Probably Totally Handle", 14 | "Doesn't want you to see" : "Doesn't Really Care If You See", 15 | "Epic" : "Mundane", 16 | "Everything You Need To Know" : "Something You Don't Need To Know", 17 | "Gasp-Worthy" : "Yawn-Worthy", 18 | "Go Viral" : "Be Overused So Much That You'll Silently Pray for the Sweet Release of Death to Make it Stop", 19 | "Greatest" : "Average", 20 | "Incredible" : "Painfully Ordinary", 21 | "Infuriate" : "Mildly Annoy", 22 | "Literally" : "Figuratively", 23 | "Mind Blowing" : "Mind-Numbingly Ordinary", 24 | "Mind-Blowing" : "Painfully Ordinary", 25 | "Mind BLOWN" : "Meh", 26 | "Mind Blown" : "Meh", 27 | "Need To Visit Before You Die" : "May Enjoy If You Get Around To It", 28 | "Nothing Could Prepare Me For" : "Does ANYONE Fucking Care About", 29 | "Of All Time" : "For Now", 30 | "Of All Time" : "Of The Last 30 Seconds", 31 | "Of All-Time" : "For Now", 32 | "OMG" : "*yawn*", 33 | "OMG" : "No One Cares. At All", 34 | "One Weird Trick" : "One Piece of Completely Anecdotal Horseshit", 35 | "Perfection" : "Mediocrity", 36 | "Priceless" : "Painfully Ordinary", 37 | "Prove" : "Suggest", 38 | "Right Now" : "Eventually", 39 | "Scientific Reasons" : "Vaguely Science-y Reasons", 40 | "Shocked" : "Vaguely Surprised", 41 | "Shocking" : "Barely Noticeable", 42 | "Simple Lessons" : "Inane Pieces of Bullshit Advice", 43 | "Stop What You're Doing" : "Bookmark Now and Later Completely Forget About", 44 | "Stop What You’re Doing" : "Bookmark Now and Later Completely Forget About", 45 | "Stop What You’re Doing" : "Bookmark Now and Later Completely Forget About", 46 | "TERRIFYING" : "MODERATELY UNCOMFORTABLE", 47 | "Terrifying" : "Thoroughly Banal", 48 | "That Will Make You Rethink" : "That You May Find Vaguely Interesting But Won't Change Your Life in Any Way", 49 | "The World's Best" : "An Adequate", 50 | "This Is What Happens" : "This Is Our Bullshit Clickbait Version Of What Happens", 51 | "Totally blew my mind" : "Bored Me To Tears", 52 | "Unbelievable" : "Painfully Ordinary", 53 | "Unimaginable" : "Actually Kind of Droll", 54 | "WHAT?" : "Some Other Crap", 55 | "Whoa" : "*yawn*", 56 | "WHOA" : "Zzzzzzzzzzz", 57 | "Whoah" : "*yawn*", 58 | "Will Blow Your Mind" : "Might Perhaps Mildly Entertain You For a Moment", 59 | "Will Change Your Life Forever" : "Will Not Change Your Life in ANY Meaningful or Lasting Way", 60 | "Won the Internet" : "Seemed Pretty Cool", 61 | "Wonderful" : "Mildly Decent", 62 | "Worst" : "Vaguely Unpleasant", 63 | "Wow" : "Oh GOD This is SO Boring. Please Kill Me", 64 | "WOW" : "Zzzzzzzzzzz", 65 | "You Didn't Know Exist" : "No One Gives a Shit About", 66 | "You Didn't Know Existed" : "No One Gives a Shit About", 67 | "You Didn’t Know Exist" : "No One Gives a Shit About", 68 | "You Didn’t Know Existed" : "No One Gives a Shit About", 69 | "You Didn’t Know Exist" : "No One Gives a Shit About", 70 | "You Didn’t Know Existed" : "No One Gives a Shit About", 71 | "You Won't Believe" : "In All Likelihood, You'll Believe", 72 | "You Won’t Believe" : "In All Likelihood, You'll Believe", 73 | "You Won’t Believe" : "In All Likelihood, You'll Believe", 74 | "You Wont Believe" : "In All Likelihood, You'll Believe", 75 | "Have To See To Believe": "Might Have Trouble Picturing" 76 | }, 77 | 78 | "expressions": { 79 | "\\b(?:Top )?((?:(?:\\d+|One|Two|Three|Four|Five|Six|Seven|Eight|Nine|Ten|Eleven|Twelve|Thirteen|Fourteen|Fifteen|Sixteen|Seventeen|Eighteen|Nineteen|Twenty|Thirty|Forty|Fourty|Fifty|Sixty|Seventy|Eighty|Ninety|Hundred)(?: |-)?)+) Things" : "Inane Listicle of $1 Things You've Already Seen Somewhere Else", 80 | "\\b[Rr]estored [Mm]y [Ff]aith [Ii]n [Hh]umanity\\b" : "Affected Me In No Meaningful Way Whatsoever", 81 | "\\b[Rr]estored [Oo]ur [Ff]aith [Ii]n [Hh]umanity\\b" : "Affected Us In No Meaningful Way Whatsoever", 82 | "\\b(?:Top )?((?:(?:\\d+|One|Two|Three|Four|Five|Six|Seven|Eight|Nine|Ten|Eleven|Twelve|Thirteen|Fourteen|Fifteen|Sixteen|Seventeen|Eighteen|Nineteen|Twenty|Thirty|Forty|Fourty|Fifty|Sixty|Seventy|Eighty|Ninety|Hundred)(?: |-)?)+) Weird" : "$1 Boring", 83 | "\\b^(Is|Can|Do|Will) (.*)\\?\\B" : "$1 $2? Maybe, but Most Likely Not.", 84 | "\\b^([Rr]easons\\s|[Ww]hy\\s|[Hh]ow\\s|[Ww]hat\\s[Yy]ou\\s[Ss]hould\\s[Kk]now\\s[Aa]bout\\s)(.*)\\b$":"$2", 85 | "\\bThe Best(\\s\\w+)+\\sEver\\b":"Some Lame $1" 86 | } 87 | }; 88 | -------------------------------------------------------------------------------- /firefox/data/content.js: -------------------------------------------------------------------------------- 1 | (function() { 2 | var _self = this; 3 | var _dictionary = { 4 | "replacements": { 5 | "A Single" : "A", 6 | "Absolutely" : "Moderately", 7 | "Amazing" : "Barely Noticeable", 8 | "Awesome" : "Probably Slightly Less Boring Than Working", 9 | "Best" : "Most Unexceptional", 10 | "Breathtaking" : "Fleetingly Inspirational", 11 | "But what happened next" : "And As You Expect It", 12 | "Can change your life" : "Will Not Change Your Life in ANY Meaningful Way", 13 | "Can't Even Handle" : "Can Totally Handle Without Any Significant Issue", 14 | "Can't Handle" : "Can Totally Handle Without Any Significant Issue", 15 | "Cannot Even Handle" : "Can Probably Totally Handle", 16 | "Doesn't want you to see" : "Doesn't Really Care If You See", 17 | "Epic" : "Mundane", 18 | "Everything You Need To Know" : "Something You Don't Need To Know", 19 | "Gasp-Worthy" : "Yawn-Worthy", 20 | "Go Viral" : "Be Overused So Much That You'll Silently Pray for the Sweet Release of Death to Make it Stop", 21 | "Greatest" : "Average", 22 | "Incredible" : "Painfully Ordinary", 23 | "Infuriate" : "Mildly Annoy", 24 | "Literally" : "Figuratively", 25 | "Mind Blowing" : "Mind-Numbingly Ordinary", 26 | "Mind-Blowing" : "Painfully Ordinary", 27 | "Mind BLOWN" : "Meh", 28 | "Mind Blown" : "Meh", 29 | "Need To Visit Before You Die" : "May Enjoy If You Get Around To It", 30 | "Nothing Could Prepare Me For" : "Does ANYONE Fucking Care About", 31 | "Of All Time" : "For Now", 32 | "Of All Time" : "Of The Last 30 Seconds", 33 | "Of All-Time" : "For Now", 34 | "OMG" : "*yawn*", 35 | "OMG" : "No One Cares. At All", 36 | "One Weird Trick" : "One Piece of Completely Anecdotal Horseshit", 37 | "Perfection" : "Mediocrity", 38 | "Priceless" : "Painfully Ordinary", 39 | "Prove" : "Suggest", 40 | "Right Now" : "Eventually", 41 | "Scientific Reasons" : "Vaguely Science-y Reasons", 42 | "Shocked" : "Vaguely Surprised", 43 | "Shocking" : "Barely Noticeable", 44 | "Simple Lessons" : "Inane Pieces of Bullshit Advice", 45 | "Stop What You're Doing" : "Bookmark Now and Later Completely Forget About", 46 | "Stop What You’re Doing" : "Bookmark Now and Later Completely Forget About", 47 | "Stop What You’re Doing" : "Bookmark Now and Later Completely Forget About", 48 | "TERRIFYING" : "MODERATELY UNCOMFORTABLE", 49 | "Terrifying" : "Thoroughly Banal", 50 | "That Will Make You Rethink" : "That You May Find Vaguely Interesting But Won't Change Your Life in Any Way", 51 | "The World's Best" : "An Adequate", 52 | "This Is What Happens" : "This Is Our Bullshit Clickbait Version Of What Happens", 53 | "Totally blew my mind" : "Bored Me To Tears", 54 | "Unbelievable" : "Painfully Ordinary", 55 | "Unimaginable" : "Actually Kind of Droll", 56 | "WHAT?" : "Some Other Crap", 57 | "Whoa" : "*yawn*", 58 | "WHOA" : "Zzzzzzzzzzz", 59 | "Whoah" : "*yawn*", 60 | "Will Blow Your Mind" : "Might Perhaps Mildly Entertain You For a Moment", 61 | "Will Change Your Life Forever" : "Will Not Change Your Life in ANY Meaningful or Lasting Way", 62 | "Won the Internet" : "Seemed Pretty Cool", 63 | "Wonderful" : "Mildly Decent", 64 | "Worst" : "Vaguely Unpleasant", 65 | "Wow" : "Oh GOD This is SO Boring. Please Kill Me", 66 | "WOW" : "Zzzzzzzzzzz", 67 | "You Didn't Know Exist" : "No One Gives a Shit About", 68 | "You Didn't Know Existed" : "No One Gives a Shit About", 69 | "You Didn’t Know Exist" : "No One Gives a Shit About", 70 | "You Didn’t Know Existed" : "No One Gives a Shit About", 71 | "You Didn’t Know Exist" : "No One Gives a Shit About", 72 | "You Didn’t Know Existed" : "No One Gives a Shit About", 73 | "You Won't Believe" : "In All Likelihood, You'll Believe", 74 | "You Won’t Believe" : "In All Likelihood, You'll Believe", 75 | "You Won’t Believe" : "In All Likelihood, You'll Believe", 76 | "You Wont Believe" : "In All Likelihood, You'll Believe" 77 | }, 78 | 79 | "expressions": { 80 | "\\b(?:Top )?((?:(?:\\d+|One|Two|Three|Four|Five|Six|Seven|Eight|Nine|Ten|Eleven|Twelve|Thirteen|Fourteen|Fifteen|Sixteen|Seventeen|Eighteen|Nineteen|Twenty|Thirty|Forty|Fourty|Fifty|Sixty|Seventy|Eighty|Ninety|Hundred)(?: |-)?)+) Things" : "Inane Listicle of $1 Things You've Already Seen Somewhere Else", 81 | "\\b[Rr]estored [Mm]y [Ff]aith [Ii]n [Hh]umanity\\b" : "Affected Me In No Meaningful Way Whatsoever", 82 | "\\b[Rr]estored [Oo]ur [Ff]aith [Ii]n [Hh]umanity\\b" : "Affected Us In No Meaningful Way Whatsoever", 83 | "\\b(?:Top )?((?:(?:\\d+|One|Two|Three|Four|Five|Six|Seven|Eight|Nine|Ten|Eleven|Twelve|Thirteen|Fourteen|Fifteen|Sixteen|Seventeen|Eighteen|Nineteen|Twenty|Thirty|Forty|Fourty|Fifty|Sixty|Seventy|Eighty|Ninety|Hundred)(?: |-)?)+) Weird" : "$1 Boring", 84 | "\\b^(Is|Can|Do|Will) (.*)\\?\\B" : "$1 $2? Maybe, but Most Likely Not.", 85 | "\\b^([Rr]easons\\s|[Ww]hy\\s|[Hh]ow\\s|[Ww]hat\\s[Yy]ou\\s[Ss]hould\\s[Kk]now\\s[Aa]bout\\s)(.*)\\b$":"$2" 86 | } 87 | }; 88 | 89 | function handleText(textNode) { 90 | var replacements = _dictionary.replacements; 91 | var expressions = _dictionary.expressions; 92 | var v = textNode.nodeValue; 93 | var matchFound = false; 94 | 95 | var regex, original; 96 | 97 | //text replacements 98 | for(original in replacements) { 99 | original_escaped = original; 100 | 101 | regex_for_question_mark = /\?/g 102 | regex_for_period = /\./g 103 | 104 | original_escaped = original_escaped.replace(regex_for_question_mark, "\\?"); 105 | original_escaped = original_escaped.replace(regex_for_period, "\\."); 106 | 107 | regex = new RegExp('\\b' + original_escaped + '\\b', "gi"); 108 | if (v.match(regex)) { 109 | v = v.replace(regex, replacements[original]); 110 | matchFound = true; 111 | } 112 | 113 | } 114 | 115 | // regex replacements 116 | for(original in expressions) { 117 | regex = new RegExp(original, "g"); 118 | if (v.match(regex)) { 119 | v = v.replace(regex, expressions[original]); 120 | matchFound = true; 121 | } 122 | 123 | } 124 | 125 | // Only change the node if there was any actual text change 126 | if (matchFound) { 127 | textNode.nodeValue = v; 128 | } 129 | 130 | } 131 | 132 | function walk(node) { 133 | 134 | // I stole this function from here: - ZW 135 | // And I stole it from ZW - AG 136 | // http://is.gd/mwZp7E 137 | 138 | var child, next; 139 | 140 | switch(node.nodeType) { 141 | case 1: // Element 142 | case 9: // Document 143 | case 11: // Document fragment 144 | child = node.firstChild; 145 | while(child) { 146 | next = child.nextSibling; 147 | walk(child); 148 | child = next; 149 | } 150 | break; 151 | case 3: // Text node 152 | handleText(node); 153 | break; 154 | } 155 | } 156 | 157 | 158 | 159 | // Flag to prevent multiple triggering of DOMSubtreeModified 160 | // set it to true initially so that the DOMSubtreeModified event 161 | // does not trigger work until the two chrome.extension requests 162 | // have been handled 163 | var running = false; 164 | 165 | 166 | // Function that calls walk() but makes sure that it only is called once 167 | // the first call has finished. Any changes that we make to the DOM in walk() 168 | // will trigget DOMSubtreeModified, so we handle this by using the running flag 169 | function work() { 170 | // Set running to true to prevent more calls until the first one is done 171 | running = true; 172 | 173 | // Go through the DOM 174 | walk(document.body); 175 | 176 | // Set running to false to allow additional calls 177 | running = false; 178 | } 179 | 180 | /** 181 | The below solution to handle dynamically added content 182 | is borrowed from http://stackoverflow.com/a/7326468 183 | */ 184 | 185 | // Add a timer to prevent instant triggering on each DOM change 186 | var timeout = null; 187 | 188 | // Add an eventlistener for changes to the DOM, e.g. new content has been loaded via AJAX or similar 189 | // Any changes that we do to the DOM will trigger this event, so we need to prevent infinite looping 190 | // by checking the running flag first. 191 | document.addEventListener('DOMSubtreeModified', function(){ 192 | if (running) { 193 | return; 194 | } 195 | 196 | if (timeout) { 197 | clearTimeout(timeout); 198 | } 199 | 200 | timeout = setTimeout(work, 500); 201 | }, false); 202 | })(); 203 | 204 | --------------------------------------------------------------------------------