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


                                                     
                                                            
                                           

                    
                       
 




                                                                              

                                          
 
                   
                

                                                                    
                                                       
   


                                                           
                                                               
                      


                                       

                                                                   


                                          
                                 










                                                            
                                                                       
         

     
 
   
                                                     
  






                                                                                        
   


                                                 
     
 






                                                                          
                                                       
                                                                   
                       

                




























































































                                                                                           
     
 

   






















                                                                                     
                             
  


                                                                              

               
                                       
                                                                 
                                 
                                                               
                            

                          
                               
                                                                        



                       
                             
                                        
                                              








                                     


                                                                        
                                                                       



                                                     
 








                                                              

                                                           
                  
                                                        




                                                                            
                                                           
                                                          


                                                                   


                  


                                               
                            
 

   






                                                                                        
                                                                  

               

                                                                                          



                                   
                                                    



                                      
 
                                               



                                                      

                                









                                                          
 
                                                 
                                                            
                                   
                                                                        
     

                                    
 


   








                                                                             
                                                    






                                                                                    

                                        

                                                                     


                
                                                    

                                             
                                  
                                                       
                               

              
                                                             
 



                                                               
                                                             


                                                                                  
                                                             

                                                                 





                                                                                      
     
 

   
                           
   
                                         


                                            







                                                                 
                                        

                                                        



                                                              


                                           



















                                                                      


             




                                                                                         
 
 


                                                                                       
                                                                                         

                                                         
                                                                    

                       
                                                            


                                  
 
 
   








                                                                             



                                                    

                                                                                 

                                                                               
                                                                                       
     
                                                                                                
                                        
 
 

                                                    

                          
     
                
 
 






                                                  
 


                                                       

                                            
       
 




                                                                               



                                                                    
                                               
                                     
                                                               

         
 
 
                             
                    
                                


                                                                      
 


   
                                                                        






                                                                       
   
                                              
                     

                         
                                          
                      
                    
                                                          


                                                 
 
 
                                                    


                         
                                          

                                                             
                                               
       





                                       
                                                          


                                                 
 
 





                                                        


                                                                              
                                                          
                                                                
 








                                                                

                                              



                                                       
                                                         
     
 






                                                          
                                             
                                                 
                                                                      
 







                                                                          
                                   

                 
                                                



                                                                               
                                                       
 






                                      

                                             
 

   


                                                                    
   

                                                          

                                                                  
                                                                           

                                                                                   
                                                       
                                                                              
       
 
                             

                                                        

                
 

   






                                                                


                                              
 



   
                      
                                        

                                                                                    
 

   




                                                                             

                                                                           
                   
 

   





                                                                           

                                                
   
                                      




                                                                  
                    


                                     

                                                                         
                                             



                                                     
                                                            

                                 

                                                                                           



                                                   
 

   






                                                                           
                            
                     

                                                           
                                                         
                                                   

                   
 

   



                                                                     

                                                                                   


                                                         
 
 



















                                                      
   
   
                          
                                     
                                                      


                
                                                       
                                                                                       


                                        

                                                                                         
                                          



                                      
                                      

     
                                                                       
                                                                        
                       
                                                   
                             
 
                                                              
                       
                                                
                             
 
                                                                    
                       
                                                 
                             
 
           
                                                             
                             

                                                            


                                                           
     
      
 
 



                                                
                                                               

                
 










                                                                            

                                                                                              



                                               













                                                                             
     
 











                                                                                   
                                                           
     
 




                                                                             
                              
                              



                                                               
     
 






                                                                     
                            



                         
                                                            
            


                                            

         
 





                                                 

                                                     
                       





                                                                    
 
 
                    

                                                                
                                                              


                                         

                                                                            

     















                                                                                     
                                                                                   
                                                                    
                                                                                 
 

                                                         

                                   
 
                    
 



                                                     
 

          
// 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;

// 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
var EmptyLogsColor = new Color(0, 255, 0);
var FullLogsColor = new Color(0, 40, 103);

// global variables
var config = {};
var constantData = {}; // This should be probably eliminated ASAP or
                       // or done by other means. TODO
