Skip to content
This repository has been archived by the owner on Jun 13, 2024. It is now read-only.

Fix recursive substitutions, add an option to use the XKCD font, and add mouseovers. #48

Open
wants to merge 4 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
13 changes: 13 additions & 0 deletions substitutions/html/config.html
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,19 @@ <h1>XKCD Substitutions</h1>
</div>
</div>
<div id="options">
<div>
<h3>Use XKCD Font</h3>
<p>To help you remember that the page is more awesome, this extension can write its substitutions in Randall's handwriting, just like the font on this page.</p>
<form id="use-font-form">
<div class="checkbox">
<label>
<input id="use-font" type="checkbox" value="">
Use XKCD Font
</label>
</div>
</form>
</div>

<div>
<h3>Website Blacklist</h3>
<p>While this extension tries not to affect things like login forms or emails, due to the complex nature of websites this cannot be guaranteed. So there is a way to stop this extension working on specific sites.</p>
Expand Down
10 changes: 9 additions & 1 deletion substitutions/js/background.js
Original file line number Diff line number Diff line change
Expand Up @@ -79,6 +79,7 @@ var default_blacklisted_sites = [
"outlook.com",
"xkcd.com"
];
var default_use_font = true;

var debug = false;

Expand Down Expand Up @@ -113,7 +114,9 @@ function addMessage(request, sender, sendResponse) {
if (debug) { console.log("message fire"); }
chrome.storage.sync.get(null, function(result) {
if (request === "config" && result["replacements"]) {
sendResponse(result["replacements"]);
sendResponse({"replacements": result["replacements"],
"useFont": result["useFont"],
"fontUrl": chrome.extension.getURL("fonts/xkcd.otf")});
}
});
return true;
Expand All @@ -137,6 +140,11 @@ function fixDataCorruption() {
"blacklist": default_blacklisted_sites
});
}
if (!result["useFont"]) {
chrome.storage.sync.set({
"useFont": default_use_font
});
}
});
}

