aboutsummaryrefslogblamecommitdiffstats
path: root/data/bzpage.js
blob: 086fe44f796f4c472969ee85339fe9cdc20e86de (plain) (tree)
1
2
3
4
5
6
7
8
9
                                                                                                                                                                                                                                                           


                                                     
                                                            
                                           

                    
                       
                                        
 




                                                                              
 




                                                                    















                                                    









                                                                    
   












                                                                           
             













                                                                       
 
   
                                                     
  






                                                                                        
   


                                                 
     
 






                                                                          
                                                       





                          
                                              

                      
                                                  

                        
                                                    

                  
                                              

                        
                                                    

                      
                                                        

                          
                                                    

                             
                                                          

                             
                                                             

                                
                                                                  

                        
                                      

                         

                                                                    

                   

                                                                      


                                                             

                                                      

                               

                                                           

                         

                                                    

                            

                                                         

                       
                                                    

                          
                                                               



                                                                       
                                  

                     
                                   
                  
                                      
                                
                  
                               

                            

                                                                


                  
 

   
                             
  


                                                                              

               
                                       
                                 
                                                               
                            

                          
                               
                                                                        



                       
                             
                                        
                                              








                                     



                                                                              



                                                     
 








                                                              

                                                           
                  
                                                        




                                                                            
                                                           
                                                          


                                                                   


                  


                                               
                            
 

   









                                                                                        

                                                                                          



                                   
                                                    



                                      
 
                                               







                                                      
                         



                              
                                                 
                                                            
                                   
                                                                        
     

                                    
 


   








                                                                             

                                                                                

                                        

                                                                     




                                                                                   

                                                             



                    
                                                    

                                             
                                  
                                                       
                               

              
                                                             
 



                                                               
                                                             


                                                                                  
                                                             

                                                                 





                                                                                      
     
 

   
                            
   
                                 



                                            

                                                        



                                                              


                                           

                                      
                                                                   

                                 
                                                                      

                                
                                                  



                                                             
                                                                     



                          
 
 


                                                                                       
                                                                                         

                                                         
                                                                    

                       
                                                            


                                  
 
 
  


                                                                                    




                                                                
                                
                                    
                    
                   
                                                                    




                                                                                                 
         
                           

                
 
 




                                                    

                                                                                 

                                                                               
                                                                                       
     
                                                                                                
                                        
 
 

                                                    

                          
     
                
 
 






                                                  
 


                                                       

                                            
       
 




                                                                               



                                                                    
                                               
                                     
                                                               

         
 
 
                             
                    
                                


                                                                      
 


   
                                                                        






                                                                       
   
                                              
                     

                         
                                          
                      
                    
                                                          


                                                 
 
 
                                                    


                         
                                          

                                                             
                                               
       





                                       
                                                          


                                                 
 
 





                                                        


                                                                              
                                                          
                                                                
 








                                                                

                                              



                                                       
                                                         
     
 






                                                          
                                             
                                                 
                                                                      
 







                                                                          
                                   

                 
                                                



                                                                               
                                                       
 






                                      

                                             
 

   


                                                                    
   

                                                          

                                                                  
                                                                           

                                                                                   
                                                       
                                                                              
       
 
                             

                                                        

                
 

   






                                                                


                                              
 



   
                      
                                        

                                                                                    
 

   




                                                                             

                                                                           
                   
 

   






                                                                           
                                      



















                                                                         

                                                                                           



                                                   
 

   






                                                                           
                            
                     

                                                           
                                                         
                                                   

                   
 

   



                                                                     

                                                                                   


                                                         
 

   
                                                          
   
                          
                                     
                                                      


                


                                    
                                                       
                                                                                       


                                        


                                                                                         
                                                   
                                



                                      
                                      

     
                            


                                                                             

                                                                    
                             
 
                            



                                                                   
 
                            




                                                                         
 
                                                             
                             

                                                            


                                                           
     
 
 




                                                        

                
 










                                                                            

                                                                                              



                                               













                                                                             
     
 











                                                                                   
                                                            
     
 




                                                                             





                                                               
     
 






                                                                     
                            



                         
                                                            
            


                                            

         
 





                                                 

                                                     
                       





                                                                    
 
 
                    

                                                                
                                                              


                                         

                                                                            

     



                                                                                   

                                                         

                                   
 
                    
 


                                                                      
/*jslint forin: true, rhino: true, onevar: false, browser: true, evil: true, laxbreak: true, undef: true, nomen: true, eqeqeq: true, bitwise: true, maxerr: 1000, immed: false, white: false, plusplus: false, regexp: false, undef: false, strict: true */
// Released under the MIT/X11 license
// http://www.opensource.org/licenses/mit-license.php
"use strict";
var bugURL = "https://bugzilla.redhat.com/show_bug.cgi?id=";
var BTSPrefNS = "bugzilla-triage.setting.";

