diff options
author | Matěj Cepl <mcepl@redhat.com> | 2010-05-31 19:03:04 +0200 |
---|---|---|
committer | Matěj Cepl <mcepl@redhat.com> | 2010-05-31 19:03:04 +0200 |
commit | 2447e31a804eb7d7fe5ef01b974cba3b615e0594 (patch) | |
tree | f4487a9c0c10b476caa3c74f9871bd3e6617472d /lib/bzpage.js | |
parent | 951ef0d578130106e303e5586f53404a900ccf9c (diff) | |
download | bugzilla-triage-2447e31a804eb7d7fe5ef01b974cba3b615e0594.tar.gz |
First attempt to split main script into modules.
Diffstat (limited to 'lib/bzpage.js')
-rw-r--r-- | lib/bzpage.js | 746 |
1 files changed, 746 insertions, 0 deletions
diff --git a/lib/bzpage.js b/lib/bzpage.js new file mode 100644 index 0000000..c4c88f3 --- /dev/null +++ b/lib/bzpage.js @@ -0,0 +1,746 @@ +/*jslint onevar: false, browser: true, evil: true, laxbreak: true, undef: true, nomen: true, eqeqeq: true, bitwise: true, maxerr: 1000, immed: false, white: false, plusplus: false, regexp: false, undef: false */ +/*global jetpack */ +// Released under the MIT/X11 license +// http://www.opensource.org/licenses/mit-license.php +"use strict"; + +var TriagedDistro = 13; +var NumberOfFrames = 7; +var XMLRPCurl = "https://bugzilla.redhat.com/xmlrpc.cgi"; +var bugURL = "https://bugzilla.redhat.com/show_bug.cgi?id="; +var myStorage = jetpack.storage.simple; + +// ==================================================================================== +// BZPage's methods + +exports.BZPage = function BZPage(doc) { + // 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 + this.RE = { + Comment: new RegExp("^\\s*#"), // unsused + BlankLine: new RegExp("^\\s*$"), // unused + // nová řádka + // [ 65.631] (--) intel(0): Chipset: "845G" + Chipset: new RegExp("^\\s*\\[?[ 0-9.]*\\]?\\s*\\(--\\) "+ + "([A-Za-z]+)\\([0-9]?\\): Chipset: (.*)$"), + ATIgetID: new RegExp("^.*\\(ChipID = 0x([0-9a-fA-F]+)\\).*$"), + Abrt: new RegExp("^\\s*\\[abrt\\]"), + signalHandler: new RegExp("^\\s*#[0-9]*\\s*<signal handler called>"), + frameNo: new RegExp("^\\s*#([0-9]*)\\s") + }; + + + // initialize dynamic properties + this.doc = doc; + this.packages = this.getInstalledPackages(); + if ("commentStrings" in config.gJSONData) { + this.commentStrings = config.gJSONData.commentStrings; + } + + if ("constantData" in config.gJSONData) { + this.constantData = config.gJSONData.constantData; + } + + if ("CCmaintainer" in config.gJSONData.constantData) { + this.defBugzillaMaintainerArr = config.gJSONData.constantData.CCmaintainer; + } + + if ("submitsLogging" in config.gJSONData.configData && + config.gJSONData.configData.submitsLogging) { + this.log = config.logger; + this.setUpLogging(); + } + + this.submitHandlerInstalled = false; + this.bugNo = hlpr.getBugNo(this.doc.location.toString()); + this.reporter = this.getReporter(); + this.product = this.getOptionValue("product"); + this.component = this.getOptionValue("component"); + this.version = this.getVersion(); + this.title = this.doc.getElementById("short_desc_nonedit_display").textContent; + this.CCList = this.getCCList(); + + this.generateButtons(); +} + +/** + * + */ +BZPage.prototype.getInstalledPackages = function() { + let installedPackages = {}; + if (config.gJSONData && ("commentPackages" in config.gJSONData)) { + let enabledPackages = jetpack.storage.settings.enabledPacks.split(/[, ]/); + for each (let pkg in enabledPackages) { + if (pkg in config.gJSONData.commentPackages) { + installedPackages[pkg] = config.gJSONData.commentPackages[pkg]; + } + } + } + return installedPackages; +}; + +/** + * Actual execution function + * + * @param cmdLabel String with the name of the command to be executed + * @param cmdParams Object with the appropriate parameters for the command + */ +BZPage.prototype.centralCommandDispatch = function (cmdLabel, cmdParams) { + switch (cmdLabel) { + case "resolution": + case "product": + case "component": + case "version": + case "priority": + this.selectOption(cmdLabel, cmdParams); + break; + case "status": + this.selectOption("bug_status", cmdParams); + break; + case "platform": + this.selectOption("rep_platform", cmdParams); + break; + case "os": + this.selectOption("op_sys", cmdParams); + break; + case "severity": + this.selectOption("bug_severity", cmdParams); + break; + case "target": + this.selectOption("target_milestone", cmdParams); + break; + case "addKeyword": + this.addStuffToTextBox("keywords",cmdParams); + break; + case "removeKeyword": + this.removeStuffFromTextBox("keywords", cmdParams); + break; + case "addWhiteboard": + this.addStuffToTextBox("status_whiteboard",cmdParams); + break; + case "removeWhiteboard": + this.removeStuffFromTextBox("status_whiteboard",cmdParams); + break; + case "assignee": + this.changeAssignee(cmdParams); + break; + case "qacontact": + this.clickMouse("bz_qa_contact_edit_action"); + this.doc.getElementById("qa_contact").value = cmdParams; + break; + case "url": + this.clickMouse("bz_url_edit_action"); + this.doc.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); + break; + case "removeDependsOn": + this.clickMouse("dependson_edit_action"); + this.removeStuffFromTextBox("dependson", cmdParams); + break; + case "addBlocks": + this.clickMouse("blocked_edit_action"); + this.addStuffToTextBox("blocked", cmdParams); + break; + case "removeBlocks": + this.clickMouse("blocked_edit_action"); + this.removeStuffFromTextBox("blocked", cmdParams) + break; + case "comment": + this.addStuffToTextBox("comment", cmdParams); + break; + case "commentIdx": + let commentText = this.commentStrings[cmdParams]; + this.addStuffToTextBox("comment", commentText); + break; + case "setNeedinfo": + // cmdParams are actually ignored for now; we may in future + // distinguish different actors to be target of needinfo + this.setNeedinfoReporter(); + break; + case "addCC": + this.addToCCList(cmdParams); + break; + // TODO flags, see also + + case "commit": + if (cmdParams) { + // Directly commit the form + this.doc.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. + */ +BZPage.prototype.executeCommand = function(cmd) { + let [pkg, id] = cmd.split("//"); + let commentObj = this.packages[pkg][id]; + + for (let key in commentObj) { + this.centralCommandDispatch(key,commentObj[key]); + } +}; + +/** + * Add XGL to the CC list + * + * @param evt Event which made this function active + * @return none + */ +BZPage.prototype.changeAssignee = function(newAssignee) { + let defAssigneeButton = null; + this.addToCCList(this.owner); + if (newAssignee === null) { + this.doc.getElementById("set_default_assignee").removeAttribute( + "checked"); + return ; + } + + if (this.getDefaultAssignee) { + if (newAssignee == "default") { + let defAss = this.getDefaultAssignee(); + if (defAss) { + newAssignee = defAss; + } else { + return ; + } + } + } + + if (newAssignee) { + this.clickMouse("bz_assignee_edit_action"); + this.doc.getElementById("assigned_to").value = newAssignee; + this.doc.getElementById("set_default_assignee").checked = false; + if (defAssigneeButton = this.doc + .getElementById("setDefaultAssignee_btn")) { + defAssigneeButton.style.display = "none"; + } + } +}; + +/** + * Adds new option to the 'comment_action' scroll down box + * + * @param pkg String package name + * @param cmd String with the name of the command + * If the 'comment_action' scroll down box doesn't exist, this + * function will set up new one. + */ +BZPage.prototype.addToCommentsDropdown = function(pkg, cmd) { + let select = this.doc.getElementById("comment_action"); + if (!select) { + let that = this; + this.doc.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.addEventListener("change", function () { + let valueElement = that.doc.getElementById("comment_action"); + if (valueElement) { + let value = valueElement.getAttribute("value"); + } else { + return; + } + that.executeCommand(value); + }, false); + } + + let opt = that.doc.createElement("option"); + opt.value = pkg + "//" + cmd; + opt.textContent = this.packages[pkg][cmd].name; + select.appendChild(opt); +}; + +/** + * Generic function to add new button to the page. Actually copies new button + * from the old one (in order to have the same look-and-feel, etc. + * + * @param location Object around which the new button will be added + * @param after Boolean before or after location ? + * @param pkg String which package to take the command from + * @param id String which command to take + * @return none + */ +BZPage.prototype.createNewButton = function(location, after, pkg, id) { + let that = this; + let cmdObj = this.packages[pkg][id]; + let newId = id + "_btn"; + let label = cmdObj["name"]; + + // protection against double-firings + if (this.doc.getElementById(newId)) { + console.error("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)) { + return ; + } + } + + let newButton = this.doc.createElement("input"); + newButton.setAttribute("id", newId); + newButton.setAttribute("type", "button"); + newButton.value = label; + newButton.addEventListener("click", function(evt) { + that.executeCommand(pkg + "//" + id); + }, false); + + let originalLocation = this.doc.getElementById(location); + + if (after) { + originalLocation.parentNode.insertBefore(newButton, + originalLocation.nextSibling); + originalLocation.parentNode.insertBefore(this.doc + .createTextNode("\u00A0"), newButton); + } else { + originalLocation.parentNode.insertBefore(newButton, originalLocation); + originalLocation.parentNode.insertBefore(this.doc + .createTextNode("\u00A0"), originalLocation); + } +}; + +/** + * + */ +BZPage.prototype.generateButtons = function() { + let topRowPosition = "topRowPositionID"; + let bottomRowPosition = "commit"; + + // create anchor for the top toolbar + let commentBox = this.doc.getElementById("comment"); + let brElement = this.doc.createElement("br"); + brElement.setAttribute("id",topRowPosition); + commentBox.parentNode.normalize(); + commentBox.parentNode.insertBefore(brElement, commentBox); + + for (let pkg in this.packages) { + for (let cmdIdx in this.packages[pkg]) { + let cmdObj = this.packages[pkg][cmdIdx]; + switch (cmdObj.position) { + case "topRow": + this.createNewButton(topRowPosition, false, pkg, cmdIdx); + break; + case "bottomRow": + this.createNewButton(bottomRowPosition, false, pkg, cmdIdx); + break; + case "dropDown": + this.addToCommentsDropdown(pkg,cmdIdx); + break; + default: // [+-]ID + let firstChr = cmdObj.position.charAt(0); + let newId = cmdObj.position.substr(1); + this.createNewButton(newId, firstChr == "+", pkg, cmdIdx); + break; + } + } + } +}; + +/** + * Get the current email of the reporter of the bug. + * + * @return string + */ +BZPage.prototype.getReporter = function() { + return this.doc + .querySelector("#bz_show_bug_column_2 > table .vcard:first-of-type > a").textContent; +}; + +/** + * Get the current version of the Fedora release ... even if changed meanwhile + * by bug triager. + * + * @return string (integer for released Fedora, float for RHEL, rawhide) + */ +BZPage.prototype.getVersion = function() { + let verStr = this.getOptionValue("version").toLowerCase(); + let verNo = 0; + if (/rawhide/.test(verStr)) { + verNo = 999; + } else { + verNo = Number(verStr); + } + return verNo; +}; + +BZPage.prototype.commentsWalker = function(fce) { + let comments = this.doc.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 + * + */ +BZPage.prototype.checkComments = function() { + let that = this; + this.commentsWalker(function(x) { + let email = x.getElementsByClassName("vcard")[0] + .getElementsByTagName("a")[0].textContent; + if (new RegExp(that.reporter).test(email)) { + x.style.backgroundColor = that.ReporterColor.toString(); + } + }); +}; + +BZPage.prototype.collectComments = function() { + let outStr = ""; + this.commentsWalker(function(x) { + outStr += x.getElementsByTagName("pre")[0].textContent + "\n"; + }); + return outStr.trim(); +}; + + +/** + * Select option with given label on the <SELECT> element with given id. + * + * Also execute change HTMLEvent, so that the form behaves accordingly. + * + * @param id + * @param label + * @return none + * + * FIXME bugzilla-comments version has this signature: + * selectOption = function selectOption(select, value) { + let doc = select[0].ownerDocument; + select.val(value); + */ +BZPage.prototype.selectOption = function(id, label) { + let sel = this.doc.getElementById(id); + sel.value = label; + let intEvent = this.doc.createEvent("HTMLEvents"); + intEvent.initEvent("change", true, true); + sel.dispatchEvent(intEvent); +}; + +/** + * Send mouse click to the specified element + * + * @param String ID of the element to send mouseclick to + * @return None + */ +BZPage.prototype.clickMouse = function(targetID) { + let localEvent = this.doc.createEvent("MouseEvents"); + localEvent.initMouseEvent("click", true, true, this.doc.defaultView, 0, 0, + 0, 0, 0, false, false, false, false, 0, null); + this.doc.getElementById(targetID).dispatchEvent(localEvent); +}; + +/** + * 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 + */ +BZPage.prototype.filterByRegexp = function(list, chosingMark) { + let 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 ""; + } +}; + +/** + * Add object to the text box (comment box or status whiteboard) + * + * @param id String with the id of the element + * @param stuff String/Array to be added to the comment box + * + * @return none + */ +BZPage.prototype.addStuffToTextBox = function(id, stuff) { + let textBox = this.doc.getElementById(id); + if (textBox.tagName.toLowerCase() === "textarea") { + stuff = textBox.value ? "\n\n" + stuff : stuff; + textBox.value += stuff; + } else { + textBox.value = hlpr.addCSVValue(textBox.value,stuff); + } +}; + +/** + * Remove a keyword from the element if it is there + * + * @param id String with the id of the element + * @param stuff String/Array with keyword(s) to be removed + */ +BZPage.prototype.removeStuffFromTextBox = function(id, stuff) { + let changedElement = this.getElementById(id); + changedElement.value = hlpr.removeCSVValue(changedElement.value,stuff); +} + +/** + * generalized hasKeyword ... search in the value of the box with given id + * + * @param id String with ID of the element we want to check + * @param str String to be searched for + * @return Boolean found? + */ +BZPage.prototype.idContainsWord = function(id, str) { + try { + var kwd = this.doc.getElementById(id).value; + } catch (e) { + // For those who don't have particular element at all or if it is empty + return false; + } + return (kwd.trim().indexOf(str) != -1); +}; + +/** + * Check for the presence of a keyword + * + * @param str String with the keyword + * @return Boolean + */ +BZPage.prototype.hasKeyword = function(str) { + return (this.idContainsWord('keywords', str)); +}; + +/** + * + */ +BZPage.prototype.getOptionValue = function(id) { + // Some special bugs don't have version for example + let element = this.doc.getElementById(id); + if (element) { + return element.value; + } else { + console.log("Failed to find element with id = " + id); + return "#NA"; + } +}; + +/** + * Set the bug to NEEDINFO state + * + * Working function. + * @return none + * @todo TODO we may extend this to general setNeedinfo function + * with parameter [reporter|assignee|general-email-address] + */ +BZPage.prototype.setNeedinfoReporter = function() { + this.clickMouse("needinfo"); + this.selectOption("needinfo_role", "reporter"); +}; + +/** + * + */ +BZPage.prototype.getOwner = function() { + let priorityParent = this.doc.querySelector("label[for~='target_milestone']") + .parentNode.parentNode.parentNode; + let assigneeAElement = priorityParent.querySelector("tr:nth-of-type(1) a.email"); + let assgineeHref = decodeURI(assigneeAElement.getAttribute("href")); + let email = assgineeHref.split(":")[1]; + return email; +}; + +/** + * Get login of the currently logged-in user. + * + * @return String with the login name of the currently logged-in user + */ +BZPage.prototype.getLogin = function () { + let lastLIElement = this.doc.querySelector("#header ul.links li:last-of-type"); + let loginArr = lastLIElement.textContent.split("\n"); + let loginStr = loginArr[loginArr.length - 1].trim(); + return loginStr; +}; + +/** + * Return maintainer which is per default by bugzilla + * (which is not necessarily the one who is default maintainer per component) + * + * @return String with the maintainer's email address + */ +BZPage.prototype.getDefaultBugzillaMaintainer = function(component) { + let address = this.filterByRegexp(this.defBugzillaMaintainerArr, component); + return address; +} + +/** + * collect the list of attachments in a structured format + * + * @return Array of arrays, one for each attachments; + * each record has string name of the attachment, integer its id number, + * string of MIME type, integer of size in kilobytes, and the whole + * element itself + */ +BZPage.prototype.getAttachments = function() { + let outAtts = []; + let atts = this.doc.getElementById("attachment_table") + .getElementsByTagName("tr"); + for ( let i = 1, ii = atts.length - 1; i < ii; i++) { + outAtts.push(this.parseAttachmentLine(atts[i])); + } + return outAtts; +}; + +/** + * returns password from the current storage, or if there isn't + * one, then it will ask user for it. + * + * @return String with the password + */ +BZPage.prototype.getPassword = function() { + if (jetpack.storage.settings.BZpassword) { + return jetpack.storage.settings.BZpassword; + } else { + let prompts = Components.classes["@mozilla.org/embedcomp/prompt-service;1"] + .getService(Components.interfaces.nsIPromptService); + let password = { + value : "" + }; // default the password to pass + let check = { + value : true + }; // default the checkbox to true + let result = prompts.promptPassword(null, "Title", "Enter password:", + password, null, check); + // result is true if OK was pressed, false if cancel was pressed. + // password.value is + // set if OK was pressed. The checkbox is not displayed. + if (result) { + let passwordText = password.value; + jetpack.storage.settings.BZpassword = passwordText; + jetpack.storage.simple.sync(); + return passwordText; + } + } + return null; +}; + +/** + * + */ +BZPage.prototype.setUpLogging = function() { + // For adding additional buttons to the top toolbar + let additionalButtons = this.doc.querySelector("#bugzilla-body *.related_actions"); + let that = this; + + // logging all submits for timesheet + // FIXME we should merge in functionality of RHBugzillaPage.submitCallback + // and actually make it working + // Maybe rewriting whole offline capability into a separate object? + if (!this.submitHandlerInstalled) { + console.log("Installing submit callback!"); + this.doc.forms.namedItem("changeform").addEventListener("submit",function (evt) { + console.log("Submit callback!"); + let resp = that.log.addLogRecord(that); + console.log("resp = " + resp); + if (resp === null) { + console.log("Avoiding submitting!"); + // FIXME doesn't work ... still submitting' + evt.stopPropagation(); + evt.preventDefault(); + } + }, false); + this.submitHandlerInstalled = true; + } + + let generateTimeSheetUI = this.doc.createElement("li"); + generateTimeSheetUI.innerHTML = "\u00A0-\u00A0<a href='#' id='generateTSButton'>" + + "Generate timesheet</a>"; + additionalButtons.appendChild(generateTimeSheetUI); + this.doc.getElementById("generateTSButton").addEventListener( + "click", + function(evt) { + that.log.createBlankPage.call(that.log, "TimeSheet", + that.log.generateTimeSheet); + evt.stopPropagation(); + evt.preventDefault(); + }, false); + + let clearLogsUI = this.doc.createElement("li"); + clearLogsUI.innerHTML = "\u00A0-\u00A0<a href='#' id='clearLogs'>" + + "Clear logs</a>"; + additionalButtons.appendChild(clearLogsUI); + let clearLogAElem = this.doc.getElementById("clearLogs"); + clearLogAElem.addEventListener("click", function() { + that.log.store = {}; + jetpack.storage.simple.sync(); + this.style.color = that.log.EmptyLogsColor; + this.style.fontWeight = "normal"; + console.log("this.store wiped out!"); + }, false); + + if (!this.store) { + console.log("No this.store defined!"); + this.store = {}; + } + + if (this.log.store.length > 0) { + clearLogAElem.style.color = this.log.FullLogsColor; + clearLogAElem.style.fontWeight = "bolder"; + } else { + clearLogAElem.style.color = this.log.EmptyLogsColor; + clearLogAElem.style.fontWeight = "normal"; + } +}; + +/** + * adds a person to the CC list, if it isn't already there + * + * @param who String with email address or "self" if the current user + * of the bugzilla should be added + */ +BZPage.prototype.addToCCList = function(who) { + if (!who) { + return ; + } + if (who == "self") { + this.doc.getElementById("addselfcc").checked = true; + } else { + this.clickMouse("cc_edit_area_showhide"); + if (!hlpr.isInList(who, this.CCList)) { + this.addStuffToTextBox("newcc",who); + } + } +}; + +/** + * a collect a list of emails on CC list + * + * @return Array with email addresses as Strings. + */ +BZPage.prototype.getCCList = function() { + let CCListSelect = this.doc.getElementById("cc"); + outCCList = []; + if (CCListSelect) { + outCCList = Array.map(CCListSelect.options, function(item) { + return item.value; + }); + } + return outCCList; +}; |