User:DavidHOzAu/votescript/code.js

From Wikipedia, the free encyclopedia

Note: After saving, you have to bypass your browser's cache to see the changes. In Internet Explorer and Firefox, hold down the Ctrl key and click the Refresh or Reload button. Opera users have to clear their caches through Tools→Preferences, see the instructions for Opera. Konqueror and Safari users can just click the Reload button.

/*
== Code ==
{{GPL}} <pre> */
//--------------------------------------------------------------
// GLOBAL VARIABLES
//--------------------------------------------------------------
var vs_data = new Array(3);	// persistent data : { users, votes, headings }
 
//--------------------------------------------------------------
// voteProcessor - process an entire dom, extract users & votes
//--------------------------------------------------------------
function voteProcessor(dom) {
    var lists = dom.getElementsByTagName("ol");
    var votes = new Array();
    var users = new Array();
    for (i = 0; list = lists[i]; i++) {
        var ret = processList(list, users, votes, (lists.length - i) - 1);
        if (ret) { // checksafe
            users = ret[0];
            votes = ret[1];
        }
    }
    vs_data[0] = users;
    vs_data[1] = votes;
}
 
//--------------------------------------------------------------
// getAllHeadings - Detect all headings in the DOM
//--------------------------------------------------------------
function getAllHeadings(dom, headings) {
    for (var i = 0; n = dom.childNodes[i]; i++) {
         if (n.nodeName.length == 2
             && !isNaN(n.nodeName.substr(1,1))
             && n.nodeName.toLowerCase().substr(0,1) == "h") {
             headings[headings.length] = getHeading(n);
         }
         if (n.childNodes.length > 0) {
             headings = getAllHeadings(n, headings); // recurse
         }
    }
    return headings;
}
 
//--------------------------------------------------------------
// getHeading - Detect the heading text in a node
//--------------------------------------------------------------
function getHeading(n) {
    for (var i = 0; el = n.getElementsByTagName("span")[i]; i++) {
        if (el.className == "mw-headline") {
            return el.innerHTML;
        }
    }
    return "unknown";
}
 
//--------------------------------------------------------------
// processList - extract users and votes from an option's list
//--------------------------------------------------------------
function processList(list, users, votes, choice) {
    if (!list) { return; } // failsafe
    for (var i = 0; el = list.getElementsByTagName("li")[i]; i++) {
        var uname = getUserFromLiElement(el);
        if (uname == "") {
            uname = getUnknownUser(el);
            if (uname == "") {
                return;
            }
        }
        var uid = wasUserRead(uname, users);
        if (uid < 0) {
           uid=users.length;
           votes[uid] = 0;
        }
        users[uid] = uname;
        votes[uid] += Math.pow(2, choice); // binary store
    }
    ret = new Array(); // pass back by array
    ret[0] = users;
    ret[1] = votes;
    return ret;
}
 
//--------------------------------------------------------------
// getUserFromLiElement - auto-magically detect user name
//--------------------------------------------------------------
function getUserFromLiElement(lielem) {
    for (var i = 0; link = lielem.getElementsByTagName("a")[i]; i++) {
        if (link.title.substr(0, 4) == "User") {
            return link.title.substr(5, link.title.length);
        }
    }
    return "";
}
 
//--------------------------------------------------------------
// getUnknownUser - user intervention for weird entries
//--------------------------------------------------------------
function getUnknownUser(el) {
    var text = getInnerText(el);
    var msg = "Couldn't identify user.  Please extract the user's name from the following text.";
    var reply = prompt(msg, text);
    if (reply == null) { return ""; }
    return reply;
}
 
//--------------------------------------------------------------
// getInnerText - get innerHTML without the HTML tags
//--------------------------------------------------------------
function getInnerText(el) {
    var re, text = el.innerHTML;
    // remove XML tags
    do {
        a = text;
        text = text.replace(/<[^>\/]*\/>/gi, "");
    } while (a != text);
    // strip HTML tags
    do {
        a = text;
        text = text.replace(/<[^>]*>([^<]*)<\/[^>]*>/gi, "$1");
    } while (a != text);
    // remove leading and trailing spaces
    text = text.replace(/^\s/gi, "");
    text = text.replace(/\s$/gi, "");
    return text;
}
 
