FlightGear wiki:Instant-Refs: Difference between revisions

Jump to navigation Jump to search
Re-design of the script. It does the same job, just differently and more generically. Please test and give feedback via #Issues.2Flimitations, etc. P.S. I will comment things, etc later.
m (Cleaup comment + regex simplification)
(Re-design of the script. It does the same job, just differently and more generically. Please test and give feedback via #Issues.2Flimitations, etc. P.S. I will comment things, etc later.)
Line 72: Line 72:
<syntaxhighlight lang="javascript" enclose="div">
<syntaxhighlight lang="javascript" enclose="div">
// ==UserScript==
// ==UserScript==
// @name       Instant-Cquotes
// @name       Instant-Cquotes
// @license    Public Domain
// @version    0.18
// @description Automatically converts mailing list and forum quotes into Mediawiki markup.
// @description Automatically converts mailing list and forum quotes into Mediawiki markup.
// @namespace  http://wiki.flightgear.org/
// @icon        http://wiki.flightgear.org/images/6/62/FlightGear_logo.png
// @version    0.17
// @match      http://sourceforge.net/p/flightgear/mailman/*",
// @icon       http://wiki.flightgear.org/images/6/62/FlightGear_logo.png
// @match       http://forum.flightgear.org/*
// @require   http://code.jquery.com/jquery-1.11.1.min.js
// @namespace  http://wiki.flightgear.org/
// @require   https://code.jquery.com/ui/1.11.0/jquery-ui.min.js
// @run-at      document-end
// @resource   jqUI_CSS  https://code.jquery.com/ui/1.11.0/themes/smoothness/jquery-ui.css
// @require     http://code.jquery.com/jquery-2.1.4.min.js
// @grant     GM_addStyle
// @require     http://code.jquery.com/ui/1.11.4/jquery-ui.min.js
// @grant     GM_getResourceText
// @resource   jQUI_CSS http://code.jquery.com/ui/1.11.4/themes/smoothness/jquery-ui.css
// @match      http://sourceforge.net/p/flightgear/mailman/*
// @grant       GM_addStyle
// @match      http://forum.flightgear.org/*
// @grant       GM_getResourceText
// @copyright  2013-2015, FlightGear.org (bigstones, Philosopher, Red Leader & Hooray)
// ==/UserScript==
// ==/UserScript==


var jqUI_CssSrc = GM_getResourceText("jqUI_CSS");
try {
GM_addStyle(jqUI_CssSrc);
    GM_addStyle(GM_getResourceText("jQUI_CSS"));
}
catch(error){}
 
"use strict";
 
// Define constants
var DEBUG = false;


var debug = 0;
/* content:
*    - 'selection' supports only getSelectedText, will support getSelectedHtml
*    - 'transform' takes a "tranform operator", or an ordered array of operators
* author, title, date, url:
*    - 'xpath' takes the path to the field, relative to the html parent of the selected text
*    - 'transform' takes a "transform operator", or an ordered array of operators
*/
var CONFIG = {
var CONFIG = {
     "sourceforge.net": {
     "sourceforge.net": {
        name: "mailing list",
         content: {
         content: {
             selection: getSelectedText,
             selection: getSelectedText,
             transform: newline2br()
            idStyle: /msg[0-9]{8}/,
            parentTag: ["tagName", "PRE"],
             transform: [newline2br]
         },  
         },  
         author: {
         author: {
             xpath: "../../../tr/td/div/small/text()",
             xpath: "tbody/tr[1]/td/div/small/text()",
             transform: extract(/From: (.*) <.*@.*>/)
             transform: extract(/From: (.*) <.*@.*>/)
         },
         },
         title: {
         title: {
             xpath: "../../../tr/td/div/div/b/a/text()"
             xpath: "tbody/tr[1]/td/div/div[1]/b/a/text()"
         },
         },
         date: {
         date: {
             xpath: "../../../tr/td/div/small/text()",
             xpath: "tbody/tr[1]/td/div/small/text()",
             transform: extract(/- (.*-.*-.*) /)
             transform: extract(/- (.*-.*-.*) /)
         },
         },
         url: {
         url: {
             xpath: "../../../tr/td/div/div/b/a/@href",
             xpath: "tbody/tr[1]/td/div/div[1]/b/a/@href",
             transform: prepend("http://sourceforge.net")
             transform: prepend("http://sourceforge.net")
         }
         }
     },
     },
     "forum.flightgear.org": {
     "forum.flightgear.org": {
        name: "forum",
         content: {
         content: {
             selection: getSelectedHtml,
             selection: getSelectedHtml,
             transform: [removeComments(),
            idStyle: /p[0-9]{6}/,
                        escapePipes(),
            parentTag: ["className", "content", "postbody"],
                        a2wikilink(),
             transform: [removeComments, escapePipes, a2wikilink, phpBB_smilies2text,
                        phpBB_smilies2text(),
                         img2link, phpBB_fontstyle2wikistyle, phpBB_code2syntaxhighlight,
                         img2link(),
                         phpBB_quote2cquote, vid2wiki, escapeEquals, addNewlines]
                        phpBB_fontstyle2wikistyle(),
                        phpBB_code2syntaxhighlight(), // FIXME: could be much better, see below
                         phpBB_quote2cquote(),
vid2wiki(),
                        escapeEquals(),
                        addNewlines()]
         },
         },
         author: {
         author: {
             xpath: "../p/strong/a/text()"
             xpath: "div/div[1]/p/strong/a/text()"
         },
         },
         title: {
         title: {
             xpath: "../h3/a/text()"
             xpath: "div/div[1]/h3/a/text()"
         },
         },
         date: {
         date: {
             xpath: "../p/text()[2]",
             xpath: "div/div[1]/p/text()[2]",
             transform: extract(/» (.*, [0-9]{4})/)
             transform: extract(/» (.*?[0-9]{4})/)
         },
         },
         url: {
         url: {
             xpath: "../p/a/@href",
             xpath: "div/div[1]/p/a/@href",
             transform: [extract(/\.(.*)/),
             transform: [extract(/\.(.*)/), prepend("http://forum.flightgear.org")]
                        prepend("http://forum.flightgear.org")]
         }
         }
     }
     }
};
};
var OUTPUT = OutputMethods().msgbox;
function OutputMethods(){
    var methods = {
        // Shows an alert(); message box
        msgbox: function(msg){
            if (window.prompt("Copy to clipboard: Ctrl+C, Enter", msg) !== null){
                window.getSelection().removeAllRanges(); // deselect all text
            }
        },


        // Show a jQuery UI dialog
var METHODS = {
        jqueryDiag: function(msg){
    // Shows a window.prompt() message box
            var diagDiv = $('<div id="MyDialog"><textarea id="quotedtext" rows="10" cols="80" style="width: 290px; height: 290px">' + msg + '</textarea></div>');
    msgbox: function(msg) {
        window.prompt("Copy to clipboard", msg);
    },


            var diagParam = {
    jQueryDiag: function(msg) {
                title: "Copy your quote with Ctrl-c",
        var diagDiv = $('<div id="MyDialog"><textarea id="quotedtext" rows="10"cols="80" style="width: 290px; height: 290px">' + msg + '</textarea></div>');
                modal: true,
                width: 'auto',
                buttons: [
                    { text: "Select all", click: function(){ $('#quotedtext').select(); } },
                    { text: "Cancel", click: function(){ $(this).dialog('close'); } }
                ]
            };


             diagDiv.dialog(diagParam);
        var diagParam = {
         },
            title: "Copy your quote with Ctrl+c",
    };
            modal: true,
            width: 'auto',
             buttons: [
                { text: "Select all", click: function() { $('#quotedtext').select(); } },
                { text: "OK", click: function() { $(this).dialog('close'); } }
            ]
         };


     return methods;
        diagDiv.dialog(diagParam);
     }
};
};
//////////////////////
// TRANSFORM OPERATORS
// prepend 'prefix'
function prepend(prefix) {
    return function(text) {
        return prefix + text;
    };
}
// extract the first capture group in the regex (i.e. '(xxx)')
function extract(regex) {
    return function(text) {
        return text.match(regex)[1];
    };
}
// replaces newlines with "unescaped" <br/>'s
function newline2br() {
    return function(text) {
        return text.replace(/\n/g, "<br/>\n");
    };
}
// remove html comments (e.g. '<!-- this is a comment -->')
function removeComments() {
    return function(html) {
        return html.replace(/<!--.*?-->/g, "");
    };
}
// Converts HTML <a href="...">...</a> tags to wiki links, internal if possible.
function a2wikilink(){
return function(html){
// Links to wiki images, because
// they need special treatment, or else they get displayed.
html = html.replace(/<a.*?href="http:\/\/wiki\.flightgear\.org\/File:(.*?)".*?>(.*?)<\/a>/g, "[[Media:$1|$2]]");


// Wiki links without custom text.
var OUTPUT = METHODS.msgbox;
html = html.replace(/<a.*?href="http:\/\/wiki\.flightgear\.org\/(.*?)".*?>http:\/\/wiki\.flightgear\.org\/.*?<\/a>/g, "[[$1]]");


// Links to the wiki with custom text
var MONTHS = ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"];
html = html.replace(/<a.*?href="http:\/\/wiki\.flightgear\.org\/(.*?)".*?>(.*?)<\/a>/g, "[[$1|$2]]");


// Remove underscores from all wiki links
// ##################
var list = html.match(/\[\[.*?\]\]/g);
// # Main functions #
if(list !== null){
// ##################
for(var i = 0; i < list.length; i++){
html = html.replace(list[i], underscore2Space(list[i]));
}
}


// Convert non-wiki links
window.addEventListener('load', init);
html = html.replace(/<a.*?href="(.*?)".*?>(.*?)<\/a>/g, "[$1 $2]");


// Remove triple dots from external links.
function init() {
// Replace with raw URL (MediaWiki converts it to a link).
    document.onmouseup = instantCquote;
list = html.match(/\[.*?(\.\.\.).*?\]/g);
    dbLog("init(): Instant-Cquotes script initialized and ready to use");
if(list !== null){
for(var i = 0; i < list.length; i++){
html = html.replace(list[i], list[i].match(/\[(.*?) .*?\]/)[1]);
}
}
 
return html;
};
}
}


function vid2wiki(){
function instantCquote() {
return function(html){
    var profile = CONFIG[window.location.hostname],
// YouTube
        selection = document.getSelection(),
html = html.replace(/<div.*?><iframe width="(.*?)" height="(.*?)" src="http:\/\/www\.youtube\.com\/embed\/(.*?)".*?><\/iframe><\/div>/g, "{{#ev:youtube|$3|$1x$2}}");
        output = {},
html = html.replace(/\[.*? Watch on Youtube\]/g, "");
        field = {};
   
    try {
        var post_id = getPostId(selection, profile);
    }
    catch (error) {
        return;
    }


// Vimeo: Doesn't currently work (cannot select video viewer on post)
    if (selection.toString() === "") {
// html = html.replace(/<iframe src="http:\/\/player\.vimeo\.com\/video\/(.*?)\?.*?" width="(.*?)" height="(.*?)".*?>.*?<\/iframe>/g, "{{#ev:vimeo|$3|$1x$2}}");
         dbLog("instantCquote(): No text is selected, aborting function");
// html = html.replace(/\[.*? Watch on Vimeo\]/g, "");
         return;
return html;
}
}
// converts embedded html images to external links,
// but shows them as little images if they're from the wiki
function img2link() {
    return function(html) {
         html = html.replace(/<img.*?src="http:\/\/wiki\.flightgear\.org\/images\/.*?\/.*?\/(.*?)".*?>/g, "[[File:$1|250px]]");
        return html.replace(/<img.*?src="(.*?)".*?>/g, "(see the [$1 linked image])");
    };
}
// puts newlines where it makes for more readable wiki "source"
function addNewlines() {
    return function(html) {
        html = html.replace(/<br\/?>/g, "<br/>\n");
        html = html.replace(/(<\/?[uo]l>)/g, "$1\n");
         return html.replace(/<\/li>/g, "</li>\n");
     }
     }
}
 
     if (!checkValid(selection, profile)) {
// converts phpBB way of doing font style to the wiki way
         dbLog("instantCquote(): Selection is not valid, aborting function");
function phpBB_fontstyle2wikistyle() {
         return;
     return function(bbhtml) {
        bbhtml = bbhtml.replace(/<span style="font-weight: bold">(.*?)<\/span>/g, "'''$1'''");
        bbhtml = bbhtml.replace(/<span style="text-decoration: underline">(.*?)<\/span>/g, "<u>$1</u>");
        bbhtml = bbhtml.replace(/<span style="font-style: italic">(.*?)<\/span>/g, "''$1''");
         bbhtml = bbhtml.replace(/<span class="posthilit">(.*?)<\/span>/g, "$1");
        return bbhtml;
    };
}
// converts (html) phpBB code blocks to wiki <syntaxhighlight>
// FIXME: not actually using the above tag, because the copied html has
//        unconverted html entities and br's are not interpreted.
//        Solution would be to extract the code, fix it and use the proper tag...
function phpBB_code2syntaxhighlight() {
    return function(bbhtml) {
        return bbhtml.replace(/<dl.*?><dt>.*?<\/dt><dd><code>(.*?)<\/code><\/dd><\/dl>/g, '<pre style="white-space:pre-wrap">\n$1\n</pre>');
    };
}
// converts (html) phpBB quotes to simple wiki cquotes (author not cited, it'd get messy anyway)
function phpBB_quote2cquote() {
    return function(bbhtml) {
         return bbhtml.replace(/<blockquote.*?><div>(?:<cite>.*?<\/cite>)?(.*?)<\/div><\/blockquote>/g, "{{cquote|$1}}");
    };
}
// converts smilies images from phpBB.
// Must be used BEFORE img2link(), because it makes a broken link of them
function phpBB_smilies2text() {
    return function(bbhtml) {
        return bbhtml.replace(/<img src="\.\/images\/smilies\/icon_.*?\.gif" alt="(.*?)".*?>/g, "$1");
     }
     }
}
 
    for (field in profile) {      
// escapes pipe characters that can be problematic inside a template.
        if (field === "name") continue;
// Must be used BEFORE html tags were converted, or they get messed up.
 
function escapePipes() {
         var fieldData = extractFieldInfo(profile, post_id, field);
    return function(text) {
 
         text = text.replace(/\|\|/g,"{{!!}}");
         var transform = profile[field].transform;
         text = text.replace(/\|\-/g,"{{!-}}");
        if (transform !== undefined) {
        return text.replace(/\|/g,"{{!}}");
            dbLog("instantCquote(): Field '" + field + "' before transformation:\n'" + fieldData + "'");
            fieldData = applyTransformations(fieldData, transform);
            dbLog("instantCquote(): Field '" + field + "' after transformation:\n'" + fieldData + "'");
        }
 
        output[field] = fieldData;
 
     }
     }
    output = createCquote(output);
    OUTPUT(output);
}
}
 
// escapes the equal sign that can be problematic inside a template.
function getSelectedHtml() {
// Must be used AFTER html tags were converted, or they get messed up.
    // From http://stackoverflow.com/a/6668159
function escapeEquals() {
    var html = "",
     return function(text) {
        selection = document.getSelection();
         return text.replace(/=/g,"{{=}}");
 
     if (selection.rangeCount) {
         var container = document.createElement('div');
        for (var i = 0; i < selection.rangeCount; i++) {
            container.appendChild(selection.getRangeAt(i).cloneContents());
        }
        html = container.innerHTML;
     }
     }
    dbLog("instantCquote(): Unprocessed HTML\n'" + html + "'");
    return html;
}
}
 
// IE8 compat. function
///////////////////
function getSelectedText() {
// SCRIPT MAIN BODY
     return document.getSelection().toString();
window.addEventListener('load', register);
function register() {
     // check if we have a matching domain in the CONFIG hash
    if (CONFIG.hasOwnProperty(window.location.hostname)) {
        document.onmouseup = instantCquote; // register the callback for "mouse button up" event
    }
}
}
 
// main function
function getPostId(selection, profile, focus) {
function instantCquote() {
     if (focus !== undefined) {
     if(getSelectedText() === "") return;
        selection = selection.focusNode.parentNode;
    var profile = CONFIG[window.location.hostname];
    var parent = getSelectionParent();
    var info = parent ? extractInfo(profile, parent) : extractInfoFallback();
    if (info) {
        var cquote = createCquote(info);
        OUTPUT(cquote);
     } else {
     } else {
         alert("info == null");
         selection = selection.anchorNode.parentNode;
    }
 
    while (selection.id.match(profile.content.idStyle) === null) {
        selection = selection.parentNode;
     }
     }
    return selection.id;
}
}
 
function extractInfoFallback() { // XXX untested
function checkValid(selection, profile) {
     var info = {};
     var ret = true,
     info.content = getSelectedText();
        selection_cp = {},
    info.url = document.URL;
        tags = profile.content.parentTag;
    info.title = "from " + window.location.hostname;
 
    info.author = "";
     for (var n = 0; n < 2; n++) {
    info.date = "";
        if (n === 0) {
    return info;
            selection_cp = selection.anchorNode.parentNode;
}
        } else {
            selection_cp = selection.focusNode.parentNode;
function extractInfo(profile, parent) {
        }
    var info = {};
 
        while(true) {
    for (var field in profile) {
            if (selection_cp.tagName === "BODY") {
        if (!profile.hasOwnProperty(field)) continue;
                ret = false;
                break;
        // this part gets the data, both for field and content (i.e. text, html)
            } else {
        var fieldInfo = extractFieldInfo(profile, parent, field);
                var cont = false;
                for (var i = 0; i < tags.length; i++) {
        if (debug) alert("pre trans:\n" + field + " = " + fieldInfo);
                    if (selection_cp[tags[0]] === tags[i]) {
                        cont = true;
        var transform = profile[field].transform;
                        break;
        if(transform) fieldInfo = applyTransformations(fieldInfo, transform);
                    }
                }
        if (debug) alert("post trans:\n" + field + " = " + fieldInfo);
 
                if (cont) {
         info[field] = fieldInfo;
                    break;
                } else {
                    selection_cp = selection_cp.parentNode;
                }
            }
         }
     }
     }
 
     return info;
    ret = ret && (getPostId(selection, profile) === getPostId(selection, profile, 1));
 
     return ret;
}
}
 
function extractFieldInfo(profile, parent, field) {
function extractFieldInfo(profile, id, field) {
     if (field != "content") {
     if (field === "content") {
         var xpath = profile[field].xpath;
         return profile[field].selection();
        var parentXPath = getXPathTo(parent);
        var fullPath =  parentXPath + "/" + xpath;
        return document.evaluate(fullPath, document, null, XPathResult.STRING_TYPE, null).stringValue;
     } else {
     } else {
         return profile[field].selection(); // here we extract the contents of the selection
         var xpath = '//*[@id="' + id + '"]/' + profile[field].xpath;
        return document.evaluate(xpath, document, null, XPathResult.STRING_TYPE, null).stringValue;
     }
     }
}
}
 
function applyTransformations(fieldInfo, trans) {
function applyTransformations(fieldInfo, trans) {
     if(typeof trans === "function") {
     if (typeof trans === "function") {
        dbLog(fieldInfo);
         return trans(fieldInfo);
         return trans(fieldInfo);
     } else if (Array.isArray(trans)) {
     } else if (Array.isArray(trans)) {
         for (var i = 0; i < trans.length; i++) {
         for (var i = 0; i < trans.length; i++) {
            if (debug) alert("pre trans in array:\n = " + fieldInfo);
             fieldInfo = trans[i](fieldInfo);
             fieldInfo = trans[i](fieldInfo);
             if (debug) alert("post trans in array:\n = " + fieldInfo);
             dbLog("applyTransformations(): Multiple transformation, transformation after loop #" + (i + 1) + ":\n'" + fieldInfo + "'");
         }
         }
         return fieldInfo;
         return fieldInfo;
Line 427: Line 330:
}
}


function createCquote(info) {
function createCquote(data) {
    //TODO: add template string to profile
     var wikiText = "{{FGCquote\n" +
     var template = "{{FGCquote" + "\n" +
         "|" + data.content + "\n" +
         "|" + info.content + "\n" +
         "| {{cite web\n" +
         "|{{cite web" + "\n" +
        " | url    = " + data.url + "\n" +
" |url    = " + info.url + "\n" +
         " | title  = " + nowiki(data.title) + "\n" +
         " |title  = " + nowiki(info.title) + "\n" +
         " | author = " + nowiki(data.author) + "\n" +
         " |author = " + nowiki(info.author) + "\n" +
         " | date  = " + datef(data.date) + "\n" +
         " |date  = " + datef(info.date) + "\n" +
         " }}\n" +
         " }}" + "\n" +
         "}}";
         "}}";
     return template;
 
     return wikiText;
}
 
// #############
// # Utilities #
// #############
 
function extract(regex) {
    return function(text) {
        return text.match(regex)[1];
    };
}
 
function prepend(prefix) {
    return function(text) {
        return prefix + text;
    };
}
 
function removeComments(html) {
    return html.replace(/<!--.*?-->/g, "");
}
 
function escapePipes(text) {
    text = text.replace(/\|\|/g,"{{!!}}");
    text = text.replace(/\|\-/g,"{{!-}}");
    return text.replace(/\|/g,"{{!}}");
}
 
// Converts HTML <a href="...">...</a> tags to wiki links, internal if possible.
function a2wikilink(html){
    // Links to wiki images, because
    // they need special treatment, or else they get displayed.
    html = html.replace(/<a.*?href="http:\/\/wiki\.flightgear\.org\/File:(.*?)".*?>(.*?)<\/a>/g, "[[Media:$1|$2]]");
 
    // Wiki links without custom text.
    html = html.replace(/<a.*?href="http:\/\/wiki\.flightgear\.org\/(.*?)".*?>http:\/\/wiki\.flightgear\.org\/.*?<\/a>/g, "[[$1]]");
 
    // Links to the wiki with custom text
    html = html.replace(/<a.*?href="http:\/\/wiki\.flightgear\.org\/(.*?)".*?>(.*?)<\/a>/g, "[[$1|$2]]");
 
    // Remove underscores from all wiki links
    var list = html.match(/\[\[.*?\]\]/g);
    if(list !== null){
        for(var i = 0; i < list.length; i++){
            html = html.replace(list[i], underscore2Space(list[i]));
        }
    }
 
    // Convert non-wiki links
    html = html.replace(/<a.*?href="(.*?)".*?>(.*?)<\/a>/g, "[$1 $2]");
 
    // Remove triple dots from external links.
    // Replace with raw URL (MediaWiki converts it to a link).
    list = html.match(/\[.*?(\.\.\.).*?\]/g);
    if(list !== null){
        for(var i = 0; i < list.length; i++){
            html = html.replace(list[i], list[i].match(/\[(.*?) .*?\]/)[1]);
        }
    }
    return html;
}
 
function img2link(html) {
    html = html.replace(/<img.*?src="http:\/\/wiki\.flightgear\.org\/images\/.*?\/.*?\/(.*?)".*?>/g, "[[File:$1|250px]]");
    return html.replace(/<img.*?src="(.*?)".*?>/g, "(see the [$1 linked image])");
}
 
function phpBB_smilies2text(html) {
    return html.replace(/<img src="\.\/images\/smilies\/icon_.*?\.gif" alt="(.*?)".*?>/g, "$1");
}
 
function phpBB_fontstyle2wikistyle(html) {
    html = html.replace(/<span style="font-weight: bold">(.*?)<\/span>/g, "'''$1'''");
    html = html.replace(/<span style="text-decoration: underline">(.*?)<\/span>/g, "<u>$1</u>");
    html = html.replace(/<span style="font-style: italic">(.*?)<\/span>/g, "''$1''");
    return html.replace(/<span class="posthilit">(.*?)<\/span>/g, "$1");
}
}
 
function createForumQuote(info) {
function phpBB_code2syntaxhighlight(html) {
     //TODO: add template string to profile
     return html.replace(/<dl.*?><dt>.*?<\/dt><dd><code>(.*?)<\/code><\/dd><\/dl>/g, '<pre style="white-space:pre-wrap">\n$1\n</pre>');
    // nowiki(info.date)
    var template = "[url='"+info.url+"']"+info.title+"("+nowiki(info.date)+")[/url][quote='"+nowiki(info.author)+"']" + nowiki(info.content) + "\n" +
        "[/quote]";
    return template;
}
}
 
function phpBB_quote2cquote(html) {
    return html.replace(/<blockquote.*?><div>(?:<cite>.*?<\/cite>)?(.*?)<\/div><\/blockquote>/g, "{{cquote|$1}}");
}
 
function vid2wiki(html){
    // YouTube
    html = html.replace(/<div.*?><iframe width="(.*?)" height="(.*?)" src="http:\/\/www\.youtube\.com\/embed\/(.*?)".*?><\/iframe><\/div>/g, "{{#ev:youtube|$3|$1x$2}}");
    html = html.replace(/\[.*? Watch on Youtube\]/g, "");
 
    // Vimeo: Doesn't currently work (cannot select video viewer on post)
    html = html.replace(/<iframe src="http:\/\/player\.vimeo\.com\/video\/(.*?)\?.*?" width="(.*?)" height="(.*?)".*?>.*?<\/iframe>/g, "{{#ev:vimeo|$3|$1x$2}}");
    return html.replace(/\[.*? Watch on Vimeo\]/g, "");
}
 
function escapeEquals(html) {
    return html.replace(/=/g,"{{=}}");
}
 
function addNewlines(html) {
    html = html.replace(/<br\/?>/g, "<br/>\n");
    html = html.replace(/(<\/?[uo]l>)/g, "$1\n");
    return html.replace(/<\/li>/g, "</li>\n");
}
 
function nowiki(text) {
function nowiki(text) {
     return "<nowiki>" + text + "</nowiki>";
     return "<nowiki>" + text + "</nowiki>";
}
}
////////////////////
// UTILITY FUNCTIONS
var MONTHS = ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"];


// Returns the correct ordinal adjective
// Returns the correct ordinal adjective
function ordAdj(date){
function ordAdj(date){
date = date.toString();
    date = date.toString();
if(date == "11" || date == "12" || date == "13"){
    if(date == "11" || date == "12" || date == "13"){
return "th";
        return "th";
}else if(date.substr(1) == "1" || date == "1"){
    }else if(date.substr(1) == "1" || date == "1"){
return "st";
        return "st";
}else if(date.substr(1) == "2" || date == "2"){
    }else if(date.substr(1) == "2" || date == "2"){
return "nd";
        return "nd";
}else if(date.substr(1) == "3" || date == "3"){
    }else if(date.substr(1) == "3" || date == "3"){
return "rd";
        return "rd";
}else{
    }else{
return "th";
        return "th";
}
    }
};
};


// Formats the date to this format: Apr 26th, 2015
// Formats the date to this format: Apr 26th, 2015
function datef(text){
function datef(text){
var date = new Date(text);
    var date = new Date(text);
return MONTHS[date.getMonth()] + " " + date.getDate() + ordAdj(date.getDate()) + ", " + date.getFullYear();
    return MONTHS[date.getMonth()] + " " + date.getDate() + ordAdj(date.getDate()) + ", " + date.getFullYear();
}
}


function underscore2Space(str){
function underscore2Space(str){
return str.replace(/_/g, " ");
    return str.replace(/_/g, " ");
}
}
 
// IE8 compat. function
function newline2br(text) {
function getSelectionParent() {
     return text.replace(/\n/g, "<br/>\n");
     if(document.getSelection) { // for modern, standard compliant browsers
        // anchorNode is the textnode, parentNode is the parent html element
        return document.getSelection().anchorNode.parentNode;
    } else if (document.selection) { // for IE <= v8 - XXX: not tested!
        return document.selection.createRange().parentElement();
    }
}
}
 
// IE8 compat. function
function dbLog(message) {
function getSelectedText() {
     if (Boolean(DEBUG)) console.log(message);
     if(document.getSelection) { // for modern, standard compliant browsers
        return document.getSelection().toString();
    } else if (document.selection) { // for IE <= v8 - XXX: not tested!
        return document.selection.createRange().text;
    }
}
// IE8 compat. function (copied from http://stackoverflow.com/a/6668159 )
function getSelectedHtml() {
    var html = "";
    if (typeof window.getSelection != "undefined") {
        var sel = window.getSelection();
        if (sel.rangeCount) {
            var container = document.createElement("div");
            for (var i = 0, len = sel.rangeCount; i < len; ++i) {
                container.appendChild(sel.getRangeAt(i).cloneContents());
            }
            html = container.innerHTML;
        }
    } else if (typeof document.selection != "undefined") {
        if (document.selection.type == "Text") {
            html = document.selection.createRange().htmlText;
        }
    }
        return html;
}
// copied from http://stackoverflow.com/a/2631931
function getXPathTo(element) {
    if (element.id !== '')
        return 'id("' + element.id + '")';
    if (element === document.body)
        return element.tagName;
    var ix= 0;
    var siblings = element.parentNode.childNodes;
    for (var i= 0; i<siblings.length; i++) {
        var sibling = siblings[i];
        if (sibling === element)
            return getXPathTo(element.parentNode) + '/' + element.tagName + '[' + (ix+1) + ']';
        if (sibling.nodeType === 1 && sibling.tagName === element.tagName)
            ix++;
    }
}
// copied from http://stackoverflow.com/a/12034334
var entityMap = {
    "&": "&amp;",
    "<": "&lt;",
    ">": "&gt;",
    '"': '&quot;',
    "'": '&#39;',
    "/": '&#x2F;',
    "\n": "<br/>"
};
function escapeHtml(string) {
    return String(string).replace(/[&<>"'\/]|[\n]/g, function (s) {
        return entityMap[s];
    });
}
}
</syntaxhighlight>
</syntaxhighlight>


[[Category:Wiki maintenance]]
[[Category:Wiki maintenance]]

Navigation menu