// Shared contstants
var NumberOfFrames = 7;
exports.NumberOfFrames = NumberOfFrames;

// constants
var SalmonPink = new Color(255, 224, 176); // RGB 255, 224, 176; HSL 36, 2,
                                             // 85
var ReporterColor = new Color(255, 255, 166); // RGB 255, 255, 166; HSL 60, 2,
                                              // 83

// global variables
var constantData = {}; // This should be probably eliminated ASAP or
                       // or done by other means. TODO
var submitHandlerInstalled = false; // for setUpLogging 
// ============================================
/**
 * object to pack messaging. Use as in
        postMessage(new Message("GetPassword", {
            login: login,
            hostname: location.hostname
        }));
 */
function Message(cmd, data) {
	this.cmd = cmd;
	this.data = data;
}

function log(msg) {
	postMessage(new Message("LogMessage", msg));
}

var NotLoggedinException = function NotLoggedinException (message) {
    this.message = message;
    this.name = "NotLoggedinException";
};

NotLoggedinException.prototype.toString = function () {
    return this.name + ': "' + this.message + '"';
};
exports.NotLoggedinException = NotLoggedinException;

/**
* central handler processing messages from the main script.
*/
onMessage = function onMessage(msg) {
    console.log("onMessage: msg = " + msg.toSource());
    switch (msg.cmd) {
        case "ReloadThePage":
            document.location.reload(true);
            break;
        case "RetClipboard":
            if (msg.data.cmd == "queryLocal") {
                queryInNewTab(msg.data.data, getComponent(), getProduct());
            } else if (msg.data.cmd == "queryUpstream") {
                //
            }
            break;
        case "CreateButtons":
            constantData = msg.data.constData;
            generateButtons(msg.data.instPkgs);
            break;
        case "Error":
            alert("Error " + msg.data);
            break;
        case "Unhandled":
            break;
        default:
            console.error("Error: unknown RPC call " + msg.toSource());
    }
};

/**
 * @param cmd Object with all commands to be executed
 *
 * PROBLEM: according to https://developer.mozilla.org/en/Core_JavaScript_1.5_Reference\
 * /Statements/for...in there is no guaranteed order of execution of
 * commands (i.e., key, commentObj[key] pairs) in for..in cycle.
 * According to https://developer.mozilla.org/en/Core_JavaScript_1.5_Reference\
 * /Operators/Special_Operators/delete_Operator#Cross-browser_issues it seems that
 * everywhere except of Internet Explorer this should work well, but waiting
 * impatiently when this bite us.
 */
function executeCommand(cmdObj) {
    for (var key in cmdObj) {
        centralCommandDispatch(key, cmdObj[key]);
    }
}

/**
 * Actual execution function
 *
 * @param cmdLabel String with the name of the command to be executed
 * @param cmdParams Object with the appropriate parameters for the command
 */
function centralCommandDispatch (cmdLabel, cmdParams) {
    switch (cmdLabel) {
        case "resolution":
        case "product":
        case "component":
        case "version":
        case "priority":
            selectOption(cmdLabel, cmdParams);
            break;
        case "status":
            selectOption("bug_status", cmdParams);
            break;
        case "platform":
            selectOption("rep_platform", cmdParams);
            break;
        case "os":
            selectOption("op_sys", cmdParams);
            break;
        case "severity":
            selectOption("bug_severity", cmdParams);
            break;
        case "target":
            selectOption("target_milestone", cmdParams);
            break;
        case "addKeyword":
            addStuffToTextBox("keywords",cmdParams);
            break;
        case "removeKeyword":
            removeStuffFromTextBox("keywords", cmdParams);
            break;
        case "addWhiteboard":
            addStuffToTextBox("status_whiteboard",cmdParams);
            break;
        case "removeWhiteboard":
            removeStuffFromTextBox("status_whiteboard",cmdParams);
            break;
        case "assignee":
            changeAssignee(cmdParams);
            break;
        case "qacontact":
            clickMouse("bz_qa_contact_edit_action");
            document.getElementById("qa_contact").value = cmdParams;
            break;
        case "url":
            clickMouse("bz_url_edit_action");
            document.getElementById("bug_file_loc").value = cmdParams;
            break;
        // TODO dependson/blocked doesn't work. Find out why.
        case "addDependsOn":
            clickMouse("dependson_edit_action");
            addStuffToTextBox("dependson", cmdParams);
            break;
        case "removeDependsOn":
            clickMouse("dependson_edit_action");
            removeStuffFromTextBox("dependson", cmdParams);
            break;
        case "addBlocks":
            clickMouse("blocked_edit_action");
            addStuffToTextBox("blocked", cmdParams);
            break;
        case "removeBlocks":
            clickMouse("blocked_edit_action");
            removeStuffFromTextBox("blocked", cmdParams);
            break;
        case "comment":
            addStuffToTextBox("comment", cmdParams);
            break;
        case "commentIdx":
            throw "There should be no commentIdx here at all.";
            break;
        case "setNeedinfo":
            // cmdParams are actually ignored for now; we may in future
            // distinguish different actors to be target of needinfo
            setNeedinfoReporter();
            break;
        case "addCC":
            addToCCList(cmdParams);
            break;
        case "queryStringOurBugzilla":
            queryForSelection();
            break;
        // TODO flags, see also
        case "commit":
            if (cmdParams) {
              // Directly commit the form
                document.forms.namedItem("changeform").submit();
            }
            break;
    }
}

/**
 * Change assignee of the bug
 *
 * @param newAssignee String with the email address of new assigneeAElement
 *        or 'default' if the component's default assignee should be used.
 *        Value null clears "Reset Assignee to default for component" checkbox
 * @return none
 */
function changeAssignee (newAssignee) {
    var defAssigneeButton = null;
    // Previous assignee should know what's going on in his bug
    addToCCList(getOwner());

    // Optional value null
    if (newAssignee === null) {
        document.getElementById("set_default_assignee").removeAttribute(
            "checked");
        return ;
    }

    if (getDefaultAssignee) {
        if (newAssignee === "default") {
            var defAss = getDefaultAssignee();
            if (defAss) {
                newAssignee = defAss;
            } else {
                return ;
            }
        }
    }

    if (newAssignee) {
        clickMouse("bz_assignee_edit_action");
        document.getElementById("assigned_to").value = newAssignee;
        document.getElementById("set_default_assignee").checked = false;
        defAssigneeButton = document.getElementById("setDefaultAssignee_btn");
        if (defAssigneeButton) {
            defAssigneeButton.style.display = "none";
        }
    }
}

/**
 * Adds new option to the 'comment_action' scroll down box
 *
 * @param pkg String package name
 * @param cmd String with the name of the command
 * If the 'comment_action' scroll down box doesn't exist, this
 * function will set up new one.
 */
