/*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;
// ==============================================================
// 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"
},
{
name : "enabledPacks",
type : "text",
label : "comment packs which should be enabled",
"default" : ""
}
]
};
jetpack.future.import("storage.settings");
// TODO we should have an array SpecialFlags instead of multiple Boolean
// variables
var logSubmits = false;
var chipIDsGroupings = [];
var AddrArray = [];
var topRow = {};
var bottomRow = {};
/*
*
* 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() {
let method = this.method;
// assemble the XML message header
let xml = "";
xml += "\n";
xml += "\n";
xml += "" + method + "\n";
xml += "\n";
// do individual parameters
for ( let i = 0; i < this.params.length; i++) {
let data = this.params[i];
xml += "\n";
xml += ""
+ this.getParamXML(this.dataTypeOf(data),
data) + "\n";
xml += "\n";
}
xml += "\n";
xml += "";
return xml; // for now
};
XMLRPCMessage.prototype.dataTypeOf = function(o) {
// identifies the data type
let type = typeof (o);
type = type.toLowerCase();
switch (type) {
case "number":
if (Math.round(o) == o)
type = "i4";
else
type = "double";
break;
case "object":
let 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) {
let xml = "<" + type + ">" + data + "" + type + ">";
return xml;
};
XMLRPCMessage.prototype.doBooleanXML = function(data) {
let value = (data == true) ? 1 : 0;
let xml = "" + value + "";
return xml;
};
XMLRPCMessage.prototype.doDateXML = function(data) {
let leadingZero = function (n) {
// pads a single number with a leading zero. Heh.
if (n.length == 1)
n = "0" + n;
return n;
};
let dateToISO8601 = function(date) {
// wow I hate working with the Date object
let year = new String(date.getYear());
let month = this.leadingZero(new String(date.getMonth()));
let day = this.leadingZero(new String(date.getDate()));
let time = this.leadingZero(new String(date.getHours())) + ":"
+ this.leadingZero(new String(date.getMinutes())) + ":"
+ this.leadingZero(new String(date.getSeconds()));
let converted = year + month + day + "T" + time;
return converted;
};
let xml = "";
xml += dateToISO8601(data);
xml += "";
return xml;
};
XMLRPCMessage.prototype.doArrayXML = function(data) {
let xml = "\n";
for ( let i = 0; i < data.length; i++) {
xml += ""
+ this.getParamXML(this.dataTypeOf(data[i]),
data[i]) + "\n";
}
xml += "\n";
return xml;
};
XMLRPCMessage.prototype.doStructXML = function(data) {
let xml = "\n";
for ( let i in data) {
xml += "\n";
xml += "" + i + "\n";
xml += ""
+ this.getParamXML(this.dataTypeOf(data[i]),
data[i]) + "\n";
xml += "\n";
}
xml += "\n";
return xml;
};
XMLRPCMessage.prototype.getParamXML = function(type, data) {
let xml;
switch (type) {
case "date":
xml = this.doDateXML(data);
break;
case "array":
xml = this.doArrayXML(data);
break;
case "struct":
xml = this.doStructXML(data);
break;
case "boolean":
xml = this.doBooleanXML(data);
break;
default:
xml = this.doValueXML(type, data);
break;
}
return xml;
};
// ==============================================================
let hlpr = function () {
};
/**
* Function for the management of the prototypal inheritace
* David Flanagan, Javascript: The Definitve Guide,
* IV. edition, O'Reilly, 2006, p. 168
*
* @param superobject
* @return new object, it needs new prototype.constructor
*
*
*/
hlpr.heir = function(p) {
function f() {};
f.prototype = p.prototype;
return new f();
}
/**
* 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) {
if (!list) {
return false;
}
return (list.indexOf(mbr) !== -1);
};
/**
* format date to be in ISO format (just day part)
*
* @param date
* @return string with the formatted date
*/
hlpr.getISODate = function (dateStr) {
function pad(n) {
return n < 10 ? '0' + n : n;
}
let date = new Date(dateStr);
return date.getFullYear() + '-' + pad(date.getMonth() + 1) + '-'
+ pad(date.getDate());
};
/**
* 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.
*/
hlpr.valToArray = function valToArray(val) {
return (val instanceof Array) ? 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
*/
hlpr.addCSVValue = function addCSVValue(str, value) {
console.log("addCSVValue: str = " + str + ", value = " + value);
console.log("addCSVValue: typeof str = " + typeof str + ", typeof value = " + typeof value);
let parts = (str.trim().length > 0 ? str.split(",") : []);
console.log("parts before = " + parts.toSource());
if (!hlpr.isInList(value,parts)) {
let newValue = hlpr.valToArray(value);
console.log("newValue = " + newValue);
parts = parts.concat(newValue);
}
console.log("parts on leaving = " + parts.toSource());
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
*/
hlpr.removeCSVValue = function removeCSVValue(str, value) {
str = str.trim();
let parts = str ? str.split(",") : [];
let valueArr = value instanceof Array ? value : value.split(",");
parts = parts.filter(function(e,i,a) {
return (hlpr.isInList(e,valueArr));
});
return parts.join(",");
};
/**
* From given URL returns hostname of the server
*
* @param url String with the investigated URL
* @return String with hostname
*/
hlpr.getHost = function(url) {
let aElem = jetpack.tabs.focused.contentWindow.document.createElement("a");
aElem.setAttribute("href",url);
return aElem.hostname;
}
/**
*
*/
hlpr.getBugNo = function(url) {
// let bugNoTitle = this.doc.querySelector("#title > p").textContent.trim();
// console.log("bugNoTitle = " + bugNoTitle);
// this.bugNo = this.getBugNo(this.doc.location.toString());
let re = new RegExp(".*id=([0-9]+).*$");
let bugNo = null;
let reResult = re.exec(url);
if (reResult[1]) {
bugNo = reResult[1];
}
return bugNo;
};
/**
* Load text from URL
*
* @param URL String
* @param cb_function Function to be called when the download happens with
* the downloaded body of the HTTP message as the only parameter
* @param what optional Object argument representing this for this call
* @return none
*/
hlpr.loadText = function(URL, cb_function, what) {
if (what === undefined) { // missing optional argument
what = this;
}
let 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("");
};
/**
* Load JSON from URL
*
* @param URL String
* @param cb_function Function to be called when the download happens with
* the downloaded JSON as the only parameter
* @param what optional Object argument representing this for this call
* @return none
*/
hlpr.loadJSON = function(URL, cb_function, what) {
if (what === undefined) { // missing optional argument
what = this;
}
console.log("URL = " + URL);
hlpr.loadText(URL, function(text) {
let data = JSON.parse(text);
cb_function.call(what, data);
}, what);
};
// ============================================================================
// 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() {
let rH = Number(this.r.toFixed()).toString(16);
let gH = Number(this.g.toFixed()).toString(16);
let 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() {
let r = this.r / 255;
let g = this.g / 255;
let b = this.b / 255;
let max = Math.max(r, g, b), min = Math.min(r, g, b);
let h, s, l = (max + min) / 2;
if (max === min) {
h = s = 0; // achromatic
} else {
let 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;
}
let r, g, b;
if (s === 0) {
r = g = b = l; // achromatic
} else {
let q = l < 0.5 ? l * (1 + s) : l + s - l * s;
let 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() {
let r = this.r / 255;
let g = this.g / 255;
let b = this.b / 255;
let max = Math.max(r, g, b), min = Math.min(r, g, b);
let h, s, v = max;
let 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) {
let r, g, b;
let i = Math.floor(h * 6);
let f = h * 6 - i;
let p = v * (1 - s);
let q = v * (1 - f * s);
let 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() {
let hslArray = this.hsl();
let h = Number(hslArray[0]);
let s = Number(hslArray[1]) * this.Desaturated;
let l = this.Luminosity;
let desA = this.hslToRgb(h, s, l);
return new Color(desA[0], desA[1], desA[2]);
};
// ====================================================================================
// Logger
function Logger(store, abbsMap) {
this.EmptyLogsColor = new Color(0, 255, 0);
this.FullLogsColor = new Color(0, 40, 103);
this.store = store;
this.abbsMap = abbsMap;
};
Logger.prototype.addLogRecord = function(that) {
let rec = {};
rec.date = new Date();
rec.url = that.doc.location.toString();
rec.title = that.title;
let comment = jetpack.tabs.focused.contentWindow.prompt(
"Enter comments for this comment").trim();
if (comment.length > 0) {
console.log("comment = " + comment);
console.log("");
rec.comment = comment;
let recKey = hlpr.getISODate(rec.date) + "+" + hlpr.getHost(rec.url)
+ "+" + that.bugNo;
let clearLogAElem = that.doc.getElementById("clearLogs");
clearLogAElem.style.color = this.FullLogsColor;
clearLogAElem.style.fontWeight = "bolder";
if (this.store[recKey]) {
this.store[recKey].comment += " \n" + comment;
} else {
this.store[recKey] = rec;
}
jetpack.storage.simple.sync();
return rec;
} else {
return comment;
}
};
Logger.prototype.getBugzillaAbbr = function(url) {
// for https://bugzilla.redhat.com/show_bug.cgi?id=579123 get RH
// for https://bugzilla.mozilla.org/show_bug.cgi?id=579123 get MoFo
var abbr = this.abbsMap[hlpr.getHost(url)];
console.log("abbr = " + abbr);
return abbr;
}
Logger.prototype.timeSheetRecordsPrinter = function(body, records) {
let that = this;
let commentBugRE = new RegExp("[bB]ug\\s+([0-9]+)","g");
console.log("body = " + body);
console.log("records = " + records);
// sort the records into temporary array
let tmpArr = [];
for ( let i in records) {
if (records.hasOwnProperty(i)) {
tmpArr.push( [ i, records[i] ]);
}
}
tmpArr.sort(function(a, b) {
return a[0] > b[0] ? 1 : -1;
});
let currentDay = "";
// now print the array
tmpArr.forEach(function(rec) {
let x = rec[1];
let dayStr = hlpr.getISODate(x.date);
let host = hlpr.getHost(x.url);
let BZName = that.getBugzillaAbbr(x.url);
let bugNo = hlpr.getBugNo(x.url);
if (dayStr != currentDay) {
currentDay = dayStr;
body.innerHTML += "
" + currentDay
+ "
";
}
console.log("comment = " + x.comment);
// replace "bug ####" with a hyperlink to the current bugzilla
let comment = x.comment.replace(commentBugRE,
"$&");
console.log("comment = " + comment);
body.innerHTML += "
";
});
};
/**
*
*/
Logger.prototype.createBlankPage = function (ttl, bodyBuildCB) {
let title = ttl || "Yet another untitled page";
let that = this;
let logTab = jetpack.tabs.open("about:blank");
jetpack.tabs.onReady(function() {
let otherDoc = logTab.contentDocument;
otherDoc.title = title;
otherDoc.body.innerHTML = "
" + title + "
";
bodyBuildCB.call(that, otherDoc.body);
logTab.focus();
});
};
Logger.prototype.generateTimeSheet = function(body) {
let doc = body.ownerDocument;
console.log("doc = " + doc);
this.timeSheetRecordsPrinter(body, this.store);
};
// ====================================================================================
// BZPage's methods
function BZPage(doc) {
// constants
this.SalmonPink = new Color(255, 224, 176); // RGB 255, 224, 176; HSL 36, 2,
// 85
this.ReporterColor = new Color(255, 255, 166); // RGB 255, 255, 166; HSL 60, 2,
// 83
this.RE = {
Comment: new RegExp("^\\s*#"), // unsused
BlankLine: new RegExp("^\\s*$"), // unused
// nová řádka
// [ 65.631] (--) intel(0): Chipset: "845G"
Chipset: new RegExp("^\\s*\\[?[ 0-9.]*\\]?\\s*\\(--\\) "+
"([A-Za-z]+)\\([0-9]?\\): Chipset: (.*)$"),
ATIgetID: new RegExp("^.*\\(ChipID = 0x([0-9a-fA-F]+)\\).*$"),
Abrt: new RegExp("^\\s*\\[abrt\\]"),
signalHandler: new RegExp("^\\s*#[0-9]*\\s*"),
frameNo: new RegExp("^\\s*#([0-9]*)\\s")
};
// initialize dynamic properties
this.doc = doc;
this.packages = this.getInstalledPackages();
if ("commentStrings" in config.gJSONData) {
this.commentStrings = config.gJSONData.commentStrings;
}
if ("constantData" in config.gJSONData) {
this.constantData = config.gJSONData.constantData;
}
if ("CCmaintainer" in config.gJSONData.constantData) {
this.defBugzillaMaintainerArr = config.gJSONData.constantData.CCmaintainer;
}
if ("submitsLogging" in config.gJSONData.configData &&
config.gJSONData.configData.submitsLogging) {
this.log = config.logger;
this.setUpLogging();
}
this.submitHandlerInstalled = false;
this.bugNo = hlpr.getBugNo(this.doc.location.toString());
this.reporter = this.getReporter();
this.product = this.getOptionValue("product");
this.component = this.getOptionValue("component");
this.version = this.getVersion();
this.title = this.doc.getElementById("short_desc_nonedit_display").textContent;
this.CCList = this.getCCList();
this.generateButtons();
}
/**
*
*/
BZPage.prototype.getInstalledPackages = function() {
let installedPackages = {};
if (config.gJSONData && ("commentPackages" in config.gJSONData)) {
let enabledPackages = jetpack.storage.settings.enabledPacks.split(/[, ]/);
for each (let pkg in enabledPackages) {
if (pkg in config.gJSONData.commentPackages) {
installedPackages[pkg] = config.gJSONData.commentPackages[pkg];
}
}
}
return installedPackages;
};
/**
* Actual execution function
*
* @param cmdLabel String with the name of the command to be executed
* @param cmdParams Object with the appropriate parameters for the command
*/
BZPage.prototype.centralCommandDispatch = function (cmdLabel, cmdParams) {
console.log("BZPage centralCommandDispatch\n"+
"cmdLabel = " + cmdLabel + ", cmdParams = " + cmdParams);
switch (cmdLabel) {
// FIXME we don't have guaranteed order of execution, but
// for example resolution can be executed only after status;
// we may need to eliminate these two commands and replace them
// with "closed", "assigned", etc. with parameter resolution?
// or only "closed" and other commands which require resolution?
case "resolution":
case "product":
case "component":
case "version":
case "priority":
this.selectOption(cmdLabel, cmdParams);
break;
case "status":
this.selectOption("bug_status", cmdParams);
break;
case "platform":
this.selectOption("rep_platform", cmdParams);
break;
case "os":
this.selectOption("op_sys", cmdParams);
break;
case "severity":
this.selectOption("bug_severity", cmdParams);
break;
case "target":
this.selectOption("target_milestone", cmdParams);
break;
case "addKeyword":
this.addStuffToTextBox("keywords",cmdParams);
break;
case "removeKeyword":
this.removeStuffFromTextBox("keywords", cmdParams);
break;
case "addWhiteboard":
this.addStuffToTextBox("status_whiteboard",cmdParams);
break;
case "removeWhiteboard":
this.removeStuffFromTextBox("status_whiteboard",cmdParams);
break;
case "assignee":
this.changeAssignee(cmdParams);
break;
case "qacontact":
this.clickMouse("bz_qa_contact_edit_action");
this.doc.getElementById("qa_contact").value = cmdParams;
break;
case "url":
this.clickMouse("bz_url_edit_action");
this.doc.getElementById("bug_file_loc").value = cmdParams;
break;
// TODO dependson/blocked doesn't work. Find out why.
case "addDependsOn":
this.clickMouse("dependson_edit_action");
this.addStuffToTextBox("dependson", cmdParams);
break;
case "removeDependsOn":
this.clickMouse("dependson_edit_action");
this.removeStuffFromTextBox("dependson", cmdParams);
break;
case "addBlocks":
this.clickMouse("blocked_edit_action");
this.addStuffToTextBox("blocked", cmdParams);
break;
case "removeBlocks":
this.clickMouse("blocked_edit_action");
this.removeStuffFromTextBox("blocked", cmdParams)
break;
case "comment":
this.addStuffToTextBox("comment", cmdParams);
break;
case "commentIdx":
console.log("commentIdx: idx = " + cmdParams);
let commentText = this.commentStrings[cmdParams];
console.log("comment = " + commentText);
this.addStuffToTextBox("comment", commentText);
break;
case "setNeedinfo":
// cmdParams are actually ignored for now; we may in future
// distinguish different actors to be target of needinfo
this.setNeedinfoReporter();
break;
case "addCC":
this.addToCCList(cmdParams);
break;
// TODO flags, see also
case "commit":
if (cmdParams) {
// Directly commit the form
this.doc.forms.namedItem("changeform").submit();
}
break;
}
};
/**
* Take the ID of the package/id combination, and execute it
*
* @param String combined package + "//" + id combination
* Fetches the command object from this.installedPackages and then
* goes through all commands contained in it, and calls
* this.centralCommandDispatch to execute them.
*/
BZPage.prototype.executeCommand = function(cmd) {
console.log("executeCommand / cmd = " + cmd);
let [pkg, id] = cmd.split("//");
let commentObj = this.packages[pkg][id];
console.log("commentObj:\n" + commentObj.toSource()+
"\n----------------------------------");
for (let key in commentObj) {
console.log("key = " + key +
"\ncommentObj = " + commentObj.toSource());
this.centralCommandDispatch(key,commentObj[key]);
}
};
/**
* Add XGL to the CC list
*
* @param evt Event which made this function active
* @return none
*/
BZPage.prototype.changeAssignee = function(newAssignee) {
let defAssigneeButton = null;
this.addToCCList(this.owner);
if (newAssignee === null) {
this.doc.getElementById("set_default_assignee").removeAttribute(
"checked");
return ;
}
if (this.getDefaultAssignee) {
if (newAssignee == "default") {
let defAss = this.getDefaultAssignee();
console.log("defAss = " + defAss);
if (defAss) {
newAssignee = defAss;
} else {
return ;
}
}
}
if (newAssignee) {
this.clickMouse("bz_assignee_edit_action");
this.doc.getElementById("assigned_to").value = newAssignee;
this.doc.getElementById("set_default_assignee").checked = false;
if (defAssigneeButton = this.doc
.getElementById("setDefaultAssignee_btn")) {
defAssigneeButton.style.display = "none";
}
}
};
/**
* Adds new option to the 'comment_action' scroll down box
*
* @param pkg String package name
* @param cmd String with the name of the command
* If the 'comment_action' scroll down box doesn't exist, this
* function will set up new one.
*/
BZPage.prototype.addToCommentsDropdown = function(pkg, cmd) {
let select = this.doc.getElementById("comment_action");
if (!select) {
let that = this;
this.doc.getElementById("comments").innerHTML +=
"
" +
" " +
"
";
select = this.doc.getElementById("comment_action");
select.addEventListener("change", function () {
let valueElement = that.doc.getElementById("comment_action");
if (valueElement) {
let value = valueElement.getAttribute("value");
} else {
return;
}
that.executeCommand(value);
}, false);
}
let opt = that.doc.createElement("option");
opt.value = pkg + "//" + cmd;
opt.textContent = this.packages[pkg][cmd].name;
select.appendChild(opt);
};
/**
* Generic function to add new button to the page. Actually copies new button
* from the old one (in order to have the same look-and-feel, etc.
*
* @param location Object around which the new button will be added
* @param after Boolean before or after location ?
* @param pkg String which package to take the command from
* @param id String which command to take
* @return none
*/
BZPage.prototype.createNewButton = function(location, after, pkg, id) {
let that = this;
let cmdObj = this.packages[pkg][id];
let newId = id + "_btn";
let label = cmdObj["name"];
// protection against double-firings
if (this.doc.getElementById(newId)) {
console.error("Element with id " + newId + "already exists!");
return ;
}
// creation of button might be conditional on existence of data in constantData
if ("ifExist" in cmdObj) {
if (!(cmdObj["ifExist"] in this.constantData)) {
return ;
}
}
let newButton = this.doc.createElement("input");
newButton.setAttribute("id", newId);
newButton.setAttribute("type", "button");
newButton.value = label;
newButton.addEventListener("click", function(evt) {
that.executeCommand(pkg + "//" + id);
}, false);
let originalLocation = this.doc.getElementById(location);
if (after) {
originalLocation.parentNode.insertBefore(newButton,
originalLocation.nextSibling);
originalLocation.parentNode.insertBefore(this.doc
.createTextNode("\u00A0"), newButton);
} else {
originalLocation.parentNode.insertBefore(newButton, originalLocation);
originalLocation.parentNode.insertBefore(this.doc
.createTextNode("\u00A0"), originalLocation);
}
};
/**
*
*/
BZPage.prototype.generateButtons = function() {
let topRowPosition = "topRowPositionID";
let bottomRowPosition = "commit";
// create anchor for the top toolbar
let commentBox = this.doc.getElementById("comment");
let brElement = this.doc.createElement("br");
brElement.setAttribute("id",topRowPosition);
commentBox.parentNode.normalize();
commentBox.parentNode.insertBefore(brElement, commentBox);
for (let pkg in this.packages) {
for (let cmdIdx in this.packages[pkg]) {
let cmdObj = this.packages[pkg][cmdIdx];
switch (cmdObj.position) {
case "topRow":
this.createNewButton(topRowPosition, false, pkg, cmdIdx);
break;
case "bottomRow":
this.createNewButton(bottomRowPosition, false, pkg, cmdIdx);
break;
case "dropDown":
this.addToCommentsDropdown(pkg,cmdIdx);
break;
default: // [+-]ID
let firstChr = cmdObj.position.charAt(0);
let newId = cmdObj.position.substr(1);
this.createNewButton(newId, firstChr == "+", pkg, cmdIdx);
break;
}
}
}
};
/**
* Get the current email of the reporter of the bug.
*
* @return string
*/
BZPage.prototype.getReporter = function() {
return this.doc
.querySelector("#bz_show_bug_column_2 > table .vcard:first-of-type > a").textContent;
};
/**
* Get the current version of the Fedora release ... even if changed meanwhile
* by bug triager.
*
* @return string (integer for released Fedora, float for RHEL, rawhide)
*/
BZPage.prototype.getVersion = function() {
let verStr = this.getOptionValue("version").toLowerCase();
let verNo = 0;
if (/rawhide/.test(verStr)) {
verNo = 999;
} else {
verNo = Number(verStr);
}
return verNo;
};
BZPage.prototype.commentsWalker = function(fce) {
let comments = this.doc.getElementById("comments").getElementsByClassName(
"bz_comment");
Array.forEach(comments, function(item) {
fce(item);
}, this);
};
/**
* Set background color of all comments made by reporter in ReporterColor color
*
*/
BZPage.prototype.checkComments = function() {
let that = this;
this.commentsWalker(function(x) {
let email = x.getElementsByClassName("vcard")[0]
.getElementsByTagName("a")[0].textContent;
if (new RegExp(that.reporter).test(email)) {
x.style.backgroundColor = that.ReporterColor.toString();
}
});
};
BZPage.prototype.collectComments = function() {
let outStr = "";
this.commentsWalker(function(x) {
outStr += x.getElementsByTagName("pre")[0].textContent + "\n";
});
return outStr.trim();
};
/**
* Select option with given label on the