Expand Down
13 changes: 10 additions & 3 deletions substitutions/js/options.js
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ function makeClean() {
function saveOptions(e) {
e.preventDefault;
console.log("ping");
var useFont = $("#use-font").prop("checked");
var blacklist = $("#website-blacklist").val().replace(/\s+/g, "").toLowerCase().split(",");
var replacements = [];
var originals = $('#replacements [name="origin"]');
Expand All @@ -30,7 +31,8 @@ function saveOptions(e) {
};
chrome.storage.sync.set({
"blacklist": blacklist,
"replacements": replacements
"replacements": replacements,
"useFont": useFont
},
function() {
makeClean();
Expand All @@ -41,7 +43,8 @@ function saveOptions(e) {
function reset(){
chrome.storage.sync.set({
"blacklist": default_blacklisted_sites,
"replacements": default_replacements
"replacements": default_replacements,
"useFont": default_use_font
},
function() {
makeClean();
Expand All @@ -53,7 +56,9 @@ function reset(){
function populateSettings() {
$("#replacements").empty();
$("blacklist input").val("");
$("#use-font").prop("checked", false);
chrome.storage.sync.get(null, function(result) {
$("#use-font").prop("checked", result["useFont"]);
$("#blacklist input").val(result["blacklist"].join(", "));
var replacements = result['replacements'];
for (var i = 0; i < replacements.length; i++) {
Expand Down Expand Up @@ -94,7 +99,9 @@ $(document).ready(function() {

$("#refresh").on('click', populateSettings);

$("#reset").on('click', reset)
$("#reset").on('click', reset);

$("#use-font").on('click', makeDirty);

$("#blacklist").keypress(function(e) {
if (e.which == 13) {
Expand Down
159 changes: 127 additions & 32 deletions substitutions/js/substitutions.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,8 @@
// Icon and idea are from www.xkcd.com/1288
chrome.runtime.sendMessage("config", function(response) {
"use strict";
// taken from http://stackoverflow.com/questions/17264639/replace-text-but-keep-case

// Taken from http://stackoverflow.com/questions/17264639/replace-text-but-keep-case
function matchCase(text, pattern) {
var result = '';
for (var i = 0; i < text.length; i++) {
Expand All @@ -16,40 +17,134 @@ chrome.runtime.sendMessage("config", function(response) {
}
return result;
}
var substitute = (function() {
"use strict";
var replacements, ignore, i, replacementsObject, original;
replacements = response;
replacementsObject = [];
for (i = replacements.length - 1; i >= 0; i--) {
original = new RegExp("\\b" + replacements[i][0] + "\\b", "gi");
replacementsObject.push([original, replacements[i][1]]);
}
return function(node) {
var i;
var ignore = {
"STYLE": 0,
"SCRIPT": 0,
"NOSCRIPT": 0,
"IFRAME": 0,
"OBJECT": 0,
"INPUT": 0,
"FORM": 0,
"TEXTAREA": 0
};
if (node.parentElement.tagName in ignore) {
return;

// Taken from Google's Closure library, goog.string.regExpEscape
function regExpEscape(s) {
return String(s)
.replace(/([-()\[\]{}+?*.$\^|,:#<!\\])/g, '\\$1')
.replace(/\x08/g, '\\x08');
}

// Inject the stylesheet.
if (response.useFont) {
var stylesheet = document.createElement('style');
stylesheet.setAttribute('type', 'text/css');
stylesheet.textContent =
'@font-face {' +
' font-family: xkcdSubstitutionsFont;' +
' src: url("' + response.fontUrl + '") format("opentype");' +
'}' +
'.xkcdSubstitutionsExtensionSubbed {' +
' font-family: xkcdSubstitutionsFont !important;' +
' font-variant: small-caps;' +
'}';
document.head.appendChild(stylesheet);
}

// Compute the objects we'll need for each node.
var replacements = response.replacements;
var originalsRegexpSegments = replacements.map(function (replacement) {
return regExpEscape(replacement[0]);
});
var originalsRegexp = new RegExp("\\b(" +
originalsRegexpSegments.join("|") +
")\\b", "gi");
var replacementsMap = {};
for (var i = replacements.length - 1; i >= 0; i--) {
replacementsMap[replacements[i][0].toLowerCase()] = replacements[i][1];
}
var ignore = {
"STYLE": 0,
"SCRIPT": 0,
"NOSCRIPT": 0,
"IFRAME": 0,
"OBJECT": 0,
"INPUT": 0,
"FORM": 0,
"TEXTAREA": 0
};

// Set up the functions we'll need to perform the iteration.
var node;
var iter;

var filter = {
acceptNode: function(node) {
if (node.nodeType == Node.TEXT_NODE) {
// This is a text node that we should show to the
// filter.
return NodeFilter.FILTER_ACCEPT;
}
if (node.nodeType == Node.ELEMENT_NODE) {
// Fold to upper case before checking the tag name,
// since this may be XHTML etc.
if (node.tagName.toUpperCase() in ignore) {
// Ignore this element, and all its children.
return NodeFilter.FILTER_REJECT;
}
if (node.classList.contains("xkcdSubstitutionsExtensionSubbed")) {
// We've already changed this text. Note that some
// other extension or the page's own scripts may
// have made more changes to what we did, so don't
// fight back and forth, constantly changing the
// DOM. Instead, just skip this subtree entirely.
return NodeFilter.FILTER_REJECT;
}
}
// This is not a node we're interested in. Skip this node,
// but process its children.
return NodeFilter.FILTER_SKIP;
}
for (i = replacementsObject.length - 1; i >= 0; i--) {
node.nodeValue = node.nodeValue.replace(replacementsObject[i][0], function(match) {
return matchCase(replacementsObject[i][1], match);
});
};

function substitute(node) {
var replacementIdx;
var splitIdx;

// Before starting, make sure there's something to substitute.
// Otherwise, we end up doing a lot of expensive tree modification
// for no reason.
if (!node.nodeValue.match(originalsRegexp)) {
return;
}

// Prepare a document fragment to hold the result.
var docFrag = document.createDocumentFragment();

// Split the string into substring, where each substring either contains
// something we'll substitute, or something that we won't. We do this
// by using the capturing parentheses in originalsRegexp.
var splits = node.nodeValue.split(originalsRegexp);
for (splitIdx = 0; splitIdx < splits.length; splitIdx++) {
var splitString = splits[splitIdx];
var splitStringLower = splitString.toLowerCase();
var newNode;
if (splitStringLower in replacementsMap) {
// This is something that needs to be changed.
newNode = document.createElement("span");
newNode.setAttribute("class", "xkcdSubstitutionsExtensionSubbed");
newNode.setAttribute("title", splitString);
newNode.textContent = matchCase(replacementsMap[splitStringLower],
splitString);
} else {
// This is a stretch between stuff that needs changing.
newNode = document.createTextNode(splitString);
}
};
})();
docFrag.appendChild(newNode);
}

// Let the tree walker know that its place has changed: the old
// node it sent us is gone, and so we'll update its current place
// to refer to the last node we've processed.
iter.currentNode = docFrag.lastChild;
// Make the changes.
node.parentNode.replaceChild(docFrag, node);
}

var node, iter;
var iter = document.createNodeIterator(document.body, NodeFilter.SHOW_TEXT);
iter = document.createTreeWalker(document.body,
NodeFilter.SHOW_ELEMENT |
NodeFilter.SHOW_TEXT,
filter);
while ((node = iter.nextNode())) {
substitute(node);
}
Expand Down
3 changes: 3 additions & 0 deletions substitutions/manifest.json
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,9 @@
],
"persistent": false
},
"web_accessible_resources": [
"fonts/xkcd.otf"
],
"permissions": [
"tabs",
"storage",
Expand Down