/*jslint onevar: false, browser: true, evil: true, laxbreak: true, undef: true, nomen: true, eqeqeq: true, bitwise: true, maxerr: 1000, immed: false, white: false, plusplus: false, regexp: false, undef: false */ /*global jetpack */ // Released under the MIT/X11 license // http://www.opensource.org/licenses/mit-license.php "use strict"; jetpack.future.import("pageMods"); jetpack.future.import("storage.simple"); jetpack.future.import("selection"); jetpack.future.import("clipboard"); var TriagedDistro = 13; var NumberOfFrames = 7; var XMLRPCurl = "https://bugzilla.redhat.com/xmlrpc.cgi"; var bugURL = "https://bugzilla.redhat.com/show_bug.cgi?id="; var myStorage = jetpack.storage.simple; var badMIMEArray = [ "application/octet-stream", "text/x-log", "undefined" ]; // ============================================================== // TODO https://wiki.mozilla.org/Labs/Jetpack/JEP/24 var manifest = { settings : [ { name : "BZpassword", type : "password", label : "Bugzilla password" }, { name : "JSONURL", type : "text", label : "Configuration file URL", "default" : "http://mcepl.fedorapeople.org/scripts/BugZappers_data.json" } ] }; jetpack.future.import("storage.settings"); // if (!jetpack.storage.settings.BZpassword) { // jetpack.settings.open(); // } var jsonDataURL = myStorage.JSONURL ? myStorage.JSONURL : "http://mcepl.fedorapeople.org/scripts/BugZappers_data.json"; var PCIIDsURL = "http://mcepl.fedorapeople.org/scripts/drm_pciids.json"; var abrtQueryURL = "https://bugzilla.redhat.com/buglist.cgi?" + "cmdtype=dorem&remaction=run&namedcmd=all%20NEW%20abrt%20crashes&sharer_id=74116"; var reqCounter = 0; var msgStrs = {}; var CommentRe = new RegExp("^\\s*#"); var BlankLineRe = new RegExp("^\\s*$"); // nová řádka // [ 65.631] (--) intel(0): Chipset: "845G" var ChipsetRE = new RegExp("^\\s*\\[?[ 0-9.]*\\]?\\s*\\(--\\) ([A-Za-z]+)\\([0-9]?\\): Chipset: (.*)$"); var ATIgetIDRE = new RegExp("^.*\\(ChipID = 0x([0-9a-fA-F]+)\\).*$"); var AbrtRE = new RegExp("^\\s*\\[abrt\\]"); var signalHandlerRE = new RegExp("^\\s*#[0-9]*\\s*"); var frameNoRE = new RegExp("^\\s*#([0-9]*)\\s"); // Initialize data from remote URL var XMLHttpRequestDone = false; var hashBugzillaName = []; var hashBugzillaWholeURL = []; var defAssigneeList = []; var suspiciousComponents = []; var signatureFedoraString = ""; // TODO we should have an array SpecialFlags instead of multiple Boolean // variables var parseAbrtBacktraces = false; var queryButtonAvailable = false; var upstreamButtonAvailable = false; var logSubmits = false; var chipIDsGroupings = []; var AddrArray = []; var PCI_ID_Array = []; var topRow = {}; var bottomRow = {}; // ======== load external library =============================== // var XMLRPCMessage = {}; // var req = new XMLHttpRequest(); // req.open("GET","http://mcepl.fedorapeople.org/scripts/xmlrpc.js",true); // req.onreadystatechange = function (aEvt) { // if (req.readyState == 4) { // if (req.status == 200) { // var thisDoc = jetpack.tabs.focused.contentDocument; // var script = thisDoc.createElement("script"); // script.setAttribute("type","text/javascript"); // script.innerHTML = req.responseText; // thisDoc.getElementsByTagName("head")[0].appendChild(script); // XMLRPCMessage = // jetpack.tabs.focused.contentWindow.wrappedJSObject.XMLRPCMessage; // console.log("XMLHTTPRequest should be loaded."); // console.log(XMLRPCMessage); // } // } // }; // console.log("Now we are calling XMLHTTPRequest to load xmlrpc.js"); // req.send(""); /* * * xmlrpc.js beta version 1 Tool for creating XML-RPC formatted requests in * JavaScript * * Copyright 2001 Scott Andrew LePera scott@scottandrew.com * http://www.scottandrew.com/xml-rpc * * License: You are granted the right to use and/or redistribute this code only * if this license and the copyright notice are included and you accept that no * warranty of any kind is made or implied by the author. * */ function XMLRPCMessage(methodname) { this.method = methodname || "system.listMethods"; this.params = []; return this; } XMLRPCMessage.prototype.setMethod = function(methodName) { if (!methodName) return; this.method = methodName; }; XMLRPCMessage.prototype.addParameter = function(data) { if (arguments.length == 0) return; this.params[this.params.length] = data; }; XMLRPCMessage.prototype.xml = function() { var method = this.method; // assemble the XML message header var xml = ""; xml += "\n"; xml += "\n"; xml += "" + method + "\n"; xml += "\n"; // do individual parameters for ( var i = 0; i < this.params.length; i++) { var data = this.params[i]; xml += "\n"; xml += "" + XMLRPCMessage.getParamXML(XMLRPCMessage.dataTypeOf(data), data) + "\n"; xml += "\n"; } xml += "\n"; xml += ""; return xml; // for now }; XMLRPCMessage.prototype.dataTypeOf = function(o) { // identifies the data type var type = typeof (o); type = type.toLowerCase(); switch (type) { case "number": if (Math.round(o) == o) type = "i4"; else type = "double"; break; case "object": var con = o.constructor; if (con == Date) type = "date"; else if (con == Array) type = "array"; else type = "struct"; break; } return type; }; XMLRPCMessage.prototype.doValueXML = function(type, data) { var xml = "<" + type + ">" + data + ""; return xml; }; XMLRPCMessage.prototype.doBooleanXML = function(data) { var value = (data == true) ? 1 : 0; var xml = "" + value + ""; return xml; }; XMLRPCMessage.prototype.doDateXML = function(data) { var leadingZero = function (n) { // pads a single number with a leading zero. Heh. if (n.length == 1) n = "0" + n; return n; }; var dateToISO8601 = function(date) { // wow I hate working with the Date object var year = new String(date.getYear()); var month = this.leadingZero(new String(date.getMonth())); var day = this.leadingZero(new String(date.getDate())); var time = this.leadingZero(new String(date.getHours())) + ":" + this.leadingZero(new String(date.getMinutes())) + ":" + this.leadingZero(new String(date.getSeconds())); var converted = year + month + day + "T" + time; return converted; }; var xml = ""; xml += dateToISO8601(data); xml += ""; return xml; }; XMLRPCMessage.prototype.doArrayXML = function(data) { var xml = "\n"; for ( var i = 0; i < data.length; i++) { xml += "" + XMLRPCMessage.getParamXML(XMLRPCMessage.dataTypeOf(data[i]), data[i]) + "\n"; } xml += "\n"; return xml; }; XMLRPCMessage.prototype.doStructXML = function(data) { var xml = "\n"; for ( var i in data) { xml += "\n"; xml += "" + i + "\n"; xml += "" + XMLRPCMessage.getParamXML(XMLRPCMessage.dataTypeOf(data[i]), data[i]) + "\n"; xml += "\n"; } xml += "\n"; return xml; }; XMLRPCMessage.prototype.getParamXML = function(type, data) { var xml; switch (type) { case "date": xml = XMLRPCMessage.doDateXML(data); break; case "array": xml = XMLRPCMessage.doArrayXML(data); break; case "struct": xml = XMLRPCMessage.doStructXML(data); break; case "boolean": xml = XMLRPCMessage.doBooleanXML(data); break; default: xml = XMLRPCMessage.doValueXML(type, data); break; } return xml; }; /* * Create and return an object that has p as its prototype * * @param p parent Object * @return child Object */ if (typeof Object.create !== 'function') { Object.prototype.create = function () { function F() {} F.prototype = this; return new F(); }; } // ============================================================== var hlpr = function () { }; hlpr.valToArray = function valToArray(val) { let arr = []; if (typeof val == "string") { arr = [val]; } else if (val instanceof Array) { arr = val; } return arr; }; hlpr.addCSVValue = function addCSVValue(str, value) { let parts = (str.trim().length > 0 ? str.split(",") : []); if (parts.indexOf(value) < 0) { parts.concat(hlpr.valToArray(value)); } return parts.join(","); }; // FIXME rewrite so that it doesn't break Eclipse //hlpr.removeCSVValue = function removeCSVValue(str, value) { // let parts = (str.trim().length > 0 ? str.split(",") : []); // for each (let val in hlpr.valToArray(value)) { // let pos = parts.indexOf(val); // if (pos > -1) { // parts.splice(pos, 1); // } // } // return parts.join(","); //}; /** * 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. */ hlpr.isInList = function(mbr, list) { return (list.indexOf(mbr) !== -1); }; hlpr.createBlankPage = function (ttl, bodyBuildCB) { var title = ttl || "Yet another untitled page"; var that = this; var logTab = jetpack.tabs.open("about:blank"); jetpack.tabs.onReady(function() { var otherDoc = logTab.contentDocument; otherDoc.title = title; otherDoc.body.innerHTML = "