function addToCommentsDropdown (cmdObj) {
    var select = document.getElementById("comment_action");
    if (!select) {
        document.getElementById("comments").innerHTML +=
            "<div id='make_bugzilla_comment_action'>" +
            "  <label for='comment_action'>Add Comment: </label>" +
            "  <select id='comment_action'>" +
            "    <option value=''>-- Select Comment from List --</option>" +
            "</div>";
        select = document.getElementById("comment_action");
        select.addEventListener("change", function (evt) {
            var value = select.options[select.selectedIndex].value;
            log("value = " + value);
            executeCommand(value);
        }, false);
    }

    var opt = document.createElement("option");
    opt.value = cmdObj;
    opt.textContent = cmdObj.name;
    select.appendChild(opt);
}

/**
 * Create a A element leadink nowhere, but with listener running a callback on the click
 *
 * @param id String with a id to be added to the element
 * @param text String with a string to be added as a textContent of the element
 * @param parent Node which is a parent of the object
 * @param callback Function to be called after clicking on the link
 * @param params Array with parameters of the callback
 * @param Boolean breakBefore if there should be a <br> element before.
 * @return none
 */
function createDeadLink (id, text, parent, callback, params, before, covered, accesskey) {
    params = valToArray(params);
    var locParent = {};

    // Yes, I want != here, not !==
    if (covered != null) {
        locParent = document.createElement(covered);
        parent.appendChild(locParent);
    } else {
        locParent = parent;
    }

    var newAElem = document.createElement("a");
    newAElem.setAttribute("id", id);
    if (accesskey) {
        newAElem.setAttribute("accesskey", accesskey);
    }
    newAElem.setAttribute("href", "");
    newAElem.textContent = text;

    newAElem.addEventListener("click", function(evt) {
        callback(params);
        evt.stopPropagation();
        evt.preventDefault();
    }, false);

    if ((before === "br") || (before === true)) {
        locParent.appendChild(document.createElement("br"));
    } else if (before === "dash") {
        locParent.appendChild(document.createTextNode("\u00A0-\u00A0"));
    }

    locParent.appendChild(newAElem);
}


/**
 * Generic function to add new button to the page. Actually copies new button
 * from the old one (in order to have the same look-and-feel, etc.
 *
 * @param location Object around which the new button will be added
 * @param after Boolean before or after location ?
 * @param pkg String which package to take the command from
 * @param id String which command to take
 * @return none
 */
function createNewButton (location, after, cmdObj) {
    var newId = cmdObj.name.toLowerCase().replace(/[^a-z0-9]+/,"","g") + "_btn";

    // protection against double-firings
    if (document.getElementById(newId)) {
        console.log("Element with id " + newId + " already exists!");
        return ;
    }

    // creation of button might be conditional on existence of data in constantData
    if ("ifExist" in cmdObj) {
        // We do have constantData now, but we shouldn't TODO
        if (!(cmdObj.ifExist in constantData)) {
            return ;
        }
    }

    var newButton = document.createElement("input");
    newButton.setAttribute("id", newId);
    newButton.setAttribute("type", "button");
    newButton.value = cmdObj.name;
    newButton.addEventListener("click", function(evt) {
        executeCommand(cmdObj);
    }, false);

    var originalLocation = document.getElementById(location);

    try {
        if (after) {
            originalLocation.parentNode.insertBefore(newButton,
                    originalLocation.nextSibling);
            originalLocation.parentNode.insertBefore(document
                    .createTextNode("\u00A0"), newButton);
        } else {
            originalLocation.parentNode.insertBefore(newButton, originalLocation);
            originalLocation.parentNode.insertBefore(document
                    .createTextNode("\u00A0"), originalLocation);
        }
    } catch (e) {
        if (e instanceof TypeError) {
            console.error("cannot find originalLocation element with id " + location);
        } else {
            throw e;
        }
    }
}

/**
 * Generate button based on 
 */
function generateButtons (pkgs) {
    var topRowPosition = "topRowPositionID";
    var bottomRowPosition = "commit";

    // create anchor for the top toolbar
    var commentBox = document.getElementById("comment");
    var brElement = document.createElement("br");
    brElement.setAttribute("id",topRowPosition);
    commentBox.parentNode.normalize();
    commentBox.parentNode.insertBefore(brElement, commentBox);

    for (var pkg in pkgs) {
        for (var cmdIdx in pkgs[pkg]) {
            var cmdObj = pkgs[pkg][cmdIdx];
            switch (cmdObj.position) {
                case "topRow":
                    createNewButton(topRowPosition, cmdObj, false);
                    break;
                case "bottomRow":
                    createNewButton(bottomRowPosition, cmdObj, false);
                    break;
                case "dropDown":
                    addToCommentsDropdown(cmdObj);
                    break;
                default: // [+-]ID
                    var firstChr = cmdObj.position.charAt(0);
                    var newId = cmdObj.position.substr(1);
                    createNewButton(newId, firstChr === "+", cmdObj);
                    break;
            }
        }
    }
}

function setConfigurationButton () {
    var additionalButtons = document.querySelector("#bugzilla-body *.related_actions");
    var configurationButtonUI = document.createElement("li");
    configurationButtonUI.innerHTML = "\u00A0-\u00A0<a href='' id='configurationButton'>"
        + "Triage configuration</a>";
    additionalButtons.appendChild(configurationButtonUI);
    document.getElementById("configurationButton").addEventListener(
        "click",
        function(evt) {
            postMessage(new Message("ChangeJSONURL", null));
            evt.stopPropagation();
            evt.preventDefault();
    }, false);
}

/*
 * From <a> element diggs out just plain email address
 * Note that bugzilla.gnome.org doesn't have mailto: url but
 * https://bugzilla.gnome.org/page.cgi?id=describeuser.html&login=mcepl%40redhat.com
 *
 * @param aElement Element with href attribute or something else
 * @return String with the address or null
 *
 */
function parseMailto(aElement) {
    var emailStr = "", hrefStr = "";
    // use url utils
    if (aElement) {
        hrefStr = decodeURIComponent(aElement.getAttribute("href"));
        emailStr = hrefStr.split(":");
        // workaround for Gnome bugzilla ... no mailto: here.
        if (emailStr.length < 2) {
            var params = getParamsFromURL("https://" + window.location.hostname + "/" + hrefStr);
            return decodeURI(params.login);
        }
        return emailStr[1];
    }
    return null;
}

/**
 * Get the current email of the reporter of the bug.
 *
 * @return string
 */
function getReporter () {
    var reporterElement = getOptionTableCell("bz_show_bug_column_2", "Reported");
    // RH Bugzilla after upgrade to 3.6.2 moved the information to other column
    if (!reporterElement) {
        reporterElement = getOptionTableCell("bz_show_bug_column_1", "Reported", true);
    }
    // Maemo calls the label "Reporter" and it doesn't have ids on table columns ... TODO(maemo)
    return parseMailto(reporterElement);
}

function getComponent() {
    var elem = document.getElementById("component");
    if (elem) {
        return elem.value;
    }
    return null;
}

function getProduct() {
    var elem = document.getElementById("product");
    if (elem) {
        return elem.value;
    }
    return null;
}

function commentsWalker (fce) {
    var comments = document.getElementById("comments").
        getElementsByClassName("bz_comment");
    Array.forEach(comments, function(item) {
        fce(item);
    });
}

/**
 * Set background color of all comments made by reporter in ReporterColor color
 *
 */
function checkComments () {
    var reporterRE = new RegExp(getReporter());
    commentsWalker(function(x) {
        var email = parseMailto(x.getElementsByClassName("vcard")[0]
                .getElementsByTagName("a")[0]);
        if (reporterRE.test(email)) {
            x.style.backgroundColor = ReporterColor.toString();
        }
    });
}

function collectComments () {
    var outStr = "";
    commentsWalker(function(x) {
        outStr += x.getElementsByTagName("pre")[0].textContent + "\n";
    });
    return outStr.trim();
}


/**
 * Select option with given value on the <SELECT> element with given id.
 *
 * Also execute change HTMLEvent, so that the form behaves accordingly.
 *
 * @param id
 * @param label
 * @return none
 *
 */
function selectOption (id, label, fireEvent) {
    if (!fireEvent) {
        fireEvent = true;
    }
    var sel = document.getElementById(id);
    sel.value = label;
    if (fireEvent) {
        var intEvent = document.createEvent("HTMLEvents");
        intEvent.initEvent("change", true, true);
        sel.dispatchEvent(intEvent);
    }
}

function selectOptionByLabel(id, label, fireEvent) {
    if (!fireEvent) {
        fireEvent = true;
    }
    var sel = document.getElementById(id);
    var labelRE = new RegExp(label.trim());
    var ourOption = Array.filter(sel.options, function (op) {
        return op.textContent.trim() === label;
    });

    if (ourOption[0]) {
        sel.value = ourOption[0].value;
    }

    if (fireEvent) {
        var intEvent = document.createEvent("HTMLEvents");
        intEvent.initEvent("change", true, true);
        sel.dispatchEvent(intEvent);
    }
}

/**
 * Send mouse click to the specified element
 *
 * @param String ID of the element to send mouseclick to
 * @return None
 */
function clickMouse (targetID) {
    var localEvent = document.createEvent("MouseEvents");
    localEvent.initMouseEvent("click", true, true, document.defaultView, 0, 0,
            0, 0, 0, false, false, false, false, 0, null);
    document.getElementById(targetID).dispatchEvent(localEvent);
}

/**
 * Add object to the text box (comment box or status whiteboard)
 *
 * @param id String with the id of the element
 * @param stuff String/Array to be added to the comment box
 *
 * @return none
 */
function addStuffToTextBox (id, stuff) {
    var textBox = document.getElementById(id);
    if (textBox.tagName.toLowerCase() === "textarea") {
        stuff = textBox.value ? "\n\n" + stuff : stuff;
        textBox.value += stuff;
    } else {
        textBox.value = addCSVValue(textBox.value,stuff);
    }
}

/**
 * Remove a keyword from the element if it is there
 *
 * @param id String with the id of the element
 * @param stuff String/Array with keyword(s) to be removed
 */
function removeStuffFromTextBox (id, stuff) {
    var changedElement = this.getElementById(id);
    changedElement.value = removeCSVValue(changedElement.value,stuff);
}

/**
 * generalized hasKeyword ... search in the value of the box with given id
 *
 * @param id String with ID of the element we want to check
 * @param str String to be searched for
 * @return Boolean found?
 */
function idContainsWord (id, str) {
    var kwd = "";
    try {
        kwd = document.getElementById(id).value;
    } catch (e) {
        // For those who don't have particular element at all or if it is empty
        return false;
    }
    return (isInList(str, kwd.trim().split(/[,\s]+/)));
}

/**
 * Check for the presence of a keyword
 *
 * @param str String with the keyword
 * @return Boolean
 */
function hasKeyword (str) {
    return (idContainsWord('keywords', str));
}

/**
 * dd
 *
 * @return Element with the href attribute containng the information
 */
function getOptionTableCell(tableId, label) {
    var cleanLabelRE = new RegExp("^\\s*([^.:]*):?\\s*$");
    label = label.trim().replace(cleanLabelRE,"$1").toLowerCase();

    var rows = document.getElementById(tableId).getElementsByTagName("tr");
    var ourLine = Array.filter(rows, function(row) {
        var curLabel = row.getElementsByTagName("td")[0].textContent.toLowerCase();
        curLabel = curLabel.replace(cleanLabelRE,"$1");
        return (curLabel === label); // maybe this could be a RE match instead
    });

    if (ourLine.length > 0) {
        return ourLine[0].getElementsByTagName("td")[1].
            getElementsByTagName("a")[0];
    }
    return null;
}

/**
 * Set the bug to NEEDINFO state
 *
 * Working function.
 * @return none
 * @todo TODO we may extend this to general setNeedinfo function
 * with parameter [reporter|assignee|general-email-address]
 */
function setNeedinfoReporter () {
    clickMouse("needinfo");
    selectOption("needinfo_role", "reporter");
}

/**
 *
 */
function getOwner () {
    // TODO(maemo) doesn't work on maemo
    var assigneeAElement = getOptionTableCell("bz_show_bug_column_1","Assigned To");
    return parseMailto(assigneeAElement);
}

