├── 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 |
--------------------------------------------------------------------------------