/*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 */
// Released under the MIT/X11 license
// http://www.opensource.org/licenses/mit-license.php
"use strict";
var util = require("util");
var apiUtils = require("api-utils");
var simpleStorage = require("simple-storage");
var preferences = require("preferences-service");
var Color = require("color").Color;
var TriagedDistro = 13;
var NumberOfFrames = 7;
var bugURL = "https://bugzilla.redhat.com/show_bug.cgi?id=";
// ====================================================================================
// BZPage's methods
var BZPage = function BZPage(win, config) {
var keys = "";
for (var key in config) {
keys += key + ", ";
}
console.log("config keys = " + keys);
// 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
// initialize dynamic properties
this.win = win;
this.doc = win.document;
this.packages = this.getInstalledPackages(config);
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 ("suspiciousComponents" in config.gJSONData.configData) {
this.suspiciousComponents = config.gJSONData.configData.suspiciousComponents;
}
if ("submitsLogging" in config.gJSONData.configData &&
config.gJSONData.configData.submitsLogging) {
this.log = config.logger;
console.log("length of this.log.store = " + this.log.getLength());
this.setUpLogging();
}
this.submitHandlerInstalled = false;
this.bugNo = util.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();
// Prepare for query buttons
// FIXME getting null for commentArea sometimes
var commentArea = this.doc.getElementById("comment_status_commit");
if (commentArea) {
var brElementPlacer = commentArea.getElementsByTagName("br");
brElementPlacer = brElementPlacer[0];
if (brElementPlacer) {
brElementPlacer.setAttribute("id","brElementPlacer_location");
brElementPlacer.parentNode.insertBefore(this.doc.createElement("br"),
brElementPlacer);
}
} else {
console.log("Cannot find element with 'comment_status_commit' ID!");
}
this.generateButtons();
};
/**
* Get the ID of the bug.
*
* @return string
*/
BZPage.prototype.getBugId = function getBugId () {
return util.getBugNo(this.doc.location.href);
};
/**
*
*/
BZPage.prototype.getInstalledPackages = function getInstalledPackages(cfg) {
var installedPackages = {};
if (cfg.gJSONData && ("commentPackages" in cfg.gJSONData)) {
// TODO: Temporary hack ... should be replaced by a proper configuration
// when replacement for jetpack.storage.settings lands
var enabledPackages = cfg.gJSONData.configData.enabledPacks.split(/[, ]/);
enabledPackages.forEach(function (pkg, idx, arr) {
if (pkg in cfg.gJSONData.commentPackages) {
installedPackages[pkg] = cfg.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 centralCommandDispatch (cmdLabel, cmdParams) {
switch (cmdLabel) {
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":
var commentText = this.commentStrings[cmdParams];
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 executeCommand (cmd) {
var cmdArr = cmd.split("//");
var commentObj = this.packages[cmdArr[0]][cmdArr[1]];
for (var key in commentObj) {
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 changeAssignee (newAssignee) {
var defAssigneeButton = null;
this.addToCCList(this.owner);
if (newAssignee === null) {
this.doc.getElementById("set_default_assignee").removeAttribute(
"checked");
return ;
}
if (this.getDefaultAssignee) {
if (newAssignee === "default") {
var defAss = this.getDefaultAssignee();
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;
defAssigneeButton = this.doc.getElementById("setDefaultAssignee_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.
*/
BZPage.prototype.addToCommentsDropdown = function addToCommentsDropdown (pkg, cmd) {
var select = this.doc.getElementById("comment_action");
if (!select) {
var that = this;
this.doc.getElementById("comments").innerHTML +=
"
" +
" " +
"
";
select = this.doc.getElementById("comment_action");
select.addEventListener("change", function () {
var value = "";
var valueElement = that.doc.getElementById("comment_action");
if (valueElement) {
value = valueElement.getAttribute("value");
} else {
return;
}
that.executeCommand(value);
}, false);
}
var opt = this.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 createNewButton (location, after, pkg, id) {
var that = this;
var cmdObj = this.packages[pkg][id];
var newId = id + "_btn";
var label = cmdObj.name;
// protection against double-firings
if (this.doc.getElementById(newId)) {
console.log("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 ;
}
}
var 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);
var 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 generateButtons () {
var topRowPosition = "topRowPositionID";
var bottomRowPosition = "commit";
// create anchor for the top toolbar
var commentBox = this.doc.getElementById("comment");
var brElement = this.doc.createElement("br");
brElement.setAttribute("id",topRowPosition);
commentBox.parentNode.normalize();
commentBox.parentNode.insertBefore(brElement, commentBox);
for (var pkg in this.packages) {
for (var cmdIdx in this.packages[pkg]) {
var 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
var firstChr = cmdObj.position.charAt(0);
var 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 getReporter () {
var reporterElement = this.doc.
querySelector("#bz_show_bug_column_2 > table .vcard:first-of-type > a");
if (reporterElement) {
return reporterElement.textContent;
}
return "";
};
/**
* 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 getVersion () {
var verStr = this.getOptionValue("version").toLowerCase();
var verNo = 0;
if (/rawhide/.test(verStr)) {
verNo = 999;
} else {
verNo = Number(verStr);
}
return verNo;
};
BZPage.prototype.commentsWalker = function commentsWalker (fce) {
var 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 checkComments () {
var that = this;
this.commentsWalker(function(x) {
var 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 collectComments () {
var outStr = "";
this.commentsWalker(function(x) {
outStr += x.getElementsByTagName("pre")[0].textContent + "\n";
});
return outStr.trim();
};
/**
* Select option with given label on the