/**
 * Return maintainer which is per default by bugzilla
 * (which is not necessarily the one who is default maintainer per component)
 *
 * @return String with the maintainer's email address
 */
function getDefaultBugzillaMaintainer (component) {
    var address = filterByRegexp(this.defBugzillaMaintainerArr, component);
    return address;
}

/**
 * Parse the row with the attachment
 *
 * @param DOM 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 MIMEtype = "";
    var size = 0;

    // Skip over obsolete attachments
    if (inElem.getElementsByClassName("bz_obsolete").length > 0) {
        return ( []);
    }

    // getting name of the attachment
    // TODO probably could use url.URL object
    var attName = inElem.getElementsByTagName("b")[0].textContent.trim();

    var aHrefsArr = inElem.getElementsByTagName("a");
    var aHref = Array.filter(aHrefsArr, function(x) {
        return x.textContent.trim() === "Details";
    })[0];
    var id = parseInt(aHref.getAttribute("href").replace(
            /^.*attachment.cgi\?id=/, ""), 10);

    // getting MIME type and size
    var stringArray = inElem.getElementsByClassName("bz_attach_extra_info")[0].textContent.
            replace(/[\n ()]+/g, " ").trim().split(", ");
    size = parseInt(stringArray[0], 10);
    MIMEtype = stringArray[1].split(" ")[0];

    return [ attName, id, MIMEtype, size, inElem ];
}

/**
 * collect the list of attachments in a structured format
 *
 * @return Array of arrays, one for each attachments;
 * each record has string name of the attachment, integer its id number,
 *         string of MIME type, integer of size in kilobytes, and the whole
 *         element itself
 */
function getAttachments () {
    var outAtts = [];
    var atts = document.getElementById("attachment_table").
            getElementsByTagName("tr");
    for ( var i = 1, ii = atts.length - 1; i < ii; i++) {
        outAtts.push(parseAttachmentLine(atts[i]));
    }
    return outAtts;
}

/**
 * Get login of the currently logged-in user.
 *
 * @return String with the login name of the currently logged-in user
 */
function getLogin () {
    var lastLIElement = document.querySelector("#header ul.links li:last-of-type");
    var loginArr = lastLIElement.textContent.split("\n");
    var loginStr = loginArr[loginArr.length - 1].trim();
    return loginStr;
}

/**
 * TODO THIS IS COMPLETELY BROKEN AND NEED TO BE REWRITTEN
 */
function setUpLogging () {
    // Protection against double-call
    if (document.getElementById("generateTSButton")) {
        return ;
    }

    // TODO fix later
    return ; // switched off for now

    // For adding additional buttons to the top toolbar
    var additionalButtons = document.querySelector("#bugzilla-body *.related_actions");
    var that = this;

    // logging all submits for timesheet
    if (!submitHandlerInstalled) {
        document.forms.namedItem("changeform").addEventListener("submit",function (evt) {
            // TODO this is probably another RPC call
            var resp = that.log.addLogRecord(that);
            if (resp === null) {
                evt.stopPropagation();
                evt.preventDefault();
            }
        }, false);
        submitHandlerInstalled = true;
    }

    // TODO another RPC call
    // (id, text, parent, callback, params, before, covered, accesskey)
    this.createDeadLink("generateTSButton", "Generate TS", additionalButtons,
        function(evt) {
                that.log.createBlankPage.call(that.log, "TimeSheet",
                        that.log.generateTimeSheet);
        }, [], "dash", "li");

    // TODO another RPC call
    this.createDeadLink("clearLogs", "Clear TS", additionalButtons,
        function(evt) {
            that.log.clearStore(this);
        }, [], "dash", "li");

    // TODO another RPC call
    this.createDeadLink("importTSButton", "Import TS", additionalButtons,
        function(evt) {
            jsonPaths = prompts.promptFileOpenPicker(that.win);
            that.log.importOtherStore(jsonPaths, clearLogAElem);
        }, [], "dash", "li");

    var clearLogAElem = document.getElementById("clearLogs");
    if (this.log.isEmpty()) {
        clearLogAElem.style.color = this.log.EmptyLogsColor;
        clearLogAElem.style.fontWeight = "normal";
    } else {
        clearLogAElem.style.color = this.log.FullLogsColor;
        clearLogAElem.style.fontWeight = "bolder";
    }
}