" + title + "

"; bodyBuildCB.call(that, otherDoc.body); logTab.focus(); }); }; //Get JSON configuration data hlpr.loadText = function(URL, cb_function, what) { if (what === undefined) { // missing optional argument what = this; } var req = new XMLHttpRequest(); req.open("GET", URL, true); req.onreadystatechange = function(aEvt) { if (req.readyState == 4) { if (req.status == 200) { cb_function.call(what, req.responseText); } else { throw "Getting " + URL + "failed!"; } } }; req.send(""); }; /** * Converts attributes value of the given list of elements to the Javascript * list. * * @param list * array of elements * @return array of values * @depreceated FIXME never used */ hlpr.valuesToList = function(list) { var outL = []; list.forEach(function(e, i, a) { if (e.hasAttribute("value")) { outL.push(e.getAttribute("value").trim()); } }); return outL; }; // Initialization hlpr.loadJSON = function(URL, cb_function, what) { if (what === undefined) { // missing optional argument what = this; } hlpr.loadText(URL, function(text) { var data = JSON.parse(text); cb_function.call(what, data); }, what); }; hlpr.loadJSON(jsonDataURL, function(response) { msgStrs = response.strings; signatureFedoraString = response.signature; suspiciousComponents = response.suspiciousComponents; hashBugzillaName = response.bugzillalabelNames; hashBugzillaWholeURL = response.bugzillaIDURLs; // [{'regexp to match component':'email address of an universal // maintainer'}, ...] AddrArray = response.CCmaintainer; defAssigneeList = response.defaultAssignee; queryButtonAvailable = response.queryButton; upstreamButtonAvailable = response.upstreamButton; parseAbrtBacktraces = response.parseAbrtBacktraces; logSubmits = response.submitsLogging; newUpstreamBugsURLArray = response.newUpstreamBug; queryUpstreamBugsURLArray = response.queryUpstreamBug; chipIDsGroupings = response.chipIDsGroupings; topRow = response.topRow; bottomRow = response.bottomRow; }); // Get card translation table hlpr.loadJSON(PCIIDsURL, function(response) { PCI_ID_Array = response; }); // ============================================================================ // 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]); }; // ==================================================================================== // BZPage's methods function BZPage(doc) { } /** * Select option with given label on the elements in the page this.addNewButton(this.doc.getElementById("commit_middle"), "changeOwnerbtn", "Mark Triaged", "", "ASSIGNED", "NODEFAULTASSIGNEE"); // THE MAIN BUTTON ROWS var commentBox = this.doc.getElementById("comment"); var brElement = this.doc.createElement("br"); commentBox.parentNode.normalize(); commentBox.parentNode.insertBefore(brElement, commentBox); // this.generateToolBar(commentBox.previousSibling,above); this.generateToolBar(brElement, above); this.generateToolBar(this.originalButton, below); var commentArea = this.doc.getElementById("comment_status_commit"); var brElementPlacer = commentArea.getElementsByTagName("br")[0]; if (queryButtonAvailable || upstreamButtonAvailable) { brElementPlacer.parentNode.insertBefore(this.doc.createElement("br"), brElementPlacer); } if (queryButtonAvailable) { // Add query search button this.addNewButton(brElementPlacer, "newqueryintab", "Query for string", "", "QUERYSEL", "", false); } if (queryUpstreamBugsURLArray) { console.log("Adding upstream query search box"); if (this.filterByRegexp(queryUpstreamBugsURLArray, this.component)) { this.addNewButton(brElementPlacer, "upstreamqueryintab", "Query upstream", "", "QUERYUP", "", false); } } // Button for upstreaming if (upstreamButtonAvailable) { this.addNewButton(brElementPlacer, "sendupstream", "Send upstream", "", "SENDUPSTREAM", "", false); } // var brElement2BMoved = this.doc.querySelector("#comment_status_commit // br:last-of-type"); // var brWhereMove = this.doc.getElementsByClassName("status")[0]; // brWhereMove.parentNode.insertBefore(brElement2BMoved.cloneNode(true), // brWhereMove); // brElement2BMoved.parentNode.removeChild(brElement2BMoved); // TODO Get compiz bugs as well if ((chipIDsGroupings.length > 0) && this.maintCCAddr === "xgl-maint@redhat.com") { // Add find chip magic button var whiteboard_string = this.doc.getElementById("status_whiteboard").value; if (!/card_/.test(whiteboard_string)) { this.fillInChipMagic(); } } // Add setting default assignee if ((this.defaultAssignee.length > 0) && (this.defaultAssignee !== this.owner)) { this.addNewButton( this.doc.getElementById("bz_assignee_edit_container"), "setdefaultassigneebutton", "Def. Assignee", "", "SETDEFASS", this.defaultAssignee, false, true); } }; RHBugzillaPage.prototype.addLogRecord = function() { var rec = {}; rec.date = new Date(); rec.bugId = this.bugNo; rec.title = this.title; var comment = jetpack.tabs.focused.contentWindow.prompt( "Enter comments for this comment").trim(); if (comment.length > 0) { rec.comment = comment; var recKey = this.getISODate(rec.date) + "+" + rec.bugId; var clearLogAElem = this.doc.getElementById("clearLogs"); clearLogAElem.style.color = this.FullLogsColor; clearLogAElem.style.fontWeight = "bolder"; if (myStorage.logs[recKey]) { myStorage.logs[recKey].comment += "
\n" + comment; } else { myStorage.logs[recKey] = rec; } jetpack.storage.simple.sync(); } else if (comment === null) { return null; } }; RHBugzillaPage.prototype.timeSheetRecordsPrinter = function(body, records) { var that = this; // sort the records into temporary array var tmpArr = []; for ( var i in records) { if (records.hasOwnProperty(i)) { tmpArr.push( [ i, records[i] ]); } } tmpArr.sort(function(a, b) { return a[0] > b[0] ? 1 : -1; }); var currentDay = ""; // now print the array tmpArr .forEach(function(rec) { var x = rec[1]; var dayStr = that.getISODate(x.date); if (dayStr != currentDay) { currentDay = dayStr; body.innerHTML += "

" + currentDay + "

"; } body.innerHTML += "

Bug " + x.title + "" + " \n
" + x.comment + "

"; }); }; RHBugzillaPage.prototype.generateTimeSheet = function(body) { var doc = body.ownerDocument; this.timeSheetRecordsPrinter(body, myStorage.logs); }; // ///////////////////////////////////////////////////////////////////////////// var callback = function(doc) { var curPage = new RHBugzillaPage(doc); }; var options = {}; options.matches = [ "https://bugzilla.redhat.com/show_bug.cgi" ]; jetpack.pageMods.add(callback, options);