Skip to content

Instantly share code, notes, and snippets.

@ihanson
Created October 20, 2011 17:25
Show Gist options
  • Save ihanson/1301725 to your computer and use it in GitHub Desktop.
Save ihanson/1301725 to your computer and use it in GitHub Desktop.
Typographical Perfectionist
/** Run this code as a bookmarklet to replace certain
* punctuation marks (dashes, quotation marks, apostrophes,
* and ellipses) with their typographically correct
* variants.
*
* If a quotation mark or apostrophe ends up as the wrong
* character, click it to correct it.
*/
(function () {
"use strict";
var doc = (window.content ? window.content.document : document);
var replacements = [
// double quotes
[/(^|[\s([{'=])"(?=\w|$)/g, "$1\u201c"],
[/"/g, "\u201d"],
// single quotes/apostrophes
[/(^|[\s([{\u201c=])'(?=\w|$)(?!(em|t(wa|i)s|cause|s|[0-9]{2}s?)\W)/gi, "$1\u2018"],
[/'/g, "\u2019"],
// en dashes
[/([0-9](\s*[ap]\.?m\.?|st|[nr]d|th)?)\s*-\s*(?=\$?[0-9])/gi, "$1\u2013"],
// em dashes
[/(\s*-{2,}\s*|\s+-\s+|\s*-\s*$)/g, "\u2014\u200b"],
// minus signs
[/(\W|^)-(?=\$?\d)/g, "$1\u2212"],
// ellipses
[/\s*(?:\.\s*){2,}/g, "\u2026\u200b"]
];
var quotes = /[\u201c\u201d\u2018\u2019]/;
var opposites = {
"\u201c": "\u201d",
"\u201d": "\u201c",
"\u2018": "\u2019",
"\u2019": "\u2018"
};
var noTraverse = ["SCRIPT", "STYLE", "CODE", "PRE", "TEXTAREA", "QUOTE-FIX"];
var repl = function (text) {
for (var i = 0; i < replacements.length; i++) {
text = text.replace(replacements[i][0], replacements[i][1]);
}
return text;
};
var clicked = function (event) {
var child = event.target.firstChild;
child.nodeValue = opposites[child.nodeValue];
event.preventDefault();
};
(function traverse(node) {
if (node.hasAttribute && node.hasAttribute("title")) {
node.setAttribute("title", repl(node.getAttribute("title")));
}
var i;
if (node.nodeType === doc.TEXT_NODE) {
node.nodeValue = repl(node.nodeValue);
if (node.parentNode.nodeName === "TITLE") {
return;
}
var val;
while ((i = (val = node.nodeValue).search(quotes)) !== -1) {
var q = doc.createElement("quote-fix");
q.appendChild(doc.createTextNode(val.charAt(i)));
q.style.cursor = "default";
q.addEventListener("click", clicked);
node.nodeValue = val.substring(0, i);
node.parentNode.insertBefore(q, node.nextSibling);
node.parentNode.insertBefore(doc.createTextNode(val.substring(i + 1)), q.nextSibling);
}
} else if (noTraverse.indexOf(node.nodeName) === -1) {
for (i = 0; i < node.childNodes.length; i++) {
traverse(node.childNodes[i]);
}
if (node.contentDocument) {
traverse(node.contentDocument);
}
}
}(doc));
}());
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment