aboutsummaryrefslogblamecommitdiffstats
path: root/bugzillaBugTriage.js
blob: 4330a7ef3b106b331d62eac2d8df51e3da3f0b81 (plain) (tree)
1
2
3
4
5
6

                                                     
 

                                        
 















                                                                               
 


































                                                                            
 








                                                                               
 
                                               






                                      





                                          









                                                                                         
                            


                                               
                                                                                            


                                    
                                                     







                                                                 
  






                                 

                                                
                                                                                  
             




















                                                                   
  
   
                         
                                          
                             


                                                                                                                        

                                 

                                                                                                                                   
                                  

                                            
                                                   
 

                               


                                                              
                  
                                                                                              

                  
                                                                       
         



                         

                        














                                                                                      
 





















                                                                                       
 




                                                                                                              
 














                                                                                                         
 
                              
 

                                         
 

                                                   
 



                                                                  
 





                                                                                      
 


































                                                                                     


   










                                                                                             



                                     



































                                                                                                
 

                                       
 







                                                                    
 






                                                                


                                                               
                                                                                   







                                                                






                                                                                   
                                                             

                       






























                                                                                                               

































                                                                                          
                                                                   









                                                        



                                                                
            
                                                                       









                                                                                    











                                                                      








                                                                          
















































                                                                                             















                                                                             






                                                                            




                                                                                     
   
                                                                 





                                                                                  
                                           

                        
                                               







                                                                                       

                                                        



                                          
                                                         










                                                      
                                                                 
                                                        
                        














                                                                                        
                                                 





                                                           
                                         
                                

                                                                                          






                                                              



                                                     
                                                   

                         























                                                                     





                                                       




                                                    



                                          
                                            




                                                                
                                                       

                                                           
                   
                                  

                                           

 











                                                                               
                                                                                  























                                                                        
                                                  
                                                                                    




                                                         
                                                                                                      











                                                                 

                                                                   






                                            

                                                        








                                                            

                                                               








                                                          



                                        

                                                                       
                                              







                                                      
                                                  
                                                                  


   
                                                    

                                                                
                                                
                                                                            






                                                                                    

                                                                     
                                 
                                                        



                          

   


                        
                                       

                                                                 










                                                                           

                                                                               










                                                                                        
                                                          
















                                                                           
                                                                                
                                                                 



                                                                
                                                                  






                                                                  
                                                                           
























                                                                                             
                                                                                                              


                                                             
                                                            
                        
                                                

                           
                                         

                                     
                                                 



                                                                         
                                   











                                                                            
                                           
                                                                                  













                                                   

                                                 

                                        
 





                                                   
                                                   






                                                               
                                                                                  









                                                   

                                                    









                                                             

                                                           














                                                               

                                            




                                       
  






                                                                                       
                                                                                                


                                                    
 










                                                                               

                                                                 














                                                                  
 
                                                     
                                                                     
                                                                    
                                                                                 









                                                              
                                                        














                                                                                   
                                                          














                                                                    


                                                                
                              


                                                   
                                                                             
 


                                                       

                                                                 
                                             
                                                                  
                                                          
                                                                


                                                                        








                                                                            
                                                             



                                                                                           
                                                                                                       







                                            
                                                           








                                                              
                                                    
                                                    
                                                            









                                                                              

                                              




                                         


                                                    
                                                      

                                                                                








                                                                         
                                                         







                                                                                                               
                                                        

               
                                                







                                                             


   
                                                                                                            
                                                               


                                          





















                                                                                               


                                                         
                                                                    








                                                                        

                                                               
                                                                                  
                                                              

                                        
                                                        
 
                                                                                      
                    
                                       
                                          



                                                                                      


                                                                 


                                              


                                                          

                                                                            

                              


                                               







                                                                                            
                                                     
                                                                     

                                                                     



                                              
         

                                                                
                                         

                                                                    


                                                                                                        
                                 
                           

 

                                                     
 


                                                                   
 
                              


                                   
                                      


          

                   
                                                 

                                                  
                                        
