From 5b9f263ac1485c0ab18c2d42c5cd18746208bad4 Mon Sep 17 00:00:00 2001 From: Matěj Cepl Date: Wed, 23 Feb 2011 02:50:39 +0100 Subject: Moved .js files from data/ to data/lib/ so that we don't mix data and code. --- data/lib/bzpage.js | 985 +++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 985 insertions(+) create mode 100644 data/lib/bzpage.js (limited to 'data/lib/bzpage.js') diff --git a/data/lib/bzpage.js b/data/lib/bzpage.js new file mode 100644 index 0000000..807793a --- /dev/null +++ b/data/lib/bzpage.js @@ -0,0 +1,985 @@ +// Released under the MIT/X11 license +// 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; + +// 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 +var EmptyLogsColor = new Color(0, 255, 0); +var FullLogsColor = new Color(0, 40, 103); + +// global variables +var config = {}; +var constantData = {}; // This should be probably eliminated ASAP or + // or done by other means. TODO +var submitHandlerInstalled = false; // for setUpLogging +/** +* central handler processing messages from the main script. +*/ +onMessage = function onMessage(msg) { + console.log("onMessage - incoming : msg.cmd = " + msg.cmd); + switch (msg.cmd) { + case "ReloadThePage": + document.location.reload(true); + break; + case "queryLocal": + queryInNewTab(msg.data, getComponent(), getProduct()); + break; + case "CreateButtons": + constantData = msg.data.constData; + config = msg.data.config; + generateButtons(msg.data.instPkgs, msg.data.kNodes); + break; + case "Error": + alert("Error " + msg.data); + break; + case "Unhandled": + break; + default: + if (RHOnMessageHandler) { + RHOnMessageHandler(msg); + } else { + 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. + */ +function executeCommand(cmdObj) { + for (var key in cmdObj) { + centralCommandDispatch(key, cmdObj[key]); + } +} + +/** + * 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 + */ +function centralCommandDispatch (cmdLabel, cmdParams) { + console.log("centralCommandDispatch : cmdLabel = " + cmdLabel); + switch (cmdLabel) { + case "name": + break; + case "resolution": + case "product": + case "component": + case "version": + case "priority": + selectOption(cmdLabel, cmdParams); + break; + case "status": + selectOption("bug_status", cmdParams); + break; + case "platform": + selectOption("rep_platform", cmdParams); + break; + case "os": + selectOption("op_sys", cmdParams); + break; + case "severity": + selectOption("bug_severity", cmdParams); + break; + case "target": + selectOption("target_milestone", cmdParams); + break; + case "addKeyword": + addStuffToTextBox("keywords",cmdParams); + break; + case "removeKeyword": + removeStuffFromTextBox("keywords", cmdParams); + break; + case "addWhiteboard": + addStuffToTextBox("status_whiteboard",cmdParams); + break; + case "removeWhiteboard": + removeStuffFromTextBox("status_whiteboard",cmdParams); + break; + case "assignee": + changeAssignee(cmdParams); + break; + case "qacontact": + clickMouse("bz_qa_contact_edit_action"); + document.getElementById("qa_contact").value = cmdParams; + break; + case "url": + clickMouse("bz_url_edit_action"); + document.getElementById("bug_file_loc").value = cmdParams; + break; + // TODO dependson/blocked doesn't work. Find out why. + case "addDependsOn": + clickMouse("dependson_edit_action"); + addStuffToTextBox("dependson", cmdParams); + break; + case "removeDependsOn": + clickMouse("dependson_edit_action"); + removeStuffFromTextBox("dependson", cmdParams); + break; + case "addBlocks": + clickMouse("blocked_edit_action"); + addStuffToTextBox("blocked", cmdParams); + break; + case "removeBlocks": + clickMouse("blocked_edit_action"); + removeStuffFromTextBox("blocked", cmdParams); + break; + case "comment": + addStuffToTextBox("comment", cmdParams); + break; + case "commentIdx": + 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 + setNeedinfoReporter(); + break; + case "addCC": + addToCCList(cmdParams); + break; + case "queryStringOurBugzilla": + queryForSelection(); + break; + // TODO flags, see also + case "commit": + if (cmdParams) { + // Directly commit the form + document.forms.namedItem("changeform").submit(); + } + break; + default: + if (RHcentralCommandDispatch) { + RHcentralCommandDispatch(cmdLabel, cmdParams); + } else { + console.error("Unknown command:\n" + cmdLabel + "\nparameters:\n" + cmdParams); + } + break; + } +} + +/** + * 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. + */ +function killNodes(doc, target, remove) { + var targetArr = target instanceof Array ? target : target.trim().split(/[,\s]+/); + targetArr.forEach(function(x) { + if (remove) { + var targetNode = doc.getElementById(x); + targetNode.parentNode.removeChild(targetNode); + } else { + x.style.display = "none"; + } + }); +} + +/** + * Change assignee of the bug + * + * @param newAssignee String with the email address of new assigneeAElement + * or 'default' if the component's default assignee should be used. + * Value null clears "Reset Assignee to default for component" checkbox + * @return none + */ +function changeAssignee (newAssignee) { + var defAssigneeButton = null; + // Previous assignee should know what's going on in his bug + addToCCList(getOwner()); + + // Optional value null + if (newAssignee === null) { + document.getElementById("set_default_assignee").removeAttribute( + "checked"); + return ; + } + + if (getDefaultAssignee) { + if (newAssignee === "default") { + var defAss = getDefaultAssignee(); + if (defAss) { + newAssignee = defAss; + } else { + return ; + } + } + } + + if (newAssignee) { + clickMouse("bz_assignee_edit_action"); + document.getElementById("assigned_to").value = newAssignee; + document.getElementById("set_default_assignee").checked = false; + defAssigneeButton = document.getElementById("defassignee_btn"); + if (defAssigneeButton) { + 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. + */ +function addToCommentsDropdown (cmdObj) { + var select = document.getElementById("comment_action"); + if (!select) { + document.getElementById("comments").innerHTML += + "
" + + " " + + " element with given id. + * + * Also execute change HTMLEvent, so that the form behaves accordingly. + * + * @param id + * @param label + * @return none + * + */ +function selectOption (id, label, fireEvent) { + if (!fireEvent) { + fireEvent = true; + } + var sel = document.getElementById(id); + sel.value = label; + if (fireEvent) { + var intEvent = document.createEvent("HTMLEvents"); + intEvent.initEvent("change", true, true); + sel.dispatchEvent(intEvent); + } +} + +function selectOptionByLabel(id, label, fireEvent) { + if (!fireEvent) { + fireEvent = true; + } + var sel = document.getElementById(id); + var labelRE = new RegExp(label.trim()); + var ourOption = Array.filter(sel.options, function (op) { + return op.textContent.trim() === label; + }); + + if (ourOption[0]) { + sel.value = ourOption[0].value; + } + + if (fireEvent) { + var intEvent = document.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 + */ +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); + document.getElementById(targetID).dispatchEvent(localEvent); +} + +/** + * 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 + */ +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 = 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 + */ +function removeStuffFromTextBox (id, stuff) { + var changedElement = document.getElementById(id); + changedElement.value = 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? + */ +function idContainsWord (id, str) { + var kwd = ""; + try { + 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 (isInList(str, kwd.trim().split(/[,\s]+/))); +} + +/** + * Check for the presence of a keyword + * + * @param str String with the keyword + * @return Boolean + */ +function hasKeyword (str) { + return (idContainsWord('keywords', str)); +} + +/** + * dd + * + * @return Element with the href attribute containng the information + */ +function getOptionTableCell(tableId, label) { + var cleanLabelRE = new RegExp("^\\s*([^.:]*):?\\s*$"); + label = label.trim().replace(cleanLabelRE,"$1").toLowerCase(); + + 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"); + return (curLabel === label); // maybe this could be a RE match instead + }); + + if (ourLine.length > 0) { + return ourLine[0].getElementsByTagName("td")[1]. + getElementsByTagName("a")[0]; + } + return null; +} + +/** + * 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] + */ +function setNeedinfoReporter () { + clickMouse("needinfo"); + selectOption("needinfo_role", "reporter"); +} + +/** + * + */ +function getOwner () { + // TODO(maemo) doesn't work on maemo + var assigneeAElement = getOptionTableCell("bz_show_bug_column_1","Assigned To"); + return parseMailto(assigneeAElement); +} + +/** + * 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 + */ +function getDefaultBugzillaMaintainer (component) { + var address = filterByRegexp(constantData.defBugzillaMaintainerArr, component); + return address; +} + +/** + * Parse the row with the attachment + * + * @param DOM element to be parsed + * @return array with string name of the attachment, integer its id number, + * string of MIME type, integer of size in kilobytes, and the whole + * element itself + * + * TODO error handling is missing ... it's bleee + */ +function parseAttachmentLine(inElem) { + var MIMEtype = ""; + var size = 0; + + // Skip over obsolete attachments + if (inElem.getElementsByClassName("bz_obsolete").length > 0) { + return ([]); + } + + // getting name of the attachment + var attName = inElem.getElementsByTagName("b")[0].textContent.trim(); + + // TODO probably could use url.URL object + var aHrefsArr = inElem.getElementsByTagName("a"); + var aHref = Array.filter(aHrefsArr, function(x) { + return x.textContent.trim() === "Details"; + })[0]; + var id = parseURL(aHref.getAttribute("href")).params.id; + + // getting MIME type and size + 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]; + + return [ attName, id, MIMEtype, size, inElem ]; +} + +/** + * 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 + */ +function getAttachments () { + var outAtts = []; + var atts = document.getElementById("attachment_table"). + getElementsByTagName("tr"); + for ( var i = 1, ii = atts.length - 1; i < ii; i++) { + outAtts.push(parseAttachmentLine(atts[i])); + } + return outAtts; +} + +/** + * Get login of the currently logged-in user. + * + * @return String with the login name of the currently logged-in user + */ +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; +} + +function addLogRecord() { + console.log("addLogRecord entered"); + var rec = {}; + rec.date = new Date(); + rec.url = document.location.toString(); + rec.title = document.title; + var comment = window.prompt( + "Enter comments for this comment"); + console.log("window.prompt = " + window.prompt); + console.log("comment = " + comment); + if (comment && comment.length > 0) { + comment = comment.trim(); + rec.comment = comment; + var dateStr = getISODate(rec.date); + var urlStr = window.location.hostname; + var bugNo = getBugNoFromURL(window.location.href); + rec.key = dateStr + "+" + + urlStr + "+" + bugNo; + console.log("addLogRecord : rec = " + rec.toSource()) + postMessage(new Message("AddLogRecord", rec)); + return rec; + } + return null; +} + +/** + */ +function setUpLogging () { + // Protection against double-call + if (document.getElementById("generateTSButton")) { + return ; + } + + // For adding additional buttons to the top toolbar + var additionalButtons = document.querySelector("#bugzilla-body *.related_actions"); + var that = this; + + // logging all submits for timesheet + if (!submitHandlerInstalled) { + document.forms.namedItem("changeform").addEventListener("submit",function (evt) { + console.log("addLogRecord = " + addLogRecord); + var logRecord = addLogRecord(); + console.log("logRecord = " + logRecord.toSource()); + if (logRecord === null) { + evt.stopPropagation(); + evt.preventDefault(); + } + }, false); + submitHandlerInstalled = true; + } + + // (id, text, parent, callback, params, before, covered, accesskey) + createDeadLink("generateTSButton", "Generate TS", additionalButtons, + function(evt) { + postMessage(new Message("GenerateTS")); + }, [], "dash", "li"); + + createDeadLink("clearLogs", "Clear TS", additionalButtons, + function(evt) { + postMessage(new Message("ClearTS")); + }, [], "dash", "li"); + + createDeadLink("importTSButton", "Import TS", additionalButtons, + function(evt) { + postMessage(new Message("ImportTS")); + }, [], "dash", "li"); + + /* TODO + var clearLogAElem = document.getElementById("clearLogs"); + if (this.log.isEmpty()) { + clearLogAElem.style.color = this.log.EmptyLogsColor; + clearLogAElem.style.fontWeight = "normal"; + } else { + clearLogAElem.style.color = this.log.FullLogsColor; + clearLogAElem.style.fontWeight = "bolder"; + } + */ +} + +function getSelection () { + var text = ""; + var selectionObject = window.getSelection(); + if (selectionObject.rangeCount > 0) { + text = selectionObject.getRangeAt(0).toString().trim(); + } + return text; +} + +/** + * Opens a new tab with a query for the given text in the selected component + * + * @param text to be searched for + * @param component String with the component name (maybe latter regexp?) + * @param product (optional) string with the product name, if undefined, + * search in all products + * @return None + * + */ +function queryInNewTab(text, component, product) { + var urlStr = "https://" + window.location.hostname + "/buglist.cgi?query_format=advanced"; + if (product) { + urlStr += "&product=" + product.trim(); + } + if (component) { + if ("equivalentComponents" in constantData) { + var equivCompsArr = constantData.equivalentComponents. + filter(function (REstr) { + return new RegExp(REstr).test(getComponent()); + }, this); + if (equivCompsArr.length > 0) { + component = equivCompsArr[0]; + } + } + urlStr += "&component=" + component.trim(); + } + + // using more complicated query tables here, because they can be more easily + // edited + // for further investigative searches + if (text) { + text = encodeURIComponent(text.trim()); + var searchText = "&field1-0-0=longdesc&type1-0-0=substring&value1-0-0=" + + text + + "&field1-0-1=attach_data.thedata&type1-0-1=substring&value1-0-1=" + + text + + "&field1-0-2=status_whiteboard&type1-0-2=substring&value1-0-2=" + + text; + urlStr += searchText; + postMessage(new Message("OpenURLinTab", urlStr)); + } +} + +/** + * Get the text to search for and prepare other things for the real executive + * function this.queryInNewTab, and run it. + */ +function queryForSelection() { + var text = getSelection(); + if (!text) { + postMessage(new Message("GetClipboard", "queryLocal")); + } else { + queryInNewTab(text, getComponent(), getProduct()); + } +} + +/** + * 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 + */ +function addToCCList (who) { + if (!who) { + return ; + } + if (who === "self") { + document.getElementById("addselfcc").checked = true; + } else { + clickMouse("cc_edit_area_showhide"); + if (!isInList(who, getCCList())) { + addStuffToTextBox("newcc",who); + } + } +} + +/** + * a collect a list of emails on CC list + * + * @return Array with email addresses as Strings. + */ +function getCCList () { + var CCListSelect = document.getElementById("cc"); + var outCCList = []; + if (CCListSelect) { + outCCList = Array.map(CCListSelect.options, function(item) { + return item.value; + }); + } + return outCCList; +} + +function startup() { + // First, preflight check ... if we are not logged in, there + // is nothing we can do. + var logoutLink = Array.some(document.links, function (x) { + return x.search === "?logout=1" ; + }); + if (!logoutLink) { + return null; // This should just finish whole content script without + // doing any harm to anybody. + } + + // 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 = document.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(document.createElement("br"), + brElementPlacer); + } + } + + // TODO Probably could be ignored ... used only once on line 973 of rhbzpage.js + // if (parseAbrtBacktraces && this.RE.Abrt.test(getSummary())) { + // title = document.getElementById("short_desc_nonedit_display").textContent; + + // So, now we know we are logged in, so we can get to + // the real work. + setConfigurationButton(); + submitHandlerInstalled = false; + + checkComments(); + + postMessage(new Message("GetInstalledPackages", { + location: window.location.href, + login: getLogin() + })); +} + +startup(); -- cgit