//--------------------------------------------------------------
// wasUserRead - O(n) search for user name presence
//--------------------------------------------------------------
function wasUserRead(username, users) {
    for (var i = 0; i < users.length; i++) {
       if (users[i] == username) { return i; }
    }
    return -1;
}
 
//--------------------------------------------------------------
// gen_table - make HTML table preview of results
//--------------------------------------------------------------
function gen_table() {
    var users = vs_data[0];
    var votes = vs_data[1];
    var headings = vs_data[2];
 
    var totals = new Array();
    var str = '<table class="wikitable"><tr><th>User</th>';
    var i, j, k;
    for (i = 0; i < headings.length; i++) {
        str += "<th>" + headings[i] + "</th>";
        totals[i] = 0;
    }
    str += "</tr>";
    for (i = 0; i < users.length; i++) {
        str += "<tr><td>" + users[i] + "</td>";
        k = votes[i];
        for (j = headings.length-1; j >= 0; j--) { // MSB to LSB
            if (k - Math.pow(2, j) >= 0) { // is bit set?
                k -= Math.pow(2, j);       // turn it off
                str += "<td>X</td>";
                totals[j]++;
            } else {
                str += "<td>-</td>";
            }
        }
        str += "</tr>";
    }
    str += "<tr><th>Total</th>";
        for (i = headings.length-1; i >= 0; i--) { // Reverse order
        str += "<th>" + totals[i] + "</th>";
    }
 
    str += "</table>";
    str += "<p><i>" + users.length + " editors voted in this survey.</i></p>"
    return str;
}
 
//--------------------------------------------------------------
// gen_wiki - generate results as wikicode
//--------------------------------------------------------------
function gen_wiki() {
    var users = vs_data[0];
    var votes = vs_data[1];
    var headings = vs_data[2];
 
    var totals = new Array();
    var str = '<' + 'pre style="border-style: none; padding: 0; margin: 0"' + '>{|class="wikitable"\n!Users';
    var i, j, k;
    for (i = 0; i < headings.length; i++) {
        str += "!!" + headings[i];
        totals[i] = 0;
    }
    for (i = 0; i < users.length; i++) {
        str += "\n|-\n|" + users[i];
        k = votes[i];
        for (j = headings.length-1; j >= 0; j--) { // MSB to LSB
            if (k - Math.pow(2, j) >= 0) { // is bit set?
                k -= Math.pow(2, j);       // turn it off
                str += "|| X ";
                totals[j]++;
            } else {
                 str += "|| - ";
            }
        }
    }
    str += "\n|-\n!Total";
        for (i = headings.length-1; i >= 0; i--) { // Reverse order
        str += "!!" + totals[i];
    }
 
    str += "\n|}";
    str += "\n\n''" + users.length + " editors voted in this survey.''<" + "/pre" + ">";
    return str;
}
 
//--------------------------------------------------------------
// Adapt code for MediaWiki use
//--------------------------------------------------------------
var votescript_url = "User:DavidHOzAu/votescript/ui";
var vs_path_len = document.location.pathname.length;
if (document.location.pathname.substring(vs_path_len - votescript_url.length, vs_path_len) == votescript_url) {
    hookDOMEvent(window, "load", uiSetup);
}
 
//--------------------------------------------------------------
// hookDOMEvent - useful for hooking events in any element
//--------------------------------------------------------------
function hookDOMEvent(dom, hookName, hookFunct) {
    if (dom.addEventListener)
	dom.addEventListener(hookName, hookFunct, false);
    else if (dom.attachEvent)
	dom.attachEvent("on" + hookName, hookFunct);
}
 
//--------------------------------------------------------------
// uiHandler - UI setup code
//--------------------------------------------------------------
function uiSetup() {
    // blank the inner contents of the page
    var bodyContent = document.getElementById("bodyContent");
    while (bodyContent.childNodes.length > 0) bodyContent.removeChild(bodyContent.lastChild);
 
    bodyContent.innerHTML = '<div id="vs-dialog" style="margin: 1em auto 0; padding: 1em; width: 40em; background-color: #eef; border: 1px solid #cbf; position: relative; font-size: medium; -moz-border-radius: 1em; ">'
    + uiLabel("Survey&nbsp;page") + uiWrap("2em", uiButton("fetch", "Fetch", "button") + uiInput("page", "Wikipedia:Village_pump_(proposals)/Sidebar_redesign/Final_draft_vote"))
    + uiLabel("Headers") + uiTextArea("headers", "")
    + uiLabel("Preview") + uiOutput("preview", "")
    + uiLabel("Wikitable") + uiOutput("wiki", "")
    + '</div>' + uiHiddenText("hidden");
 
    document.getElementById("vs-page").style.position = "absolute";
    document.getElementById("vs-page").style.left = "0em";
    document.getElementById("vs-page").style.width = "32em";
    document.getElementById("vs-fetch").style.position = "absolute";
    document.getElementById("vs-fetch").style.right = "0em";
    document.getElementById("vs-fetch").style.width = "5.5em";
 
    hookDOMEvent(document.getElementById("vs-fetch"), "click", fetchVotes);
    hookDOMEvent(document.getElementById("vs-headers"), "change", uiUpdater);
}
 
