Saren / HKGolden CodeMirror

// ==UserScript==
// @name        HKGolden CodeMirror
// @namespace   Saren
// @description HKGolden code tag syntax highlighter, powered by CodeMirror and cdnjs
// @include     http://*.hkgolden.com/view.aspx*message=*
// @require     http://ajax.googleapis.com/ajax/libs/jquery/1.10.2/jquery.min.js
// @version     0.0.2c
// ==/UserScript==

this.$ = this.jQuery = jQuery.noConflict(true);

var similarTable = [];
var theme = "ambiance";

var languages = {
    "xml": "xml",
    "html": ["xml", "javascript", "css", "vbscript", "htmlmixed"],
    "lisp": "commonlisp",
    "sql": "sql",
    "mysql": "sql",
    "postgresql": "sql",
    "mssql": "sql",
    "livescript": "livescript",
    "gas": "gas",
    "python": "python",
    "q": "q",
    "haml": "haml",
    "eiffel": "eiffel",
    "pig latin": "pig",
    "jinja2": "jinja2",
    "ruby": "ruby",
    "scala": "clike",
    "shell": "shell",
    "vbscript": "vbscript",
    "stex": "stex",
    "latex": "stex",
    "vb.net": "vb",
    "fortran": "fortran",
    "cobol": "cobol",
    "tcl": "tcl",
    "gherkin": "gherkin",
    "perl": "perl",
    "cython": "python",
    "xquery": "xquery",
    "toml": "toml",
    "sieve": "sieve",
    "smalltalk": "smalltalk",
    "sparql": "sparql",
    "django": "django",
    "haxe": "haxe",
    "f#": "mllike",
    "groovy": "groovy",
    "ocaml": "mllike",
    "diff": "diff",
    "puppet": "puppet",
    "rust": "rust",
    "pascal": "pascal",
    "javascript": "javascript",
    "less": "css",
    "apl": "apl",
    "smarty": ["xml", "javascript", "css", "htmlmixed", "smarty", "smartymixed"],
    "octave": "octave",
    "velocity": "velocity",
    "d": "d",
    "php": "php",
    "rpm": "rpm",
    "rpm spec": "spec",
    "rpm changelog": "changes",
    "r": "r",
    "yaml": "yaml",
    "verilog": "verilog",
    "systemverilog": "verilog",
    "asterisk dialplan": "asterisk",
    "asterisk": "asterisk",
    "go": "go",
    "tiki wiki": "tiki",
    "properties files": "properties",
    "ntriples": "ntriples",
    "nginx": "nginx",
    "sass": "sass",
    "html embedded": ["xml", "javascript", "css", "htmlmixed", "htmlembedded"],
    "scss": "css",
    "haskell": "haskell",
    "clojure": "clojure",
    "ecl": "ecl",
    "c": "clike",
    "c++": "clike",
    "c#": "clike",
    "java": "clike",
    "http": "http",
    "turtle": "turtle",
    "mirc": "mirc",
    "erlang": "erlang",
    "restructuredtext": "rst",
    "tiddlywiki": "tiddlywiki",
    "peg.js": "pegjs",
    "css": "css",
    "coffeescript": "coffeescript",
    "z80": "z80",
    "lua": "lua",
    "solr": "solr",
    "jade": "jade",
    "julia": "julia",
    "scheme": "scheme",
    "cypher": "cypher",
    "markdown": ["xml", "markdown"],
    "github markdown": ["xml", "markdown", "javascript", "css", "htmlmixed", "clike", "gfm"],
    "dtd": "dtd",
    "dylan": "dylan"
};


function levDist(s, t) {
    var d = []; //2d matrix

    // Step 1
    var n = s.length;
    var m = t.length;

    if (n == 0) return m;
    if (m == 0) return n;

    //Create an array of arrays in javascript (a descending loop is quicker)
    for (var i = n; i >= 0; i--) d[i] = [];

    // Step 2
    for (var i = n; i >= 0; i--) d[i][0] = i;
    for (var j = m; j >= 0; j--) d[0][j] = j;

    // Step 3
    for (var i = 1; i <= n; i++) {
        var s_i = s.charAt(i - 1);

        // Step 4
        for (var j = 1; j <= m; j++) {

            //Check the jagged ld total so far
            if (i == j && d[i][j] > 4) return n;

            var t_j = t.charAt(j - 1);
            var cost = (s_i == t_j) ? 0 : 1; // Step 5

            //Calculate the minimum
            var mi = d[i - 1][j] + 1;
            var b = d[i][j - 1] + 1;
            var c = d[i - 1][j - 1] + cost;

            if (b < mi) mi = b;
            if (c < mi) mi = c;

            d[i][j] = mi; // Step 6

            //Damerau transposition
            if (i > 1 && j > 1 && s_i == t.charAt(j - 2) && s.charAt(i - 2) == t_j) {
                d[i][j] = Math.min(d[i][j], d[i - 2][j - 2] + cost);
            }
        }
    }

    // Step 7
    return d[n][m];
}

