From 9279d5bc3902ab0b18af3a3f8440b8ddd884dec5 Mon Sep 17 00:00:00 2001 From: Matěj Cepl Date: Thu, 17 Jun 2010 18:20:03 +0200 Subject: Inheritance works, now there are jetpack-prototype-related bugs --- lib/bzpage.js | 748 +++++++++++++++++++++++++++++++++++++++- lib/clipboard.js | 125 +++++++ lib/color.js | 236 +++++++++++++ lib/logger.js | 114 ++++++ lib/main.js | 75 +++- lib/mozillabzpage.js | 16 + lib/offline-support.js | 153 ++++++++ lib/puvodni/bzpage.js | 729 --------------------------------------- lib/puvodni/clipboard.js | 125 ------- lib/puvodni/color.js | 236 ------------- lib/puvodni/logger.js | 108 ------ lib/puvodni/mozillabzpage.js | 14 - lib/puvodni/offline-support.js | 153 -------- lib/puvodni/old-main.js | 106 ------ lib/puvodni/skip-process-bug.js | 46 --- lib/puvodni/xmlrpc.js | 168 --------- lib/skip-process-bug.js | 46 +++ lib/xmlrpc.js | 168 +++++++++ 18 files changed, 1661 insertions(+), 1705 deletions(-) create mode 100644 lib/clipboard.js create mode 100644 lib/color.js create mode 100644 lib/logger.js create mode 100644 lib/mozillabzpage.js create mode 100644 lib/offline-support.js delete mode 100644 lib/puvodni/bzpage.js delete mode 100644 lib/puvodni/clipboard.js delete mode 100644 lib/puvodni/color.js delete mode 100644 lib/puvodni/logger.js delete mode 100644 lib/puvodni/mozillabzpage.js delete mode 100644 lib/puvodni/offline-support.js delete mode 100644 lib/puvodni/old-main.js delete mode 100644 lib/puvodni/skip-process-bug.js delete mode 100644 lib/puvodni/xmlrpc.js create mode 100644 lib/skip-process-bug.js create mode 100644 lib/xmlrpc.js (limited to 'lib') diff --git a/lib/bzpage.js b/lib/bzpage.js index dcaa43c..329165d 100644 --- a/lib/bzpage.js +++ b/lib/bzpage.js @@ -6,25 +6,757 @@ var util = require("util"); var apiUtils = require("api-utils"); var simpleStorage = require("simple-storage"); +var Color = require("color").Color; + +var TriagedDistro = 13; +var NumberOfFrames = 7; +var XMLRPCurl = "https://bugzilla.redhat.com/xmlrpc.cgi"; +var bugURL = "https://bugzilla.redhat.com/show_bug.cgi?id="; // ==================================================================================== // BZPage's methods - var BZPage = function BZPage(doc, config) { + console.log("doc = " + doc); + console.log("config = " + 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.doc = doc; - console.log("Now we are inside!"); - console.log("this = " + this); -}; + this.doc = doc; + this.packages = this.getInstalledPackages(config); + if ("commentStrings" in config.gJSONData) { + this.commentStrings = config.gJSONData.commentStrings; + } + + + if ("constantData" in config.gJSONData) { + // this is wrong, I shouldn't bother anybody with my Xorg data, and + // I should load it from URL + // var stuff = JSON.parse(self.data.load("chiIDsGroupings.json")); + 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 = 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(); + + this.packages = this.getInstalledPackages(); + + if ("commentStrings" in config.gJSONData) { + this.commentStrings = config.gJSONData.commentStrings; + } + + if ("constantData" in config.gJSONData) { + // this is wrong, I shouldn't bother anybody with my Xorg data, and + // I should load it from URL + // var stuff = JSON.parse(self.data.load("chiIDsGroupings.json")); + this.constantData = config.gJSONData.constantData; + } + + if ("CCmaintainer" in config.gJSONData.constantData) { + this.defBugzillaMaintainerArr = config.gJSONData.constantData.CCmaintainer; + } -BZPage.prototype.getURL = function getURL () { - console.log("url = " + this.doc.location.href); - return this.doc.location.href; + if ("submitsLogging" in config.gJSONData.configData && + config.gJSONData.configData.submitsLogging) { + this.log = config.logger; + this.setUpLogging(); + } + + 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(config) { + var installedPackages = {}; + if (config.gJSONData && ("commentPackages" in config.gJSONData)) { + var enabledPackages = jetpack.storage.settings.enabledPacks.split(/[, ]/); + enabledPackages.forEach(function (pkg, idx, arr) { + 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 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 += + "
" + + " " + + " element with given id. + * + * Also execute change HTMLEvent, so that the form behaves accordingly. + * + * @param id + * @param label + * @return none + * + * FIXME bugzilla-comments version has this signature: + * selectOption = function selectOption(select, value) { + var doc = select[0].ownerDocument; + select.val(value); + */ +BZPage.prototype.selectOption = function selectOption (id, label) { + var sel = this.doc.getElementById(id); + sel.value = label; + var intEvent = this.doc.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 + */ +BZPage.prototype.clickMouse = function clickMouse (targetID) { + var localEvent = this.doc.createEvent("MouseEvents"); + localEvent.initMouseEvent("click", true, true, this.doc.defaultView, 0, 0, + 0, 0, 0, false, false, false, false, 0, null); + this.doc.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 + */ +BZPage.prototype.addStuffToTextBox = function addStuffToTextBox (id, stuff) { + var textBox = this.doc.getElementById(id); + if (textBox.tagName.toLowerCase() === "textarea") { + stuff = textBox.value ? "\n\n" + stuff : stuff; + textBox.value += stuff; + } else { + textBox.value = util.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 + */ +BZPage.prototype.removeStuffFromTextBox = function removeStuffFromTextBox (id, stuff) { + var changedElement = this.getElementById(id); + changedElement.value = util.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? + */ +BZPage.prototype.idContainsWord = function idContainsWord (id, str) { + var kwd = ""; + try { + kwd = this.doc.getElementById(id).value; + } catch (e) { + // For those who don't have particular element at all or if it is empty + return false; + } + return (kwd.trim().indexOf(str) !== -1); +}; + +/** + * Check for the presence of a keyword + * + * @param str String with the keyword + * @return Boolean + */ +BZPage.prototype.hasKeyword = function hasKeyword (str) { + return (this.idContainsWord('keywords', str)); +}; + +/** + * + */ +BZPage.prototype.getOptionValue = function getOptionValue (id) { + // Some special bugs don't have version for example + var element = this.doc.getElementById(id); + if (element) { + return element.value; + } else { + console.log("Failed to find element with id = " + id); + return "#NA"; + } +}; + +/** + * 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] + */ +BZPage.prototype.setNeedinfoReporter = function setNeedinfoReporter () { + this.clickMouse("needinfo"); + this.selectOption("needinfo_role", "reporter"); +}; + +/** + * + */ +BZPage.prototype.getOwner = function getOwner () { + var priorityParent = this.doc.querySelector("label[for~='target_milestone']") + .parentNode.parentNode.parentNode; + var assigneeAElement = priorityParent.querySelector("tr:nth-of-type(1) a.email"); + var assgineeHref = decodeURI(assigneeAElement.getAttribute("href")); + var email = assgineeHref.split(":")[1]; + return email; +}; + +/** + * Get login of the currently logged-in user. + * + * @return String with the login name of the currently logged-in user + */ +BZPage.prototype.getLogin = function getLogin () { + var lastLIElement = this.doc.querySelector("#header ul.links li:last-of-type"); + var loginArr = lastLIElement.textContent.split("\n"); + var loginStr = loginArr[loginArr.length - 1].trim(); + return loginStr; +}; + +/** + * 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 + */ +BZPage.prototype.getDefaultBugzillaMaintainer = function getDefaultBugzillaMaintainer (component) { + var address = util.filterByRegexp(this.defBugzillaMaintainerArr, component); + return address; +}; + +/** + * 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 + */ +BZPage.prototype.getAttachments = function getAttachments () { + var outAtts = []; + var atts = this.doc.getElementById("attachment_table") + .getElementsByTagName("tr"); + for ( var i = 1, ii = atts.length - 1; i < ii; i++) { + outAtts.push(this.parseAttachmentLine(atts[i])); + } + return outAtts; +}; + +/** + * returns password from the current storage, or if there isn't + * one, then it will ask user for it. + * + * @return String with the password + */ +BZPage.prototype.getPassword = function getPassword () { + if (jetpack.storage.settings.BZpassword) { + return jetpack.storage.settings.BZpassword; + } else { + var prompts = Components.classes["@mozilla.org/embedcomp/prompt-service;1"] + .getService(Components.interfaces.nsIPromptService); + var password = { + value : "" + }; // default the password to pass + var check = { + value : true + }; // default the checkbox to true + var result = prompts.promptPassword(null, "Title", "Enter password:", + password, null, check); + // result is true if OK was pressed, false if cancel was pressed. + // password.value is + // set if OK was pressed. The checkbox is not displayed. + if (result) { + var passwordText = password.value; + jetpack.storage.settings.BZpassword = passwordText; + jetpack.storage.simple.sync(); + return passwordText; + } + } + return null; +}; + +/** + * + */ +BZPage.prototype.setUpLogging = function setUpLogging () { + // For adding additional buttons to the top toolbar + var additionalButtons = this.doc.querySelector("#bugzilla-body *.related_actions"); + var that = this; + + // logging all submits for timesheet + // FIXME we should merge in functionality of RHBugzillaPage.submitCallback + // and actually make it working + // Maybe rewriting whole offline capability into a separate object? + if (!this.submitHandlerInstalled) { + console.log("Installing submit callback!"); + this.doc.forms.namedItem("changeform").addEventListener("submit",function (evt) { + console.log("Submit callback!"); + var resp = that.log.addLogRecord(that); + console.log("resp = " + resp); + if (resp === null) { + console.log("Avoiding submitting!"); + // FIXME doesn't work ... still submitting' + evt.stopPropagation(); + evt.preventDefault(); + } + }, false); + this.submitHandlerInstalled = true; + } + + var generateTimeSheetUI = this.doc.createElement("li"); + generateTimeSheetUI.innerHTML = "\u00A0-\u00A0" + + "Generate timesheet"; + additionalButtons.appendChild(generateTimeSheetUI); + this.doc.getElementById("generateTSButton").addEventListener( + "click", + function(evt) { + that.log.createBlankPage.call(that.log, "TimeSheet", + that.log.generateTimeSheet); + evt.stopPropagation(); + evt.preventDefault(); + }, false); + + var clearLogsUI = this.doc.createElement("li"); + clearLogsUI.innerHTML = "\u00A0-\u00A0" + + "Clear logs"; + additionalButtons.appendChild(clearLogsUI); + var clearLogAElem = this.doc.getElementById("clearLogs"); + clearLogAElem.addEventListener("click", function() { + that.log.store = {}; + jetpack.storage.simple.sync(); + this.style.color = that.log.EmptyLogsColor; + this.style.fontWeight = "normal"; + console.log("this.store wiped out!"); + }, false); + + if (!this.log.store) { + console.log("No this.store defined!"); + this.log.store = {}; + } + + if (this.log.store.length > 0) { + clearLogAElem.style.color = this.log.FullLogsColor; + clearLogAElem.style.fontWeight = "bolder"; + } else { + clearLogAElem.style.color = this.log.EmptyLogsColor; + clearLogAElem.style.fontWeight = "normal"; + } +}; + +/** + * 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 + */ +BZPage.prototype.addToCCList = function addToCCList (who) { + if (!who) { + return ; + } + if (who === "self") { + this.doc.getElementById("addselfcc").checked = true; + } else { + this.clickMouse("cc_edit_area_showhide"); + if (!util.isInList(who, this.CCList)) { + this.addStuffToTextBox("newcc",who); + } + } +}; + +/** + * a collect a list of emails on CC list + * + * @return Array with email addresses as Strings. + */ +BZPage.prototype.getCCList = function getCCList () { + var CCListSelect = this.doc.getElementById("cc"); + outCCList = []; + if (CCListSelect) { + outCCList = Array.map(CCListSelect.options, function(item) { + return item.value; + }); + } + return outCCList; +}; + //exports.BZPage = apiUtils.publicConstructor(BZPage); exports.BZPage = BZPage; \ No newline at end of file diff --git a/lib/clipboard.js b/lib/clipboard.js new file mode 100644 index 0000000..0051a55 --- /dev/null +++ b/lib/clipboard.js @@ -0,0 +1,125 @@ +// Released under the MIT/X11 license +// http://www.opensource.org/licenses/mit-license.php + +/** + * returns content of the system clipboard + * @return string with the content of the clipboard or "" if empty. + * originally from + * https://developer.mozilla.org/en/Using_the_Clipboard + * https://wiki.mozilla.org/Labs/Jetpack/JEP/10 + */ + +function getClipboard() { + var clip = Cc["@mozilla.org/widget/clipboard;1"]. + getService(Ci.nsIClipboard); + if (!clip) { + throw new Error("No access to the clipboard!"); + } + return clip; +} + +function createTransferable() { + var trans = Cc["@mozilla.org/widget/transferable;1"]. + createInstance(Ci.nsITransferable); + if (!trans) { + throw new Error("No access to the transfer object during the set of clipboard!"); + } + return trans; +} + +var getMethod = exports.get = function getMethod( flavor ) { + var pastetext = "", mimeType = "", stuff = {}; + var len = 0, clipId = 0, clip = {}, trans = {}; + + // flavor argument is optional + if (flavor === undefined) { + flavor = "plain"; + } + + if (flavor === "plain") { + mimeType = "text/unicode"; + } else if (favor === "html") { + mimeType = "text/html"; + } else { + throw new Error("Unsupported flavor '" + flavor + "'!"); + } + + clip = getClipboard(); + + trans = createTransferable(); + + trans.addDataFlavor(mimeType); + clip.getData(trans, clip.kGlobalClipboard); + + var str = {}; + var strLength = {}; + + trans.getTransferData(mimeType, str, strLength); + + if (str) { + str = str.value.QueryInterface(Ci.nsISupportsString); + pastetext = str.data.substring(0, strLength.value / 2); + } + return pastetext; +}; + +var setMethod = exports.set = function setMethod(content, flavor) { + var mimeType = "", stuff = {}; + var len = 0, clipId = 0, clip = {}, trans = {}; + + // flavor argument is optional + if (flavor === undefined) { + flavor = "plain"; + } + + if (flavor === "plain") { + mimeType = "text/unicode"; + } else if (favor === "html") { + mimeType = "text/html"; + } else { + throw new Error("Unsupported flavor '" + flavor + "'!"); + } + + stuff = Cc["@mozilla.org/supports-string;1"]. + createInstance(Ci.nsISupportsString); + if (!stuff) { + return false; + } + stuff.data = content; + len = content.length * 2; + + clip = getClipboard(); + + trans = createTransferable(); + + trans.addDataFlavor(mimeType); + trans.setTransferData(mimeType, stuff, content.length * 2); + + clip.setData(trans, null, clip.kGlobalClipboard); + return true; +}; + +var flavorsMethod = exports.getCurrentFlavors = function flavorsMethod(test) { + // currently the only possible flavors in Jetpack-prototype are "plain" and + // "html", i.e., "text/plain" (or text/unicode?) and "text/html" (or + // application/xml+xhtml?) + var possibleTypes = { + "text/unicode": "plain", + "text/plain": "plain", + "text/html": "html" + }; + var flavorArray = []; + var clip = getClipboard(); + + for (var flavor in possibleTypes) { + var presentFlavor = clip.hasDataMatchingFlavors( + [flavor], + 1, + clip.kGlobalClipboard + ); + if (presentFlavor) { + flavorArray.push(possibleTypes[flavor]) + } + } + return flavorArray; +}; \ No newline at end of file diff --git a/lib/color.js b/lib/color.js new file mode 100644 index 0000000..2da2fa7 --- /dev/null +++ b/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 +var Color = exports.Color = 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]); +}; diff --git a/lib/logger.js b/lib/logger.js new file mode 100644 index 0000000..b817056 --- /dev/null +++ b/lib/logger.js @@ -0,0 +1,114 @@ +// Released under the MIT/X11 license +// http://www.opensource.org/licenses/mit-license.php +"use strict"; +var urlMod = require("url"); +var urilMod = require("util"); +var Color = require("color").Color; + +var Logger = exports.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"); + if (comment && comment.length > 0) { + comment = comment.trim(); + rec.comment = comment; + let recKey = utilMod.getISODate(rec.date) + "+" + + urlMod.parse(rec.url).host + + "+" + that.bugNo; + console.log("rec = " + rec.toSource()); + + let clearLogAElem = that.doc.getElementById("clearLogs"); + if (clearLogAElem.style.color != this.FullLogsColor) { + 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 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[urlMod.parse(url).host]; + return abbr; +} + +Logger.prototype.timeSheetRecordsPrinter = function(body, records) { + let that = this; + let commentBugRE = new RegExp("[bB]ug\\s+([0-9]+)","g"); + // 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 = utilMod.getISODate(x.date); + let host = urlMod.parse(x.url).host; + let BZName = that.getBugzillaAbbr(x.url); + let bugNo = utilMod.getBugNo(x.url); + if (dayStr != currentDay) { + currentDay = dayStr; + body.innerHTML += "

" + currentDay + + "

"; + } + // replace "bug ####" with a hyperlink to the current bugzilla + let comment = x.comment.replace(commentBugRE, + "$&"); + body.innerHTML += "

Bug " + + BZName + "/" + bugNo + ": " + + x.title + + "" + + " \n
" + comment + "

"; + }); +}; + +/** + * + */ +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; + this.timeSheetRecordsPrinter(body, this.store); +}; diff --git a/lib/main.js b/lib/main.js index cb803f8..f083b3f 100644 --- a/lib/main.js +++ b/lib/main.js @@ -13,44 +13,95 @@ // "use strict"; var util = require("util"); -var file = require("file"); +var logger = require("logger"); var myStorage = require("simple-storage").storage; var browser = require("tab-browser"); var JSONURL = "http://matej.ceplovi.cz/progs/data/RH_Data-packages.json"; -var config = {}; +var TriagedDistro = 13; +var NumberOfFrames = 7; +var XMLRPCurl = "https://bugzilla.redhat.com/xmlrpc.cgi"; -var matches = [ - "https://bugzilla.redhat.com/show_bug.cgi.*", - "https://bugzilla.mozilla.org/show_bug.cgi.*" +var config = {}; +config.matches = [ + "https://bugzilla.redhat.com/show_bug.cgi", + "https://bugzilla.mozilla.org/show_bug.cgi" ]; -function initialize(callback) { - util.loadJSON(JSONURL, function(parsedData) { - config = {}; - callback(config); - }, this); -} +// ============================================================== +// https://wiki.mozilla.org/Labs/Jetpack/JEP/24 +var WillBemanifest = { + 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" : "" + } + ] +}; +// TODO: sometime in the future we should include +// also skip-process.js functionality and these URLs +// "https://bugzilla.redhat.com/process_bug.cgi", +// "https://bugzilla.redhat.com/post_bug.cgi", +// "https://bugzilla.mozilla.org/post_bug.cgi", +// "https://bugzilla.mozilla.org/process_bug.cgi" function isOurPage(window, matchingURLs) { if ("window" in window) { window = window.window; } var url = window.location.href; + // like ["regexp-url1", "regexp-url2"] return matchingURLs.some(function (element,i,a) { return new RegExp(element).test(url); }); } +function initialize(callback) { + util.loadJSON(JSONURL, function(parsedData) { + config.gJSONData = parsedData; + + var keys = "", key = ""; + for (key in config.gJSONData) { + keys += key + " "; + } + console.log("loaded JSON object keys: " + keys); + + // Get card translation table + if ("PCIIDsURL" in config.gJSONData.configData) { + util.loadJSON(config.gJSONData.configData.PCIIDsURL, function(response) { + config.PCI_ID_Array = response; + }); + } + + config.logger = new logger.Logger(myStorage.logs, + config.gJSONData.constantData.bugzillalabelAbbreviations); + + callback(config); + }, this); +} + exports.main = function main(options, callbacks) { initialize(function (config) { browser.whenContentLoaded( function(window) { var doc = window.document; var construct = require("rhbzpage").RHBugzillaPage; - if (isOurPage(window, matches)) { + if (isOurPage(window, config.matches)) { var curPage = new construct(doc, config); } else { console.log("Not our page: " + window.location.href); diff --git a/lib/mozillabzpage.js b/lib/mozillabzpage.js new file mode 100644 index 0000000..add0a3d --- /dev/null +++ b/lib/mozillabzpage.js @@ -0,0 +1,16 @@ +// Released under the MIT/X11 license +// http://www.opensource.org/licenses/mit-license.php +"use strict"; +var utilMod = require("util"); + +// ============================================================================ +// MozillaBugzilla object + +var MozillaBugzilla = function MozillaBugzilla (doc, config) { + BZPage.call(this, doc, config) +}; + +MozillaBugzilla.prototype = utilMod.heir(BZPage); +MozillaBugzilla.prototype.constructor = MozillaBugzilla; + +exports.MozillaBugzilla = MozillaBugzilla; \ No newline at end of file diff --git a/lib/offline-support.js b/lib/offline-support.js new file mode 100644 index 0000000..4849bd3 --- /dev/null +++ b/lib/offline-support.js @@ -0,0 +1,153 @@ +/*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"; + +/* Offline supporting functions */ +/** + * + * @todo FIXME this probably makes a closure and a memory leak name='changeform' + * investigate + * https://developer.mozilla.org/en/How_to_Turn_Off_Form_Autocompletion + * + *
+ * + * Reading + * http://www.w3.org/TR/1999/REC-html401-19991224/interact/forms.html#h-17.13 + * random notes: - 17.13.3 provides all steps necessary - enctype != + * application/x-www-form-urlencoded => SHOULD fails (no further questions + * needed) - http://www.w3.org/MarkUp/html-spec/html-spec_8.html#SEC8.2.1. is + * nice explanation (albeit quite dated) - on multiple values + * http://www.w3.org/TR/1999/REC-html401-19991224/interact/forms.html#h-17.6.1 - + * příliš jednoduché + * http://www.innovation.ch/java/HTTPClient/emulating_forms.html - + */ +RHBugzillaPage.prototype.serializeForm = function(form) { + let serialForm = { + dataOut : "", + name : form.name, + method : form.method, + acceptCharset : form.acceptCharset, + action : form.action, // TODO shouldn't we get a non-relative URL? + enctype : form.enctype, + cookie : this.doc.cookie, + autocomplete : form.getAttribute("autocomplete"), + bugNo : this.bugNo + }; + + function genURIElement(sName, sValue) { + return encodeURIComponent(sName) + "=" + encodeURIComponent(sValue); + } + + /** + * @param o + * control to be serialized + * @return String with the serialized control + */ + function serializeControl(element) { + let val = element.value; + // console.log("val.toSource() = " + val.toSource()); + /* + * on HTMLSelectElement we have an attribute 'type' of type DOMString, + * readonly The type of this form control. This is the string + * "select-multiple" when the multiple attribute is true and the string + * "select-one" when false. + */ + if ((val == null) || (val == undefined) || (val == "")) { + return; + } else if (val instanceof Array) { + return val.map(function(x) { + return genURIElement(element.name, x.value); + }).join("&"); + } else if (val instanceof String) { + return genURIElement(element.name, val); + } else { // assume HTMLCollection + return Array.map(val, function(x) { + return genURIElement(element.name, x.value); + }).join("&"); + } + } + + serialForm.dataOut = Array.filter(form.elements,function(el) { + return !el.disabled && el.name && + // FIXME shouldn't I just add && el.value here? + (el.checked || /select|textarea/i.test(el.nodeName) || + /text|hidden|password|search/i.test(el.type)); + }).map(serializeControl).join("&"); + return serialForm; +}; + +//RHBugzillaPage.prototype.submitCallback = function(evt) { +// console.log("Submit Callback!"); +// if (jetpack.__parent__.navigator.onLine) { +// let serForm = this +// .serializeForm(jetpack.tabs.focused.contentWindow.document.forms +// .namedItem("changeform")); +//// console.log("serForm:\n" + serForm.toSource()); +// } else { +// let serForm = this +// .serializeForm(jetpack.tabs.focused.contentWindow.document.forms +// .namedItem("changeform")); +// myStorage.forms[this.bugNo] = serForm; +// evt.stopPropagation(); +// evt.preventDefault(); +// } +//}; + +/** + * + * + * Yes, this is correct, this is NOT method of RHBugzillaPage! + */ +/*function onlineCallback() { + function deserializeAndSend(formData) { + // FIXME notImplemented + // is it enough to just + // run XMLHttpRequest? Probably yes, this is just a form + // and this is just a HTTP request + // it is probably better to get already processed + // application/x-www-form-urlencoded + // see http://htmlhelp.com/reference/html40/forms/form.html for details + // and also https://developer.mozilla.org/en/AJAX/Getting_Started + // what's? + // https://developer.mozilla.org/en/Core_JavaScript_1.5_Reference\ + // /Global_Functions/encodeURI & co. + // this seems to be also interesting + // https://developer.mozilla.org/en/Code_snippets/Post_data_to_window + console.error("Sending bugs not implemented yet!"); + return ""; // FIXME check other HTTP headers to be set + + let bugID = formData.bugNo; + let req = new XMLHttpRequest(); + req.open("POST", formData.action, true); + // FIXME co očekávám za odpověď? req.overrideMimeType("text/xml"); + // * Accept-Encoding + // * Accept-Language + // * Accept (MIME types) + req.setRequestHeader("Connection", "keep-alive"); + req.setRequestHeader("Keep-Alive", 300); + req.setRequestHeader("Content-Type", formData.enctype); + req.setRequestHeader("Referer", bugURL + bugID); + req.setRequestHeader("Accept-Charset", formData.acceptCharset); + req.setRequestHeader("Cookie", formData.cookie); + req.onreadystatechange = function(aEvt) { + if (req.readyState == 4) { + if (req.status == 200) { + console.log("Sent form for bug " + bugID); + delete myStorage.forms[bugID]; + } else { + console.error("Sending form for bug " + bugID + "failed!"); + } + } + }; + req.send(formData.data); + } + + if (myStorage.forms.length > 0) { + myStorage.forms.forEach(function(x) { + deserializeAndSend(x); + }); + } +} +*/ \ No newline at end of file diff --git a/lib/puvodni/bzpage.js b/lib/puvodni/bzpage.js deleted file mode 100644 index 12b2fb0..0000000 --- a/lib/puvodni/bzpage.js +++ /dev/null @@ -1,729 +0,0 @@ -/*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"; -var util = require("util"); -var simpleStorage = require("simple-storage"); -var Color = require("color").Color; - -var TriagedDistro = 13; -var NumberOfFrames = 7; -var XMLRPCurl = "https://bugzilla.redhat.com/xmlrpc.cgi"; -var bugURL = "https://bugzilla.redhat.com/show_bug.cgi?id="; - -// ==================================================================================== -// BZPage's methods - -var BZPage = exports.BZPage = function BZPage(doc, config) { - console.log("doc = " + doc); - console.log("config = " + config); - // 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.doc = doc; - console.log("this.doc = " + this.doc); - - this.submitHandlerInstalled = false; - this.bugNo = util.getBugNo(this.doc.location.toString()); - - var keys = ""; - for (var key in this.prototype) { - keys += key + ", "; - } - console.log("keys = " + keys); - - 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.packages = this.getInstalledPackages(); - - if ("commentStrings" in config.gJSONData) { - this.commentStrings = config.gJSONData.commentStrings; - } - - if ("constantData" in config.gJSONData) { - // this is wrong, I shouldn't bother anybody with my Xorg data, and - // I should load it from URL - // var stuff = JSON.parse(self.data.load("chiIDsGroupings.json")); - 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.generateButtons(); -}; - -/** - * - */ -BZPage.prototype.getInstalledPackages = function getInstalledPackages() { - var installedPackages = {}; - if (config.gJSONData && ("commentPackages" in config.gJSONData)) { - var enabledPackages = jetpack.storage.settings.enabledPacks.split(/[, ]/); - enabledPackages.forEach(function (pkg, idx, arr) { - 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 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 += - "
" + - " " + - " element with given id. - * - * Also execute change HTMLEvent, so that the form behaves accordingly. - * - * @param id - * @param label - * @return none - * - * FIXME bugzilla-comments version has this signature: - * selectOption = function selectOption(select, value) { - var doc = select[0].ownerDocument; - select.val(value); - */ -BZPage.prototype.selectOption = function selectOption (id, label) { - var sel = this.doc.getElementById(id); - sel.value = label; - var intEvent = this.doc.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 - */ -BZPage.prototype.clickMouse = function clickMouse (targetID) { - var localEvent = this.doc.createEvent("MouseEvents"); - localEvent.initMouseEvent("click", true, true, this.doc.defaultView, 0, 0, - 0, 0, 0, false, false, false, false, 0, null); - this.doc.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 - */ -BZPage.prototype.addStuffToTextBox = function addStuffToTextBox (id, stuff) { - var textBox = this.doc.getElementById(id); - if (textBox.tagName.toLowerCase() === "textarea") { - stuff = textBox.value ? "\n\n" + stuff : stuff; - textBox.value += stuff; - } else { - textBox.value = util.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 - */ -BZPage.prototype.removeStuffFromTextBox = function removeStuffFromTextBox (id, stuff) { - var changedElement = this.getElementById(id); - changedElement.value = util.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? - */ -BZPage.prototype.idContainsWord = function idContainsWord (id, str) { - var kwd = ""; - try { - kwd = this.doc.getElementById(id).value; - } catch (e) { - // For those who don't have particular element at all or if it is empty - return false; - } - return (kwd.trim().indexOf(str) !== -1); -}; - -/** - * Check for the presence of a keyword - * - * @param str String with the keyword - * @return Boolean - */ -BZPage.prototype.hasKeyword = function hasKeyword (str) { - return (this.idContainsWord('keywords', str)); -}; - -/** - * - */ -BZPage.prototype.getOptionValue = function getOptionValue (id) { - // Some special bugs don't have version for example - var element = this.doc.getElementById(id); - if (element) { - return element.value; - } else { - console.log("Failed to find element with id = " + id); - return "#NA"; - } -}; - -/** - * 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] - */ -BZPage.prototype.setNeedinfoReporter = function setNeedinfoReporter () { - this.clickMouse("needinfo"); - this.selectOption("needinfo_role", "reporter"); -}; - -/** - * - */ -BZPage.prototype.getOwner = function getOwner () { - var priorityParent = this.doc.querySelector("label[for~='target_milestone']") - .parentNode.parentNode.parentNode; - var assigneeAElement = priorityParent.querySelector("tr:nth-of-type(1) a.email"); - var assgineeHref = decodeURI(assigneeAElement.getAttribute("href")); - var email = assgineeHref.split(":")[1]; - return email; -}; - -/** - * Get login of the currently logged-in user. - * - * @return String with the login name of the currently logged-in user - */ -BZPage.prototype.getLogin = function getLogin () { - var lastLIElement = this.doc.querySelector("#header ul.links li:last-of-type"); - var loginArr = lastLIElement.textContent.split("\n"); - var loginStr = loginArr[loginArr.length - 1].trim(); - return loginStr; -}; - -/** - * 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 - */ -BZPage.prototype.getDefaultBugzillaMaintainer = function getDefaultBugzillaMaintainer (component) { - var address = util.filterByRegexp(this.defBugzillaMaintainerArr, component); - return address; -}; - -/** - * 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 - */ -BZPage.prototype.getAttachments = function getAttachments () { - var outAtts = []; - var atts = this.doc.getElementById("attachment_table") - .getElementsByTagName("tr"); - for ( var i = 1, ii = atts.length - 1; i < ii; i++) { - outAtts.push(this.parseAttachmentLine(atts[i])); - } - return outAtts; -}; - -/** - * returns password from the current storage, or if there isn't - * one, then it will ask user for it. - * - * @return String with the password - */ -BZPage.prototype.getPassword = function getPassword () { - if (jetpack.storage.settings.BZpassword) { - return jetpack.storage.settings.BZpassword; - } else { - var prompts = Components.classes["@mozilla.org/embedcomp/prompt-service;1"] - .getService(Components.interfaces.nsIPromptService); - var password = { - value : "" - }; // default the password to pass - var check = { - value : true - }; // default the checkbox to true - var result = prompts.promptPassword(null, "Title", "Enter password:", - password, null, check); - // result is true if OK was pressed, false if cancel was pressed. - // password.value is - // set if OK was pressed. The checkbox is not displayed. - if (result) { - var passwordText = password.value; - jetpack.storage.settings.BZpassword = passwordText; - jetpack.storage.simple.sync(); - return passwordText; - } - } - return null; -}; - -/** - * - */ -BZPage.prototype.setUpLogging = function setUpLogging () { - // For adding additional buttons to the top toolbar - var additionalButtons = this.doc.querySelector("#bugzilla-body *.related_actions"); - var that = this; - - // logging all submits for timesheet - // FIXME we should merge in functionality of RHBugzillaPage.submitCallback - // and actually make it working - // Maybe rewriting whole offline capability into a separate object? - if (!this.submitHandlerInstalled) { - console.log("Installing submit callback!"); - this.doc.forms.namedItem("changeform").addEventListener("submit",function (evt) { - console.log("Submit callback!"); - var resp = that.log.addLogRecord(that); - console.log("resp = " + resp); - if (resp === null) { - console.log("Avoiding submitting!"); - // FIXME doesn't work ... still submitting' - evt.stopPropagation(); - evt.preventDefault(); - } - }, false); - this.submitHandlerInstalled = true; - } - - var generateTimeSheetUI = this.doc.createElement("li"); - generateTimeSheetUI.innerHTML = "\u00A0-\u00A0" - + "Generate timesheet"; - additionalButtons.appendChild(generateTimeSheetUI); - this.doc.getElementById("generateTSButton").addEventListener( - "click", - function(evt) { - that.log.createBlankPage.call(that.log, "TimeSheet", - that.log.generateTimeSheet); - evt.stopPropagation(); - evt.preventDefault(); - }, false); - - var clearLogsUI = this.doc.createElement("li"); - clearLogsUI.innerHTML = "\u00A0-\u00A0" - + "Clear logs"; - additionalButtons.appendChild(clearLogsUI); - var clearLogAElem = this.doc.getElementById("clearLogs"); - clearLogAElem.addEventListener("click", function() { - that.log.store = {}; - jetpack.storage.simple.sync(); - this.style.color = that.log.EmptyLogsColor; - this.style.fontWeight = "normal"; - console.log("this.store wiped out!"); - }, false); - - if (!this.log.store) { - console.log("No this.store defined!"); - this.log.store = {}; - } - - if (this.log.store.length > 0) { - clearLogAElem.style.color = this.log.FullLogsColor; - clearLogAElem.style.fontWeight = "bolder"; - } else { - clearLogAElem.style.color = this.log.EmptyLogsColor; - clearLogAElem.style.fontWeight = "normal"; - } -}; - -/** - * 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 - */ -BZPage.prototype.addToCCList = function addToCCList (who) { - if (!who) { - return ; - } - if (who === "self") { - this.doc.getElementById("addselfcc").checked = true; - } else { - this.clickMouse("cc_edit_area_showhide"); - if (!util.isInList(who, this.CCList)) { - this.addStuffToTextBox("newcc",who); - } - } -}; - -/** - * a collect a list of emails on CC list - * - * @return Array with email addresses as Strings. - */ -BZPage.prototype.getCCList = function getCCList () { - var CCListSelect = this.doc.getElementById("cc"); - outCCList = []; - if (CCListSelect) { - outCCList = Array.map(CCListSelect.options, function(item) { - return item.value; - }); - } - return outCCList; -}; diff --git a/lib/puvodni/clipboard.js b/lib/puvodni/clipboard.js deleted file mode 100644 index 0051a55..0000000 --- a/lib/puvodni/clipboard.js +++ /dev/null @@ -1,125 +0,0 @@ -// Released under the MIT/X11 license -// http://www.opensource.org/licenses/mit-license.php - -/** - * returns content of the system clipboard - * @return string with the content of the clipboard or "" if empty. - * originally from - * https://developer.mozilla.org/en/Using_the_Clipboard - * https://wiki.mozilla.org/Labs/Jetpack/JEP/10 - */ - -function getClipboard() { - var clip = Cc["@mozilla.org/widget/clipboard;1"]. - getService(Ci.nsIClipboard); - if (!clip) { - throw new Error("No access to the clipboard!"); - } - return clip; -} - -function createTransferable() { - var trans = Cc["@mozilla.org/widget/transferable;1"]. - createInstance(Ci.nsITransferable); - if (!trans) { - throw new Error("No access to the transfer object during the set of clipboard!"); - } - return trans; -} - -var getMethod = exports.get = function getMethod( flavor ) { - var pastetext = "", mimeType = "", stuff = {}; - var len = 0, clipId = 0, clip = {}, trans = {}; - - // flavor argument is optional - if (flavor === undefined) { - flavor = "plain"; - } - - if (flavor === "plain") { - mimeType = "text/unicode"; - } else if (favor === "html") { - mimeType = "text/html"; - } else { - throw new Error("Unsupported flavor '" + flavor + "'!"); - } - - clip = getClipboard(); - - trans = createTransferable(); - - trans.addDataFlavor(mimeType); - clip.getData(trans, clip.kGlobalClipboard); - - var str = {}; - var strLength = {}; - - trans.getTransferData(mimeType, str, strLength); - - if (str) { - str = str.value.QueryInterface(Ci.nsISupportsString); - pastetext = str.data.substring(0, strLength.value / 2); - } - return pastetext; -}; - -var setMethod = exports.set = function setMethod(content, flavor) { - var mimeType = "", stuff = {}; - var len = 0, clipId = 0, clip = {}, trans = {}; - - // flavor argument is optional - if (flavor === undefined) { - flavor = "plain"; - } - - if (flavor === "plain") { - mimeType = "text/unicode"; - } else if (favor === "html") { - mimeType = "text/html"; - } else { - throw new Error("Unsupported flavor '" + flavor + "'!"); - } - - stuff = Cc["@mozilla.org/supports-string;1"]. - createInstance(Ci.nsISupportsString); - if (!stuff) { - return false; - } - stuff.data = content; - len = content.length * 2; - - clip = getClipboard(); - - trans = createTransferable(); - - trans.addDataFlavor(mimeType); - trans.setTransferData(mimeType, stuff, content.length * 2); - - clip.setData(trans, null, clip.kGlobalClipboard); - return true; -}; - -var flavorsMethod = exports.getCurrentFlavors = function flavorsMethod(test) { - // currently the only possible flavors in Jetpack-prototype are "plain" and - // "html", i.e., "text/plain" (or text/unicode?) and "text/html" (or - // application/xml+xhtml?) - var possibleTypes = { - "text/unicode": "plain", - "text/plain": "plain", - "text/html": "html" - }; - var flavorArray = []; - var clip = getClipboard(); - - for (var flavor in possibleTypes) { - var presentFlavor = clip.hasDataMatchingFlavors( - [flavor], - 1, - clip.kGlobalClipboard - ); - if (presentFlavor) { - flavorArray.push(possibleTypes[flavor]) - } - } - return flavorArray; -}; \ No newline at end of file diff --git a/lib/puvodni/color.js b/lib/puvodni/color.js deleted file mode 100644 index 2da2fa7..0000000 --- a/lib/puvodni/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 -var Color = exports.Color = 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]); -}; diff --git a/lib/puvodni/logger.js b/lib/puvodni/logger.js deleted file mode 100644 index cc3f213..0000000 --- a/lib/puvodni/logger.js +++ /dev/null @@ -1,108 +0,0 @@ -// Released under the MIT/X11 license -// http://www.opensource.org/licenses/mit-license.php -"use strict"; -var urlMod = require("url"); -var urilMod = require("util"); - -var Logger = exports.Logger = function Logger(store, abbsMap) { - - 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"); - if (comment && comment.length > 0) { - comment = comment.trim(); - rec.comment = comment; - let recKey = utilMod.getISODate(rec.date) + "+" - + urlMod.parse(rec.url).host - + "+" + that.bugNo; - let clearLogAElem = that.doc.getElementById("clearLogs"); - 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[urlMod.parse(url).host]; - return abbr; -} - -Logger.prototype.timeSheetRecordsPrinter = function(body, records) { - let that = this; - let commentBugRE = new RegExp("[bB]ug\\s+([0-9]+)","g"); - // 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 = utilMod.getISODate(x.date); - let host = urlMod.parse(x.url).host; - let BZName = that.getBugzillaAbbr(x.url); - let bugNo = utilMod.getBugNo(x.url); - if (dayStr != currentDay) { - currentDay = dayStr; - body.innerHTML += "

" + currentDay - + "

"; - } - // replace "bug ####" with a hyperlink to the current bugzilla - let comment = x.comment.replace(commentBugRE, - "$&"); - body.innerHTML += "

Bug " - + BZName + "/" + bugNo + ": " - + x.title - + "" - + " \n
" + comment + "

"; - }); -}; - -/** - * - */ -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; - this.timeSheetRecordsPrinter(body, this.store); -}; diff --git a/lib/puvodni/mozillabzpage.js b/lib/puvodni/mozillabzpage.js deleted file mode 100644 index 7efaf16..0000000 --- a/lib/puvodni/mozillabzpage.js +++ /dev/null @@ -1,14 +0,0 @@ -// Released under the MIT/X11 license -// http://www.opensource.org/licenses/mit-license.php -"use strict"; -var utilMod = require("util"); - -// ============================================================================ -// MozillaBugzilla object - -exports.MozillaBugzilla = function MozillaBugzilla (doc, config) { - BZPage.call(this, doc, config) -}; - -MozillaBugzilla.prototype = utilMod.heir(BZPage); -MozillaBugzilla.prototype.constructor = MozillaBugzilla; diff --git a/lib/puvodni/offline-support.js b/lib/puvodni/offline-support.js deleted file mode 100644 index 4849bd3..0000000 --- a/lib/puvodni/offline-support.js +++ /dev/null @@ -1,153 +0,0 @@ -/*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"; - -/* Offline supporting functions */ -/** - * - * @todo FIXME this probably makes a closure and a memory leak name='changeform' - * investigate - * https://developer.mozilla.org/en/How_to_Turn_Off_Form_Autocompletion - * - * - * - * Reading - * http://www.w3.org/TR/1999/REC-html401-19991224/interact/forms.html#h-17.13 - * random notes: - 17.13.3 provides all steps necessary - enctype != - * application/x-www-form-urlencoded => SHOULD fails (no further questions - * needed) - http://www.w3.org/MarkUp/html-spec/html-spec_8.html#SEC8.2.1. is - * nice explanation (albeit quite dated) - on multiple values - * http://www.w3.org/TR/1999/REC-html401-19991224/interact/forms.html#h-17.6.1 - - * příliš jednoduché - * http://www.innovation.ch/java/HTTPClient/emulating_forms.html - - */ -RHBugzillaPage.prototype.serializeForm = function(form) { - let serialForm = { - dataOut : "", - name : form.name, - method : form.method, - acceptCharset : form.acceptCharset, - action : form.action, // TODO shouldn't we get a non-relative URL? - enctype : form.enctype, - cookie : this.doc.cookie, - autocomplete : form.getAttribute("autocomplete"), - bugNo : this.bugNo - }; - - function genURIElement(sName, sValue) { - return encodeURIComponent(sName) + "=" + encodeURIComponent(sValue); - } - - /** - * @param o - * control to be serialized - * @return String with the serialized control - */ - function serializeControl(element) { - let val = element.value; - // console.log("val.toSource() = " + val.toSource()); - /* - * on HTMLSelectElement we have an attribute 'type' of type DOMString, - * readonly The type of this form control. This is the string - * "select-multiple" when the multiple attribute is true and the string - * "select-one" when false. - */ - if ((val == null) || (val == undefined) || (val == "")) { - return; - } else if (val instanceof Array) { - return val.map(function(x) { - return genURIElement(element.name, x.value); - }).join("&"); - } else if (val instanceof String) { - return genURIElement(element.name, val); - } else { // assume HTMLCollection - return Array.map(val, function(x) { - return genURIElement(element.name, x.value); - }).join("&"); - } - } - - serialForm.dataOut = Array.filter(form.elements,function(el) { - return !el.disabled && el.name && - // FIXME shouldn't I just add && el.value here? - (el.checked || /select|textarea/i.test(el.nodeName) || - /text|hidden|password|search/i.test(el.type)); - }).map(serializeControl).join("&"); - return serialForm; -}; - -//RHBugzillaPage.prototype.submitCallback = function(evt) { -// console.log("Submit Callback!"); -// if (jetpack.__parent__.navigator.onLine) { -// let serForm = this -// .serializeForm(jetpack.tabs.focused.contentWindow.document.forms -// .namedItem("changeform")); -//// console.log("serForm:\n" + serForm.toSource()); -// } else { -// let serForm = this -// .serializeForm(jetpack.tabs.focused.contentWindow.document.forms -// .namedItem("changeform")); -// myStorage.forms[this.bugNo] = serForm; -// evt.stopPropagation(); -// evt.preventDefault(); -// } -//}; - -/** - * - * - * Yes, this is correct, this is NOT method of RHBugzillaPage! - */ -/*function onlineCallback() { - function deserializeAndSend(formData) { - // FIXME notImplemented - // is it enough to just - // run XMLHttpRequest? Probably yes, this is just a form - // and this is just a HTTP request - // it is probably better to get already processed - // application/x-www-form-urlencoded - // see http://htmlhelp.com/reference/html40/forms/form.html for details - // and also https://developer.mozilla.org/en/AJAX/Getting_Started - // what's? - // https://developer.mozilla.org/en/Core_JavaScript_1.5_Reference\ - // /Global_Functions/encodeURI & co. - // this seems to be also interesting - // https://developer.mozilla.org/en/Code_snippets/Post_data_to_window - console.error("Sending bugs not implemented yet!"); - return ""; // FIXME check other HTTP headers to be set - - let bugID = formData.bugNo; - let req = new XMLHttpRequest(); - req.open("POST", formData.action, true); - // FIXME co očekávám za odpověď? req.overrideMimeType("text/xml"); - // * Accept-Encoding - // * Accept-Language - // * Accept (MIME types) - req.setRequestHeader("Connection", "keep-alive"); - req.setRequestHeader("Keep-Alive", 300); - req.setRequestHeader("Content-Type", formData.enctype); - req.setRequestHeader("Referer", bugURL + bugID); - req.setRequestHeader("Accept-Charset", formData.acceptCharset); - req.setRequestHeader("Cookie", formData.cookie); - req.onreadystatechange = function(aEvt) { - if (req.readyState == 4) { - if (req.status == 200) { - console.log("Sent form for bug " + bugID); - delete myStorage.forms[bugID]; - } else { - console.error("Sending form for bug " + bugID + "failed!"); - } - } - }; - req.send(formData.data); - } - - if (myStorage.forms.length > 0) { - myStorage.forms.forEach(function(x) { - deserializeAndSend(x); - }); - } -} -*/ \ No newline at end of file diff --git a/lib/puvodni/old-main.js b/lib/puvodni/old-main.js deleted file mode 100644 index db81448..0000000 --- a/lib/puvodni/old-main.js +++ /dev/null @@ -1,106 +0,0 @@ -/*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 -// -// Links to read through -// http://ehsanakhgari.org/blog/2010-01-07/bugzilla-tweaks-enhanced -// http://hg.mozilla.org/users/ehsan.akhgari_gmail.com/extensions/file/tip/bugzillatweaks -// http://hg.mozilla.org/users/ehsan.akhgari_gmail.com/extensions/file/ecfa0f028b81/bugzillatweaks/lib/main.js -// http://hg.mozilla.org/users/avarma_mozilla.com/atul-packages/file/42ac1e99a107/packages\ -// /facebook-acquaintances/lib/main.js#l11 -// http://ehsanakhgari.org/blog/2010-05-31/my-experience-jetpack-sdk#comment-1253 -// -"use strict"; -var util = require("util"); -var logger = require("logger"); -var file = require("file"); -var myStorage = require("simple-storage").storage; - -var TriagedDistro = 13; -var NumberOfFrames = 7; -var XMLRPCurl = "https://bugzilla.redhat.com/xmlrpc.cgi"; - -let config = {}; -config.matches = [ - "https://bugzilla.redhat.com/show_bug.cgi", - "https://bugzilla.mozilla.org/show_bug.cgi" -]; - - -// ============================================================== -// 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"); - -// ///////////////////////////////////////////////////////////////////////////// -function isOurPage(window) { - if ("window" in window) { - window = window.window; - } - - if (window.location.protocol == "https:") { - // like ["name1": "url1", "name2":"url2"] - // FIXME the real name of bugzillaPageModLocation array - for (var loc in bugzillaPageModLocation) { - if (bugzillaPageModLocation[loc].test(window.location.href)) { - return true; - } - } - } - // we haven't found a conforming bugzilla - return false; -} - - -function initialize() { - -} - -util.loadJSON(jetpack.storage.settings.JSONURL, function(parsedData) { - config.gJSONData = parsedData; - - // Get card translation table - let keys = ""; - for (let key in config.gJSONData) { - keys += key + " "; - } - if ("PCIIDsURL" in config.gJSONData.configData) { - util.loadJSON(config.gJSONData.configData.PCIIDsURL, function(response) { - config.PCI_ID_Array = response; - }); - } - - config.logger = new logger.Logger(myStorage.logs, - config.gJSONData.constantData.bugzillalabelAbbreviations); - - let callback = function(doc) { - if (config.gJSONData.configData.objectStyle = "RH") { - let curPage = new RHBugzillaPage(doc); - } else if (config.gJSONData.configData.objectStyle = "MoFo") { - let curPage = new MozillaBugzilla(doc); - } - }; - - jetpack.pageMods.add(callback, config); -}, this); diff --git a/lib/puvodni/skip-process-bug.js b/lib/puvodni/skip-process-bug.js deleted file mode 100644 index 3f82578..0000000 --- a/lib/puvodni/skip-process-bug.js +++ /dev/null @@ -1,46 +0,0 @@ -jetpack.future.import("pageMods"); - -// http://maymay.net/blog/2008/06/15/\ -// ridiculously-simple-javascript-version-string-to-object-parser/ -function parseVersionString (str) { - if (typeof(str) != 'string') { return false; } - var x = str.split('.'); - // parse from string or default to 0 if can't parse - var maj = parseInt(x[0]) || 0; - var min = parseInt(x[1]) || 0; - var pat = parseInt(x[2]) || 0; - return { - major: maj, - minor: min, - patch: pat - }; -} - -var callback = function(document){ - var stemURL = "https://HOSTNAME/show_bug.cgi?id="; - var titleStr = $("title",document).text(); - var REArr = RegExp("[0-9]+").exec(titleStr); - var REHostname = RegExp("\/\/([^/]+)\/").exec(document.location.toString()); - if (REArr) { - var bugNo = REArr[0]; - var hostname = REHostname[1]; - console.log("bugNo = " + bugNo + ", hostname = " + hostname); - var currentFFVersion = parseVersionString(jetpack.__parent__.navigator.vendorSub); - console.log("currentFFVersion = " + currentFFVersion.toSource()); - if ((currentFFVersion.major >= 3) && (currentFFVersion.minor >= 6)) { - var emailsSent = $("#bugzilla-body > dl:first",document).text(); - emailsSent = emailsSent.replace(/^(\s*)$/mg,""); - jetpack.notifications.show(emailsSent); - } - document.location = stemURL.replace("HOSTNAME",hostname) + bugNo; - } -}; - -var options = {}; -options.matches = [ - "https://bugzilla.redhat.com/process_bug.cgi", - "https://bugzilla.redhat.com/post_bug.cgi", - "https://bugzilla.mozilla.org/post_bug.cgi", - "https://bugzilla.mozilla.org/process_bug.cgi" - ]; -jetpack.pageMods.add(callback, options); diff --git a/lib/puvodni/xmlrpc.js b/lib/puvodni/xmlrpc.js deleted file mode 100644 index 69bb77e..0000000 --- a/lib/puvodni/xmlrpc.js +++ /dev/null @@ -1,168 +0,0 @@ -// Released under the MIT/X11 license -// http://www.opensource.org/licenses/mit-license.php -"use strict"; -/* - * - * 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. - * - */ - -var XMLRPCMessage = exports.XMLRPCMessage = 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 + ""; - 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; -}; diff --git a/lib/skip-process-bug.js b/lib/skip-process-bug.js new file mode 100644 index 0000000..3f82578 --- /dev/null +++ b/lib/skip-process-bug.js @@ -0,0 +1,46 @@ +jetpack.future.import("pageMods"); + +// http://maymay.net/blog/2008/06/15/\ +// ridiculously-simple-javascript-version-string-to-object-parser/ +function parseVersionString (str) { + if (typeof(str) != 'string') { return false; } + var x = str.split('.'); + // parse from string or default to 0 if can't parse + var maj = parseInt(x[0]) || 0; + var min = parseInt(x[1]) || 0; + var pat = parseInt(x[2]) || 0; + return { + major: maj, + minor: min, + patch: pat + }; +} + +var callback = function(document){ + var stemURL = "https://HOSTNAME/show_bug.cgi?id="; + var titleStr = $("title",document).text(); + var REArr = RegExp("[0-9]+").exec(titleStr); + var REHostname = RegExp("\/\/([^/]+)\/").exec(document.location.toString()); + if (REArr) { + var bugNo = REArr[0]; + var hostname = REHostname[1]; + console.log("bugNo = " + bugNo + ", hostname = " + hostname); + var currentFFVersion = parseVersionString(jetpack.__parent__.navigator.vendorSub); + console.log("currentFFVersion = " + currentFFVersion.toSource()); + if ((currentFFVersion.major >= 3) && (currentFFVersion.minor >= 6)) { + var emailsSent = $("#bugzilla-body > dl:first",document).text(); + emailsSent = emailsSent.replace(/^(\s*)$/mg,""); + jetpack.notifications.show(emailsSent); + } + document.location = stemURL.replace("HOSTNAME",hostname) + bugNo; + } +}; + +var options = {}; +options.matches = [ + "https://bugzilla.redhat.com/process_bug.cgi", + "https://bugzilla.redhat.com/post_bug.cgi", + "https://bugzilla.mozilla.org/post_bug.cgi", + "https://bugzilla.mozilla.org/process_bug.cgi" + ]; +jetpack.pageMods.add(callback, options); diff --git a/lib/xmlrpc.js b/lib/xmlrpc.js new file mode 100644 index 0000000..69bb77e --- /dev/null +++ b/lib/xmlrpc.js @@ -0,0 +1,168 @@ +// Released under the MIT/X11 license +// http://www.opensource.org/licenses/mit-license.php +"use strict"; +/* + * + * 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. + * + */ + +var XMLRPCMessage = exports.XMLRPCMessage = 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 + ""; + 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; +}; -- cgit