var submitHandlerInstalled = false; // for setUpLogging
/**
* central handler processing messages from the main script.
*/
onMessage = function onMessage(msg) {
    console.log("onMessage - incoming : msg.cmd = " + msg.cmd);
    switch (msg.cmd) {
    case "ReloadThePage":
        document.location.reload(true);
        break;
    case "queryLocal":
        queryInNewTab(msg.data.data, getComponent(), getProduct());
        break;
    case "CreateButtons":
        constantData = msg.data.constData;
        config = msg.data.config;
        generateButtons(msg.data.instPkgs, msg.data.kNodes);
        break;
    case "Error":
        alert("Error " + msg.data);
        break;
    case "Unhandled":
        break;
    default:
        if (RHOnMessageHandler) {
            RHOnMessageHandler(msg);
        } else {
            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) {
    console.log("centralCommandDispatch : cmdLabel = " + cmdLabel);
    switch (cmdLabel) {
    case "name":
        break;
    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;
    default:
        if (RHcentralCommandDispatch) {
            RHcentralCommandDispatch(cmdLabel, cmdParams);
        } else {
            console.error("Unknown command:\n" + cmdLabel + "\nparameters:\n" + cmdParams);
        }
        break;
    }
}

/**
 * remove elements from the page based on their IDs
 *
 * @param doc Document object
 * @param target String/Array with ID(s)
 * @param remove Boolean indicating whether the node should be
 * actually removed or just hidden.
 * @return none
 * TODO remove parameter could be replaced by function which would
 * do actual activity.
 */
function killNodes(doc, target, remove) {
    var targetArr = target instanceof Array ? target : target.trim().split(/[,\s]+/);
    targetArr.forEach(function(x) {
        if (remove) {
            var targetNode = doc.getElementById(x);
            targetNode.parentNode.removeChild(targetNode);
        } else {
            x.style.display = "none";
        }
    });
}

/**
 * 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) {
    console.log("changeAssignee : newAssignee = " + 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("defassignee_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 before 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.textContent = text;

    if (typeof callback === "string") {
        newAElem.setAttribute("href", callback);
    } else {
        newAElem.setAttribute("href", "");
        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) {
    try {
        var newId = cmdObj.name.toLowerCase().replace(/[^a-z0-9]+/,"","g") + "_btn";
    } catch (e) {
        console.log("createNewButton : e = " + e);
        console.log("createNewButton : cmdObj.toSource() = " +
            cmdObj.toSource());
    }

    // protection against double-firings
    if (document.getElementById(newId)) {
        console.log("Element with id " + newId + " already exists!");
        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, kNodes) {
    var topRowPosition = "topRowPositionID";
    var bottomRowPosition = "commit";

    setUpLogging();    

    // ========================================================= 
    if (kNodes && window.location.hostname in kNodes) {
            var killConf = killNodes[window.location.hostname];
            killNodes(document, killConf[0], killConf[1]);
    }

    // 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];
            if (cmdObj.position !== undefined) {
                switch (cmdObj.position) {
                case "topRow":
                    createNewButton(topRowPosition, false, cmdObj);
                    break;
                case "bottomRow":
                    createNewButton(bottomRowPosition, false, cmdObj);
                    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;
                }
            } else {
                console.log("generateButtons : rejected cmdObj = " +
                    cmdObj.toSource());
            }
        }
    }
    // TODO This is weird in this place, but that's the place where all constantData etc.
    // are finally defined and available.
    if (RHBZinit) {
        RHBZinit();
    }
}

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);
}

/**
 * Get the current title of the bug
 *
 * @return string
 */
function getSummary() {
    return document.getElementById("short_desc_nonedit_display").textContent;
}

/**
 * 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
 *
 * TODO error handling is missing ... it's bleee
 */
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
    var attName = inElem.getElementsByTagName("b")[0].textContent.trim();

    // TODO probably could use url.URL object
    var aHrefsArr = inElem.getElementsByTagName("a");
    var aHref = Array.filter(aHrefsArr, function(x) {
        return x.textContent.trim() === "Details";
    })[0];
    var id = parseURL(aHref.getAttribute("href")).params.id;

    // 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;
}

function addLogRecord() {
    var rec = {};
    rec.date = new Date();
    rec.url = document.location.toString();
    rec.title = document.title;
    var comment = window.prompt(
            "Enter comments for this comment");
    if (comment && comment.length > 0) {
        comment = comment.trim();
        rec.comment = comment;
        var dateStr = getISODate(rec.date);
        var urlStr = window.location.hostname;
        rec.key = dateStr + "+" +
            urlStr + "+" + getBugNo();
        postMessage(new Message("AddLogRecord", rec));
        return rec;
    }
    return null;
}

/**
 */
function setUpLogging () {
    // Protection against double-call
    if (document.getElementById("generateTSButton")) {
        return ;
    }

    // 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) {
            if (addLogRecord() === null) {
                evt.stopPropagation();
                evt.preventDefault();
            }
        }, false);
        submitHandlerInstalled = true;
    }

    // (id, text, parent, callback, params, before, covered, accesskey)
    createDeadLink("generateTSButton", "Generate TS", additionalButtons,
        function(evt) {
            postMessage(new Message("GenerateTS"));
        }, [], "dash", "li");

    createDeadLink("clearLogs", "Clear TS", additionalButtons,
        function(evt) {
            postMessage(new Message("ClearTS"));
        }, [], "dash", "li");

    createDeadLink("importTSButton", "Import TS", additionalButtons,
        function(evt) {
            postMessage(new Message("ImportTS"));
        }, [], "dash", "li");

    /* TODO
    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().trim();
    }
    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("OpenURLinPanel", 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.
    }

    // Prepare for query buttons
    // element ID brElementPlace_location is later used in JSON files
    // Stay with this add_comment element even if RH BZ upgrades, this seems
    // to be generally much more stable (even with other bugzillas, e.g. b.gnome.org)
    // then some getElementById.
    var commentArea = document.getElementsByName("add_comment")[0].parentNode;
    if (commentArea) {
        var brElementPlacer = commentArea.getElementsByTagName("br");
        brElementPlacer = brElementPlacer[0];
        if (brElementPlacer) {
            brElementPlacer.setAttribute("id","brElementPlacer_location");
            brElementPlacer.parentNode.insertBefore(document.createElement("br"),
                brElementPlacer);
        }
    }

    // TODO Probably could be ignored ... used only once on line 973 of rhbzpage.js
    // if (parseAbrtBacktraces && this.RE.Abrt.test(getSummary())) {
    // 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();

    postMessage(new Message("GetInstalledPackages", {
        location: window.location.href,
        login: getLogin()
    }));
}

startup();