diff options
author | Matěj Cepl <mcepl@redhat.com> | 2011-01-28 02:04:52 +0100 |
---|---|---|
committer | Matěj Cepl <mcepl@redhat.com> | 2011-01-28 02:04:52 +0100 |
commit | 83a7d703cbabd6e514a6d8a948b39325b59e9f58 (patch) | |
tree | e1bf21f956c5f0e151bb254dec8771e22c3261df /data | |
parent | 6424471ab36679138c1708ddaf97c40e6bf24155 (diff) | |
download | bugzilla-triage-83a7d703cbabd6e514a6d8a948b39325b59e9f58.tar.gz |
bzpage.js mostly done
Also:
* created libbugzilla.js for putting aside most RPCed functions
* utils.js and color.js moved to data
* tons and tons of restructing to make things work via RPC
Diffstat (limited to 'data')
-rw-r--r-- | data/bzpage.js | 671 | ||||
-rw-r--r-- | data/color.js | 236 | ||||
-rw-r--r-- | data/rhbzpage.js | 26 | ||||
-rw-r--r-- | data/util.js | 244 |
4 files changed, 760 insertions, 417 deletions
diff --git a/data/bzpage.js b/data/bzpage.js index def12a7..086fe44 100644 --- a/data/bzpage.js +++ b/data/bzpage.js @@ -3,16 +3,23 @@ // http://www.opensource.org/licenses/mit-license.php "use strict"; var bugURL = "https://bugzilla.redhat.com/show_bug.cgi?id="; +var BTSPrefNS = "bugzilla-triage.setting."; // Shared contstants var NumberOfFrames = 7; exports.NumberOfFrames = NumberOfFrames; -var BTSPrefNS = "bugzilla-triage.setting."; -exports.BTSPrefNS = BTSPrefNS; -var BTSPassRealm = "BTSXMLRPCPass"; -// ============================================ +// constants +var SalmonPink = new Color(255, 224, 176); // RGB 255, 224, 176; HSL 36, 2, + // 85 +var ReporterColor = new Color(255, 255, 166); // RGB 255, 255, 166; HSL 60, 2, + // 83 +// global variables +var constantData = {}; // This should be probably eliminated ASAP or + // or done by other means. TODO +var submitHandlerInstalled = false; // for setUpLogging +// ============================================ /** * object to pack messaging. Use as in postMessage(new Message("GetPassword", { @@ -39,64 +46,51 @@ NotLoggedinException.prototype.toString = function () { }; exports.NotLoggedinException = NotLoggedinException; -// ==================================================================================== /** - * In case URL contains alias, not the real bug number, get the real bug no - * from the XML representation. Sets correct value to this.bugNo. - */ -nonTestedFunction getRealBugNo() { - console.log("We have to deal with bug aliased as " + this.bugNo); - var that = this; - // https://bugzilla.redhat.com/show_bug.cgi?ctype=xml&id=serialWacom - Request({ - url: this.win.location.href+"&ctype=xml", - onComplete: function(response) { - if (response.status === 200) { - var xmlRepr = response.xml; - var bugID = parseInt(xmlRepr.getElementsByTagName("bug_id")[0].textContent, 10); - if (isNaN(bugID)) { - throw new Error("Cannot get bug no. even from XML representation!"); - } - that.bugNo = bugID; - console.log("The real bug no. is " + bugID); +* central handler processing messages from the main script. +*/ +onMessage = function onMessage(msg) { + console.log("onMessage: msg = " + msg.toSource()); + switch (msg.cmd) { + case "ReloadThePage": + document.location.reload(true); + break; + case "RetClipboard": + if (msg.data.cmd == "queryLocal") { + queryInNewTab(msg.data.data, getComponent(), getProduct()); + } else if (msg.data.cmd == "queryUpstream") { + // } - } - }).get(); -} + break; + case "CreateButtons": + constantData = msg.data.constData; + generateButtons(msg.data.instPkgs); + break; + case "Error": + alert("Error " + msg.data); + break; + case "Unhandled": + break; + default: + console.error("Error: unknown RPC call " + msg.toSource()); + } +}; /** + * @param cmd Object with all commands to be executed * + * PROBLEM: according to https://developer.mozilla.org/en/Core_JavaScript_1.5_Reference\ + * /Statements/for...in there is no guaranteed order of execution of + * commands (i.e., key, commentObj[key] pairs) in for..in cycle. + * According to https://developer.mozilla.org/en/Core_JavaScript_1.5_Reference\ + * /Operators/Special_Operators/delete_Operator#Cross-browser_issues it seems that + * everywhere except of Internet Explorer this should work well, but waiting + * impatiently when this bite us. */ -nonTestedFunction getInstalledPackages(cfg) { - var installedPackages = {}; - var enabledPackages = []; - - // Collect enabled packages per hostname (plus default ones) - if (cfg.gJSONData && ("commentPackages" in cfg.gJSONData)) { - if ("enabledPackages" in cfg.gJSONData.configData) { - var epObject = cfg.gJSONData.configData.enabledPackages; - if (this.hostname in epObject) { - enabledPackages = enabledPackages.concat(epObject[this.hostname].split(/[,\s]+/)); - } - if ("any" in epObject) { - enabledPackages = enabledPackages.concat(epObject.any.split(/[,\s]+/)); - } - } - - if ((enabledPackages.length === 1) && (enabledPackages[0] === "all")) { - enabledPackages = []; - for (var key in cfg.gJSONData.commentPackages) { - enabledPackages.push(key); - } - } - - enabledPackages.forEach(function (pkg, idx, arr) { - if (pkg in cfg.gJSONData.commentPackages) { - installedPackages[pkg] = cfg.gJSONData.commentPackages[pkg]; - } - }); +function executeCommand(cmdObj) { + for (var key in cmdObj) { + centralCommandDispatch(key, cmdObj[key]); } - return installedPackages; } /** @@ -105,125 +99,98 @@ nonTestedFunction getInstalledPackages(cfg) { * @param cmdLabel String with the name of the command to be executed * @param cmdParams Object with the appropriate parameters for the command */ -nonTestedFunction centralCommandDispatch (cmdLabel, cmdParams) { +function centralCommandDispatch (cmdLabel, cmdParams) { switch (cmdLabel) { case "resolution": case "product": case "component": case "version": case "priority": - this.selectOption(cmdLabel, cmdParams); + selectOption(cmdLabel, cmdParams); break; case "status": - this.selectOption("bug_status", cmdParams); + selectOption("bug_status", cmdParams); break; case "platform": - this.selectOption("rep_platform", cmdParams); + selectOption("rep_platform", cmdParams); break; case "os": - this.selectOption("op_sys", cmdParams); + selectOption("op_sys", cmdParams); break; case "severity": - this.selectOption("bug_severity", cmdParams); + selectOption("bug_severity", cmdParams); break; case "target": - this.selectOption("target_milestone", cmdParams); + selectOption("target_milestone", cmdParams); break; case "addKeyword": - this.addStuffToTextBox("keywords",cmdParams); + addStuffToTextBox("keywords",cmdParams); break; case "removeKeyword": - this.removeStuffFromTextBox("keywords", cmdParams); + removeStuffFromTextBox("keywords", cmdParams); break; case "addWhiteboard": - this.addStuffToTextBox("status_whiteboard",cmdParams); + addStuffToTextBox("status_whiteboard",cmdParams); break; case "removeWhiteboard": - this.removeStuffFromTextBox("status_whiteboard",cmdParams); + removeStuffFromTextBox("status_whiteboard",cmdParams); break; case "assignee": - this.changeAssignee(cmdParams); + changeAssignee(cmdParams); break; case "qacontact": - this.clickMouse("bz_qa_contact_edit_action"); - this.doc.getElementById("qa_contact").value = cmdParams; + clickMouse("bz_qa_contact_edit_action"); + document.getElementById("qa_contact").value = cmdParams; break; case "url": - this.clickMouse("bz_url_edit_action"); - this.doc.getElementById("bug_file_loc").value = cmdParams; + clickMouse("bz_url_edit_action"); + document.getElementById("bug_file_loc").value = cmdParams; break; // TODO dependson/blocked doesn't work. Find out why. case "addDependsOn": - this.clickMouse("dependson_edit_action"); - this.addStuffToTextBox("dependson", cmdParams); + clickMouse("dependson_edit_action"); + addStuffToTextBox("dependson", cmdParams); break; case "removeDependsOn": - this.clickMouse("dependson_edit_action"); - this.removeStuffFromTextBox("dependson", cmdParams); + clickMouse("dependson_edit_action"); + removeStuffFromTextBox("dependson", cmdParams); break; case "addBlocks": - this.clickMouse("blocked_edit_action"); - this.addStuffToTextBox("blocked", cmdParams); + clickMouse("blocked_edit_action"); + addStuffToTextBox("blocked", cmdParams); break; case "removeBlocks": - this.clickMouse("blocked_edit_action"); - this.removeStuffFromTextBox("blocked", cmdParams); + clickMouse("blocked_edit_action"); + removeStuffFromTextBox("blocked", cmdParams); break; case "comment": - this.addStuffToTextBox("comment", cmdParams); + addStuffToTextBox("comment", cmdParams); break; case "commentIdx": - var commentText = this.commentStrings[cmdParams]; - this.addStuffToTextBox("comment", commentText); + throw "There should be no commentIdx here at all."; break; case "setNeedinfo": // cmdParams are actually ignored for now; we may in future // distinguish different actors to be target of needinfo - this.setNeedinfoReporter(); + setNeedinfoReporter(); break; case "addCC": - this.addToCCList(cmdParams); + addToCCList(cmdParams); break; case "queryStringOurBugzilla": - this.queryForSelection(); + queryForSelection(); break; // TODO flags, see also - case "commit": if (cmdParams) { - // Directly commit the form - this.doc.forms.namedItem("changeform").submit(); + // Directly commit the form + document.forms.namedItem("changeform").submit(); } break; } } /** - * Take the ID of the package/id combination, and execute it - * - * @param String combined package + "//" + id combination - * Fetches the command object from this.installedPackages and then - * goes through all commands contained in it, and calls - * this.centralCommandDispatch to execute them. - * - * PROBLEM: according to https://developer.mozilla.org/en/Core_JavaScript_1.5_Reference\ - * /Statements/for...in there is no guaranteed order of execution of - * commands (i.e., key, commentObj[key] pairs) in for..in cycle. - * According to https://developer.mozilla.org/en/Core_JavaScript_1.5_Reference\ - * /Operators/Special_Operators/delete_Operator#Cross-browser_issues it seems that - * everywhere except of Internet Explorer this should work well, but waiting - * impatiently when this bite us. - */ -nonTestedFunction executeCommand (cmd) { - var cmdArr = cmd.split("//"); - var commentObj = this.packages[cmdArr[0]][cmdArr[1]]; - - for (var key in commentObj) { - this.centralCommandDispatch(key,commentObj[key]); - } -} - -/** * Change assignee of the bug * * @param newAssignee String with the email address of new assigneeAElement @@ -231,21 +198,21 @@ nonTestedFunction executeCommand (cmd) { * Value null clears "Reset Assignee to default for component" checkbox * @return none */ -nonTestedFunction changeAssignee (newAssignee) { +function changeAssignee (newAssignee) { var defAssigneeButton = null; // Previous assignee should know what's going on in his bug - this.addToCCList(this.owner); + addToCCList(getOwner()); // Optional value null if (newAssignee === null) { - this.doc.getElementById("set_default_assignee").removeAttribute( + document.getElementById("set_default_assignee").removeAttribute( "checked"); return ; } - if (this.getDefaultAssignee) { + if (getDefaultAssignee) { if (newAssignee === "default") { - var defAss = this.getDefaultAssignee(); + var defAss = getDefaultAssignee(); if (defAss) { newAssignee = defAss; } else { @@ -255,10 +222,10 @@ nonTestedFunction changeAssignee (newAssignee) { } if (newAssignee) { - this.clickMouse("bz_assignee_edit_action"); - this.doc.getElementById("assigned_to").value = newAssignee; - this.doc.getElementById("set_default_assignee").checked = false; - defAssigneeButton = this.doc.getElementById("setDefaultAssignee_btn"); + clickMouse("bz_assignee_edit_action"); + document.getElementById("assigned_to").value = newAssignee; + document.getElementById("set_default_assignee").checked = false; + defAssigneeButton = document.getElementById("setDefaultAssignee_btn"); if (defAssigneeButton) { defAssigneeButton.style.display = "none"; } @@ -273,34 +240,26 @@ nonTestedFunction changeAssignee (newAssignee) { * If the 'comment_action' scroll down box doesn't exist, this * function will set up new one. */ -nonTestedFunction addToCommentsDropdown (pkg, cmd) { - var select = this.doc.getElementById("comment_action"); +function addToCommentsDropdown (cmdObj) { + var select = document.getElementById("comment_action"); if (!select) { - var that = this; - this.doc.getElementById("comments").innerHTML += + document.getElementById("comments").innerHTML += "<div id='make_bugzilla_comment_action'>" + " <label for='comment_action'>Add Comment: </label>" + " <select id='comment_action'>" + " <option value=''>-- Select Comment from List --</option>" + "</div>"; - select = this.doc.getElementById("comment_action"); + select = document.getElementById("comment_action"); select.addEventListener("change", function (evt) { - var value = ""; - var valueElement = that.doc.getElementById("comment_action"); - if (valueElement) { - var selIdx = valueElement.selectedIndex; - value = valueElement.options[selIdx].value; - console.log("value = " + value); - } else { - return; - } - that.executeCommand(value); + var value = select.options[select.selectedIndex].value; + log("value = " + value); + executeCommand(value); }, false); } - var opt = this.doc.createElement("option"); - opt.value = pkg + "//" + cmd; - opt.textContent = this.packages[pkg][cmd].name; + var opt = document.createElement("option"); + opt.value = cmdObj; + opt.textContent = cmdObj.name; select.appendChild(opt); } @@ -315,20 +274,19 @@ nonTestedFunction addToCommentsDropdown (pkg, cmd) { * @param Boolean breakBefore if there should be a <br> element before. * @return none */ -nonTestedFunction createDeadLink (id, text, parent, callback, params, before, covered, accesskey) { - var that = this; - params = util.valToArray(params); +function createDeadLink (id, text, parent, callback, params, before, covered, accesskey) { + params = valToArray(params); var locParent = {}; // Yes, I want != here, not !== if (covered != null) { - locParent = this.doc.createElement(covered); + locParent = document.createElement(covered); parent.appendChild(locParent); } else { locParent = parent; } - var newAElem = this.doc.createElement("a"); + var newAElem = document.createElement("a"); newAElem.setAttribute("id", id); if (accesskey) { newAElem.setAttribute("accesskey", accesskey); @@ -337,15 +295,15 @@ nonTestedFunction createDeadLink (id, text, parent, callback, params, before, co newAElem.textContent = text; newAElem.addEventListener("click", function(evt) { - callback.apply(that, params); + callback(params); evt.stopPropagation(); evt.preventDefault(); }, false); if ((before === "br") || (before === true)) { - locParent.appendChild(this.doc.createElement("br")); + locParent.appendChild(document.createElement("br")); } else if (before === "dash") { - locParent.appendChild(this.doc.createTextNode("\u00A0-\u00A0")); + locParent.appendChild(document.createTextNode("\u00A0-\u00A0")); } locParent.appendChild(newAElem); @@ -362,44 +320,42 @@ nonTestedFunction createDeadLink (id, text, parent, callback, params, before, co * @param id String which command to take * @return none */ -nonTestedFunction createNewButton (location, after, pkg, id) { - var that = this; - var cmdObj = this.packages[pkg][id]; - var newId = id + "_btn"; - var label = cmdObj.name; +function createNewButton (location, after, cmdObj) { + var newId = cmdObj.name.toLowerCase().replace(/[^a-z0-9]+/,"","g") + "_btn"; // protection against double-firings - if (this.doc.getElementById(newId)) { - console.log("Element with id " + newId + "already exists!"); + if (document.getElementById(newId)) { + console.log("Element with id " + newId + " already exists!"); return ; } // creation of button might be conditional on existence of data in constantData if ("ifExist" in cmdObj) { - if (!(cmdObj.ifExist in this.constantData)) { + // We do have constantData now, but we shouldn't TODO + if (!(cmdObj.ifExist in constantData)) { return ; } } - var newButton = this.doc.createElement("input"); + var newButton = document.createElement("input"); newButton.setAttribute("id", newId); newButton.setAttribute("type", "button"); - newButton.value = label; + newButton.value = cmdObj.name; newButton.addEventListener("click", function(evt) { - that.executeCommand(pkg + "//" + id); + executeCommand(cmdObj); }, false); - var originalLocation = this.doc.getElementById(location); + var originalLocation = document.getElementById(location); try { if (after) { originalLocation.parentNode.insertBefore(newButton, originalLocation.nextSibling); - originalLocation.parentNode.insertBefore(this.doc + originalLocation.parentNode.insertBefore(document .createTextNode("\u00A0"), newButton); } else { originalLocation.parentNode.insertBefore(newButton, originalLocation); - originalLocation.parentNode.insertBefore(this.doc + originalLocation.parentNode.insertBefore(document .createTextNode("\u00A0"), originalLocation); } } catch (e) { @@ -412,60 +368,52 @@ nonTestedFunction createNewButton (location, after, pkg, id) { } /** - * + * Generate button based on */ -nonTestedFunction generateButtons () { +function generateButtons (pkgs) { var topRowPosition = "topRowPositionID"; var bottomRowPosition = "commit"; // create anchor for the top toolbar - var commentBox = this.doc.getElementById("comment"); - var brElement = this.doc.createElement("br"); + var commentBox = document.getElementById("comment"); + var brElement = document.createElement("br"); brElement.setAttribute("id",topRowPosition); commentBox.parentNode.normalize(); commentBox.parentNode.insertBefore(brElement, commentBox); - for (var pkg in this.packages) { - for (var cmdIdx in this.packages[pkg]) { - var cmdObj = this.packages[pkg][cmdIdx]; + for (var pkg in pkgs) { + for (var cmdIdx in pkgs[pkg]) { + var cmdObj = pkgs[pkg][cmdIdx]; switch (cmdObj.position) { case "topRow": - this.createNewButton(topRowPosition, false, pkg, cmdIdx); + createNewButton(topRowPosition, cmdObj, false); break; case "bottomRow": - this.createNewButton(bottomRowPosition, false, pkg, cmdIdx); + createNewButton(bottomRowPosition, cmdObj, false); break; case "dropDown": - this.addToCommentsDropdown(pkg,cmdIdx); + addToCommentsDropdown(cmdObj); break; default: // [+-]ID var firstChr = cmdObj.position.charAt(0); var newId = cmdObj.position.substr(1); - this.createNewButton(newId, firstChr === "+", pkg, cmdIdx); + createNewButton(newId, firstChr === "+", cmdObj); break; } } } } -nonTestedFunction setConfigurationButton () { - var additionalButtons = this.doc.querySelector("#bugzilla-body *.related_actions"); - var configurationButtonUI = this.doc.createElement("li"); +function setConfigurationButton () { + var additionalButtons = document.querySelector("#bugzilla-body *.related_actions"); + var configurationButtonUI = document.createElement("li"); configurationButtonUI.innerHTML = "\u00A0-\u00A0<a href='' id='configurationButton'>" + "Triage configuration</a>"; additionalButtons.appendChild(configurationButtonUI); - this.doc.getElementById("configurationButton").addEventListener( + document.getElementById("configurationButton").addEventListener( "click", function(evt) { - var prfNm = BTSPrefNS+"JSONURL"; - var url = preferences.get(prfNm,""); - - var reply = prompts.prompt("New location of JSON configuration file", url); - if (reply) { - preferences.set(prfNm, reply.trim()); - prompts.alert("For now, you should really restart Firefox!"); - } - + postMessage(new Message("ChangeJSONURL", null)); evt.stopPropagation(); evt.preventDefault(); }, false); @@ -480,18 +428,18 @@ nonTestedFunction setConfigurationButton () { * @return String with the address or null * */ -nonTestedFunction parseMailto(aElement) { +function parseMailto(aElement) { var emailStr = "", hrefStr = ""; // use url utils if (aElement) { hrefStr = decodeURIComponent(aElement.getAttribute("href")); - emailStr = hrefStr.split(":")[1]; - // - if (emailStr === undefined) { - var params = util.getParamsFromURL("https://" + this.hostname + "/" + hrefStr); - emailStr = decodeURI(params.login); + emailStr = hrefStr.split(":"); + // workaround for Gnome bugzilla ... no mailto: here. + if (emailStr.length < 2) { + var params = getParamsFromURL("https://" + window.location.hostname + "/" + hrefStr); + return decodeURI(params.login); } - return emailStr; + return emailStr[1]; } return null; } @@ -501,53 +449,58 @@ nonTestedFunction parseMailto(aElement) { * * @return string */ -nonTestedFunction getReporter () { - var reporterElement = this.getOptionTableCell("bz_show_bug_column_2", "Reported"); +function getReporter () { + var reporterElement = getOptionTableCell("bz_show_bug_column_2", "Reported"); // RH Bugzilla after upgrade to 3.6.2 moved the information to other column if (!reporterElement) { - reporterElement = this.getOptionTableCell("bz_show_bug_column_1", "Reported", true); + reporterElement = getOptionTableCell("bz_show_bug_column_1", "Reported", true); } // Maemo calls the label "Reporter" and it doesn't have ids on table columns ... TODO(maemo) - - return this.parseMailto(reporterElement); + return parseMailto(reporterElement); } -nonTestedFunction getComponent() { - var elem = this.doc.getElementById("component"); +function getComponent() { + var elem = document.getElementById("component"); if (elem) { return elem.value; } return null; } +function getProduct() { + var elem = document.getElementById("product"); + if (elem) { + return elem.value; + } + return null; +} -nonTestedFunction commentsWalker (fce) { - var comments = this.doc.getElementById("comments").getElementsByClassName( - "bz_comment"); +function commentsWalker (fce) { + var comments = document.getElementById("comments"). + getElementsByClassName("bz_comment"); Array.forEach(comments, function(item) { fce(item); - }, this); + }); } /** * Set background color of all comments made by reporter in ReporterColor color * */ -nonTestedFunction checkComments () { - var that = this; - var reporterRE = new RegExp(this.getReporter()); - this.commentsWalker(function(x) { - var email = that.parseMailto(x.getElementsByClassName("vcard")[0] +function checkComments () { + var reporterRE = new RegExp(getReporter()); + commentsWalker(function(x) { + var email = parseMailto(x.getElementsByClassName("vcard")[0] .getElementsByTagName("a")[0]); if (reporterRE.test(email)) { - x.style.backgroundColor = that.ReporterColor.toString(); + x.style.backgroundColor = ReporterColor.toString(); } }); } -nonTestedFunction collectComments () { +function collectComments () { var outStr = ""; - this.commentsWalker(function(x) { + commentsWalker(function(x) { outStr += x.getElementsByTagName("pre")[0].textContent + "\n"; }); return outStr.trim(); @@ -564,35 +517,35 @@ nonTestedFunction collectComments () { * @return none * */ -nonTestedFunction selectOption (id, label, fireEvent) { +function selectOption (id, label, fireEvent) { if (!fireEvent) { fireEvent = true; } - var sel = this.doc.getElementById(id); + var sel = document.getElementById(id); sel.value = label; if (fireEvent) { - var intEvent = this.doc.createEvent("HTMLEvents"); + var intEvent = document.createEvent("HTMLEvents"); intEvent.initEvent("change", true, true); sel.dispatchEvent(intEvent); } } -nonTestedFunction selectOptionByLabel(id, label, fireEvent) { +function selectOptionByLabel(id, label, fireEvent) { if (!fireEvent) { fireEvent = true; } - var sel = this.doc.getElementById(id); + var sel = document.getElementById(id); var labelRE = new RegExp(label.trim()); var ourOption = Array.filter(sel.options, function (op) { return op.textContent.trim() === label; - }, this); + }); if (ourOption[0]) { sel.value = ourOption[0].value; } if (fireEvent) { - var intEvent = this.doc.createEvent("HTMLEvents"); + var intEvent = document.createEvent("HTMLEvents"); intEvent.initEvent("change", true, true); sel.dispatchEvent(intEvent); } @@ -604,11 +557,11 @@ nonTestedFunction selectOptionByLabel(id, label, fireEvent) { * @param String ID of the element to send mouseclick to * @return None */ -nonTestedFunction clickMouse (targetID) { - var localEvent = this.doc.createEvent("MouseEvents"); - localEvent.initMouseEvent("click", true, true, this.doc.defaultView, 0, 0, +function clickMouse (targetID) { + var localEvent = document.createEvent("MouseEvents"); + localEvent.initMouseEvent("click", true, true, document.defaultView, 0, 0, 0, 0, 0, false, false, false, false, 0, null); - this.doc.getElementById(targetID).dispatchEvent(localEvent); + document.getElementById(targetID).dispatchEvent(localEvent); } /** @@ -619,13 +572,13 @@ nonTestedFunction clickMouse (targetID) { * * @return none */ -nonTestedFunction addStuffToTextBox (id, stuff) { - var textBox = this.doc.getElementById(id); +function addStuffToTextBox (id, stuff) { + var textBox = document.getElementById(id); if (textBox.tagName.toLowerCase() === "textarea") { stuff = textBox.value ? "\n\n" + stuff : stuff; textBox.value += stuff; } else { - textBox.value = util.addCSVValue(textBox.value,stuff); + textBox.value = addCSVValue(textBox.value,stuff); } } @@ -635,9 +588,9 @@ nonTestedFunction addStuffToTextBox (id, stuff) { * @param id String with the id of the element * @param stuff String/Array with keyword(s) to be removed */ -nonTestedFunction removeStuffFromTextBox (id, stuff) { +function removeStuffFromTextBox (id, stuff) { var changedElement = this.getElementById(id); - changedElement.value = util.removeCSVValue(changedElement.value,stuff); + changedElement.value = removeCSVValue(changedElement.value,stuff); } /** @@ -647,15 +600,15 @@ nonTestedFunction removeStuffFromTextBox (id, stuff) { * @param str String to be searched for * @return Boolean found? */ -nonTestedFunction idContainsWord (id, str) { +function idContainsWord (id, str) { var kwd = ""; try { - kwd = this.doc.getElementById(id).value; + kwd = document.getElementById(id).value; } catch (e) { // For those who don't have particular element at all or if it is empty return false; } - return (util.isInList(str, kwd.trim().split(/[,\s]+/))); + return (isInList(str, kwd.trim().split(/[,\s]+/))); } /** @@ -664,19 +617,20 @@ nonTestedFunction idContainsWord (id, str) { * @param str String with the keyword * @return Boolean */ -nonTestedFunction hasKeyword (str) { - return (this.idContainsWord('keywords', str)); +function hasKeyword (str) { + return (idContainsWord('keywords', str)); } /** - -@return Element with the href attribute containng the information + * dd + * + * @return Element with the href attribute containng the information */ -nonTestedFunction getOptionTableCell(tableId, label) { - var cleanLabelRE = /^\s*([^.:]*):?\s*$/; +function getOptionTableCell(tableId, label) { + var cleanLabelRE = new RegExp("^\\s*([^.:]*):?\\s*$"); label = label.trim().replace(cleanLabelRE,"$1").toLowerCase(); - var rows = this.doc.getElementById(tableId).getElementsByTagName("tr"); + var rows = document.getElementById(tableId).getElementsByTagName("tr"); var ourLine = Array.filter(rows, function(row) { var curLabel = row.getElementsByTagName("td")[0].textContent.toLowerCase(); curLabel = curLabel.replace(cleanLabelRE,"$1"); @@ -698,18 +652,18 @@ nonTestedFunction getOptionTableCell(tableId, label) { * @todo TODO we may extend this to general setNeedinfo function * with parameter [reporter|assignee|general-email-address] */ -nonTestedFunction setNeedinfoReporter () { - this.clickMouse("needinfo"); - this.selectOption("needinfo_role", "reporter"); +function setNeedinfoReporter () { + clickMouse("needinfo"); + selectOption("needinfo_role", "reporter"); } /** * */ -nonTestedFunction getOwner () { +function getOwner () { // TODO(maemo) doesn't work on maemo - var assigneeAElement = this.getOptionTableCell("bz_show_bug_column_1","Assigned To"); - return this.parseMailto(assigneeAElement); + var assigneeAElement = getOptionTableCell("bz_show_bug_column_1","Assigned To"); + return parseMailto(assigneeAElement); } /** @@ -718,8 +672,8 @@ nonTestedFunction getOwner () { * * @return String with the maintainer's email address */ -nonTestedFunction getDefaultBugzillaMaintainer (component) { - var address = util.filterByRegexp(this.defBugzillaMaintainerArr, component); +function getDefaultBugzillaMaintainer (component) { + var address = filterByRegexp(this.defBugzillaMaintainerArr, component); return address; } @@ -731,8 +685,7 @@ nonTestedFunction getDefaultBugzillaMaintainer (component) { * string of MIME type, integer of size in kilobytes, and the whole * element itself */ - -nonTestedFunction parseAttachmentLine(inElem) { +function parseAttachmentLine(inElem) { var MIMEtype = ""; var size = 0; @@ -753,8 +706,8 @@ nonTestedFunction parseAttachmentLine(inElem) { /^.*attachment.cgi\?id=/, ""), 10); // getting MIME type and size - var stringArray = inElem.getElementsByClassName("bz_attach_extra_info")[0].textContent - .replace(/[\n ()]+/g, " ").trim().split(", "); + var stringArray = inElem.getElementsByClassName("bz_attach_extra_info")[0].textContent. + replace(/[\n ()]+/g, " ").trim().split(", "); size = parseInt(stringArray[0], 10); MIMEtype = stringArray[1].split(" ")[0]; @@ -769,12 +722,12 @@ nonTestedFunction parseAttachmentLine(inElem) { * string of MIME type, integer of size in kilobytes, and the whole * element itself */ -nonTestedFunction getAttachments () { +function getAttachments () { var outAtts = []; - var atts = this.doc.getElementById("attachment_table") - .getElementsByTagName("tr"); + var atts = document.getElementById("attachment_table"). + getElementsByTagName("tr"); for ( var i = 1, ii = atts.length - 1; i < ii; i++) { - outAtts.push(this.parseAttachmentLine(atts[i])); + outAtts.push(parseAttachmentLine(atts[i])); } return outAtts; } @@ -784,70 +737,43 @@ nonTestedFunction getAttachments () { * * @return String with the login name of the currently logged-in user */ -nonTestedFunction getLogin () { - var lastLIElement = this.doc.querySelector("#header ul.links li:last-of-type"); +function getLogin () { + var lastLIElement = document.querySelector("#header ul.links li:last-of-type"); var loginArr = lastLIElement.textContent.split("\n"); var loginStr = loginArr[loginArr.length - 1].trim(); return loginStr; } /** - * returns password from the current storage, or if there isn't - * one, then it will ask user for it. - * - * @return String with the password - */ -nonTestedFunction getPassword (login) { - var passPrompt = "Enter your Bugzilla password for fixing MIME attachment types"; - var switchPrompt = "Do you want to switch off features requiring password completely?"; - var prefName = BTSPrefNS+"withoutPassowrd"; - var domain = this.win.location.protocol + "//" + this.hostname; - - var pass = passUtils.getPassword(login, domain, BTSPassRealm); - // pass === null means no appropriate password in the storage - if (!preferences.get(prefName,false) && (pass === null)) { - var passwordText = prompts.promptPassword(passPrompt); - if (passwordText && passwordText.length > 0) { - passUtils.setLogin(login, passwordText, domain, - BTSPassRealm); - return passwordText; - } else { - var switchOff = prompts.promptYesNoCancel(switchPrompt); - if (switchOff) { - preferences.set(prefName,true); - } - return null; - } - } else { - return pass; - } -} - -/** - * + * TODO THIS IS COMPLETELY BROKEN AND NEED TO BE REWRITTEN */ -nonTestedFunction setUpLogging () { +function setUpLogging () { // Protection against double-call - if (this.doc.getElementById("generateTSButton")) { + if (document.getElementById("generateTSButton")) { return ; } + // TODO fix later + return ; // switched off for now + // For adding additional buttons to the top toolbar - var additionalButtons = this.doc.querySelector("#bugzilla-body *.related_actions"); + var additionalButtons = document.querySelector("#bugzilla-body *.related_actions"); var that = this; // logging all submits for timesheet - if (!this.submitHandlerInstalled) { - this.doc.forms.namedItem("changeform").addEventListener("submit",function (evt) { + if (!submitHandlerInstalled) { + document.forms.namedItem("changeform").addEventListener("submit",function (evt) { + // TODO this is probably another RPC call var resp = that.log.addLogRecord(that); if (resp === null) { evt.stopPropagation(); evt.preventDefault(); } }, false); - this.submitHandlerInstalled = true; + submitHandlerInstalled = true; } + // TODO another RPC call // (id, text, parent, callback, params, before, covered, accesskey) this.createDeadLink("generateTSButton", "Generate TS", additionalButtons, function(evt) { @@ -855,18 +781,20 @@ nonTestedFunction setUpLogging () { that.log.generateTimeSheet); }, [], "dash", "li"); + // TODO another RPC call this.createDeadLink("clearLogs", "Clear TS", additionalButtons, function(evt) { that.log.clearStore(this); }, [], "dash", "li"); + // TODO another RPC call this.createDeadLink("importTSButton", "Import TS", additionalButtons, function(evt) { jsonPaths = prompts.promptFileOpenPicker(that.win); that.log.importOtherStore(jsonPaths, clearLogAElem); }, [], "dash", "li"); - var clearLogAElem = this.doc.getElementById("clearLogs"); + var clearLogAElem = document.getElementById("clearLogs"); if (this.log.isEmpty()) { clearLogAElem.style.color = this.log.EmptyLogsColor; clearLogAElem.style.fontWeight = "normal"; @@ -876,10 +804,11 @@ nonTestedFunction setUpLogging () { } } -nonTestedFunction getSelectionOrClipboard () { - var text = selection.text; - if (!text) { - text = clip.get(); +function getSelection () { + var text = ""; + var selectionObject = window.getSelection(); + if (selectionObject.rangeCount > 0) { + text = selectionObject.getRangeAt(0).toString(); } return text; } @@ -894,8 +823,8 @@ nonTestedFunction getSelectionOrClipboard () { * @return None * */ -nonTestedFunction queryInNewTab(text, component, product) { - var urlStr = "https://bugzilla.redhat.com/buglist.cgi?query_format=advanced"; +function queryInNewTab(text, component, product) { + var urlStr = "https://" + window.location.hostname + "/buglist.cgi?query_format=advanced"; if (product) { urlStr += "&product=" + product.trim(); } @@ -928,13 +857,7 @@ nonTestedFunction queryInNewTab(text, component, product) { + "&field1-0-2=status_whiteboard&type1-0-2=substring&value1-0-2=" + text; urlStr += searchText; - tabs.open({ - url: urlStr, - inBackground: true, - onReady: function (t) { - t.activate(); - } - }); + postMessage(new Message("OpenURLinNewTab", urlStr)); } } @@ -942,10 +865,12 @@ nonTestedFunction queryInNewTab(text, component, product) { * Get the text to search for and prepare other things for the real executive * function this.queryInNewTab, and run it. */ -nonTestedFunction queryForSelection() { - var text = this.getSelectionOrClipboard(); - if (text) { - this.queryInNewTab(text, this.getComponent()); +function queryForSelection() { + var text = getSelection(); + if (!text) { + postMessage(new Message("GetClipboard", "queryLocal")); + } else { + queryInNewTab(text, getComponent(), getProduct()); } } @@ -955,16 +880,16 @@ nonTestedFunction queryForSelection() { * @param who String with email address or "self" if the current user * of the bugzilla should be added */ -nonTestedFunction addToCCList (who) { +function addToCCList (who) { if (!who) { return ; } if (who === "self") { - this.doc.getElementById("addselfcc").checked = true; + document.getElementById("addselfcc").checked = true; } else { - this.clickMouse("cc_edit_area_showhide"); - if (!util.isInList(who, this.CCList)) { - this.addStuffToTextBox("newcc",who); + clickMouse("cc_edit_area_showhide"); + if (!isInList(who, getCCList())) { + addStuffToTextBox("newcc",who); } } } @@ -974,8 +899,8 @@ nonTestedFunction addToCCList (who) { * * @return Array with email addresses as Strings. */ -nonTestedFunction getCCList () { - var CCListSelect = this.doc.getElementById("cc"); +function getCCList () { + var CCListSelect = document.getElementById("cc"); var outCCList = []; if (CCListSelect) { outCCList = Array.map(CCListSelect.options, function(item) { @@ -985,106 +910,28 @@ nonTestedFunction getCCList () { return outCCList; } -// exports.BZPage = apiUtils.publicConstructor(BZPage); -// BZPage's methods -var BZPage = function BZPage(win, config) { - // constants - this.SalmonPink = new Color(255, 224, 176); // RGB 255, 224, 176; HSL 36, 2, - // 85 - this.ReporterColor = new Color(255, 255, 166); // RGB 255, 255, 166; HSL 60, 2, - // 83 - - // initialize dynamic properties - var that = this; - this.win = win; - this.doc = win.document; - this.hostname = this.win.location.hostname; - +function startup() { // First, preflight check ... if we are not logged in, there // is nothing we can do. - var logoutLink = Array.some(this.doc.links, function (x) { + var logoutLink = Array.some(document.links, function (x) { return x.search === "?logout=1" ; }); if (!logoutLink) { - throw new NotLoggedinException("Not logged in"); + return null; // This should just finish whole content script without + // doing any harm to anybody. } + // TODO Probably could be ignored ... used only once on line 973 of rhbzpage.js + // if (parseAbrtBacktraces && this.RE.Abrt.test(this.title)) { + this.title = document.getElementById("short_desc_nonedit_display").textContent; + // So, now we know we are logged in, so we can get to // the real work. - this.packages = this.getInstalledPackages(config); - - if ("commentStrings" in config.gJSONData) { - this.commentStrings = config.gJSONData.commentStrings; - } - - this.constantData = {}; - if ("constantData" in config.gJSONData) { - this.constantData = config.gJSONData.constantData; - this.constantData.queryUpstreamBug = JSON.parse( - selfMod.data.load("queryUpstreamBug.json")); - this.constantData.XMLRPCData = JSON.parse( - selfMod.data.load("XMLRPCdata.json")); - } - - if ("CCmaintainer" in this.constantData) { - this.defBugzillaMaintainerArr = this.constantData.CCmaintainer; - } - - if ("suspiciousComponents" in config.gJSONData.configData) { - this.suspiciousComponents = config.gJSONData.configData.suspiciousComponents; - } - - if ("XorgLogAnalysis" in config.gJSONData.configData) { - this.xorglogAnalysis = config.gJSONData.configData.XorgLogAnalysis; - } + setConfigurationButton(); + submitHandlerInstalled = false; - if ("submitsLogging" in config.gJSONData.configData && - config.gJSONData.configData.submitsLogging) { - this.log = config.logger; - this.setUpLogging(); - } - - if ("killNodes" in config.gJSONData.configData && - this.hostname in config.gJSONData.configData.killNodes) { - var killConf = config.gJSONData.configData.killNodes[this.hostname]; - util.killNodes(this.doc, killConf[0], killConf[1]); - } - - this.setConfigurationButton(); - this.submitHandlerInstalled = false; - - this.login = this.getLogin(); - // XML-RPC password - if (this.hostname in this.constantData.XMLRPCData) { - this.password = this.getPassword(this.login); - } - - this.bugNo = util.getBugNo(this.doc.location.toString()); + checkComments(); - // deal with aliases - if (isNaN(parseInt(this.bugNo, 10)) && this.password) { - this.bugNo = this.getRealBugNo(); - } - - this.title = this.doc.getElementById("short_desc_nonedit_display").textContent; - this.CCList = this.getCCList(); - - // Prepare for query buttons - // element ID brElementPlace_location is later used in JSON files - // Stay with this add_comment element even if RH BZ upgrades, this seems - // to be generally much more stable (even with other bugzillas, e.g. b.gnome.org) - // then some getElementById. - var commentArea = this.doc.getElementsByName("add_comment")[0].parentNode; - if (commentArea) { - var brElementPlacer = commentArea.getElementsByTagName("br"); - brElementPlacer = brElementPlacer[0]; - if (brElementPlacer) { - brElementPlacer.setAttribute("id","brElementPlacer_location"); - brElementPlacer.parentNode.insertBefore(this.doc.createElement("br"), - brElementPlacer); - } - } - - this.checkComments(); - this.generateButtons(); -}; + var login = getLogin(); + postMessage(new Message("GetInstalledPackages", window.location)); +} diff --git a/data/color.js b/data/color.js new file mode 100644 index 0000000..574ad19 --- /dev/null +++ b/data/color.js @@ -0,0 +1,236 @@ +// Released under the MIT/X11 license +// http://www.opensource.org/licenses/mit-license.php +"use strict"; +// ============================================================================ +// Color management methods +// originally from +// http://www.mjijackson.com/2008/02\ +// /rgb-to-hsl-and-rgb-to-hsv-color-model-conversion-algorithms-in-javascript +var Color = exports.Color = function Color(r, g, b) { + this.Luminosity = 0.85; + this.Desaturated = 0.4; + + if (r instanceof Array) { + this.r = r[0]; + this.g = r[1]; + this.b = r[2]; + } else { + this.r = r; + this.g = g; + this.b = b; + } +}; + +Color.prototype.update = function(r, g, b) { + this.r = r; + this.g = g; + this.b = b; +}; + +Color.prototype.hs = function(nStr) { + if (Number(nStr) === 0) { + return "00"; + } else if (nStr.length < 2) { + return "0" + nStr; + } else { + return nStr; + } +}; + +Color.prototype.toString = function() { + var rH = Number(this.r.toFixed()).toString(16); + var gH = Number(this.g.toFixed()).toString(16); + var bH = Number(this.b.toFixed()).toString(16); + return "#" + this.hs(rH) + this.hs(gH) + this.hs(bH); +}; + +/** + * Converts an RGB color value to HSL. Conversion formula adapted from + * http://en.wikipedia.org/wiki/HSL_color_space. Assumes r, g, and b are + * contained in the set [0, 255] and returns h, s, and l in the set [0, 1].4343 + * + * @param Number r The red color value + * @param Number g The green color value + * @param Number b The blue color value + * @return Array The HSL representation + */ +Color.prototype.hsl = function() { + var r = this.r / 255; + var g = this.g / 255; + var b = this.b / 255; + var max = Math.max(r, g, b), min = Math.min(r, g, b); + var h, s, l = (max + min) / 2; + + if (max === min) { + h = s = 0; // achromatic + } else { + var d = max - min; + s = l > 0.5 ? d / (2 - max - min) : d / (max + min); + switch (max) { + case r: + h = (g - b) / d + (g < b ? 6 : 0); + break; + case g: + h = (b - r) / d + 2; + break; + case b: + h = (r - g) / d + 4; + break; + } + h /= 6; + } + + return [ h, s, l ]; +}; + +/** + * Converts an HSL color value to RGB. Conversion formula adapted from + * http://en.wikipedia.org/wiki/HSL_color_space. Assumes h, s, and l are + * contained in the set [0, 1] and returns r, g, and b in the set [0, 255]. + * + * @param Number h The hue + * @param Number s The saturation + * @param Number l The lightness + * @return Array The RGB representation + */ +Color.prototype.hslToRgb = function(h, s, l) { + function hue2rgb(p, q, t) { + if (t < 0) { + t += 1; + } + if (t > 1) { + t -= 1; + } + if (t < 1 / 6) { + return p + (q - p) * 6 * t; + } + if (t < 1 / 2) { + return q; + } + if (t < 2 / 3) { + return p + (q - p) * (2 / 3 - t) * 6; + } + return p; + } + + var r, g, b; + + if (s === 0) { + r = g = b = l; // achromatic + } else { + var q = l < 0.5 ? l * (1 + s) : l + s - l * s; + var p = 2 * l - q; + r = hue2rgb(p, q, h + 1 / 3); + g = hue2rgb(p, q, h); + b = hue2rgb(p, q, h - 1 / 3); + } + + return [ r * 255, g * 255, b * 255 ]; +}; + +/** + * Converts an RGB color value to HSV. Conversion formula adapted from + * http://en.wikipedia.org/wiki/HSV_color_space. Assumes r, g, and b are + * contained in the set [0, 255] and returns h, s, and v in the set [0, 1]. + * + * @param Number r The red color value + * @param Number g The green color value + * @param Number b The blue color value + * @return Array The HSV representation + */ +Color.prototype.hsv = function() { + var r = this.r / 255; + var g = this.g / 255; + var b = this.b / 255; + var max = Math.max(r, g, b), min = Math.min(r, g, b); + var h, s, v = max; + + var d = max - min; + s = max === 0 ? 0 : d / max; + + if (max === min) { + h = 0; // achromatic + } else { + switch (max) { + case r: + h = (g - b) / d + (g < b ? 6 : 0); + break; + case g: + h = (b - r) / d + 2; + break; + case b: + h = (r - g) / d + 4; + break; + } + h /= 6; + } + + return [ h, s, v ]; +}; + +/** + * Converts an HSV color value to RGB. Conversion formula adapted from + * http://en.wikipedia.org/wiki/HSV_color_space. Assumes h, s, and v are + * contained in the set [0, 1] and returns r, g, and b in the set [0, 255]. + * + * @param Number h The hue + * @param Number s The saturation + * @param Number v The value + * @return Array The RGB representation + */ +Color.prototype.hsvToRgb = function(h, s, v) { + var r, g, b; + + var i = Math.floor(h * 6); + var f = h * 6 - i; + var p = v * (1 - s); + var q = v * (1 - f * s); + var t = v * (1 - (1 - f) * s); + + switch (i % 6) { + case 0: + r = v; + g = t; + b = p; + break; + case 1: + r = q; + g = v; + b = p; + break; + case 2: + r = p; + g = v; + b = t; + break; + case 3: + r = p; + g = q; + b = v; + break; + case 4: + r = t; + g = p; + b = v; + break; + case 5: + r = v; + g = p; + b = q; + break; + } + + return [ r * 255, g * 255, b * 255 ]; +}; + +/** + * Provide + */ +Color.prototype.lightColor = function() { + var hslArray = this.hsl(); + var h = Number(hslArray[0]); + var s = Number(hslArray[1]) * this.Desaturated; + var l = this.Luminosity; + var desA = this.hslToRgb(h, s, l); + return new Color(desA[0], desA[1], desA[2]); +}; diff --git a/data/rhbzpage.js b/data/rhbzpage.js index 1d0907a..ba55ed6 100644 --- a/data/rhbzpage.js +++ b/data/rhbzpage.js @@ -104,6 +104,19 @@ nonTestedFunction addAttachment(data, callback, param) { var msg = new xrpc.XMLRPCMessage("bugzilla.addAttachment"); var that = this; + // Get bug no + var bugNo = getBugNo(window.location.toString()); + + // deal with aliases + // FIXME there is no password in content scripts anymore! + if (isNaN(parseInt(bugNo, 10)) && this.password) { + getRealBugNo(function(bugN) { + // Zbytek funkce addAttachment + }); + } + + + msg.addParameter(this.bugNo); msg.addParameter({ description: titleParsedAttachment, @@ -119,7 +132,7 @@ nonTestedFunction addAttachment(data, callback, param) { url: this.constantData.XMLRPCData[this.hostname].url, onComplete: function(response) { if (response.status == 200) { - var resp = util.parseXMLfromString(response.text); + var resp = parseXMLfromString(response.text); var newAttachID = parseInt(resp.params.param.value.array.data.value.int, 10); console.log("attachID = " + newAttachID); callback.call(that, param, newAttachID, data.length); @@ -310,6 +323,7 @@ nonTestedFunction addCheckShowLink(oldAtt, snippet) { nonTestedFunction markBadAttachments(atts) { var that = this; var badMIMEArray = [ "application/octet-stream", "text/x-log", "undefined" ]; + // FIXME there is no password anymore, we have to fix it somehow else if (!this.password) { return ; // User didn't provide password, so whole MIME fixing business // should be switched off. @@ -414,7 +428,7 @@ nonTestedFunction setBranding() { } // we should make visible whether maintCCAddr is in CCList - if (util.isInList(this.maintCCAddr, this.CCList)) { + if (util.isInList(this.maintCCAddr, getCCList())) { var ccEditBoxElem = this.doc.getElementById("cc_edit_area_showhide"); ccEditBoxElem.style.color = "navy"; ccEditBoxElem.style.fontWeight = "bolder"; @@ -619,9 +633,10 @@ nonTestedFunction fillInChipMagic() { // Persuade createNewButton to have mercy and to actually add // non-default button that.constantData.chipMagicTrigger = true; - that.packages["rh-xorg"].chipMagic.chipMagic = interestingLine+"\t"+interestingArray[1] - .toUpperCase(); - that.createNewButton("status_whiteboard", true, "rh-xorg", "chipMagic"); + // FIXME packages don't exist anymore + // that.packages["rh-xorg"].chipMagic.chipMagic = interestingLine+"\t"+interestingArray[1] + // .toUpperCase(); + createNewButton("status_whiteboard", true, "rh-xorg", "chipMagic"); } } } @@ -706,6 +721,7 @@ nonTestedFunction XMLRPCcallback() { } } +// FIXME this whole goes to libbzpage.js /** * The worker function -- call XMLRPC to fix MIME type of the particular * attachment diff --git a/data/util.js b/data/util.js new file mode 100644 index 0000000..4612b66 --- /dev/null +++ b/data/util.js @@ -0,0 +1,244 @@ +/*global exports: false, require: false, console: false, Cc: false, Ci: false */ +/*jslint onevar: false */ +// Released under the MIT/X11 license +// http://www.opensource.org/licenses/mit-license.php +"use strict"; +// ============================================================== +var Cc = require("chrome").Cc; +var Ci = require("chrome").Ci; +var urlMod = require("url"); + +/** + * Function for the management of the prototypal inheritace + * David Flanagan, Javascript: The Definitve Guide, + * IV. edition, O'Reilly, 2006, p. 168 + * + * @param superobject + * @return new object, it needs new prototype.constructor + * + * <pre> + * function Father(x) { + * this.family = x; + * } + * + * function Son(x,w) { + * Father.call(this,x); + * this.wife = w; + * } + * Son.prototype = heir(Father); + * Son.prototype.constructor = Son; + * </pre> + */ +exports.heir = function heir(p) { + function F() {}; + F.prototype = p.prototype; + return new F(); +}; + +/** + * get parameters of URL as an object (name, value) + */ +var getParamsFromURL = exports.getParamsFromURL = function getParamsFromURL (url, base) { + if (!url || (url.toString().length === 0)) { + throw new Error("Missing URL value!"); + } + + if (!(url instanceof urlMod.URL)) { + url = new urlMod.URL(url.toString(), base); + } + + var paramsArr = url.path.split("?"); + if (paramsArr.length === 1) { + return {}; + } + + // get convert URL parameters to an Object + var params = {}, s = []; + paramsArr[1].split('&').forEach(function(par) { + s = par.split('='); + params[s[0]] = s[1]; + }); + return params; +}; + +/** + * parse XML object out of string working around various bugs in Gecko implementation + * see https://developer.mozilla.org/en/E4X for more information + * + * @param inStr String with unparsed XML string + * @return XML object + */ +exports.parseXMLfromString = function parseXMLfromString (inStuff) { + // if (typeof inStuff !== 'string') In future we should recognize this.response + // and get just .text property out of it. TODO + var respStr = inStuff.replace(/^<\?xml\s+version\s*=\s*(["'])[^\1]+\1[^?]*\?>/, ""); // bug 336551 + return new XML(respStr); +}; + +/** + * Get a bug no from URL ... fails with aliases + * It should theoretically belong to bzpage.js, but we don't have + * unit tests there yet, so keeping here. + * + * @param url String with URL to be analyzed + * @return String with the bug ID (hopefully number, but not for aliases) + */ +exports.getBugNo = function getBugNo(url) { + var params = getParamsFromURL(url); + if (params && params.id) { + return params.id; + } +}; + +/** + * format date to be in ISO format (just day part) + * + * @param date + * @return string with the formatted date + */ +exports.getISODate = function getISODate(dateStr) { + function pad(n) { + return n < 10 ? '0' + n : n; + } + var date = new Date(dateStr); + return date.getFullYear() + '-' + pad(date.getMonth() + 1) + '-' + + pad(date.getDate()); +}; + +/** + * Check whether an item is member of the list. Idea is just to make long if + * commands slightly more readable. + * + * @param mbr string to be searched in the list + * @param list list + * @return position of the string in the list, or -1 if none found. + */ +var isInList = exports.isInList = function isInList(mbr, list) { + if (!list) { + return false; + } + return (list.indexOf(mbr) !== -1); +}; + +/** + * Make sure value returned is Array + * + * @param Array/String + * @return Array + * + * If something else than Array or String is passed to the function + * the result will be untouched actual argument of the call. + */ +var valToArray = exports.valToArray = function valToArray(val) { + var isArr = val && + val.constructor && + val.constructor.name === "Array"; + return isArr ? val : [val]; +}; + +/** + * Merges two comma separated string as a list and returns new string + * + * @param str String with old values + * @param value String/Array with other values + * @return String with merged lists + */ +exports.addCSVValue = function addCSVValue(str, value) { + var parts = (str.trim().length > 0 ? str.split(/[,\s]+/) : []); + if (!value) { + return str; + } + if (!isInList(value, parts)) { + var newValue = valToArray(value); + parts = parts.concat(newValue); + } + // this is necessary to get comma-space separated string even when + // value is an array already + parts = parts.join(",").split(","); + return parts.join(", "); +}; + +/** + * Treats comma separated string as a list and removes one item from it + * + * @param str String treated as a list + * @param value String with the value to be removed from str + * @return String with the resulting list comma separated + */ +exports.removeCSVValue = function removeCSVValue(str, value) { + str = str.trim(); + var parts = str ? str.split(/[,\s]+/) : []; + var valueArr = value instanceof Array ? value : value.split(/[,\s]+/); + parts = parts.filter(function (e, i, a) { + return (!isInList(e, valueArr)); + }); + return parts.join(", "); +}; + +/** + * select element of the array where regexp in the first element matches second + * parameter of this function + * + * @param list Array with regexps and return values + * @param chosingMark String by which the element of array is to be matched + * @return Object chosen element + */ +var filterByRegexp = exports.filterByRegexp = + function filterByRegexp(list, chosingMark) { + var chosenPair = []; + if (list.length > 0) { + chosenPair = list.filter(function (pair) { + return new RegExp(pair.regexp, "i").test(chosingMark); + }); + } + if (chosenPair.length > 0) { + return chosenPair[0].addr; + } else { + return ""; + } +}; + +/** + * remove elements from the page based on their IDs + * + * @param doc Document object + * @param target String/Array with ID(s) + * @param remove Boolean indicating whether the node should be + * actually removed or just hidden. + * @return none + * TODO remove parameter could be replaced by function which would + * do actual activity. + */ +exports.killNodes = function killNodes(doc, target, remove) { + target = target.trim(); + var targetArr = target instanceof Array ? target : target.split(/[,\s]+/); + targetArr.forEach(function(x) { + if (remove) { + var targetNode = doc.getElementById(x); + targetNode.parentNode.removeChild(targetNode); + } else { + x.style.display = "none"; + } + }); +}; + +exports.getObjectKeys = function getObjectKeys(obj) { + var keys = []; + for (var key in obj) { + if (obj.hasOwnProperty(key)) { + keys.push(key); + } + } + return keys; +}; + +exports.removeDuplicates = function removeDuplicates (arr) { + for (var i = 0; i < arr.length; i++) { + for (var j = i + 1; j < arr.length; j++) { + if (arr[i] == arr[j]) { + arr.splice (j, 1); + } + } + } + return arr; +}; |