// Released under the MIT/X11 license // http://www.opensource.org/licenses/mit-license.php jetpack.future.import("pageMods"); jetpack.future.import("storage.simple"); // Static values // ****************** STATIC DATA ************************* var XMLRPCurl = "https://bugzilla.redhat.com/xmlrpc.cgi"; var myStorage = jetpack.storage.simple; // CONFIGURE: The easiest method how to set up the configuration // value is to uncomment the following line with proper URL as // the second parameter. Then reload the bug page and comment out // again. //GM_setValue("JSONURL","URL-somewhere-with-your-JSON"); var jsonDataURL = ""; myStorage.JSONURL = "http://barstool.build.redhat.com/~mcepl/RH_Data.json"; if (myStorage.JSONURL) { jsonDataURL = myStorage.JSONURL; } if (!jsonDataURL) { jsonDataURL = "http://mcepl.fedorapeople.org/scripts/BugZappers_data.json"; } console.log("jsonDataURL = " + jsonDataURL); var PCIIDsURL = "http://mcepl.fedorapeople.org/scripts/drm_pciids.json"; //var debug = GM_getValue("debug",false); var debug = true; var reqCounter = 0; var msgStrs = {}; var RHColor = "#9E292B"; var FedoraColor = "#002867"; var RawhideColor = "#007700"; // or "green" var RHITColor = "#660066"; var SalmonPink = "#FFE0B0"; var CommentRe = RegExp("^\\s*#"); var BlankLineRe = RegExp("^\\s*$"); var ChipsetRE = RegExp("^\\(--\\) ([A-Za-z]+)\\([0-9]?\\): Chipset: (.*)$"); var ATIgetIDRE = RegExp("^.*\\(ChipID = 0x([0-9a-fA-F]+)\\).*$"); var PCI_ID_Array = []; // For identification of graphics card var manuChipStrs = [ ["ATI Radeon","ATI","1002"], ["ATI Mobility Radeon","ATI","1002"], ["Intel Corporation","INTEL","8086"], ["NVIDIA","NV","10de"] ]; var backTranslateManufacturerPCIID = [{ regexp: "ATI Technologies Inc", addr: "1002" },{ regexp: "Intel Corporation", addr: "8086" },{ regexp: "nVidia Corporation", addr: "10de" }]; // Get card translation table XMLHttpRequest({ // anything called inside of this Request cannot have variables set in MAIN method: 'GET', url: PCIIDsURL, onload: function(response) { PCI_ID_Array = JSON.parse(response.responseText); } }); // ************* FUNCTIONS ******************** /** * Clean the string from spaces around * * @param string to be cleaned * @return string which was cleaned * */ function trim(str,cleanRE) { var re = RegExp("^\\s*(.*)\\s*$"); if (cleanRE) { re = RegExp(cleanRE); } return str.replace(re,"$1"); } /** * select element of the array where regexp in the first element matches second parameter * of this function * @param list array with regexps and return values * @param chosingMark string by which the element of array is to be matched * @return string chosen element */ function filterByRegexp(list,chosingMark) { var chosenPair = []; if (list.length > 0) { chosenPair = list.filter( function(pair){ return RegExp(pair['regexp'],"i").test(chosingMark); }); }; if (chosenPair.length > 0) { return $.trim(chosenPair[0]['addr']); } else { return ""; } } /** * Converts attributes value of the given list of elements to the * Javascript list. * * @param list array of elements * @return array of values */ function valuesToList(list) { var outL = []; var member = ""; for (var element in list) { if (element.hasAttribute("value")) { outL[outL.length] = $.trim(element.getAttribute("value")); } } return outL; } /** * Check whether an item is member of the list. Idea is just to * make long if commands slightly more readable. * * @param mbr string to be searched in the list * @param list list * @return position of the string in the list, or -1 if none found. */ function isInList(mbr,list) { return(list.indexOf(mbr) !== -1); } /** * 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 * */ // TODO to-be-replaced by // var contents = jetpack.clipboard.get(); function getClipboardText() { this.netscape.security.PrivilegeManager.enablePrivilege('UniversalXPConnect'); var clip = Components.classes["@mozilla.org/widget/clipboard;1"].getService(Components.interfaces.nsIClipboard); if (!clip) return(false); ; var trans = Components.classes["@mozilla.org/widget/transferable;1"].createInstance(Components.interfaces.nsITransferable); if (!trans) return(false); trans.addDataFlavor("text/unicode"); clip.getData(trans, clip.kGlobalClipboard); var str = new {}; var strLength = new {}; trans.getTransferData("text/unicode", str, strLength); if (str) { str = str.value.QueryInterface(Components.interfaces.nsISupportsString); } if (str) { pastetext = str.data.substring(0, strLength.value / 2); } return pastetext; }; /* Bugzilla functions.*/ function bzPage(doc) { this.document = $(doc); this.hashBugzillaName = []; this.hashBugzillaWholeURL = []; this.defAssigneeList = []; this.signatureFedoraString = ""; // TODO we should have an array SpecialFlags instead of multiple Boolean variables this.queryButtonAvailable = false; this.chipIDsGroupings = []; this.AddrArray = []; // Initialize data from remote URL this.XMLHttpRequestDone = false; this.XorgLogAttList = []; this.XorgLogAttListIndex = 0; // Get JSON configuration data XMLHttpRequest({ // anything called inside of this Request cannot have variables set in MAIN method: 'GET', url: jsonDataURL, onload: function(response) { var data = JSON.parse(response.responseText); msgStrs = data['strings']; signatureFedoraString = data['signature']; hashBugzillaName = data['bugzillalabelNames']; hashBugzillaWholeURL = data['bugzillaIDURLs']; AddrArray = data['CCmaintainer']; defAssigneeList = data['defaultAssignee']; queryButtonAvailable = data['queryButton']; chipIDsGroupings = data['chipIDsGroupings']; buildButtons(data['topRow'],data['bottomRow']); if (signatureFedoraString.length > 0) { // (or a form named "changeform") document.forms[1].addEventListener("submit",addSignature,true); } } }); // FOR DEBUGGING ONLY!!! if (debug) { //console.log("signatureFedoraString = " + signatureFedoraString); $("#bz_field_status",this.document).append(""+jsonDataURL+""); } // *** collect information about the bug // var bugNo = getBugNo(); console.log("BBB"); var bugNo = $("#title > p:first",this.document).text(); console.log("BBB"); console.log(bugNo); this.reporter = $('#bz_show_bug_column_2 > .fn:first',this.document).text(); this.owner = $.trim($("#bz_assignee_edit_container > .fn:first",this.document).text()).toLowerCase(); this.CCList = $("select[name*='cc']:first > *[value]",this.document); console.log(typeof(this.CCList)); this.product = this.getProduct(); this.version = this.getVersion(); this.issueTracker = this.getIssueTracker(); this.maintCCAddr = this.getCCMaintainer(AddrArray); this.component = this.getComponent(); this.checkPrivateValues(); this.login = this.getLogin(); this.password = myStorage.BZpassword; //*** set the main environment this.setBranding(product,version,issueTracker); // fix process.cgi HREFs so that to avoid confusion on IRC if (this.document.location.href.search(/process.cgi/) != -1) { this.fixAllHrefs(bugNo); } this.fixAttachments(); this.originalButton = this.document("#commit"); // source button to be copied from this.originalButton.attr("accesskey",'s'); this.originalButton.attr("value","Submit"); } /** * This function creates a new anchor element and uses location properties (inherent) * to get the desired URL data. Some String operations are used (to normalize results * across browsers). * originally from http://snipplr.com/view.php?codeview&id=12659 * * @param url String with URL * @return object with parameters set * */ function bzPage.prototype.parseURL(url) { var a = $('',this.document).get(0); return { source: url, protocol: a.protocol.replace(':',''), host: a.hostname, port: a.port, query: a.search, params: (function(){ var ret = {}, seg = a.search.replace(/^\?/,'').split('&'), len = seg.length, i = 0, s; for (;i 0) && ($.trim(cmntText.value).length > 0)) { cmntText.value = $.trim(cmntText.value) + this.signatureFedoraString; } } /** * Get list of attachments to the bug * * @return array of attachments */ function bzPage.prototype.fixAttachments(){ /** * Parse the row with the attachment * @param element to be parsed * @return array with string name of the attachment, integer its id number, string of MIME type, integer of size in kilobytes, and the whole element itself */ function parseAttachmentLine(inElem) { var name = ""; var MIMEtype = ""; var size = Number(); var id = Number(); // FIXME we should skip obsolete attachments inElem.normalize(); // getting name of the attachment var bElem = $("b:first",inElem); name = $.trim(bElem.text()); // getting id id = parseInt($("a:first",inElem).attr("href").replace(/^.*attachment.cgi\?id=/,""),10); //getting MIME type and size var roundedText = $(".bz_attach_extra_info:first",inElem).html(); var stringArray = $.trim(roundedText.replace(/^\s*\((.*)\s*KB,\s*$/,"$1")); size = parseInt(stringArray,10); MIMEtype = roundedText.split("\n")[2].replace(/^\s*(.*)\)\s*$/,"$1"); return([name,id,MIMEtype,size,inElem]); } function getAttachments(attTable) { var attList = []; var tempT = $("tr",attTable).get(); if (tempT.length > 2) { for (var i=1;i0) { var titleElement = $(".bz_alias_short_desc_container:first",this.document); titleElement.css("background-color","olive"); titleElement.append(createFixAllButton(badAttachments)); for(var i=0;i 0) { cardIDStr = cardIDArr[0]; if (cardIDArr[1]) { optionStr = cardIDArr[1]; outStr = groupIDs(driverStr,cardIDStr)+"/" + optionStr; console.log("cardIDArr = " + cardIDArr.toSource() + ", outStr = "+outStr); } else { outStr = groupIDs(driverStr,cardIDStr); optionStr = ""; } console.log("found IDs: " + cardIDStr + "," + optionStr); } else { outStr = "**** FULLSTRING: " + iLine; } } else { // Intel Corporation, NVIDIA cardIDArr = manuChipStrs.filter(function (el, ind, arr) { return RegExp(el[0],"i").test(iLine); }); console.log("cardIDArr = " + cardIDArr.toSource()); if (cardIDArr && (cardIDArr.length > 0)) { cardIDArr = cardIDArr[0]; } else { outStr = iLine; break chipSwitchboard; } // cardIDArr [0] = RE, [1] = ("RADEON","INTEL","NOUVEAU"), [2] = manu PCIID iLine = $.trim(iLine.replace(RegExp(cardIDArr[0],"i"),"")); // FIXME is this necessary? Let's try without it // outStr = iLine.replace(/^\W*(\w*).*$/,"$1"); // nVidia developers opted-out from grouping if (driverStr == "INTEL") { outStr = groupIDs(cardIDArr[1],iLine); } else { outStr = iLine; } } console.log("result = " + outStr); var whiteboardInput = $("#status_whiteboard",this.document); var attachedText = $.trim("card_"+outStr); if (whiteboardInput.val().length == 0) { whiteboardInput.val(attachedText); } else { whiteboardInput.val(whiteboardInput.val()+", " + attachedText); } } /** * Insert "Fill In" button to the status whiteboard * @param interestLine string with the interesting part of the Chipset: line * @param driverString string with name of the driver ("INTEL", "RADEON", "NOUVEAU", * etc.) * @return none */ function bzPage.prototype.fillInAddButton(interestLine,driverString) { var newButt = $(this.originalButton).clone(); var whiteboardInput = $("#status_whiteboard",this.document); newButt.attr("id","chipmagic"); newButt.attr("value","Fill In"); newButt.attr("type","button"); newButt.click(function (evt) { this.fillInWhiteBoard(interestLine,driverString); }); whiteboardInput.after(newButt); whiteboardInput.after("\u00A0"); } /** * Get attached Xorg.0.log, parse it and find the value of chip. * Does not fill the whiteboard itself, just adds button to do so, so that * slow XMLHttpRequest is done in advance. * @param none * @return none */ function bzPage.prototype.fillInChipMagic() { /** * Recursive function to run Get attached Xorg.0.log, parse it and find the value of chip * @return None */ function fillInChipMagicProcessAtts(ret) { if (ret) { if (ret.status != 200) { jetpack.notifications.show([ret.status,ret.statusText,ret.responseHeaders, ret.responseText].toSource()); throw "XMLHttpRequest got return code " + ret.status; } console.log('fetched ' + ret.finalUrl); var interestingLineArr = ret.responseText.split("\n").filter(function (v,i,a) { return ChipsetRE.test(v); }); console.log("interestingLineArr = " + interestingLineArr.toSource()); if (interestingLineArr.length >0) { // Process and exit // .replace(ChipsetRE,"$1\t$2").split("\t") var interestingArray = ChipsetRE.exec(interestingLineArr[0]); console.log("interesting array = " + interestingArray.toSource()); interestingLine = $.trim(interestingArray[2].replace(/[\s"]+/g," ")); console.log("interesting line = " + interestingLine); fillInAddButton(interestingLine,interestingArray[1].toUpperCase()); console.log("XMLHttpRequest done!"); return; } } if (XorgLogAttList[XorgLogAttListIndex]) { var XorgLogAttID = XorgLogAttList[XorgLogAttListIndex][1]; var attURL = "https://bugzilla.redhat.com/attachment.cgi?id="+XorgLogAttID; XMLHttpRequest({ method: 'GET', url: attURL, headers: { 'User-agent': 'Mozilla/4.0 (compatible) Greasemonkey getXorgLog', 'Accept': 'text/plain', 'Content-type': 'text/xml' }, onload:fillInChipMagicProcessAtts }); XorgLogAttListIndex++; } else { console.log("No more Xorg.0.log attachments!"); } } var XorgLogURL = ""; var XorgLogAttID = ""; var XorgLogFound = false; // Find out Xorg.0.log attachment URL XorgLogAttList = attachmentsList.filter(function (value, index, array) { // Xorg.0.log must be text, otherwise we cannot parse it return (RegExp("[xX].*log").test(value[0]) && /text/.test(value[2])); }); console.log("XorgLogAttList = " + XorgLogAttList.toSource()); if (XorgLogAttList.length == 0) { console.log("No Xorg.0.log attachments found.") return; } fillInChipMagicProcessAtts(); } /** * Opens a new tab with a query for the given text in the selected component * @param text to be searched for * @param component string with the component name (maybe latter regexp?) * @param product (optional) string with the product name * @return None * * old * long_desc=Xpress%20200&bug_status=NEW&bug_status=ASSIGNED * new * long_desc_type=substring&long_desc=Xpress%20200&bug_status=NEW&bug_status=ASSIGNED */ function bzPage.prototype.queryInNewTab(text,component,product) { // Optional parameter if (product == null) { product = "Fedora"; } var url = "https://bugzilla.redhat.com/buglist.cgi?query_format=advanced"; if (product) { url += "&product="+product; } if (component) { url += "&component="+component; } if (text) { url += "&long_desc_type=substring&long_desc="+ text.replace(" ","%20"); } console.log("queryInNewTab: url = " + url); window.open(url); } function bzPage.prototype.queryForSelection(component) { var text = window.getSelection().to""; if (text.length < 1) { text = getClipboardText(); }; if (text.length > 0) { this.queryInNewTab(text, getComponent()); } } /** * Sends XMLRPC request * * @param url string with URL of the XML-RPC interface * @param data string with XML of the data to be sent * @param method string -- either 'post' or 'get' * @param callback function catching callback */ function bzPage.prototype.sendRequest(url,data,method,callback) { //$.rpc(url, dataType, onLoadCallback, version); XMLHttpRequest({ method: method, url: url, headers: { 'User-agent': 'Mozilla/4.0 (compatible) Greasemonkey fixAttType XMLRPC', 'Accept': 'application/atom+xml,application/xml,text/xml', 'Content-type': 'text/xml' }, data: data, onload: callback }); } /** * Callback function for the XMLRPC request * * @param ret object with XMLHttpRequest response * with attributes: * + status -- int return code * + statusText * + responseHeaders * + responseText */ function bzPage.prototype.callBack(ret) { if (ret.status != 200) { jetpack.notifications.show([ret.status,ret.statusText,ret.responseHeaders, ret.responseText].toSource()); } if (--reqCounter <= 0) { setTimeout("document.location.reload()",1000); } } /** * Callback function for "Fix all attachments" button * @param list Array of * @return none */ function bzPage.prototype.fixAllAttachments(list) { var tmpElem = {}; /** * The worker function -- call XMLRPC to fix MIME type of the * particular attachment * * @param id integer with the attachment id to be fixed * @param type string with the new MIME type, e.g. "text/plain" * */ function fixAttachById(id,type) { // FIXME XMLRPCMessage var msg = new XMLRPCMessage("bugzilla.updateAttachMimeType"); msg.addParameter({'attach_id':id, 'mime_type':type}); msg.addParameter(login); msg.addParameter(password); try { var ret = sendRequest(XMLRPCurl, msg.xml(),'post',callBack); } catch (e) { jetpack.notifications.show([e,ret].toSource()); } reqCounter++; } for(var i=0;i"); aElem.click(function(event) { fixAllAttachments(list); }); fixElement(aElem,"","F","ix all"); return aElem; } function bzPage.prototype.addTextLink(row) { var aList = row[row.length-1].getElementsByTagName("a"); var curElem = aList[aList.length-1]; var tElem = {}; var t2Elem = {}; tElem = $(" Text").bind("click", function(event) { fixAttachById(row[1],"text/plain"); }); tElem.appendChild(t2Elem); $(curElem).parent().append("
"); $(curElem).parent().append(tElem); } //=========================== /** * Find the next (or previous) sibling element of given tagName * starting from given element. * * @param startElement element from which to start searching * @param tagName string tagName of the searched for element (case insensitive) * @param forward boolean optional should we search forward or backward? * @return element of the given tagName or null */ function bzPage.prototype.findNextSiblingByTagName(startElement,tagName,forward) { if (forward === null) { // missing optional argument forward = true; } var tempElement = startElement; tagName = tagName.toUpperCase(); while (tempElement && tempElement.tagName != tagName) { if (forward) { tempElement = tempElement.nextSibling; } else { tempElement = tempElement.previousSibling; } } return tempElement; } /** * Select option with given label on the elements in the page var IBList = $("input[type*='submit']",this.document).get(); // BUTTONS ON THE BOTTOM OF THE FORM // Create a new SUBMIT button adding current owner of the bug to // the CC list var IBLast = IBList[IBList.length-4]; addNewButton(IBLast,"changeOwnerbtn","reASSIGN", "","ASSIGNED","NODEFAULTASSIGNEE"); // BUTTONS ABOVE THE COMMENT BOX var insertAfterElement = $("
"); $("#comment",this.document).before(insertAfterElement); //var brElement = findNextSiblingByTagName(textCommentElement,"BR",false); this.generateToolBar(insertAfterElement.get(0),above); // BUTTONS BELOW THE COMMENT BOX this.generateToolBar(this.originalButton,below); // TODO put this somehow into addNewButton and generalPurposeCureForAllDisease // framework if (this.queryButtonAvailable){ // Add query search button var newPosition = $("#newcommentprivacy",this.document).siblings()[2]; var newButt = $(this.originalButton).clone(); newButt.attr("id","newqueryintab"); newButt.attr("value","Query for string"); newButt.addEventListener('click',function (evt) { queryForSelection(); },true); newButt.attr("type","button"); newPosition.before(newButt); newPosition.before("\u00A0"); } if ((chipIDsGroupings.length >0) && isXorgBug()) { // Add find chip magic button var whiteboardInput = $("#status_whiteboard",this.document); if (whiteboardInput.text().search(/card_/) == -1) { fillInChipMagic(); } } // Add setting default assignee var defAssignee = getDefaultAssignee(); if ((defAssignee.length > 0) && (defAssignee != this.owner)) { var divAssigned = $("#bz_assignee_edit_container",this.document); var divAssignedInput = $("#assigned_to",this.document); var divAssignedActiveCheckbox = $("#bz_assignee_edit_action",this.document); newButt = $(this.originalButton).clone(); newButt.attr("id","setdefaultassigneebutton"); newButt.attr("value","Def. Assignee"); newButt.click(function (evt) { if (defAssignee.length > 0) { $(divAssignedActiveCheckbox).click(); divAssignedInput.value = defAssignee; } }); newButt.attr("type","button"); divAssigned.append(newButt); newButt.before("\u00A0"); } var curComponentElement = $("#component",this.document); $(curComponentElement).change( function(event) { var assignee = getDefaultAssignee(); if (assignee.length > 0) { $("#bz_assignee_edit_action",this.document).click(); $("#assigned_to",this.document).val(assignee); $("#set_default_assignee",this.document).get(0).checked = false; } }); } // https://bugzilla.redhat.com/show_bug.cgi?id=451951 // // Interesting JEPs // https://wiki.mozilla.org/Labs/Jetpack/JEP/10 -- clipboard access // https://wiki.mozilla.org/Labs/Jetpack/JEP/17 -- page mods var callback = function(doc) { jetpack.statusBar.append({ onReady: function(widget) { console.log("Boom!"); curPage = new bzPage(doc); }, }); }; var options = {}; options.matches = [ "https://bugzilla.redhat.com/show_bug.cgi*", "https://bugzilla.redhat.com/process_bug.cgi" ]; jetpack.pageMods.add(callback, options);