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 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 + ' </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> */

