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/bzpage.js | 985 --------------------------------------------------- data/color.js | 236 ------------ data/cssUtils.js | 77 ---- data/lib/bzpage.js | 985 +++++++++++++++++++++++++++++++++++++++++++++++++++ data/lib/color.js | 236 ++++++++++++ data/lib/cssUtils.js | 77 ++++ data/lib/rhbzpage.js | 924 +++++++++++++++++++++++++++++++++++++++++++++++ data/lib/skip-bug.js | 20 ++ data/lib/util.js | 282 +++++++++++++++ data/rhbzpage.js | 924 ----------------------------------------------- data/skip-bug.js | 20 -- data/util.js | 282 --------------- 12 files changed, 2524 insertions(+), 2524 deletions(-) delete mode 100644 data/bzpage.js delete mode 100644 data/color.js delete mode 100644 data/cssUtils.js create mode 100644 data/lib/bzpage.js create mode 100644 data/lib/color.js create mode 100644 data/lib/cssUtils.js create mode 100644 data/lib/rhbzpage.js create mode 100644 data/lib/skip-bug.js create mode 100644 data/lib/util.js delete mode 100644 data/rhbzpage.js delete mode 100644 data/skip-bug.js delete mode 100644 data/util.js (limited to 'data') diff --git a/data/bzpage.js b/data/bzpage.js deleted file mode 100644 index 807793a..0000000 --- a/data/bzpage.js +++ /dev/null @@ -1,985 +0,0 @@ -// 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(); diff --git a/data/color.js b/data/color.js deleted file mode 100644 index c6cdc71..0000000 --- a/data/color.js +++ /dev/null @@ -1,236 +0,0 @@ -// 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 -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/cssUtils.js b/data/cssUtils.js deleted file mode 100644 index da24aa2..0000000 --- a/data/cssUtils.js +++ /dev/null @@ -1,77 +0,0 @@ -/*global console: false */ -/*jslint onevar: false */ -// Released under the MIT/X11 license -// http://www.opensource.org/licenses/mit-license.php -"use strict"; - -/** - * get CSS style from all styles in the document with given name - * - * @param ruleName String with the identificator of the rule (the same - * used on the page itself) - * @param deleteFlag ??? - * @return ??? (exact type of the object returned FIXME) - * - * e.g., getCSSRule(".tramp") gives particular style - * from http://www.hunlock.com/blogs/Totally_Pwn_CSS_with_Javascript - */ -var getCSSRule = exports.getCSSRule = function getCSSRule(ruleName, deleteFlag) { - ruleName=ruleName.toLowerCase(); // style rules are case insensitive - var foundRuleIdx = 0; - - Array.forEach(document.styleSheets, function (sheet) { - var ruleIdx = 0; - foundRule = Array.reduce(sheet.cssRules, function (ruleIdx, curRule, idx) { - if ((foundRuleIdx === 0) && (curRule. - selectorText.toLowerCase() == ruleName)) { - return idx; - } - return foundRuleIdx; - }); - if (foundRules > 0) { - if (deleteFlag === "delete") { - sheet.deleteRule(foundRuleIdx); - return true; - } - return sheet.cssRules[foundRuleIdx]; - } - }); - return false; // we found NOTHING! -}; - -/** - * - */ -exports.killCSSRule = function killCSSRule (ruleName) { - return getCSSRule(ruleName, "delete"); -}; - -/** - * - */ -exports.addCSSRule = function addCSSRule(ruleName, stylesheetTitle) { - var sheets = {}; - if (!getCSSRule(ruleName)) { - if (stylesheetTitle) { - sheets = Array.filter(document.styleSheets,function (sheet) { - return (sheet.title === stylesheetTitle); - }); - } else { - sheets = document.styleSheets; - } - sheets[0].insertRule(ruleName+' { }', 0); - } - return getCSSRule(ruleName); -}; - -/** - * - */ -exports.addCSSStylesheet = function addCSSStylesheet (StylesheetName) { - var cssNode = document.createElement("style"); - cssNode.type = 'text/css'; - cssNode.rel = 'stylesheet'; - cssNode.media = 'screen'; - cssNode.title = StylesheetName; - document.getElementsByTagName("head")[0].appendChild(cssNode); -}; 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(); diff --git a/data/lib/color.js b/data/lib/color.js new file mode 100644 index 0000000..c6cdc71 --- /dev/null +++ b/data/lib/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 +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/lib/cssUtils.js b/data/lib/cssUtils.js new file mode 100644 index 0000000..da24aa2 --- /dev/null +++ b/data/lib/cssUtils.js @@ -0,0 +1,77 @@ +/*global console: false */ +/*jslint onevar: false */ +// Released under the MIT/X11 license +// http://www.opensource.org/licenses/mit-license.php +"use strict"; + +/** + * get CSS style from all styles in the document with given name + * + * @param ruleName String with the identificator of the rule (the same + * used on the page itself) + * @param deleteFlag ??? + * @return ??? (exact type of the object returned FIXME) + * + * e.g., getCSSRule(".tramp") gives particular style + * from http://www.hunlock.com/blogs/Totally_Pwn_CSS_with_Javascript + */ +var getCSSRule = exports.getCSSRule = function getCSSRule(ruleName, deleteFlag) { + ruleName=ruleName.toLowerCase(); // style rules are case insensitive + var foundRuleIdx = 0; + + Array.forEach(document.styleSheets, function (sheet) { + var ruleIdx = 0; + foundRule = Array.reduce(sheet.cssRules, function (ruleIdx, curRule, idx) { + if ((foundRuleIdx === 0) && (curRule. + selectorText.toLowerCase() == ruleName)) { + return idx; + } + return foundRuleIdx; + }); + if (foundRules > 0) { + if (deleteFlag === "delete") { + sheet.deleteRule(foundRuleIdx); + return true; + } + return sheet.cssRules[foundRuleIdx]; + } + }); + return false; // we found NOTHING! +}; + +/** + * + */ +exports.killCSSRule = function killCSSRule (ruleName) { + return getCSSRule(ruleName, "delete"); +}; + +/** + * + */ +exports.addCSSRule = function addCSSRule(ruleName, stylesheetTitle) { + var sheets = {}; + if (!getCSSRule(ruleName)) { + if (stylesheetTitle) { + sheets = Array.filter(document.styleSheets,function (sheet) { + return (sheet.title === stylesheetTitle); + }); + } else { + sheets = document.styleSheets; + } + sheets[0].insertRule(ruleName+' { }', 0); + } + return getCSSRule(ruleName); +}; + +/** + * + */ +exports.addCSSStylesheet = function addCSSStylesheet (StylesheetName) { + var cssNode = document.createElement("style"); + cssNode.type = 'text/css'; + cssNode.rel = 'stylesheet'; + cssNode.media = 'screen'; + cssNode.title = StylesheetName; + document.getElementsByTagName("head")[0].appendChild(cssNode); +}; diff --git a/data/lib/rhbzpage.js b/data/lib/rhbzpage.js new file mode 100644 index 0000000..026e7ac --- /dev/null +++ b/data/lib/rhbzpage.js @@ -0,0 +1,924 @@ +// Released under the MIT/X11 license +// http://www.opensource.org/licenses/mit-license.php +"use strict"; +var titleParsedAttachment = "Part of the thread where crash happened"; + + +// For identification of graphics card +var manuChipStrs = [ [ "ATI Radeon", "ATI", "1002" ], + [ "ATI Mobility Radeon", "ATI", "1002" ], + [ "Intel Corporation", "INTEL", "8086" ], [ "NVIDIA", "NV", "10de" ] ]; + +// http://en.wikipedia.org/wiki/HSL_color_space +// when only the value of S is changed +// stupido!!! the string is value in hex for each color +var RHColor = new Color(158, 41, 43); // RGB 158, 41, 43; HSL 359, 1, 39 +var FedoraColor = new Color(0, 40, 103); // RGB 0, 40, 103; HSL 359, 1, 39 +var RawhideColor = new Color(0, 119, 0); // or "green", or RGB 0, 119, 0, or + // HSL +// 120, 0, 23 +var RHITColor = new Color(102, 0, 102); // RGB 102, 0, 102; HSL 300, 0, 20 + +var logAnalyzeLogic = { + "AnalyzeInterestingLine": { + "re": "^\\[[ .0-9]+\\]\\s*\\(--\\) PCI:\\*\\([0-9:]+\\)\\s*" + + "([0-9a-f:]+).*$", + "func": chipsetMagic + }, + "AnalyzeXorgLogBacktrace": { + "re": "^\\s*(\\[[0-9 .]*\\])?\\s*(\\((EE|WW)\\)|.*"+ + " [cC]hipsets?: )|\\s*Backtrace", + "func": analyzeXorg + } +}; + +var ProfessionalProducts = [ + "Red Hat Enterprise Linux", + "Red Hat Enterprise MRG" +]; + +// END OF CONSTANTS + +var btSnippet = null; +var reqCounter = 0; // TODO should be probably a dict indexed by called method + +function RHOnMessageHandler(msg) { + switch (msg.cmd) { + case "Error": + alert("Error " + msg.data); + break; + case "Unhandled": + break; + case "AddAttachmentCallback": + addAttachmentCallback(msg.data); + break; + case "FixAttachmentMIMECallback": + XMLRPCcallback(); + break; + case "AnalyzeInterestingLine": + case "AnalyzeXorgLogBacktrace": + findInterestingLine(msg.data, msg.cmd); + break; + case "queryUpstream": + queryUpstreamCallback(msg.data); + break; + default: + console.error("Error: unknown RPC call " + msg.toSource()); + break; + } +} + +// RHBugzillaPage object + +/** + * Find default assignee based on the current component + * + * @return String what would be a default assignee if + * we haven't set it up. + */ +function getDefaultAssignee() { + return filterByRegexp(constantData.defaultAssignee, + getComponent()).toLowerCase(); +} + +/** + * Set default assignee + * + * @return none + * sets this.defaultAssignee property according to defaultAssignee list + */ +function setDefaultAssignee() { + var defAss = getDefaultAssignee(); + + // Add setting default assignee + if ((defAss.length > 0) && (defAss !== getOwner())) { + createNewButton("bz_assignee_edit_container",true, { + "name": "Def. Assignee", + "assignee": "default" + }); + } +} + +/** + * Auxiliary function to compute more complicated resolution + */ +function closeSomeRelease() { + // for RAWHIDE close as RAWHIDE, + // if active selection -> CURRENTRELEASE + // and put the release version to + // "Fixed in Version" textbox + // otherwise -> NEXTRELEASE + selectOption("bug_status", "CLOSED"); + var text = getSelection(); + var resolution = ""; + + if (text.length > 0) { + resolution = "CURRENTRELEASE"; + document.getElementById("cf_fixed_in").value = text; + } else if (document.getElementById("version").value === "rawhide") { + resolution = "RAWHIDE"; + } else { + resolution = "NEXTRELEASE"; + } + centralCommandDispatch("resolution", resolution); +} + +/** + * Additional commands specific for this subclass, overriding superclass one. + */ +function RHcentralCommandDispatch(cmdLabel, cmdParams) { + switch (cmdLabel) { + // Set up our own commands + case "closeUpstream": + addClosingUpstream(); + break; + case "computeResolution": + closeSomeRelease(); + break; + case "queryStringUpstreamBugzilla": + queryUpstream(); + break; + case "sendBugUpstream": + sendBugUpstream(); + break; + case "markTriaged": + markBugTriaged(); + break; + case "chipMagic": + fillInWhiteBoard(cmdParams); + break; + // If we don't have it here, call superclass method + default: + console.error("Unknown command:\n" + cmdLabel + "\nparameters:\n" + cmdParams); + break; + } +} + +function addAttachmentCallback(resp) { + var newAttachID = parseInt(resp.params.param.value.array.data.value.int, 10); + console.log("attachID = " + newAttachID); + // FIXME callback.call(param, newAttachID, data.length); +} + +/** + * + * This has to stay in RHBugzillaPage because upstream doesn't have addAttachment + * XML-RPC call yet. + */ +function addAttachment(data, callback, param) { + var params = []; + + if (!constantData.passwordState.passAvailable) { + console.error("addAttachment : No password, no XML-RPC calls; sorry"); + return null; + } + + params.push(getBugNo()); + params.push({ + description: titleParsedAttachment, + filename: "parsed-backtrace.txt", + contenttype: "text/plain", + data: window.btoa(data), + nomail: true + }); + + postMessage(new Message("MakeXMLRPCall", { + url: constantData.XMLRPCData[window.location.hostname].url, + login: getLogin(), + method: "bugzilla.addAttachment", + params: params, + callRPC: "AddAttachmentCallback" + })); + reqCounter++; +} + +/* === Bugzilla functions === */ +/** + * + */ +function pasteBacktraceInComments(atts) { + /* + Let's comment it out, and we'll see if anything breaks. + TODO This paragraph looks suspicous ... what is it? + Does it belong to this function? + var notedLabel = document.querySelector("label[for='newcc']"); + while (notedLabel.firstChild) { + var node = notedLabel.removeChild(notedLabel.firstChild); + notedLabel.parentNode.insertBefore(node, notedLabel); + } + notedLabel.parentNode.removeChild(notedLabel); + */ + + // FIXME BROKEN and its depending functions are even more broken + return null; + + var abrtQueryURL = "https://bugzilla.redhat.com/buglist.cgi?" + + "cmdtype=dorem&remaction=run&namedcmd=all%20NEW%20abrt%20crashes&"+ + "sharer_id=74116"; + + var mainTitle = document + .getElementsByClassName("bz_alias_short_desc_container")[0]; + + createDeadLink ("callAbrtQuery_link", + "Abrt bugs", mainTitle, abrtQueryURL, [], false, null, "a"); + + if (idContainsWord("cf_devel_whiteboard", 'btparsed')) { + addStuffToTextBox('status_whiteboard', 'btparsed'); + } + + if (!(isTriaged() || idContainsWord("status_whiteboard", + 'btparsed') || (atts.length > 0))) { + var btAttachments = atts + .filter(function(att) { + return (/File: backtrace/.test(att[0])); + }); + // TODO we need to go through all backtrace attachments, but + // just the first one will do for now, we would need to do async + // parsing + btAttachments.forEach(function(x) { + var attURL = "https://bugzilla.redhat.com/attachment.cgi?id=" + + x[1]; + if ((!btSnippet) && // ???? FIXME + (!idContainsWord("status_whiteboard", 'btparsed'))) { + Request({ + url: attURL, + onComplete: function(response) { + if (response.status == 200) { + btSnippet = parseBacktrace(response.text); + if (btSnippet) { + addCheckShowLink(x,btSnippet); + } + } + } + }).get(); + } + }, this); + } + // Add "show BT" links + if (parsedAttachments.length > 0) { + this.parsedAttachments.forEach(function (att) { + addShowParsedBTLink(att); + }); + } +} + +/** + * Open new window with the content of the attachment. + * + * @param id Number of the attachment id + * @return none + */ +function showAttachment(id) { + postMessage(new Message("OpenURLinPanel", + "https://" + window.location.hostname + "/attachment.cgi?id=" + id)); +} + +/** + * add a link opening a window with the parsed backtrace + * + * @param att Attachment object + */ +function addShowParsedBTLink(att) { + var elem = att[4].querySelector("td:last-of-type"); + createDeadLink("showParsedBacktraceWindow-" + att[1], "showBT", + elem, showAttachment, att[1], true); +} + +/** + * Unfinished ... see above FIXME BROKEN AND DOESN'T WORK + */ +function addNewAttachmentRow(origAtt, + newAttId, newAttSize) { + var that = this; + var oldAddBTLink = document.getElementById("attachBacktraceActivator"); + oldAddBTLink.parentNode.removeChild(oldAddBTLink); + var newTRElem = origAtt[4].cloneNode(true); + + // fix number of the attachment + Array.forEach(newTRElem.getElementsByTagName("a"), function (aEl) { + aEl.setAttribute("href", + aEl.getAttribute("href").replace(origAtt[1], newAttId)); + }); + + var aElements = newTRElem.getElementsByTagName("a"); + aElements[0].setAttribute("name","parsed-backtrace.txt"); + aElements[0].getElementsByTagName("b")[0].textContent = titleParsedAttachment; + + var sizeSpan = newTRElem.getElementsByClassName("bz_attach_extra_info")[0]; + sizeSpan.textContent = "(" + (newAttSize / 1024).toFixed(2) + " KB, text/plain)"; + + // aElements[1].textContent = new Date().toString(); TODO we should add eventually, but not pressing + + var vcardSpan = newTRElem.getElementsByClassName("vcard")[0]; + if (vcardSpan !== undefined) { + var vcardSpanClassList = vcardSpan.classList; + if (/@redhat\.com/.test(this.login) && !vcardSpanClassList.contains("redhat_user")) { + vcardSpanClassList.add("redhat_user"); + } + var vcardAElem = vcardSpan.getElementsByTagName("a")[0]; + vcardAElem.setAttribute("title", this.login); + vcardAElem.setAttribute("href", "mailto:" + this.login); + vcardAElem.className = "email"; + vcardAElem.innerHTML="" + this.login + ""; + } + + var elem = newTRElem.querySelector("td:last-of-type"); + this.createDeadLink("showBacktrace", "show BT", elem, + this.showAttachment, newAttId, false); + + origAtt[4].parentNode.insertBefore(newTRElem, origAtt[4].nextSibling); +} + +/** + * Add a link to create a new attachment with a parsed backtrace + * + * @param oldAtt Object with an attachment row + * @param snippet String with parsed backtrace + * @return none + */ +function addCheckShowLink(oldAtt, snippet) { + var elem = oldAtt[4].querySelector("td:last-of-type"); +/* + createDeadLink("attachBacktraceActivator", "add parsed BT", elem, function(x) { + // pass function and parameters as two separate parameters, the function to be called from + // addAttachment + addAttachment(snippet, addNewAttachmentRow, oldAtt); + }, [], true); +*/ +} + +/** + * Make it sailent that the some attachments with bad MIME type are present + * + * @param atts Array of attachments subarrays + * @return none + */ +function markBadAttachments(atts) { + var badMIMEArray = [ "application/octet-stream", "text/x-log", "undefined" ]; + if (!constantData.passwordState.passAvailable) { + console.log("markBadAttachments : No password, no XML-RPC calls; sorry"); + return null; + } + + var badAttachments = atts.filter(function(att) { + return (isInList(att[2], badMIMEArray)); + }); + + if (badAttachments.length > 0) { + var titleElement = document. + getElementsByClassName("bz_alias_short_desc_container")[0]; + titleElement.style.backgroundColor = "olive"; + + createDeadLink("fixAllButton", "Fix all", titleElement, function() { + Array.forEach(badAttachments, function(x) { + fixAttachById(x[1]); + }); + }, [], false, null, "f"); + badAttachments.forEach(function(x, i, a) { + addTextLink(x); + }); + } +} + +/** + * Is this bug a RHEL bug? + * + * @return Boolean true if it is a RHEL bug + */ +function isEnterprise() { + var result = ProfessionalProducts.some(function(elem,idx,arr) { + return new RegExp(elem).test(getProduct()); + }); + return result; +} + +/** + * Find out whether the bug is needed an attention of bugZappers + * + * @return Boolean whether the bug has been triaged or not + */ +function isTriaged() { + return hasKeyword("Triaged"); +} + +/** + * Set branding colours to easily distinguish between Fedora and RHEL bugs + * + * @param brand String with product of the current bug + * @param version String with the version of the bug + * @param its String with the IsueTracker numbers + * @return none + */ +function setBranding() { + var brandColor = {}; + var TriagedColor = {}; + + var ITbutton = document.getElementById("cf_issuetracker"); + var its = ITbutton ? ITbutton.value.trim() : ""; + + if (isEnterprise()) { + if (its && (its.length > 0)) { + brandColor = RHITColor; + } else { + brandColor = RHColor; + } + } else if (new RegExp("Fedora").test(document.getElementById("product").value)) { + if (document.getElementById("version").value === "rawhide") { + brandColor = RawhideColor; + } else { + brandColor = FedoraColor; + } + } + + // Comment each of the following lines to get only partial branding + document.getElementsByTagName("body")[0].style.background = brandColor + .toString() + + " none"; + document.getElementById("titles").style.background = brandColor.toString() + + " none"; + + // Remove "Bug" from the title of the bug page, so we have more space with + // plenty of tabs + var titleElem = document.getElementsByTagName("title")[0]; + + titleElem.textContent = titleElem.textContent.slice(4); + var bodyTitleParent = document.getElementById("summary_alias_container").parentNode; + var bodyTitleElem = bodyTitleParent.getElementsByTagName("b")[0]; + bodyTitleElem.textContent = bodyTitleElem.textContent.slice(4); + + // Make background-color of the body of bug salmon pink + // for security bugs. + if (hasKeyword("Security")) { + document.getElementById("bugzilla-body").style.background = SalmonPink + .toString() + ' none'; + } + + // Make it visible whether the bug has been triaged + if (isTriaged()) { + document.getElementById("bz_field_status").style.background = brandColor + .lightColor().toString() + + " none"; + } + + var compElems; + if (config.suspiciousComponents + && isInList(getComponent(), config.suspiciousComponents) + && (compElems = document + .getElementById("bz_component_edit_container"))) { + compElems.style.background = "red none"; + } +} + +/** + * + */ +function queryUpstreamCallback(text) { + var searchData = filterByRegexp(constantData.queryUpstreamBug, getComponent()); + var urlBase = searchData.url; + text = searchData.searchBy+":"+searchData.fillIn+" "+text.trim(); + if (searchData.fillIn == "$$$") { + text = text.replace("$$$", getComponent()); + } + text = encodeURIComponent(text).replace("%20","+"); + postMessage(new Message("OpenURLinTab", urlBase + text)); +} + +/** + * Search simple query in the upstream bugzilla appropriate for the component + * + * @return none + */ +function queryUpstream() { + if (!constantData.queryUpstreamBug) { + alert("We don't have constantData.queryUpstreamBug set up!"); + return null; + } + var text = getSelection(); + if (!text) { + postMessage(new Message("GetClipboard", "queryUpstream")); + } else { + queryUpstreamCallback(text); + } +} + +/** + * Open a tab in the upstream bugzilla to create a new bug + * + * @return none + */ +function sendBugUpstream() { + var urlStr = filterByRegexp(constantData.newUpstreamBug, getComponent()); + if (!urlStr) { + return null; + } + + postMessage(new Message("OpenBugUpstream", { + url: urlStr, + subject: document.getElementById("short_desc_nonedit_display"). + textContent.trim(), + comment: collectComments() + })); +} + +/** + * Add a link opening selected lines of Xorg.0.log + * + * @return none + */ +function addCheckXorgLogLink(attList) { + if (config.xorglogAnalysis) { + attList.forEach(function (row) { + var elemS = row[4].getElementsByTagName("td"); + var elem = elemS[elemS.length - 1]; + createDeadLink("xorgLogAnalyzeLink", "check", elem, + analyzeXorgLog, [row[1], "AnalyzeXorgLogBacktrace"], "br"); + }); + } +} + +/** + * Given line to be parsed, find out which chipset it is and fill in the + * whiteboard + * + * @param iLine String with the whole unparsed "interesting line" + * @param driverStr String with the driver name + * @return None + */ +function fillInWhiteBoard(PCIidArr) { + var outStr = ""; + var cardIDStr = ""; + var cardIDArr = []; + var cardName = ""; + var PCIid = (PCIidArr[0] + "," + PCIidArr[1]).toUpperCase(); + + try { + cardName = constantData.chipNames[PCIid][0]; + } catch (e if e instanceof TypeError) { + alert("PCI ID " + PCIid + " is not known!"); + return ; // early termination + } catch (e) { + throw e; + } + clickMouse("editme_action"); + var titleElem = document.getElementById('short_desc'); + titleElem.value = '[' + cardName + ']\u00A0' + titleElem.value; + document.getElementById("fillin_btn").style.display = "none"; +} + +/** + * Get attached Xorg.0.log, parse it and find the value of chip. Does not fill + * the whiteboard itself, just adds button to do so,paramList so that slow + * XMLHttpRequest is done in advance. + * + * @param log array of XorgLogAttList + * @return None + */ +function fillInChipMagic(XlogID) { + analyzeXorgLog(XlogID, "AnalyzeInterestingLine"); +} + +function chipsetMagic (interestingLineArr) { + // parse Xorg.0.log + var RE = new RegExp(logAnalyzeLogic['AnalyzeInterestingLine'].re); + if (interestingLineArr.length >0) { + var interestingArray = RE.exec(interestingLineArr[0]); + if (interestingArray.length > 1) { + var interestingPCIID = interestingArray[1].trim().split(":"); + createNewButton("short_desc_nonedit_display", false, { + "name": "Fill In", + "chipMagic": interestingPCIID, + }); + } + } +} + +function analyzeXorg(results) { + var innerString = ""; + results = removeDuplicates(results); + // Remove headers + if (results.length >= 1) { + results.splice(0, 1); + } + if (results.length > 0) { + results.forEach(function(l) { + innerString += l + "
\n"; + }); + // Add a summary + innerString += "----------
\n" + + results.length + " interesting lines found."; + } else { + innerString += "No matching lines found!"; + } + postMessage(new Message("OpenStringInPanel", + '' + + "Xorg.0.log analysis
\n" +
+        innerString.trim() +
+        "\n
")); +} + +function analyzeXorgLog(attachID, backMsg) { + postMessage(new Message("GetURL", { + url: "https://" + window.location.hostname + "/attachment.cgi?id=" + attachID, + backMessage: backMsg + })); +} + +function findInterestingLine(wholeLog, backMsg) { + var RE = new RegExp(logAnalyzeLogic[backMsg].re); + var results = wholeLog.split("\n"). + filter(function(line) { + return (RE.test(line)); + }); + logAnalyzeLogic[backMsg].func(results); +} + +/** + * Return string with the ID for the external_id SELECT for external bugzilla + * + * @param URLhostname String hostname of the external bugzilla + * @return String with the string for the external_id SELECT + */ +function getBugzillaName(URLhostname) { + var bugzillaID = ""; + var bzLabelNames = constantData.bugzillaLabelNames; + if (bzLabelNames[URLhostname]) { + bugzillaID = bzLabelNames[URLhostname]; + } else { + bugzillaID = ""; + } + return bugzillaID; +} + +/** + * Callback function for the XMLRPC request + * + * @param ret Object with xmlhttprequest response with attributes: + * + status -- int return code + * + statusText + * + responseHeaders + * + responseText + */ +function XMLRPCcallback() { + reqCounter--; + if (reqCounter <= 0) { + setTimeout(function () { + window.location.reload(true); + }, 1000); + } +} + +/** + * The worker function -- call XMLRPC to fix MIME type of the particular + * attachment + * + * @param id Integer with the attachment id to be fixed + * @param type String with the new MIME type, optional defaults to "text/plain" + * @param email Boolean whether email should be sent to appropriate person; + * option, defaults to false + * + * updateAttachMimeType($data_ref, $username, $password) + * + * Update the attachment mime type of an attachment. The first argument is a + * data hash containing information on the new MIME type and the attachment id + * that you want to act on. + * + * $data_ref = { "attach_id" => "", # Attachment ID to perform + * MIME type change on. "mime_type" => "", # Legal MIME + * type value that you want to change the attachment to. "nomail" => 0, # + * OPTIONAL Flag that is either 1 or 0 if you want email to be sent or not for + * this change }; + * + */ +function fixAttachById(id, type, email) { + var params = []; + + if (type === undefined) { + type = "text/plain"; + } + if (email === undefined) { + email = false; + } + + // https://bugzilla.redhat.com/\ + // docs/en/html/api/extensions/compat_xmlrpc/code/webservice.html + // test on https://bugzilla.redhat.com/show_bug.cgi?id=485145 + params.push({ + 'attach_id' : id, + 'mime_type' : type, + 'nomail' : !email + }); + + postMessage(new Message("MakeXMLRPCall", { + url: constantData.XMLRPCData[window.location.hostname].url, + login: getLogin(), + method: "bugzilla.updateAttachMimeType", + params: params, + callRPC: "FixAttachmentMIMECallback" + })); + reqCounter++; +} + +/** + * Add a link to the bad attachment for fixing it. + * + * @param + * DOM jQuery element with a bad attachment + * @return none + */ +function addTextLink(row) { + var elemS = row[4].getElementsByTagName("td"); + var elem = elemS[elemS.length - 1]; + createDeadLink("addFix2TextLink", "text", elem, + fixAttachById, row[1], "br"); +} + +/** + * Add information about the upstream bug upstream, and closing it. + * + * @param evt Event which called this handler + * @return none + */ +function addClosingUpstream() { + var refs = document.getElementById("external_bugs_table") + .getElementsByTagName("tr"); + + // that's a bad id, if there is a one. :) + var inputBox = document.getElementById("inputbox"); + var externalBugID = 0; + var wholeURL = ""; + + // Fix missing ID on the external_id SELECT + document.getElementsByName("external_id")[0].setAttribute("id", + "external_id"); + + if (inputBox.value.match(/^http.*/)) { + externalBugID = getBugNoFromURL(inputBox.value); + if (externalBugID) { + inputBox.value = externalBugID; + } + // get bugzillaName and set the label + var bugzillaName = getBugzillaName(wholeURL.host); + selectOptionByLabel("external_id", bugzillaName); + } else if (!isNaN(inputBox.value)) { + externalBugID = parseInt(inputBox.value, 10); + var bugzillaHostname = document.getElementById("external_id").value; + wholeURL = bugzillaHostname+"show_bug.cgi?id="+externalBugID; + } else { + // no inputBox.value -- maybe there is an external bug from + // the previous commit? + } + + // It is not good to close bug as UPSTREAM, if there is no reference + // to the upstream bug. + if ((externalBugID > 0) || (refs.length > 2)) { + var msgStr = constantData.commentStrings.sentUpstreamString; + msgStr = msgStr.replace("§§§", wholeURL); + centralCommandDispatch("comment",msgStr); + centralCommandDispatch("status", "CLOSED"); + centralCommandDispatch("resolution", "UPSTREAM"); + } else { + console.log("No external bug specified among the External References!"); + } +} + +function markBugTriaged() { + // https://fedoraproject.org/wiki/BugZappers/Meetings/Minutes-2009-Oct-27 + // http://meetbot.fedoraproject.org/fedora-meeting/2009-11-24\ + // /fedora-meeting.2009-11-24-15.11.log.html + // http://meetbot.fedoraproject.org/fedora-meeting/2009-11-24\ + // /fedora-meeting.2009-11-24-15.11.log.html + addStuffToTextBox("keywords","Triaged"); +} + +/** + * + */ +function parseBacktrace (ret) { + var signalHandler = new RegExp("^\\s*#[0-9]*\\s*"); + var frameNo = new RegExp("^\\s*#([0-9]*)\\s"); + + var splitArray = ret.split("\n"); + var i = 0, ii = splitArray.length; + var outStr = "", curLine = "", numStr = ""; + var lineCounter = 0, endLineNo = 0; + + // TODO shouldn't we just cut off and analyze whole thread? + while (i < ii) { + if (signalHandler.test(splitArray[i])) { + break; + } + i++; + } + + if (i < ii) { + lineCounter = parseInt(frameNo.exec(splitArray[i])[1], 10); + endLineNo = lineCounter + NumberOfFrames; + curLine = splitArray[i]; + while ((lineCounter < endLineNo) && (curLine.trim().length > 0) + && (i < ii)) { + outStr += curLine + '\n'; + numStr = frameNo.exec(curLine); + if (numStr) { + lineCounter = parseInt(numStr[1], 10); + } + i++; + curLine = splitArray[i]; + } + return outStr; + } + return ""; +} + +function RHBZinit() { + // inheritance ... call superobject's constructor + var AbrtRE = new RegExp("^\\s*\\[abrt\\]"); + var FillMagicDoneRE = new RegExp("^\\s*\\[[0-9a-zA-Z]*\\]"); + var btSnippet = ""; + + var signaturesCounter = 0; + var chipMagicInterestingLine = ""; + + // getBadAttachments + var XorgLogAttList = []; + var XorgLogAttListIndex = 0; + var attachments = getAttachments(); + markBadAttachments(attachments); + + var parsedAttachments = attachments.filter(function (att) { + return (new RegExp(titleParsedAttachment).test(att[0])); + }); + + if (constantData.defaultAssignee) { + setDefaultAssignee(); + } + + // Dig out backtrace protection against double-firing? + btSnippet = ""; + + var parseAbrtBacktraces = config.parseAbrtBacktraces; + if (parseAbrtBacktraces && AbrtRE.test(getSummary())) { + pasteBacktraceInComments(parsedAttachments); + } + + // Find out Xorg.0.log attachment URL + XorgLogAttList = attachments.filter(function (value) { + // Xorg.0.log must be text, otherwise we cannot parse it + return (/[xX].*log/.test(value[0]) && /text/.test(value[2])); + }); + // Just add a link to every Xorg.0.log link analyzing it. + addCheckXorgLogLink(XorgLogAttList); + + var maintCCAddr = ""; + if (constantData.CCmaintainer) { + maintCCAddr = filterByRegexp(constantData.CCmaintainer, + getComponent())[0]; // filterByRegexp returns array, not string + } + + // TODO Get compiz bugs as well + if ((constantData.chipNames) && + (XorgLogAttList[0]) && + (!FillMagicDoneRE.test(getSummary())) && + (maintCCAddr === "xgl-maint@redhat.com")) { + // Add find chip magic button + var whiteboard_string = document.getElementById("status_whiteboard").value; + if (!/card_/.test(whiteboard_string)) { + fillInChipMagic(XorgLogAttList[0][1]); + } + } + + // we should make visible whether maintCCAddr is in CCList + if (maintCCAddr && isInList(maintCCAddr, getCCList())) { + var ccEditBoxElem = document.getElementById("cc_edit_area_showhide"); + ccEditBoxElem.style.color = "navy"; + ccEditBoxElem.style.fontWeight = "bolder"; + ccEditBoxElem.style.textDecoration = "underline"; + } + + // Take care of signature for Fedora bugzappers + if (config.signature && config.signature.length > 0) { + var signatureFedoraString = config.signature; + document.forms.namedItem("changeform").addEventListener("submit", + function(aEvt) { + if (signaturesCounter < 1) { + addStuffToTextBox("comment", signatureFedoraString); + signaturesCounter += 1; + } + }, false); + } + setBranding(); + + // set default assignee on change of the component + var compElement = document.getElementById("component"); + if (compElement && (compElement.options)) { + document.getElementById("component").addEventListener("change", + function() { + changeAssignee("default"); + }, false); + } + + // Uncheck "set default assignee" when the assignee is changed by other means + document.getElementById("assigned_to").addEventListener("change", + function() { + changeAssignee(null); + }, false); +} diff --git a/data/lib/skip-bug.js b/data/lib/skip-bug.js new file mode 100644 index 0000000..9fcb531 --- /dev/null +++ b/data/lib/skip-bug.js @@ -0,0 +1,20 @@ +// Released under the MIT/X11 license +// http://www.opensource.org/licenses/mit-license.php +// "use strict"; + +function reloadPage() { + var stemURL = 'https://HOSTNAME/show_bug.cgi?id='; + var titleElems = document.getElementsByTagName('title'); + if (titleElems) { + var REArr = new RegExp('[bB]ug\\s+([0-9]+)'). + exec(titleElems[0].textContent); + var hostname = document.location.hostname; + if (REArr) { + console.log("Reloading bug " + REArr[1] + "!"); + document.location = stemURL. + replace('HOSTNAME',hostname) + REArr[1]; + } + } +} + +reloadPage(); diff --git a/data/lib/util.js b/data/lib/util.js new file mode 100644 index 0000000..8232e9f --- /dev/null +++ b/data/lib/util.js @@ -0,0 +1,282 @@ +/*global console: false */ +/*jslint onevar: false */ +// Released under the MIT/X11 license +// http://www.opensource.org/licenses/mit-license.php +"use strict"; +// ============================================================== + +/** + * parse URL to get its parts. + * + * @param url + * @return object with all parsed parts of URL as properties + * + * Originally from http://james.padolsey.com/javascript/parsing-urls-with-the-dom/ + * Copyright February 19th, 2009, James Padolsey, + * + * This function creates a new anchor element and uses location + * properties (inherent) to get the desired URL data. Some String + * operations are used (to normalize results across browsers). + */ +function parseURL(url) { + var a = document.createElement('a'); + a.href = url; + return { + source: url, + protocol: a.protocol.replace(':',''), + host: a.hostname, + port: a.port, + query: a.search, + params: (function(){ + var ret = {}, + seg = a.search.replace(/^\?/,'').split('&'), + len = seg.length, i = 0, s; + for (;i/, ""); // bug 336551 + return new XML(respStr); +} + +/** + * Get a bug no + */ +function getBugNo() { + console.log("bugNo = " + document.forms.namedItem('changeform').getElementsByName("id")[0].value); + return document.forms.namedItem('changeform').getElementsByName("id")[0].value; +} + +/** + * Get a bug no from URL ... fails with aliases + * @param url String with URL to be analyzed + * @return String with the bug ID + */ +function getBugNoFromURL(url) { + console.log("getBugNoFromURL : url = " + url); + var params = parseURL(url).params; + console.log("getBugNoFromURL : params = " + params.toSource()); + if (params && params.id) { + return params.id; + } +} + +/* + * From element diggs out just plain email address + * Note that bugzilla.gnome.org doesn't have mailto: url but + * https://bugzilla.gnome.org/page.cgi?id=describeuser.html&login=mcepl%40redhat.com + * + * @param aElement Element with href attribute or something else + * @return String with the address or null + * + */ +function parseMailto(aElement) { + var emailStr = "", hrefStr = ""; + // use url utils + if (aElement) { + hrefStr = decodeURIComponent(aElement.getAttribute("href")); + emailStr = hrefStr.split(":"); + // workaround for Gnome bugzilla ... no mailto: here. + if (emailStr.length < 2) { + var params = parseURL("https://" + window.location.hostname + "/" + hrefStr).params; + return decodeURI(params.login); + } + return emailStr[1]; + } + return null; +} + +/** + * format date to be in ISO format (just day part) + * + * @param date + * @return string with the formatted date + */ +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. + */ +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. + */ +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 + */ +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 + */ +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 + */ +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. + */ +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"; + } + }); +} + +/** + * Remove duplicate elements from array + * + * @param arr Array which needs to be cleaned up + * @return cleaned up array + */ +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; +} + +// ============================================ +/** + * object to pack messaging. Use as in + postMessage(new Message("GetPassword", { + login: login, + hostname: location.hostname + })); + */ +function Message(cmd, data) { + this.cmd = cmd; + this.data = data; +} + +function log(msg) { + postMessage(new Message("LogMessage", msg)); +} + +var NotLoggedinException = function NotLoggedinException (message) { + this.message = message; + this.name = "NotLoggedinException"; +}; + +NotLoggedinException.prototype.toString = function () { + return this.name + ': "' + this.message + '"'; +}; diff --git a/data/rhbzpage.js b/data/rhbzpage.js deleted file mode 100644 index 026e7ac..0000000 --- a/data/rhbzpage.js +++ /dev/null @@ -1,924 +0,0 @@ -// Released under the MIT/X11 license -// http://www.opensource.org/licenses/mit-license.php -"use strict"; -var titleParsedAttachment = "Part of the thread where crash happened"; - - -// For identification of graphics card -var manuChipStrs = [ [ "ATI Radeon", "ATI", "1002" ], - [ "ATI Mobility Radeon", "ATI", "1002" ], - [ "Intel Corporation", "INTEL", "8086" ], [ "NVIDIA", "NV", "10de" ] ]; - -// http://en.wikipedia.org/wiki/HSL_color_space -// when only the value of S is changed -// stupido!!! the string is value in hex for each color -var RHColor = new Color(158, 41, 43); // RGB 158, 41, 43; HSL 359, 1, 39 -var FedoraColor = new Color(0, 40, 103); // RGB 0, 40, 103; HSL 359, 1, 39 -var RawhideColor = new Color(0, 119, 0); // or "green", or RGB 0, 119, 0, or - // HSL -// 120, 0, 23 -var RHITColor = new Color(102, 0, 102); // RGB 102, 0, 102; HSL 300, 0, 20 - -var logAnalyzeLogic = { - "AnalyzeInterestingLine": { - "re": "^\\[[ .0-9]+\\]\\s*\\(--\\) PCI:\\*\\([0-9:]+\\)\\s*" + - "([0-9a-f:]+).*$", - "func": chipsetMagic - }, - "AnalyzeXorgLogBacktrace": { - "re": "^\\s*(\\[[0-9 .]*\\])?\\s*(\\((EE|WW)\\)|.*"+ - " [cC]hipsets?: )|\\s*Backtrace", - "func": analyzeXorg - } -}; - -var ProfessionalProducts = [ - "Red Hat Enterprise Linux", - "Red Hat Enterprise MRG" -]; - -// END OF CONSTANTS - -var btSnippet = null; -var reqCounter = 0; // TODO should be probably a dict indexed by called method - -function RHOnMessageHandler(msg) { - switch (msg.cmd) { - case "Error": - alert("Error " + msg.data); - break; - case "Unhandled": - break; - case "AddAttachmentCallback": - addAttachmentCallback(msg.data); - break; - case "FixAttachmentMIMECallback": - XMLRPCcallback(); - break; - case "AnalyzeInterestingLine": - case "AnalyzeXorgLogBacktrace": - findInterestingLine(msg.data, msg.cmd); - break; - case "queryUpstream": - queryUpstreamCallback(msg.data); - break; - default: - console.error("Error: unknown RPC call " + msg.toSource()); - break; - } -} - -// RHBugzillaPage object - -/** - * Find default assignee based on the current component - * - * @return String what would be a default assignee if - * we haven't set it up. - */ -function getDefaultAssignee() { - return filterByRegexp(constantData.defaultAssignee, - getComponent()).toLowerCase(); -} - -/** - * Set default assignee - * - * @return none - * sets this.defaultAssignee property according to defaultAssignee list - */ -function setDefaultAssignee() { - var defAss = getDefaultAssignee(); - - // Add setting default assignee - if ((defAss.length > 0) && (defAss !== getOwner())) { - createNewButton("bz_assignee_edit_container",true, { - "name": "Def. Assignee", - "assignee": "default" - }); - } -} - -/** - * Auxiliary function to compute more complicated resolution - */ -function closeSomeRelease() { - // for RAWHIDE close as RAWHIDE, - // if active selection -> CURRENTRELEASE - // and put the release version to - // "Fixed in Version" textbox - // otherwise -> NEXTRELEASE - selectOption("bug_status", "CLOSED"); - var text = getSelection(); - var resolution = ""; - - if (text.length > 0) { - resolution = "CURRENTRELEASE"; - document.getElementById("cf_fixed_in").value = text; - } else if (document.getElementById("version").value === "rawhide") { - resolution = "RAWHIDE"; - } else { - resolution = "NEXTRELEASE"; - } - centralCommandDispatch("resolution", resolution); -} - -/** - * Additional commands specific for this subclass, overriding superclass one. - */ -function RHcentralCommandDispatch(cmdLabel, cmdParams) { - switch (cmdLabel) { - // Set up our own commands - case "closeUpstream": - addClosingUpstream(); - break; - case "computeResolution": - closeSomeRelease(); - break; - case "queryStringUpstreamBugzilla": - queryUpstream(); - break; - case "sendBugUpstream": - sendBugUpstream(); - break; - case "markTriaged": - markBugTriaged(); - break; - case "chipMagic": - fillInWhiteBoard(cmdParams); - break; - // If we don't have it here, call superclass method - default: - console.error("Unknown command:\n" + cmdLabel + "\nparameters:\n" + cmdParams); - break; - } -} - -function addAttachmentCallback(resp) { - var newAttachID = parseInt(resp.params.param.value.array.data.value.int, 10); - console.log("attachID = " + newAttachID); - // FIXME callback.call(param, newAttachID, data.length); -} - -/** - * - * This has to stay in RHBugzillaPage because upstream doesn't have addAttachment - * XML-RPC call yet. - */ -function addAttachment(data, callback, param) { - var params = []; - - if (!constantData.passwordState.passAvailable) { - console.error("addAttachment : No password, no XML-RPC calls; sorry"); - return null; - } - - params.push(getBugNo()); - params.push({ - description: titleParsedAttachment, - filename: "parsed-backtrace.txt", - contenttype: "text/plain", - data: window.btoa(data), - nomail: true - }); - - postMessage(new Message("MakeXMLRPCall", { - url: constantData.XMLRPCData[window.location.hostname].url, - login: getLogin(), - method: "bugzilla.addAttachment", - params: params, - callRPC: "AddAttachmentCallback" - })); - reqCounter++; -} - -/* === Bugzilla functions === */ -/** - * - */ -function pasteBacktraceInComments(atts) { - /* - Let's comment it out, and we'll see if anything breaks. - TODO This paragraph looks suspicous ... what is it? - Does it belong to this function? - var notedLabel = document.querySelector("label[for='newcc']"); - while (notedLabel.firstChild) { - var node = notedLabel.removeChild(notedLabel.firstChild); - notedLabel.parentNode.insertBefore(node, notedLabel); - } - notedLabel.parentNode.removeChild(notedLabel); - */ - - // FIXME BROKEN and its depending functions are even more broken - return null; - - var abrtQueryURL = "https://bugzilla.redhat.com/buglist.cgi?" + - "cmdtype=dorem&remaction=run&namedcmd=all%20NEW%20abrt%20crashes&"+ - "sharer_id=74116"; - - var mainTitle = document - .getElementsByClassName("bz_alias_short_desc_container")[0]; - - createDeadLink ("callAbrtQuery_link", - "Abrt bugs", mainTitle, abrtQueryURL, [], false, null, "a"); - - if (idContainsWord("cf_devel_whiteboard", 'btparsed')) { - addStuffToTextBox('status_whiteboard', 'btparsed'); - } - - if (!(isTriaged() || idContainsWord("status_whiteboard", - 'btparsed') || (atts.length > 0))) { - var btAttachments = atts - .filter(function(att) { - return (/File: backtrace/.test(att[0])); - }); - // TODO we need to go through all backtrace attachments, but - // just the first one will do for now, we would need to do async - // parsing - btAttachments.forEach(function(x) { - var attURL = "https://bugzilla.redhat.com/attachment.cgi?id=" - + x[1]; - if ((!btSnippet) && // ???? FIXME - (!idContainsWord("status_whiteboard", 'btparsed'))) { - Request({ - url: attURL, - onComplete: function(response) { - if (response.status == 200) { - btSnippet = parseBacktrace(response.text); - if (btSnippet) { - addCheckShowLink(x,btSnippet); - } - } - } - }).get(); - } - }, this); - } - // Add "show BT" links - if (parsedAttachments.length > 0) { - this.parsedAttachments.forEach(function (att) { - addShowParsedBTLink(att); - }); - } -} - -/** - * Open new window with the content of the attachment. - * - * @param id Number of the attachment id - * @return none - */ -function showAttachment(id) { - postMessage(new Message("OpenURLinPanel", - "https://" + window.location.hostname + "/attachment.cgi?id=" + id)); -} - -/** - * add a link opening a window with the parsed backtrace - * - * @param att Attachment object - */ -function addShowParsedBTLink(att) { - var elem = att[4].querySelector("td:last-of-type"); - createDeadLink("showParsedBacktraceWindow-" + att[1], "showBT", - elem, showAttachment, att[1], true); -} - -/** - * Unfinished ... see above FIXME BROKEN AND DOESN'T WORK - */ -function addNewAttachmentRow(origAtt, - newAttId, newAttSize) { - var that = this; - var oldAddBTLink = document.getElementById("attachBacktraceActivator"); - oldAddBTLink.parentNode.removeChild(oldAddBTLink); - var newTRElem = origAtt[4].cloneNode(true); - - // fix number of the attachment - Array.forEach(newTRElem.getElementsByTagName("a"), function (aEl) { - aEl.setAttribute("href", - aEl.getAttribute("href").replace(origAtt[1], newAttId)); - }); - - var aElements = newTRElem.getElementsByTagName("a"); - aElements[0].setAttribute("name","parsed-backtrace.txt"); - aElements[0].getElementsByTagName("b")[0].textContent = titleParsedAttachment; - - var sizeSpan = newTRElem.getElementsByClassName("bz_attach_extra_info")[0]; - sizeSpan.textContent = "(" + (newAttSize / 1024).toFixed(2) + " KB, text/plain)"; - - // aElements[1].textContent = new Date().toString(); TODO we should add eventually, but not pressing - - var vcardSpan = newTRElem.getElementsByClassName("vcard")[0]; - if (vcardSpan !== undefined) { - var vcardSpanClassList = vcardSpan.classList; - if (/@redhat\.com/.test(this.login) && !vcardSpanClassList.contains("redhat_user")) { - vcardSpanClassList.add("redhat_user"); - } - var vcardAElem = vcardSpan.getElementsByTagName("a")[0]; - vcardAElem.setAttribute("title", this.login); - vcardAElem.setAttribute("href", "mailto:" + this.login); - vcardAElem.className = "email"; - vcardAElem.innerHTML="" + this.login + ""; - } - - var elem = newTRElem.querySelector("td:last-of-type"); - this.createDeadLink("showBacktrace", "show BT", elem, - this.showAttachment, newAttId, false); - - origAtt[4].parentNode.insertBefore(newTRElem, origAtt[4].nextSibling); -} - -/** - * Add a link to create a new attachment with a parsed backtrace - * - * @param oldAtt Object with an attachment row - * @param snippet String with parsed backtrace - * @return none - */ -function addCheckShowLink(oldAtt, snippet) { - var elem = oldAtt[4].querySelector("td:last-of-type"); -/* - createDeadLink("attachBacktraceActivator", "add parsed BT", elem, function(x) { - // pass function and parameters as two separate parameters, the function to be called from - // addAttachment - addAttachment(snippet, addNewAttachmentRow, oldAtt); - }, [], true); -*/ -} - -/** - * Make it sailent that the some attachments with bad MIME type are present - * - * @param atts Array of attachments subarrays - * @return none - */ -function markBadAttachments(atts) { - var badMIMEArray = [ "application/octet-stream", "text/x-log", "undefined" ]; - if (!constantData.passwordState.passAvailable) { - console.log("markBadAttachments : No password, no XML-RPC calls; sorry"); - return null; - } - - var badAttachments = atts.filter(function(att) { - return (isInList(att[2], badMIMEArray)); - }); - - if (badAttachments.length > 0) { - var titleElement = document. - getElementsByClassName("bz_alias_short_desc_container")[0]; - titleElement.style.backgroundColor = "olive"; - - createDeadLink("fixAllButton", "Fix all", titleElement, function() { - Array.forEach(badAttachments, function(x) { - fixAttachById(x[1]); - }); - }, [], false, null, "f"); - badAttachments.forEach(function(x, i, a) { - addTextLink(x); - }); - } -} - -/** - * Is this bug a RHEL bug? - * - * @return Boolean true if it is a RHEL bug - */ -function isEnterprise() { - var result = ProfessionalProducts.some(function(elem,idx,arr) { - return new RegExp(elem).test(getProduct()); - }); - return result; -} - -/** - * Find out whether the bug is needed an attention of bugZappers - * - * @return Boolean whether the bug has been triaged or not - */ -function isTriaged() { - return hasKeyword("Triaged"); -} - -/** - * Set branding colours to easily distinguish between Fedora and RHEL bugs - * - * @param brand String with product of the current bug - * @param version String with the version of the bug - * @param its String with the IsueTracker numbers - * @return none - */ -function setBranding() { - var brandColor = {}; - var TriagedColor = {}; - - var ITbutton = document.getElementById("cf_issuetracker"); - var its = ITbutton ? ITbutton.value.trim() : ""; - - if (isEnterprise()) { - if (its && (its.length > 0)) { - brandColor = RHITColor; - } else { - brandColor = RHColor; - } - } else if (new RegExp("Fedora").test(document.getElementById("product").value)) { - if (document.getElementById("version").value === "rawhide") { - brandColor = RawhideColor; - } else { - brandColor = FedoraColor; - } - } - - // Comment each of the following lines to get only partial branding - document.getElementsByTagName("body")[0].style.background = brandColor - .toString() - + " none"; - document.getElementById("titles").style.background = brandColor.toString() - + " none"; - - // Remove "Bug" from the title of the bug page, so we have more space with - // plenty of tabs - var titleElem = document.getElementsByTagName("title")[0]; - - titleElem.textContent = titleElem.textContent.slice(4); - var bodyTitleParent = document.getElementById("summary_alias_container").parentNode; - var bodyTitleElem = bodyTitleParent.getElementsByTagName("b")[0]; - bodyTitleElem.textContent = bodyTitleElem.textContent.slice(4); - - // Make background-color of the body of bug salmon pink - // for security bugs. - if (hasKeyword("Security")) { - document.getElementById("bugzilla-body").style.background = SalmonPink - .toString() + ' none'; - } - - // Make it visible whether the bug has been triaged - if (isTriaged()) { - document.getElementById("bz_field_status").style.background = brandColor - .lightColor().toString() - + " none"; - } - - var compElems; - if (config.suspiciousComponents - && isInList(getComponent(), config.suspiciousComponents) - && (compElems = document - .getElementById("bz_component_edit_container"))) { - compElems.style.background = "red none"; - } -} - -/** - * - */ -function queryUpstreamCallback(text) { - var searchData = filterByRegexp(constantData.queryUpstreamBug, getComponent()); - var urlBase = searchData.url; - text = searchData.searchBy+":"+searchData.fillIn+" "+text.trim(); - if (searchData.fillIn == "$$$") { - text = text.replace("$$$", getComponent()); - } - text = encodeURIComponent(text).replace("%20","+"); - postMessage(new Message("OpenURLinTab", urlBase + text)); -} - -/** - * Search simple query in the upstream bugzilla appropriate for the component - * - * @return none - */ -function queryUpstream() { - if (!constantData.queryUpstreamBug) { - alert("We don't have constantData.queryUpstreamBug set up!"); - return null; - } - var text = getSelection(); - if (!text) { - postMessage(new Message("GetClipboard", "queryUpstream")); - } else { - queryUpstreamCallback(text); - } -} - -/** - * Open a tab in the upstream bugzilla to create a new bug - * - * @return none - */ -function sendBugUpstream() { - var urlStr = filterByRegexp(constantData.newUpstreamBug, getComponent()); - if (!urlStr) { - return null; - } - - postMessage(new Message("OpenBugUpstream", { - url: urlStr, - subject: document.getElementById("short_desc_nonedit_display"). - textContent.trim(), - comment: collectComments() - })); -} - -/** - * Add a link opening selected lines of Xorg.0.log - * - * @return none - */ -function addCheckXorgLogLink(attList) { - if (config.xorglogAnalysis) { - attList.forEach(function (row) { - var elemS = row[4].getElementsByTagName("td"); - var elem = elemS[elemS.length - 1]; - createDeadLink("xorgLogAnalyzeLink", "check", elem, - analyzeXorgLog, [row[1], "AnalyzeXorgLogBacktrace"], "br"); - }); - } -} - -/** - * Given line to be parsed, find out which chipset it is and fill in the - * whiteboard - * - * @param iLine String with the whole unparsed "interesting line" - * @param driverStr String with the driver name - * @return None - */ -function fillInWhiteBoard(PCIidArr) { - var outStr = ""; - var cardIDStr = ""; - var cardIDArr = []; - var cardName = ""; - var PCIid = (PCIidArr[0] + "," + PCIidArr[1]).toUpperCase(); - - try { - cardName = constantData.chipNames[PCIid][0]; - } catch (e if e instanceof TypeError) { - alert("PCI ID " + PCIid + " is not known!"); - return ; // early termination - } catch (e) { - throw e; - } - clickMouse("editme_action"); - var titleElem = document.getElementById('short_desc'); - titleElem.value = '[' + cardName + ']\u00A0' + titleElem.value; - document.getElementById("fillin_btn").style.display = "none"; -} - -/** - * Get attached Xorg.0.log, parse it and find the value of chip. Does not fill - * the whiteboard itself, just adds button to do so,paramList so that slow - * XMLHttpRequest is done in advance. - * - * @param log array of XorgLogAttList - * @return None - */ -function fillInChipMagic(XlogID) { - analyzeXorgLog(XlogID, "AnalyzeInterestingLine"); -} - -function chipsetMagic (interestingLineArr) { - // parse Xorg.0.log - var RE = new RegExp(logAnalyzeLogic['AnalyzeInterestingLine'].re); - if (interestingLineArr.length >0) { - var interestingArray = RE.exec(interestingLineArr[0]); - if (interestingArray.length > 1) { - var interestingPCIID = interestingArray[1].trim().split(":"); - createNewButton("short_desc_nonedit_display", false, { - "name": "Fill In", - "chipMagic": interestingPCIID, - }); - } - } -} - -function analyzeXorg(results) { - var innerString = ""; - results = removeDuplicates(results); - // Remove headers - if (results.length >= 1) { - results.splice(0, 1); - } - if (results.length > 0) { - results.forEach(function(l) { - innerString += l + "
\n"; - }); - // Add a summary - innerString += "----------
\n" + - results.length + " interesting lines found."; - } else { - innerString += "No matching lines found!"; - } - postMessage(new Message("OpenStringInPanel", - '' + - "Xorg.0.log analysis
\n" +
-        innerString.trim() +
-        "\n
")); -} - -function analyzeXorgLog(attachID, backMsg) { - postMessage(new Message("GetURL", { - url: "https://" + window.location.hostname + "/attachment.cgi?id=" + attachID, - backMessage: backMsg - })); -} - -function findInterestingLine(wholeLog, backMsg) { - var RE = new RegExp(logAnalyzeLogic[backMsg].re); - var results = wholeLog.split("\n"). - filter(function(line) { - return (RE.test(line)); - }); - logAnalyzeLogic[backMsg].func(results); -} - -/** - * Return string with the ID for the external_id SELECT for external bugzilla - * - * @param URLhostname String hostname of the external bugzilla - * @return String with the string for the external_id SELECT - */ -function getBugzillaName(URLhostname) { - var bugzillaID = ""; - var bzLabelNames = constantData.bugzillaLabelNames; - if (bzLabelNames[URLhostname]) { - bugzillaID = bzLabelNames[URLhostname]; - } else { - bugzillaID = ""; - } - return bugzillaID; -} - -/** - * Callback function for the XMLRPC request - * - * @param ret Object with xmlhttprequest response with attributes: - * + status -- int return code - * + statusText - * + responseHeaders - * + responseText - */ -function XMLRPCcallback() { - reqCounter--; - if (reqCounter <= 0) { - setTimeout(function () { - window.location.reload(true); - }, 1000); - } -} - -/** - * The worker function -- call XMLRPC to fix MIME type of the particular - * attachment - * - * @param id Integer with the attachment id to be fixed - * @param type String with the new MIME type, optional defaults to "text/plain" - * @param email Boolean whether email should be sent to appropriate person; - * option, defaults to false - * - * updateAttachMimeType($data_ref, $username, $password) - * - * Update the attachment mime type of an attachment. The first argument is a - * data hash containing information on the new MIME type and the attachment id - * that you want to act on. - * - * $data_ref = { "attach_id" => "", # Attachment ID to perform - * MIME type change on. "mime_type" => "", # Legal MIME - * type value that you want to change the attachment to. "nomail" => 0, # - * OPTIONAL Flag that is either 1 or 0 if you want email to be sent or not for - * this change }; - * - */ -function fixAttachById(id, type, email) { - var params = []; - - if (type === undefined) { - type = "text/plain"; - } - if (email === undefined) { - email = false; - } - - // https://bugzilla.redhat.com/\ - // docs/en/html/api/extensions/compat_xmlrpc/code/webservice.html - // test on https://bugzilla.redhat.com/show_bug.cgi?id=485145 - params.push({ - 'attach_id' : id, - 'mime_type' : type, - 'nomail' : !email - }); - - postMessage(new Message("MakeXMLRPCall", { - url: constantData.XMLRPCData[window.location.hostname].url, - login: getLogin(), - method: "bugzilla.updateAttachMimeType", - params: params, - callRPC: "FixAttachmentMIMECallback" - })); - reqCounter++; -} - -/** - * Add a link to the bad attachment for fixing it. - * - * @param - * DOM jQuery element with a bad attachment - * @return none - */ -function addTextLink(row) { - var elemS = row[4].getElementsByTagName("td"); - var elem = elemS[elemS.length - 1]; - createDeadLink("addFix2TextLink", "text", elem, - fixAttachById, row[1], "br"); -} - -/** - * Add information about the upstream bug upstream, and closing it. - * - * @param evt Event which called this handler - * @return none - */ -function addClosingUpstream() { - var refs = document.getElementById("external_bugs_table") - .getElementsByTagName("tr"); - - // that's a bad id, if there is a one. :) - var inputBox = document.getElementById("inputbox"); - var externalBugID = 0; - var wholeURL = ""; - - // Fix missing ID on the external_id SELECT - document.getElementsByName("external_id")[0].setAttribute("id", - "external_id"); - - if (inputBox.value.match(/^http.*/)) { - externalBugID = getBugNoFromURL(inputBox.value); - if (externalBugID) { - inputBox.value = externalBugID; - } - // get bugzillaName and set the label - var bugzillaName = getBugzillaName(wholeURL.host); - selectOptionByLabel("external_id", bugzillaName); - } else if (!isNaN(inputBox.value)) { - externalBugID = parseInt(inputBox.value, 10); - var bugzillaHostname = document.getElementById("external_id").value; - wholeURL = bugzillaHostname+"show_bug.cgi?id="+externalBugID; - } else { - // no inputBox.value -- maybe there is an external bug from - // the previous commit? - } - - // It is not good to close bug as UPSTREAM, if there is no reference - // to the upstream bug. - if ((externalBugID > 0) || (refs.length > 2)) { - var msgStr = constantData.commentStrings.sentUpstreamString; - msgStr = msgStr.replace("§§§", wholeURL); - centralCommandDispatch("comment",msgStr); - centralCommandDispatch("status", "CLOSED"); - centralCommandDispatch("resolution", "UPSTREAM"); - } else { - console.log("No external bug specified among the External References!"); - } -} - -function markBugTriaged() { - // https://fedoraproject.org/wiki/BugZappers/Meetings/Minutes-2009-Oct-27 - // http://meetbot.fedoraproject.org/fedora-meeting/2009-11-24\ - // /fedora-meeting.2009-11-24-15.11.log.html - // http://meetbot.fedoraproject.org/fedora-meeting/2009-11-24\ - // /fedora-meeting.2009-11-24-15.11.log.html - addStuffToTextBox("keywords","Triaged"); -} - -/** - * - */ -function parseBacktrace (ret) { - var signalHandler = new RegExp("^\\s*#[0-9]*\\s*"); - var frameNo = new RegExp("^\\s*#([0-9]*)\\s"); - - var splitArray = ret.split("\n"); - var i = 0, ii = splitArray.length; - var outStr = "", curLine = "", numStr = ""; - var lineCounter = 0, endLineNo = 0; - - // TODO shouldn't we just cut off and analyze whole thread? - while (i < ii) { - if (signalHandler.test(splitArray[i])) { - break; - } - i++; - } - - if (i < ii) { - lineCounter = parseInt(frameNo.exec(splitArray[i])[1], 10); - endLineNo = lineCounter + NumberOfFrames; - curLine = splitArray[i]; - while ((lineCounter < endLineNo) && (curLine.trim().length > 0) - && (i < ii)) { - outStr += curLine + '\n'; - numStr = frameNo.exec(curLine); - if (numStr) { - lineCounter = parseInt(numStr[1], 10); - } - i++; - curLine = splitArray[i]; - } - return outStr; - } - return ""; -} - -function RHBZinit() { - // inheritance ... call superobject's constructor - var AbrtRE = new RegExp("^\\s*\\[abrt\\]"); - var FillMagicDoneRE = new RegExp("^\\s*\\[[0-9a-zA-Z]*\\]"); - var btSnippet = ""; - - var signaturesCounter = 0; - var chipMagicInterestingLine = ""; - - // getBadAttachments - var XorgLogAttList = []; - var XorgLogAttListIndex = 0; - var attachments = getAttachments(); - markBadAttachments(attachments); - - var parsedAttachments = attachments.filter(function (att) { - return (new RegExp(titleParsedAttachment).test(att[0])); - }); - - if (constantData.defaultAssignee) { - setDefaultAssignee(); - } - - // Dig out backtrace protection against double-firing? - btSnippet = ""; - - var parseAbrtBacktraces = config.parseAbrtBacktraces; - if (parseAbrtBacktraces && AbrtRE.test(getSummary())) { - pasteBacktraceInComments(parsedAttachments); - } - - // Find out Xorg.0.log attachment URL - XorgLogAttList = attachments.filter(function (value) { - // Xorg.0.log must be text, otherwise we cannot parse it - return (/[xX].*log/.test(value[0]) && /text/.test(value[2])); - }); - // Just add a link to every Xorg.0.log link analyzing it. - addCheckXorgLogLink(XorgLogAttList); - - var maintCCAddr = ""; - if (constantData.CCmaintainer) { - maintCCAddr = filterByRegexp(constantData.CCmaintainer, - getComponent())[0]; // filterByRegexp returns array, not string - } - - // TODO Get compiz bugs as well - if ((constantData.chipNames) && - (XorgLogAttList[0]) && - (!FillMagicDoneRE.test(getSummary())) && - (maintCCAddr === "xgl-maint@redhat.com")) { - // Add find chip magic button - var whiteboard_string = document.getElementById("status_whiteboard").value; - if (!/card_/.test(whiteboard_string)) { - fillInChipMagic(XorgLogAttList[0][1]); - } - } - - // we should make visible whether maintCCAddr is in CCList - if (maintCCAddr && isInList(maintCCAddr, getCCList())) { - var ccEditBoxElem = document.getElementById("cc_edit_area_showhide"); - ccEditBoxElem.style.color = "navy"; - ccEditBoxElem.style.fontWeight = "bolder"; - ccEditBoxElem.style.textDecoration = "underline"; - } - - // Take care of signature for Fedora bugzappers - if (config.signature && config.signature.length > 0) { - var signatureFedoraString = config.signature; - document.forms.namedItem("changeform").addEventListener("submit", - function(aEvt) { - if (signaturesCounter < 1) { - addStuffToTextBox("comment", signatureFedoraString); - signaturesCounter += 1; - } - }, false); - } - setBranding(); - - // set default assignee on change of the component - var compElement = document.getElementById("component"); - if (compElement && (compElement.options)) { - document.getElementById("component").addEventListener("change", - function() { - changeAssignee("default"); - }, false); - } - - // Uncheck "set default assignee" when the assignee is changed by other means - document.getElementById("assigned_to").addEventListener("change", - function() { - changeAssignee(null); - }, false); -} diff --git a/data/skip-bug.js b/data/skip-bug.js deleted file mode 100644 index 9fcb531..0000000 --- a/data/skip-bug.js +++ /dev/null @@ -1,20 +0,0 @@ -// Released under the MIT/X11 license -// http://www.opensource.org/licenses/mit-license.php -// "use strict"; - -function reloadPage() { - var stemURL = 'https://HOSTNAME/show_bug.cgi?id='; - var titleElems = document.getElementsByTagName('title'); - if (titleElems) { - var REArr = new RegExp('[bB]ug\\s+([0-9]+)'). - exec(titleElems[0].textContent); - var hostname = document.location.hostname; - if (REArr) { - console.log("Reloading bug " + REArr[1] + "!"); - document.location = stemURL. - replace('HOSTNAME',hostname) + REArr[1]; - } - } -} - -reloadPage(); diff --git a/data/util.js b/data/util.js deleted file mode 100644 index 8232e9f..0000000 --- a/data/util.js +++ /dev/null @@ -1,282 +0,0 @@ -/*global console: false */ -/*jslint onevar: false */ -// Released under the MIT/X11 license -// http://www.opensource.org/licenses/mit-license.php -"use strict"; -// ============================================================== - -/** - * parse URL to get its parts. - * - * @param url - * @return object with all parsed parts of URL as properties - * - * Originally from http://james.padolsey.com/javascript/parsing-urls-with-the-dom/ - * Copyright February 19th, 2009, James Padolsey, - * - * This function creates a new anchor element and uses location - * properties (inherent) to get the desired URL data. Some String - * operations are used (to normalize results across browsers). - */ -function parseURL(url) { - var a = document.createElement('a'); - a.href = url; - return { - source: url, - protocol: a.protocol.replace(':',''), - host: a.hostname, - port: a.port, - query: a.search, - params: (function(){ - var ret = {}, - seg = a.search.replace(/^\?/,'').split('&'), - len = seg.length, i = 0, s; - for (;i/, ""); // bug 336551 - return new XML(respStr); -} - -/** - * Get a bug no - */ -function getBugNo() { - console.log("bugNo = " + document.forms.namedItem('changeform').getElementsByName("id")[0].value); - return document.forms.namedItem('changeform').getElementsByName("id")[0].value; -} - -/** - * Get a bug no from URL ... fails with aliases - * @param url String with URL to be analyzed - * @return String with the bug ID - */ -function getBugNoFromURL(url) { - console.log("getBugNoFromURL : url = " + url); - var params = parseURL(url).params; - console.log("getBugNoFromURL : params = " + params.toSource()); - if (params && params.id) { - return params.id; - } -} - -/* - * From
element diggs out just plain email address - * Note that bugzilla.gnome.org doesn't have mailto: url but - * https://bugzilla.gnome.org/page.cgi?id=describeuser.html&login=mcepl%40redhat.com - * - * @param aElement Element with href attribute or something else - * @return String with the address or null - * - */ -function parseMailto(aElement) { - var emailStr = "", hrefStr = ""; - // use url utils - if (aElement) { - hrefStr = decodeURIComponent(aElement.getAttribute("href")); - emailStr = hrefStr.split(":"); - // workaround for Gnome bugzilla ... no mailto: here. - if (emailStr.length < 2) { - var params = parseURL("https://" + window.location.hostname + "/" + hrefStr).params; - return decodeURI(params.login); - } - return emailStr[1]; - } - return null; -} - -/** - * format date to be in ISO format (just day part) - * - * @param date - * @return string with the formatted date - */ -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. - */ -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. - */ -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 - */ -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 - */ -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 - */ -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. - */ -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"; - } - }); -} - -/** - * Remove duplicate elements from array - * - * @param arr Array which needs to be cleaned up - * @return cleaned up array - */ -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; -} - -// ============================================ -/** - * object to pack messaging. Use as in - postMessage(new Message("GetPassword", { - login: login, - hostname: location.hostname - })); - */ -function Message(cmd, data) { - this.cmd = cmd; - this.data = data; -} - -function log(msg) { - postMessage(new Message("LogMessage", msg)); -} - -var NotLoggedinException = function NotLoggedinException (message) { - this.message = message; - this.name = "NotLoggedinException"; -}; - -NotLoggedinException.prototype.toString = function () { - return this.name + ': "' + this.message + '"'; -}; -- cgit