// 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("<span inline='font-size:x-small'>"+jsonDataURL+"</span>");
    }

    // *** 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 =  $('<a href="'+url+'"></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<len;i++) {
                if (!seg[i]) { continue; }
                s = seg[i].split('=');
                ret[s[0]] = s[1];
            }
            return ret;
        })(),
        file: (a.pathname.match(/\/([^\/?#]+)$/i) || [,''])[1],
        hash: a.hash.replace('#',''),
        path: a.pathname.replace(/^([^\/])/,'/$1'),
        relative: (a.href.match(/tp:\/\/[^\/]+(.+)/) || [,''])[1],
        segments: a.pathname.replace(/^\//,'').split('/')
    };
}

/**
 * add Fedora Bug zapper's signature to the comment of the current bug
 */
function bzPage.prototype.addSignature = function(evt) {
        	var cmntText = $("#comment",this.document);
        	if ((this.signatureFedoraString.length > 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 <tr> 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;i<tempT.length-1;i++) {
    			attList.push(parseAttachmentLine(tempT[i]));
    		}
    	}
    	return(attList);
    }

    function isOctetStream(element, index, array) {
    	var inArray = ["application/octet-stream","text/x-log"];
    	return(inArray.indexOf(element[2]) != -1);
    }

    var aTable = $("#attachment_table",this.document);
    var attachmentsList = getAttachments(aTable.get());
    var badAttachments = attachmentsList.filter(isOctetStream);

    if (badAttachments.length>0) {
    	var titleElement = $(".bz_alias_short_desc_container:first",this.document);
    	titleElement.css("background-color","olive");
    	titleElement.append(createFixAllButton(badAttachments));
    	for(var i=0;i<badAttachments.length;i++) {
    		addTextLink(badAttachments[i]);
    	}
    }
}

/**
 * Given line to be parsed, find out which chipset it is and fill in the whiteboard
 *
 * @param iLine string with the whole unparsed "interesting line"
 * @param driverStr string with the driver name
 * @return None
 */
function bzPage.prototype.fillInWhiteBoard(iLine,driverStr) {
    var outStr = "";
    var cardIDStr = "";
    var cardIDArr = [];

    function groupIDs(manStr,cardStrID) {
    //    console.log("RegExpArr = " + chipIDsGroupings.toSource() + ", hledam = " + manStr + "," + cardStrID);
        var outStr = filterByRegexp(chipIDsGroupings,manStr+","+cardStrID);
        if (outStr.length == 0) {
            outStr = "UNGROUPED_" + manStr+"/"+cardStrID;
        }
        return outStr;
    }

    /**
     * Given PCI IDs for manufacturer and card ID return chipset string
     *
     * @param manufacturerNo string with manufacturer PCI ID
     * @param cardNo         string with card PCI ID
     *
     * @return array with chip string and optinoal variants
     */
    function checkChipStringFromID(manufacturerNo,cardNo) {
        console.log("This is the card ID: " + cardNo + " manufactured by " + manufacturerNo);
        var soughtID = (manufacturerNo+","+cardNo).toUpperCase();
        var outList = PCI_ID_Array[soughtID];
        console.log("nalezeno = " + outList.toSource());
        if (outList) {
            return outList;
        } else {
            return "";
        }
    }

    console.log("driverStr = " + driverStr);
    console.log("iLine: " + iLine);

    chipSwitchboard:
    if (driverStr == "RADEON") {
        var cardID = iLine.replace(ATIgetIDRE,"$1");
        cardIDArr = checkChipStringFromID("1002",cardID);
        if (cardIDArr.length > 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<list.length;i++) {
		tmpElem = list[i];
		fixAttachById(tmpElem[1],"text/plain");
	}
}

function bzPage.prototype.createFixAllButton(list) {
	var aElem = $("<a href=''></a>");
	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 = $(" <a href=''>Text</a>").bind("click",
		function(event) {
			fixAttachById(row[1],"text/plain");
		});
	tElem.appendChild(t2Elem);
	$(curElem).parent().append("<br>");
	$(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 <SELECT> element with given id.
 *
 * Also execute change HTMLEvent, so that the form behaves accordingly.
 *
 * @param id
 * @param label
 * @return none
 */
function bzPage.prototype.selectOption(id,label) {
	var selectElement = jetpack.tabs.focused.contentDocument.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 = jetpack.tabs.focused.contentDocument.createEvent("HTMLEvents");
			intEvent.initEvent("change", true, true);
			selectElement.dispatchEvent(intEvent);
			break;
		}
	}
}

/**
 * Returns the component of the bug
 *
 * @return string component of the bug
 */
function bzPage.prototype.getComponent() {
	return $.trim($("#component",this.document).attr("value"));
}

/**
 * Returns the product the bug belongs to
 *
 * @return string product the bug belongs to
 */
function bzPage.prototype.getProduct() {
	var productSelect = $("#product",this.document);
	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 bzPage.prototype.getVersion() {
	var versionSelect = $("#version",this.document).get(0);
	if (versionSelect) {
		var index = versionSelect.selectedIndex;
		return versionSelect.options[index].value;
	} else {
		return null;
	}
}

/**
 * Returns the number of the current bug
 *
 * @return int with the bug number
 */
function bzPage.prototype.getBugNo() {
	var title = $.trim($("#title > p:first",this.document).html());
	return eval(title.split("&nbsp;")[1]);
}

/**
 * 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 bzPage.prototype.getCCMaintainer(addrs) {
	return filterByRegexp(addrs,getComponent()).toLowerCase();
}

/**
  * Returns default assignee for the bug's component
  * @return string with the default assignee for given component
  */
function bzPage.prototype.getDefaultAssignee() {
	return filterByRegexp(defAssigneeList,getComponent()).toLowerCase();
}

/**
 * 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 bzPage.prototype.getIssueTracker() {
	var interestingElement = $("#cf_issuetracker",this.document);
	if (interestingElement) {
		return $.trim(interestingElement.value);
	} else {
		return "";
	}
}

/**
 * Is this bug RHEL bug?
 * @return boolean
 */
function bzPage.prototype.isRHELBug() {
    return getProduct().search(/Red Hat Enterprise Linux/) != -1;
}

/**
 * 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 bzPage.prototype.fixElement(rootElement,beforeText,accKey,afterText) {
	rootElement.attr("accesskey",accKey.toLowerCase());
	rootElement.innerHTML = beforeText + "<b><u>" + accKey + "</u></b>" + 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 bzPage.prototype.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
	jetpack.tabs.focused.contentDocument.body.style.background = brandColor;
	$("#titles",this.document).style.background = brandColor;

	 // Make background-color of the body of bug salmon pink
    // for security bugs.
	 if (hasKeyword("Security")) {
		  var divBody = $("#bugzilla-body",this.document);
		  divBody.style.backgroundImage = "none";
		  divBody.style.backgroundColor = SalmonPink;
	 }


	// we should make visible whether maintCCAddr is in CCList
	if (isInList(maintCCAddr, CCList)) {
		var switchCCEdit=$("#cc_edit_area_showhide",this.document);
		//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 bzPage.prototype.addNewButton(originalLocation,newId,newLabel,commentString,nState,secPar,doSubmit) {
	if (doSubmit === null) { // missing optional argument
		doSubmit = true;
	}
	var newButton = this.originalButton.cloneNode(true);
	if (!doSubmit) {
		newButton.attr("type","button");
	}
	newButton.id=newId;
	newButton.attr("value",newLabel);
	var commStr = "";
	if (msgStrs[commentString]) {
		commStr = msgStrs[commentString];
	}
	newButton.addEventListener('click',function (evt) {
		generalPurposeCureForAllDisease(commStr, nState, secPar);
	},true);
	var textNode = $("\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 bzPage.prototype.addKeyword(str) {
	var kwd = jetpack.tabs.focused.contentDocument.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 bzPage.prototype.hasKeyword(str) {
	 var kwd = $.trim($('#keywords').text());
	 return (RegExp(str).test(kwd));
}

/**
 * Add XGL  to the CC list
 *
 * @param evt event which made this function active
 * @return none
 */
function bzPage.prototype.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 = $("#set_default_assignee",this.document);
	setDefaultAssigneeCheckbox.checked = false;
	selectOption("bug_status", "ASSIGNED");
}

/**
 * Set the bug to NEEDINFO state
 *
 * Working function.
 * @return none
 */
function bzPage.prototype.setNeedinfoReporter() {
	var checkbox = $("#needinfo",this.document);
	checkbox.click();
	selectOption("needinfo_role", "reporter");
}

/**
 * Add text to the comment.
 * @param string2BAdded string to be added to the comment box
 *
 * @return none
 */
function bzPage.prototype.addTextToComment(string2BAdded) {
	var commentTextarea = $("#comment",this.document);

	// 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 bzPage.prototype.addToCC(address) {
	var sel = $("#newcc",this.document);
	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 bzPage.prototype.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") {
 			$("#addselfcc",this.document).checked = true;
 		} else if (secondParameter == "NODEFAULTASSIGNEE") {
 			$("#set_default_assignee",this.document).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 bzPage.prototype.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 bzPage.prototype.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 bzPage.prototype.addClosingUpstream() {
	var refs = $("#external_bugs_table > tr",this.document);
	var inputBox = $("#inputbox",this.document);
	var externalBugID = 0;
	var wholeURL = "";

	// Fix missing ID on the external_id SELECT
    $("*[name*='external_id']:first",this.document).attr("id","external_id");

	if (inputBox.text().match(/^http.*/)) {
		wholeURL = inputBox.text();
		var IBURLArr = this.parseURL(wholeURL);
		console.log("IBURLArr = " + IBURLArr.toSource());
		externalBugID = parseInt(IBURLArr.params["id"]);
		inputBox.text(externalBugID);
		var bugzillaName = getBugzillaName(IBURLArr.host);
		selectOption("external_id", bugzillaName);
		console.log("externalBugID = " + externalBugID);
	} else if (!isNaN(inputBox.text())) {
		externalBugID = parseInt(inputBox.text());
		var bugzillaID = $("#external_id",this.document).text();
		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.get().length > 2) || (externalBugID > 0)) {
		addTextToComment(msgStrs['sentUpstreamString'].replace("§§§",wholeURL));
		selectOption("bug_status", "CLOSED");
		selectOption("resolution", "UPSTREAM");
	} else {
		jetpack.notifications.show("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 bzPage.prototype.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 = $("#cc",this.document);
		selBox[myAddrIndex].selected = true;
		$("#removecc",this.document).checked = true;
	}
	evt.stopPropagation();
	evt.preventDefault();
}

/**
 * Go through all &lt;a&gt; elements in the page and fix those starting with #
 * to point to the real bug, not to process.cgi page.
 * @param bugNo
 */
function bzPage.prototype.fixAllHrefs(bugNo) {
	var AList = $("a").get();
	var curA;
	var hrefStr = "";
	var reProcess = RegExp("");

	for(var i=0;i<AList.length;i++) {
		curA = $(AList[i]);
		if (curA.attr("href")) {
			hrefStr = curA.attr("href");
			if (reProcess.test(hrefStr)) {
				hrefStr = "show_bug.cgi?id="+bugNo.to""+hrefStr;
				curA.attr("href",hrefStr);
			}
		}
	}
}

/** Insert row of buttons before the marked element
 * @param anchor element before which the row of buttons will be inserted
 * @param array  array of data for buttons to be generated
 */
function bzPage.prototype.generateToolBar(anchor,array) {
	for (var i=0; i<array.length; i++) {
		var butt = array[i];
		addNewButton(anchor, butt['idx'],
				butt['msg'], butt['string'], butt['state'], butt['parameter'], butt['submit']);
	}
}

/**
 * make sure that prefs.js contain password for bugzilla
 * @return None
 */
function bzPage.prototype.checkPrivateValues() {
    var password = "";
    if (myStorage.BZpassword) {
        password = myStorage.BZpassword;
    }
    if (password == "") {
        password = prompt("Enter your Bugzilla password","");
        myStorage.BZpassword = password;
    }
}

/**
 * Main executable functioning actually building all buttons on the page -- separated into function, so that
 * it could be called from onload method of the XMLHttpRequest.
 * @param jsonList Array created from JSON
 * @return none
 */
function bzPage.prototype.buildButtons(above,below) {
    /**
     * There is a login printed on each page, go get it.
     * @return string with the login
     */
     function getLogin() {
    	var tmpElement = {};
    	var tmpText = "";

        // FIXME tohle bude špatně
    	var loginText = $.trim($("#header > ul:first > li:last >*:last",this.document).text());
    	console.log("loginText = " + loginText);
    	return loginText;
    }

    /**
     * Is this bug Xorg bug?
     * @return boolean
     */
    function isXorgBug() {
    	return this.maintCCAddr == "xgl-maint@redhat.com";
    }

	//----------------------------------------------
	//Generate a list of <input> 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 = $("<br/>");
	$("#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);