//--------------------------------------------------------------
// UI Helper functions
//--------------------------------------------------------------
function uiLabel(label) {
    return('<div id="vs-label" style="height: 2em; background-color: #eef; cursor: default; position: relative">'
    + '<div id="vs-label-text" style="position: absolute; bottom: 0; left: 0; background-color: #eef; z-index: 3">' + label
    + '&nbsp;</div><div id="vs-label-rule" style="position: absolute; bottom: 0.7em; right: 0; width: 100%; z-index: 2; height: 2px; border-bottom: 2px groove white"></div></div>');
}
 
function uiWrap(h, text) {
    return('<div id="vs-wrap" style="margin: 0.5em 1em; height: ' + h + '; position: relative">' + text + '</div>');
}
 
function uiInput(id, text) {
    return('<input name="' + id + '" value="' + text + '" id="vs-' + id + '" title="' + id + '" style="font-size: 100%"/>');
}
 
function uiButton(id, text, type) {
    return('<input type="' + type + '" value="' + text + '" id="vs-' + id + '" title="' + id + '" style="font-size: 100%"/>');
}
 
function uiTextArea(id, text) {
    return uiWrap("auto", '<textarea name="' + id + '" id="vs-' + id + '" style="cursor: text; width: 100%; height: 10em">' + text + '</textarea>');
}
 
function uiOutput(id, text) {
    return uiWrap("auto", '<div id="vs-' + id + '" style="overflow: auto; cursor: text; background-color: #f8f8ff; border: 1px solid #b8b8b8; height: 10em">' + text + '</div>');
}
 
function uiHiddenText(id) {
    return('<div id="vs-' + id + '" style="display: none; speak: none"></div>');
}
 
//--------------------------------------------------------------
// uiUpdater - update contents when heading text changes
//--------------------------------------------------------------
function uiUpdater() {
    vs_data[2] = document.getElementById("vs-headers").value.split(/[\n\r]+/);
    document.getElementById("vs-preview").innerHTML = gen_table();
    document.getElementById("vs-wiki").innerHTML = gen_wiki();
}
 
//--------------------------------------------------------------
// fetchVotes - fetch DOM of requested page
//--------------------------------------------------------------
function fetchVotes(event) {
    document.getElementById("vs-preview").innerHTML = "<i>Loading...</i>";
    x = sajax_init_object();
    if (!x) {
	alert("AJAX not supported");
	return false;
    }
    var url = document.getElementById("vs-page").value;
    x.onreadystatechange = function () { voteFetcher(x); };
    url = wgScriptPath + "/index.php?title=" + escape(url) + "&action=render";
    x.open("GET", url, true); 
    x.send(null);
}
 
//--------------------------------------------------------------
// voteFetcher - handle XMLHttpRequest messages
//--------------------------------------------------------------
function voteFetcher(x) {
    if (x.readyState == 3) { /* document.write(x.getAllResponseHeaders()); */ }
    if (x.readyState != 4) { return; }
    if (x.status != 200) {
        document.getElementById("vs-preview").innerHTML = "<i>Page not found or network error</i>";
        return;
    }
    if (x.responseText) {
        var dom = document.createElement("div"); // placeholder
        dom.innerHTML = x.responseText; // hack
        vs_data[2] = getAllHeadings(dom, new Array());	// get _ALL_ headings
        voteProcessor(dom);		// process dom for votes
        for (head in vs_data[2]) {
           document.getElementById("vs-headers").value += vs_data[2][head] + '\r\n';
        }
        uiUpdater();
    } else {
        document.getElementById("vs-preview").innerHTML = "<i>Response empty</i>";
    }
}
/* </pre> */