function loadFile(url, type, callback) {
    var head = document.getElementsByTagName('head')[0];
    if (type == "js") { //if filename is a external JavaScript file
        var fileref = document.createElement('script');
        fileref.type = "text/javascript";
        fileref.src = url;
    } else if (type == "css") { //if filename is an external CSS file
        var fileref = document.createElement('link');
        fileref.rel = "stylesheet";
        fileref.type = "text/css";
        fileref.href = url;
    }
    fileref.onreadystatechange = callback;
    fileref.onload = callback;
    head.appendChild(fileref);
}

function preSetCodeMirror(elem, jsName) {
    loadFile("//cdnjs.cloudflare.com/ajax/libs/codemirror/4.2.0/mode/" + jsName + "/" + jsName + ".js", "js", function() {
        setCodeMirror(elem, jsName);
    });
}

function preSetCodeMirrorJSArray(elem, jsNames) {
    var count = jsNames.length;
    for (var i in jsNames) {
        loadFile("//cdnjs.cloudflare.com/ajax/libs/codemirror/4.2.0/mode/" + jsNames[i] + "/" + jsNames[i] + ".js", "js", function() {
            count--;
            if (count === 0) {
                setCodeMirror(elem, jsNames[jsNames.length - 1]);
            }
        });
    }
}

function betterTab(cm) {
    if (cm.somethingSelected()) {
        cm.indentSelection("add");
    } else {
        cm.replaceSelection(cm.getOption("indentWithTabs")? "\t":
        Array(cm.getOption("indentUnit") + 1).join(" "), "end", "+input");
    }
}

function setCodeMirror(elem, langMode) {
    var CodeMirrorEditor = CodeMirror(function(elt) {
        elem.parentNode.replaceChild(elt, elem);
    }, {
        value: $(elem).text(),
        mode: langMode,
        indentWithTabs: false,
        indentUnit: 4,
        lineWrapping: true,
        lineNumbers: true,
        theme: theme,
        extraKeys: { Tab: betterTab }
    });
    var charWidth = CodeMirrorEditor.defaultCharWidth(), basePadding = 4;
    CodeMirrorEditor.on("renderLine", function(cm, line, elt) {
        var off = CodeMirror.countColumn(line.text, null, cm.getOption("tabSize")) * charWidth;
        elt.style.textIndent = "-" + off + "px";
        elt.style.paddingLeft = (basePadding + off) + "px";
    });
    CodeMirrorEditor.refresh();
}

function getValueOfSimilarestKey(string, dict) {
    if (string in similarTable) {
        return similarTable[string];
    }
    var similarites = [];
    for (var key in dict) {
        similarites[key] = levDist(string, key);
        if (similarites[key] === 0) {
            similarTable[string] = dict[key];
            return dict[key];
        }
    }
    var min = similarites[key];
    var target = key;
    for (var key in similarites) {
        if (similarites[key] < min) {
            target = key;
            min = similarites[key];
        }
    }
    similarTable[string] = dict[target];
    return dict[target];
}

function setCodeMirrors() {
    var pre;
    var postCount = 0;
    var editorCount = 0;
    var postLanguages;
    var jsName;
    var editorLanguages = [];
    $(".ContentGrid").each(function() {
        postLanguages = [];
        $(this).find("strong").each(function () {
            postLanguages.push($(this).text().toLowerCase());
        });
        editorLanguages.push(postLanguages);
    });
    for (var post in editorLanguages) {
        for (var editor in editorLanguages[post]) {
            editorLanguages[post][editor] = getValueOfSimilarestKey(editorLanguages[post][editor], languages);
        }
    }
    $(".ContentGrid").each(function() {
        editorCount = 0;
        $(this).find("pre").each(function() {
            jsName = editorLanguages[postCount][editorCount];
            pre = this;
            if (typeof jsName !== 'undefined') {
                if (jsName instanceof Array) {
                    preSetCodeMirrorJSArray(pre, jsName);
                } else {
                    preSetCodeMirror(pre, jsName);
                }
            }
            editorCount++;
        });
        postCount++;
    });
}

$(function () {
    loadFile("//cdnjs.cloudflare.com/ajax/libs/codemirror/4.2.0/codemirror.css", "css", function() {});
    loadFile("//cdnjs.cloudflare.com/ajax/libs/codemirror/4.2.0/theme/" + theme + ".css", "css", function() {});
    var css = document.createElement("style");
    css.type = "text/css";
    css.innerHTML = ".CodeMirror { border: 1px solid #eee; height: auto; } .CodeMirror-scroll { overflow-y: hidden; overflow-x: auto; } .CodeMirror-linenumber { z-index: 1; } .CodeMirror-gutters { z-index: 0; }";
    document.body.appendChild(css);
    loadFile("//cdnjs.cloudflare.com/ajax/libs/codemirror/4.2.0/codemirror.js", "js", function() {
        setCodeMirrors();
    });
});