Reference »

Smart Quotes User Script

// ==UserScript==
// @name         SmartQuotes.js
// @namespace    http://tampermonkey.net/
// @version      0.1
// @description  try to take over the world!
// @author       You
// @match        *
// @grant        none
// ==/UserScript==

(function() {
    (function (root, factory) {
        if (typeof define === 'function' && define.amd) {
            define(factory);
        } else if (typeof exports === 'object') {
            module.exports = factory();
        } else {
            root.smartquotes = factory();
        }
    } (this, function() {
        // The smartquotes function should just delegate to the other functions
        function smartquotes(context) {
            if (typeof document !== 'undefined' && typeof context === 'undefined') {
                var run = function() { smartquotes.element(document.body); };
                // if called without arguments, run on the entire body after the document has loaded
                if (document.readyState !== 'loading') {
                    // we're already ready
                    run();
                } else if (document.addEventListener) {
                    document.addEventListener("DOMContentLoaded", run, false);
                } else {
                    var readyStateCheckInterval = setInterval(function() {
                        if (document.readyState !== 'loading') {
                            clearInterval(readyStateCheckInterval);
                            run();
                        }
                    }, 10);
                }
            } else if (typeof context === 'string') {
                return smartquotes.string(context);
            } else {
                return smartquotes.element(context);
            }
        }

        smartquotes.string = function(str, retainLength) {
            return str
                .replace(/'''/g, '\u2034' + (retainLength ? '\u2063\u2063' : ''))            // triple prime
                .replace(/(\W|^)"(\w)/g, '$1\u201c$2')                                       // beginning "
                .replace(/(\u201c[^"]*)"([^"]*$|[^\u201c"]*\u201c)/g, '$1\u201d$2')          // ending "
                .replace(/([^0-9])"/g,'$1\u201d')                                            // remaining " at end of word
                .replace(/''/g, '\u2033' + (retainLength ? '\u2063' : ''))                   // double prime as two single quotes
                .replace(/(\W|^)'(\S)/g, '$1\u2018$2')                                       // beginning '
                .replace(/([a-z])'([a-z])/ig, '$1\u2019$2')                                  // conjunction's possession
                .replace(/(\u2018)([0-9]{2}[^\u2019]*)(\u2018([^0-9]|$)|$|\u2019[a-z])/ig, '\u2019$2$3')     // abbrev. years like '93
                .replace(/((\u2018[^']*)|[a-z])'([^0-9]|$)/ig, '$1\u2019$3')                 // ending '
                .replace(/(\B|^)\u2018(?=([^\u2018\u2019]*\u2019\b)*([^\u2018\u2019]*\B\W[\u2018\u2019]\b|[^\u2018\u2019]*$))/ig, '$1\u2019') // backwards apostrophe
                .replace(/"/g, '\u2033')                                                     // double prime
                .replace(/'/g, '\u2032');                                                    // prime
        };

        smartquotes.element = function(root) {
            var TEXT_NODE = typeof Element !== 'undefined' && Element.TEXT_NODE || 3;

            handleElement(root);

            function handleElement(el) {
                if (['CODE', 'PRE', 'SCRIPT', 'STYLE'].indexOf(el.nodeName.toUpperCase()) !== -1) {
                    return;
                }

                var i, node, nodeInfo;
                var childNodes = el.childNodes;
                var textNodes = [];
                var text = '';

                // compile all text first so we handle working around child nodes
                for (i = 0; i < childNodes.length; i++) {
                    node = childNodes[i];

                    if (node.nodeType === TEXT_NODE || node.nodeName === '#text') {
                        textNodes.push([node, text.length]);
                        text += node.nodeValue || node.value;
                    } else if (node.childNodes && node.childNodes.length) {
                        text += handleElement(node);
                    }

                }
                text = smartquotes.string(text, true);
                for (i in textNodes) {
                    nodeInfo = textNodes[i];
                    if (nodeInfo[0].nodeValue) {
                        nodeInfo[0].nodeValue = substring(text, nodeInfo[0].nodeValue, nodeInfo[1]);
                    } else if (nodeInfo[0].value) {
                        nodeInfo[0].value = substring(text, nodeInfo[0].value, nodeInfo[1]);
                    }
                }
                return text;
            }

            function substring(text, value, position) {
                return text.substr(position, value.length).replace('\u2063', '');
            }

            return root;
        };

        return smartquotes;
    }));

    smartquotes();
})();