function getSelection () {
    var text = "";
    var selectionObject = window.getSelection();
    if (selectionObject.rangeCount > 0) {
        text = selectionObject.getRangeAt(0).toString();
    }
    return text;
}

/**
 * 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, if undefined,
 *        search in all products
 * @return None
 *
 */
function queryInNewTab(text, component, product) {
    var urlStr = "https://" + window.location.hostname + "/buglist.cgi?query_format=advanced";
    if (product) {
        urlStr += "&product=" + product.trim();
    }
    if (component) {
        if ("equivalentComponents" in this.constantData) {
            var equivCompsArr = this.constantData["equivalentComponents"].
                filter(function (REstr) {
                    return new RegExp(REstr).test(this.getComponent());
            }, this);
            if (equivCompsArr.length > 0) {
                component = equivCompsArr[0];
            }
            urlStr += "&field0-0-0=component&type0-0-0=regexp&value0-0-0="
                    + component.trim();
        } else {
            urlStr += "&field0-0-0=component&type0-0-0=substring&value0-0-0="
                    + component.trim();
        }
    }

    // using more complicated query tables here, because they can be more easily
    // edited
    // for further investigative searches
    if (text) {
        text = encodeURIComponent(text.trim());
        var searchText = "&field1-0-0=longdesc&type1-0-0=substring&value1-0-0="
                + text
                + "&field1-0-1=attach_data.thedata&type1-0-1=substring&value1-0-1="
                + text
                + "&field1-0-2=status_whiteboard&type1-0-2=substring&value1-0-2="
                + text;
        urlStr += searchText;
        postMessage(new Message("OpenURLinNewTab", urlStr));
    }
}

/**
 * Get the text to search for and prepare other things for the real executive
 * function this.queryInNewTab, and run it.
 */
function queryForSelection() {
    var text = getSelection();    
    if (!text) {
        postMessage(new Message("GetClipboard", "queryLocal"));
    } else {
        queryInNewTab(text, getComponent(), getProduct());
    }
}

/**
 * adds a person to the CC list, if it isn't already there
 *
 * @param who String with email address or "self" if the current user
 * of the bugzilla should be added
 */
function addToCCList (who) {
    if (!who) {
        return ;
    }
    if (who === "self") {
        document.getElementById("addselfcc").checked = true;
    } else {
        clickMouse("cc_edit_area_showhide");
        if (!isInList(who, getCCList())) {
            addStuffToTextBox("newcc",who);
        }
    }
}

/**
 * a collect a list of emails on CC list
 *
 * @return Array with email addresses as Strings.
 */
function getCCList () {
    var CCListSelect = document.getElementById("cc");
    var outCCList = [];
    if (CCListSelect) {
        outCCList = Array.map(CCListSelect.options, function(item) {
            return item.value;
        });
    }
    return outCCList;
}

function startup() {
    // First, preflight check ... if we are not logged in, there
    // is nothing we can do.
    var logoutLink = Array.some(document.links, function (x) {
        return x.search === "?logout=1" ;
    });
    if (!logoutLink) {
        return null; // This should just finish whole content script without
        // doing any harm to anybody.
    }

    // TODO Probably could be ignored ... used only once on line 973 of rhbzpage.js
    // if (parseAbrtBacktraces && this.RE.Abrt.test(this.title)) {
    this.title = document.getElementById("short_desc_nonedit_display").textContent;

    // So, now we know we are logged in, so we can get to
    // the real work.
    setConfigurationButton();
    submitHandlerInstalled = false;

    checkComments();

    var login = getLogin();
    postMessage(new Message("GetInstalledPackages", window.location));
}