// 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 // global variables var config = {}; var constantData = {}; // This should be probably eliminated ASAP or // or done by other means. TODO var equivalentComponents = null; /** * central handler processing messages from the main script. */ self.on('message', 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(), equivalentComponents); break; case "CreateButtons": constantData = msg.data.constData; config = msg.data.config; equivalentComponents = ("equivalentComponents" in constantData) ? constantData.equivalentComponents : null; generateButtons(msg.data.instPkgs, msg.data.kNodes); break; case "Error": alert("Error " + msg.data); break; case "Unhandled": break; default: if (TweakOnMessageHandler) { TweakOnMessageHandler(msg, RHOnMessageHandler); } 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": case "position": 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; } } /** * 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) { config.commandsList = []; document.getElementById("comments").innerHTML += "
" + " " + " " + "
"; select = document.getElementById("comment_action"); select.addEventListener("change", function (evt) { var value = select.options[select.selectedIndex].value; executeCommand(config.commandsList[value]); }, false); } var opt = document.createElement("option"); var objIdx = config.commandsList.length + 1; opt.value = objIdx; config.commandsList[objIdx] = cmdObj; opt.textContent = cmdObj.name; select.appendChild(opt); } /** * Generate button based on */ function generateButtons (pkgs, kNodes) { var topRowPosition = "topRowPositionID"; var bottomRowPosition = "commit"; // ========================================================= if (kNodes && window.location.hostname in kNodes) { var killConf = kNodes[window.location.hostname]; killNodes(document, killConf[0], killConf[1]); } // create anchor for the top toolbar 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 pkgs) { for (var cmdIdx in pkgs[pkg]) { var cmdObj = pkgs[pkg][cmdIdx]; if (cmdObj.position !== undefined) { switch (cmdObj.position) { case "topRow": createNewButton(topRowPosition, false, cmdObj); break; case "bottomRow": createNewButton(bottomRowPosition, false, cmdObj); break; case "dropDown": addToCommentsDropdown(cmdObj); break; default: // [+-]ID var firstChr = cmdObj.position.charAt(0); var newId = cmdObj.position.substr(1); createNewButton(newId, firstChr === "+", cmdObj); break; } } else { console.error("generateButtons : rejected cmdObj = " + cmdObj.toSource()); } } } // TODO This is weird in this place, but that's the place where all // constantData etc. // are finally defined and available. if (RHBZinit) { console.log("call RHBZinit!"); RHBZinit(); } if (tweakBugzilla) { tweakBugzilla(document, constantData); } } function setConfigurationButton () { var additionalButtons = document.querySelector("#bugzilla-body *.related_actions"); var configurationButtonUI = document.createElement("li"); configurationButtonUI.innerHTML = "\u00A0-\u00A0" + "Triage configuration"; additionalButtons.appendChild(configurationButtonUI); document.getElementById("configurationButton").addEventListener( "click", function(evt) { self.postMessage(new Message("ChangeJSONURL", null)); evt.stopPropagation(); evt.preventDefault(); }, false); } /** * 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; } /** * 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 ]; } 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(); self.postMessage(new Message("GetInstalledPackages", { location: window.location.href, login: getLogin() })); } startup();