// Released under the MIT/X11 license // http://www.opensource.org/licenses/mit-license.php // https://bugzilla.redhat.com/show_bug.cgi?id=451951 // // EXAMPLE OF THE WORKING JETPACK FUNCTION!!! //jetpack.future.import("pageMods"); //var callback = function(document) { // jetpack.statusBar.append({ // html: "BOOM!", // width: 45, // onReady: function(widget) { // console.log("Boom!"); // }, // }); //}; //var options = {}; //options.matches = [ // "https://bugzilla.redhat.com/show_bug.cgi*", // "https://bugzilla.redhat.com/process_bug.cgi"]; //jetpack.pageMods.add(callback, options); // Interesting JEPs // https://wiki.mozilla.org/Labs/Jetpack/JEP/10 -- clipboard access // https://wiki.mozilla.org/Labs/Jetpack/JEP/17 -- page mods // ************* FUNCTIONS ******************** /* EXTERNAL FUNCTIONS */ /** * split URI into array * * @param str string with the URL * @return array with parsed URL * * parseUri 1.2.1 * originally from http://blog.stevenlevithan.com/archives/parseuri * (c) 2007 Steven Levithan * MIT License */ function parseUri (str) { var o = parseUri.options, m = o.parser[o.strictMode ? "strict" : "loose"].exec(str), uri = {}, i = 14; while (i--) { uri[o.key[i]] = m[i] || ""; } uri[o.q.name] = {}; uri[o.key[12]].replace(o.q.parser, function ($0, $1, $2) { if ($1) uri[o.q.name][$1] = $2; }); return uri; }; parseUri.options = { strictMode: false, key: ["source","protocol","authority","userInfo","user","password","host","port","relative","path","directory","file","query","anchor"], q: { name: "queryKey", parser: /(?:^|&)([^&=]*)=?([^&]*)/g }, parser: { strict: /^(?:([^:\/?#]+):)?(?:\/\/((?:(([^:@]*):?([^:@]*))?@)?([^:\/?#]*)(?::(\d*))?))?((((?:[^?#\/]*\/)*)([^?#]*))(?:\?([^#]*))?(?:#(.*))?)/, loose: /^(?:(?![^:@]+:[^:@\/]*@)([^:\/?#.]+):)?(?:\/\/)?((?:(([^:@]*):?([^:@]*))?@)?([^:\/?#]*)(?::(\d*))?)(((\/(?:[^?#](?![^?#\/]*\.[^?#\/.]+(?:[?#]|$)))*\/?)?([^?#\/]*))(?:\?([^#]*))?(?:#(.*))?)/ } }; /** * Clean the string from spaces around * * @param string to be cleaned * @return string which was cleaned * */ function strip(str) { return str.replace(/^\s*(.*)\s*$/,"$1"); } /** * Primitive version of sprintf * @param string with the format of the result and * @param list of parameters * @return string with the format applied * * Call as * format("And the %1 want to know whose %2 you %3", "papers", "shirt", "wear"); * originally from * http://www.sitepoint.com/blogs/2008/11/11/arguments-a-javascript-oddity/ * FIXME needs testing and debugging */ function format(string) { var args = arguments; var pattern = new RegExp("%([1-" + arguments.length + "])", "g"); return String(string).replace(pattern, function(match, index) { return args[index]; }); } /** * escape input string so that it could be used in URLs * @param clearString * @return string decoded so that it is safe for URLs */ function URLEncode (clearString) { var output = ''; var x = 0; clearString = clearString.toString(); var regex = /(^[a-zA-Z0-9_.]*)/; while (x < clearString.length) { var match = regex.exec(clearString.substr(x)); if (match != null && match.length > 1 && match[1] != '') { output += match[1]; x += match[1].length; } else { if (clearString[x] == ' ') output += '+'; else { var charCode = clearString.charCodeAt(x); var hexVal = charCode.toString(16); output += '%' + ( hexVal.length < 2 ? '0' : '' ) + hexVal.toUpperCase(); } x++; } } var output = ''; var x = 0; clearString = clearString.toString(); var regex = /(^[a-zA-Z0-9_.]*)/; while (x < clearString.length) { var match = regex.exec(clearString.substr(x)); if (match != null && match.length > 1 && match[1] != '') { output += match[1]; x += match[1].length; } else { if (clearString[x] == ' ') output += '+'; else { var charCode = clearString.charCodeAt(x); var hexVal = charCode.toString(16); output += '%' + ( hexVal.length < 2 ? '0' : '' ) + hexVal.toUpperCase(); } x++; } } return output; return output; } /** * 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 = Array(); if (list.length > 0) { chosenPair = list.filter( function(pair){ return RegExp(pair['regexp']).test(chosingMark); }); }; if (chosenPair.length > 0) { return strip(chosenPair[0]['addr']).toLowerCase(); } 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 i = 0; i < list.length; i++) { // FIXME how to say hasAttribute in jQuery? if(list[i].hasAttribute("value")) { // FIXME getAttribute in jQuery member = list[i].getAttribute("value"); member = member.replace(/\s*(.*)\s*/,"$1"); outL[outL.length] = member; } } 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 * */ // FIXME to-be-replaced by // var contents = jetpack.clipboard.get(); unsafeWindow.getClipboard = function() { 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 Object(); var strLength = new Object(); 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; }; function getClipboardText() { return unsafeWindow.getClipboard(); } /** * Compatibility layer for Firefox <3.1 which doesn't have native JSON support * * @param str string with the JSON data * @return object */ //FIXME get rid of this ... user just has to have FF > 3.1 function jsonParse(str) { if (JSON) { return JSON.parse(str); } else { return(eval('(' + str + ')')); } } /* Bugzilla functions.*/ /** * add Fedora Bug zapper's signature to the comment of the current bug */ function addSignature(evt) { var cmntText = document.getElementById("comment"); if ((signatureFedoraString.length > 0) && (strip(cmntText.value).length > 0)) { cmntText.value = strip(cmntText.value) + signatureFedoraString; } } /** * Send mouse click to the specified element * @param element where to send mouseclick to * @return None */ function clickMouse(target) { var localEvent = document.createEvent("MouseEvents"); localEvent.initMouseEvent("click", true, true, window, 0, 0, 0, 0, 0, false, false, false, false, 0, null); target.dispatchEvent(localEvent); } // ****************************************** /** * 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 = String(); var MIMEtype = String(); var size = Number(); var id = Number(); inElem.normalize(); // getting name of the attachment var bElem = inElem.getElementsByTagName("b")[0]; name = strip(bElem.textContent); // getting id var aElem = inElem.getElementsByTagName("a")[0]; id = parseInt(aElem.getAttribute("href").replace(/^.*attachment.cgi\?id=/,""),10); //getting MIME type and size // not sure whether the following is possible, otherwise // I would have to get first span element var spanElems = inElem.getElementsByClassName("bz_attach_extra_info")[0]; var roundedText = spanElems.innerHTML; var stringArray = strip(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]); } /** * Get the attachments table of the bug * * @return attachments table */ function getAttTable() { var tempList = document.getElementById("attachment_table"); return(tempList); } /** * Get list of attachments to the bug * * @return array of attachments */ function getAttachments(attTable) { var attList = Array(); var tempT = attTable.getElementsByTagName("tr"); if (tempT.length > 2) { for (var i=1;i element with given id. * * Also execute change HTMLEvent, so that the form behaves accordingly. * * @param id * @param label * @return none */ function selectOption(id,label) { var selectElement = document.getElementById(id); var values = selectElement.options; for (var i = 0; i < values.length; i++) { values[i].normalize(); if (values[i].text.search(label) != -1) { values[i].selected = true; var intEvent = document.createEvent("HTMLEvents"); intEvent.initEvent("change", true, true); selectElement.dispatchEvent(intEvent); break; } } } /** * Returns the component of the bug * * @return string component of the bug */ function getComponent() { return strip(document.getElementById("component").value); } /** * Returns the product the bug belongs to * * @return string product the bug belongs to */ function getProduct() { var productSelect = document.getElementById("product"); var index = productSelect.selectedIndex; return productSelect.options[index].value; } /** * Returns the version of product the bug belongs to * * @return string revision of the product the bug belongs to */ function getVersion() { var versionSelect = document.getElementById("version"); if (versionSelect) { var index = versionSelect.selectedIndex; return versionSelect.options[index].value; } else { return null; } } /** * Returns owner of the bug. * * @return string with the email address of the assignee. */ function getAssignedTo() { var assigneeEditContainer = document.getElementById("bz_assignee_edit_container").getElementsByClassName("fn")[0]; return strip(assigneeEditContainer.textContent).toLowerCase(); } /** * Returns reporter of the bug. * * @return string with the email address of the reporter. */ function getReporter() { var reportedSpanTag = document.getElementById("bz_show_bug_column_2").getElementsByClassName("fn")[0]; return reportedSpanTag.textContent; } /** * Returns the number of the current bug * * @return int with the bug number */ function getBugNo() { var title = strip(document.getElementById("title").getElementsByTagName("p")[0].innerHTML); var bugNo = eval(title.split(" ")[1]); return bugNo; } /** * Returns list of people getting CC of all bug messages. * * @return array with the all members of the CC list. */ function getCCList() { var selectRef = document.getElementsByName("cc"); if (selectRef.length>0) { return valuesToList(selectRef[0]); } else { return []; } } /** * Returns an email address for collective maintainers * who should be on the CC of the bug. * * @return string email address to be on CC list. */ function getCCMaintainer() { return filterByRegexp(AddrArray,component); } /** * Returns default assignee for the bug's component * @return string with the default assignee for given component */ function getDefaultAssignee() { return filterByRegexp(defAssigneeList,getComponent()); } /** * Get Issuetracker string from the bug page * @return string issuetracker numbers or empty string (either because we have no IT * or because it is not a RHEL bug). */ function getIssueTracker() { var interestingElement = document.getElementById("cf_issuetracker"); if (interestingElement) { return strip(interestingElement.value); } else { return ""; } } /** * Add accesskey to the particular element * * @param rootElement element to which the new text object will be attached * @param beforeText text before the accesskey character * @param accKey what will be the accesskey itself * @param afterText text after the accesskey character * @return modified element with the fixed accesskey * */ function fixElement(rootElement,beforeText,accKey,afterText) { rootElement.setAttribute("accesskey",accKey.toLowerCase()); rootElement.innerHTML = beforeText + "" + accKey + "" + afterText; return rootElement; } /** * Set branding colours to easily distinguish between Fedora and RHEL bugs * @param brand string with product of the current bug * @param version string with the version of the bug * @param its string with the IsueTracker numbers * @return */ function setBranding(brand,version,its) { var brandColor = ""; if (brand.search(/Red Hat Enterprise Linux/) != -1) { if (its.length > 0) { brandColor = RHITColor; } else { brandColor = RHColor; } } else if (brand.search(/Fedora/) != -1) { if (version.search(/rawhide/i) != -1) { brandColor = RawhideColor; } else { brandColor = FedoraColor; } } // Comment each of the following lines to get only partial branding document.body.style.background = brandColor; document.getElementById("titles").style.background = brandColor; // Make background-color of the body of bug salmon pink // for security bugs. if (hasKeyword("Security")) { var divBody = document.getElementById("bugzilla-body"); divBody.style.backgroundImage = "none"; divBody.style.backgroundColor = SalmonPink; } // we should make visible whether maintCCAddr is in CCList if (isInList(maintCCAddr, CCList)) { var switchCCEdit=document.getElementById("cc_edit_area_showhide"); //switchCCEdit.textContent = "*"+switchCCEdit.textContent; switchCCEdit.style.color = "navy"; switchCCEdit.style.fontWeight = "bolder"; switchCCEdit.style.textDecoration = "underline"; } } /** * Generic function to add new button to the page. * Actually copies new button from the old one (in order to have the same * look-and-feel, etc. * @param originalLocation object with the button to be copied from * @param newId string with the id of the new button; has to be unique in whole page * @param newLabel string with the label which will be shown to user * @param commentString string with comment to be added to the comment box * @param nState string with the new state bug should switch to (see * generalPurposeCureForAllDisease function for details) * @param secPar string with second parameter for generalPurposeForAllDisease * @param doSubmit bool optional whether the button should submit whole page * (default true) * * @return none */ function addNewButton(originalLocation,newId,newLabel,commentString,nState,secPar,doSubmit) { if (doSubmit === null) { // missing optional argument doSubmit = true; } var newButton = originalButton.cloneNode(true); if (!doSubmit) { newButton.setAttribute("type","button"); } newButton.id=newId; newButton.setAttribute("value",newLabel); var commStr = ""; if (msgStrs[commentString]) { commStr = msgStrs[commentString]; } newButton.addEventListener('click',function (evt) { generalPurposeCureForAllDisease(commStr, nState, secPar); },true); var textNode = document.createTextNode("\u00A0"); originalLocation.parentNode.insertBefore(textNode,originalLocation); originalLocation.parentNode.insertBefore(newButton,textNode); } /** * Add new keyword among the keywords. * * @param str string with the new keyword * @return none * * Checks for the existing keywords. */ function addKeyword(str) { var kwd = document.getElementById('keywords'); if (kwd.value.length == 0) { kwd.value = str; }else{ kwd.value = kwd.value + ", " + str; } } /** * Check for the presence of a keyword * * @param str string with the keyword * @return Boolean * */ function hasKeyword(str) { var kwd = strip(document.getElementById('keywords').value); return (RegExp(str).test(kwd)); } /** * Add XGL to the CC list * * @param evt event which made this function active * @return none */ function changeOwnerHandler(evt) { /** Take care that when changing assignment of the bug, * current owner is added to CC list. * Switch off setting to the default assignee */ if (!isInList(maintCCAddr, CCList)) { addToCC(maintCCAddr); } var setDefaultAssigneeCheckbox = document.getElementById("set_default_assignee"); setDefaultAssigneeCheckbox.checked = false; selectOption("bug_status", "ASSIGNED"); } /** * Set the bug to NEEDINFO state * * Working function. * @return none */ function setNeedinfoReporter() { var checkbox = document.getElementById("needinfo"); checkbox.click(); selectOption("needinfo_role", "reporter"); } /** * Add text to the comment. * @param string2BAdded string to be added to the comment box * * @return none */ function addTextToComment(string2BAdded) { var commentTextarea = document.getElementById("comment"); // don't remove the current content of the comment box, // just behave accordingly if (commentTextarea.value.length > 0) { commentTextarea.value += "\n\n"; } commentTextarea.value += string2BAdded; } /** * add address to CC of this bug * * @param address string address to be added * @return none */ function addToCC(address) { var sel = document.getElementById("newcc"); sel.value = address; } /** * Generalized function for all actions * * @param addString string to be added as new comment * @param nextState string signifying next state of the bug (whatever is in Bugzilla + "NEEDINFO" meaning NEEDINFO(Reporter)) * @param secondParameter string with label on the subbutton for reason * of closing the bug * @return none */ function generalPurposeCureForAllDisease(addString,nextState,secondParameter) { if (addString.length >0) { addTextToComment(addString); } if (nextState == "CLOSED") { if (secondParameter == "UPSTREAM") { addClosingUpstream(); } else if (secondParameter.length > 0) { selectOption("bug_status", nextState); selectOption("resolution",secondParameter); return 0; } else { throw("Missing resolution for CLOSED status."); } } // Now closing bugs is done, what about the rest? if (nextState == "NEEDINFO") { setNeedinfoReporter(); } else if (nextState == "ADDKEYWORD") { if (secondParameter.length == 0) { throw "Keyword has to be defined"; } addKeyword(secondParameter); } else if (nextState == "ASSIGNED") { if (!isInList(maintCCAddr, CCList)) { addToCC(maintCCAddr); } selectOption("bug_status", nextState); } else if (nextState.length >0) { selectOption("bug_status", nextState); } if (secondParameter == "ADDSELFCC") { document.getElementById("addselfcc").checked = true; } else if (secondParameter == "NODEFAULTASSIGNEE") { document.getElementById("set_default_assignee").checked = false; } } /** * Return string with the ID for the external_id SELECT for * external bugzilla * * @param URLhostname string hostname of the external bugzilla * @return string with the string for the external_id SELECT */ function getBugzillaName(URLhostname) { var bugzillaID = ""; if (hashBugzillaName[URLhostname]) { bugzillaID = hashBugzillaName[URLhostname]; } else { bugzillaID = ""; } return bugzillaID; } /** * Generate URL of the bug on remote bugzilla * @param selectValue Number which is index of the bugzilla in hashBugzillaWholeURL * @param bugID Number which is bug ID * @return string with the URL */ function getWholeURL(selectValue,bugID) { var returnURL = ""; if (hashBugzillaWholeURL[selectValue]) { returnURL = hashBugzillaWholeURL[selectValue]+bugID; } else { returnURL = ""; } return returnURL; } /** * Add information about the upstream bug upstream, and closing it. * @param evt event which called this handler * * @return none */ function addClosingUpstream() { var externalRefs = document.getElementById("external_bugs_table"); var refs = externalRefs.getElementsByTagName("tr"); // that's a bad id, if there is a one. var inputBox = document.getElementById("inputbox"); var externalBugID = 0; var externalHost = ""; var wholeURL = ""; // Fix missing ID on the external_id SELECT document.getElementsByName("external_id")[0].id = "external_id"; if (inputBox.value.match(/^http.*/)) { wholeURL = inputBox.value; var IBURLArr = parseUri(wholeURL); externalHost = IBURLArr.host; externalBugID = parseInt(IBURLArr.queryKey["id"]); inputBox.value = externalBugID; var bugzillaName = getBugzillaName(externalHost); selectOption("external_id", bugzillaName); } else if (!isNaN(inputBox.value)) { externalBugID = parseInt(inputBox.value); var bugzillaID = document.getElementById("external_id").value; wholeURL = getWholeURL(bugzillaID,externalBugID); } else { // no inputBox.value -- maybe there is an external bug from // the previous commit? ; } // It is not good to close bug as UPSTREAM, if there is no reference // to the upstream bug. if ((refs.length > 2) || (externalBugID > 0)) { addTextToComment(msgStrs['sentUpstreamString'].replace("ยงยงยง",wholeURL)); selectOption("bug_status", "CLOSED"); selectOption("resolution", "UPSTREAM"); } else { alert("No external bug specified among the External References!"); } } /** * Currently we don't use this function * @param evt event send from DOM (not used) * @return none */ function swapXGLMainToCCListHandler(evt) { /** Remove mcepl@redhat.com from CC list and put there * xgl-maint@redhat.com */ myAddrIndex = CCList.indexOf(login); if (!isInList(maintCCAddr, CCList)) { addToCC(maintCCAddr); } if (isInList(login, CCList)) { var selBox = document.getElementById("cc"); selBox[myAddrIndex].selected = true; document.getElementById("removecc").checked = true; } evt.stopPropagation(); evt.preventDefault(); } /** * Go through all <a> elements in the page and fix those starting with # * to point to the real bug, not to process.cgi page. * @param bugNo */ function fixAllHrefs(bugNo) { var AList = document.getElementsByTagName("a"); var curA; var hrefStr = ""; var reProcess = RegExp(""); for(var i=0;i elements in the page var IBList = []; var IBRawList = document.getElementsByTagName("input"); for (var i=0;i 0) && (defAssignee != owner)) { var divAssigned = document.getElementById("bz_assignee_edit_container"); var divAssignedInput = document.getElementById("assigned_to"); var divAssignedActiveCheckbox = document.getElementById("bz_assignee_edit_action"); newButt = originalButton.cloneNode(true); newButt.setAttribute("id","setdefaultassigneebutton"); newButt.setAttribute("value","Def. Assignee"); newButt.addEventListener('click',function (evt) { if (defAssignee.length > 0) { clickMouse(divAssignedActiveCheckbox); divAssignedInput.value = defAssignee; } },true); newButt.setAttribute("type","button"); divAssigned.appendChild(newButt); newButt.parentNode.insertBefore(document.createTextNode("\u00A0"),newButt); } var curComponentElement = document.getElementById("component"); curComponentElement.addEventListener('change', function(event) { //FIXME We screw up default assignee value for unknown components var assignee = getDefaultAssignee(); if (assignee.length > 0) { clickMouse(document.getElementById("bz_assignee_edit_action")); document.getElementById("assigned_to").value = assignee; document.getElementById("set_default_assignee").checked = false; } },false); } // ****************** STATIC DATA ************************* var XMLRPCurl = "https://bugzilla.redhat.com/xmlrpc.cgi"; // 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 = GM_getValue("JSONURL","http://mcepl.fedorapeople.org/scripts/BugZappers_data.json"); var debug = GM_getValue("debug",false); var reqCounter = 0; var msgStrs = {}; var RHColor = "#9E292B"; var FedoraColor = "#002867"; var RawhideColor = "#007700"; // or "green" var RHITColor = "#660066"; var SalmonPink = "#FFE0B0"; // Initialize data from remote URL var XMLHTTPRequestDone = false; var hashBugzillaName = Array(); var hashBugzillaWholeURL = Array(); var defAssigneeList = Array(); var signatureFedoraString = ""; var queryButtonAvailable = false; var AddrArray = Array(); GM_xmlhttpRequest({ method: 'GET', url: jsonDataURL, onload: function(response) { var data = jsonParse(response.responseText); msgStrs = data['strings']; signatureFedoraString = data['signature']; hashBugzillaName = data['bugzillalabelNames']; hashBugzillaWholeURL = data['bugzillaIDURLs']; // [{'regexp to match component':'email address of an universal maintainer'}, ...] AddrArray = data['CCmaintainer'], defAssigneeList = data['defaultAssignee'], queryButtonAvailable = data['queryButton']; buildButtons(data['topRow'],data['bottomRow']); if (signatureFedoraString.length > 0) { console.logs("yes, add signature listener"); // (or a form named "changeform") document.forms[1].addEventListener("submit",addSignature,true); } } }); // ******************** MAIN ********************* function main(document) { // FOR DEBUGGING ONLY!!! if (debug) { console.log("signatureFedoraString = " + signatureFedoraString); var urlWarning = document.createElement("span"); urlWarning.appendChild(document.createTextNode(jsonDataURL)); urlWarning.style.fontSize = "x-small"; var urlWarningParent = document.getElementById("bz_field_status"); urlWarningParent.appendChild(urlWarning); } // *** collect information about the bug var bugNo = getBugNo(); var reporter = getReporter(); var owner = getAssignedTo(); var CCList = getCCList(); var product = getProduct(); var version = getVersion(); var issueTracker = getIssueTracker(); var maintCCAddr = getCCMaintainer(); var component = getComponent(); checkPrivateValues(); var login = getLogin(); var password = GM_getValue("BZpassword"); //*** set the main environment setBranding(product,version,issueTracker); // fix process.cgi HREFs so that to avoid confusion on IRC if (document.location.href.search(/process.cgi/) != -1) { fixAllHrefs(bugNo); } // ---------------------------------------------- // fix attachments var aTable = getAttTable(); var badAttachments = getAttachments(aTable).filter(isOctetStream); if (badAttachments.length>0) { var titleElement = $("bz_alias_short_desc_container"); titleElement.style.backgroundColor = "olive"; titleElement.appendChild(createFixAllButton(badAttachments)); for(var i=0;i$.getScript('http://mcepl.fedorapeople.org/scripts/jquery.rpc.js');",function() { console.log("jquery.rpc.js loaded"); }); var options = {}; options.matches = [ "https://bugzilla.redhat.com/show_bug.cgi*", "https://bugzilla.redhat.com/process_bug.cgi"]; jetpack.pageMods.add(main, options); jetpack.tabs.focused. $('#header').css('backgroundColor','red')