/**
 * Global variables
 */
var nodesTab; // Nodes tabs
var origAttrs = new Object(); // Original node attributes
var nodeAttrs; // Node attributes
var nodesList; // Node list
var nodesTableId = 'nodesDatatable'; // Nodes datatable ID

/**
 * Set node tab
 * 
 * @param tab
 *            Tab object
 * @return Nothing
 */
function setNodesTab(tab) {
    nodesTab = tab;
}

/**
 * Get node tab
 * 
 * @return Tab object
 */
function getNodesTab() {
    return nodesTab;
}

/**
 * Get node list
 * 
 * @return Node list
 */
function getNodesList() {
    return nodesList;
}

/**
 * Get nodes table ID
 * 
 * @return Nodes table ID
 */
function getNodesTableId() {
    return nodesTableId;
}

/**
 * Load nodes page
 */
function loadNodesPage() {
    // If groups are not already loaded
    if (!$('#groups').length) {
        // Create a groups division
        var groups = $('<div id="groups"></div>');
        var nodes = $('<div id="nodes"></div>');
        $('#content').append(groups);
        $('#content').append(nodes);

        // Create loader and info bar
        groups.append(createLoader());

        // Get groups
        $.ajax( {
            url : 'lib/cmd.php',
            dataType : 'json',
            data : {
                cmd : 'extnoderange',
                tgt : '/.*',
                args : 'subgroups',
                msg : ''
            },

            // Load groups
            success : function(data){
                loadGroups(data);
                
                var cookieGroup = $.cookie('selectgrouponnodes');
                if (cookieGroup) {
                    $('#groups .groupdiv div').each(function(){
                        if ($(this).text() == cookieGroup){
                            $(this).trigger('click');
                            return false;
                        }
                    });
                } else {
                    // Trigger the first group click event
                    $('#groups .groupdiv div').eq(0).trigger('click');
                }
            }
        });
    }
}

/**
 * Show cluster summary in pie charts
 * 
 * @param groupName Group name
 */
function loadPieSummary(groupName){
    var summaryTable = '<table style="border: 0px none;">' +
                       '<tr>' +
                           '<td><div id="statuspie" class="summarypie"></div></td>' +
                           '<td><div id="ospie" class="summarypie"></div></td>' +
                           '<td><div id="archpie" class="summarypie"></div></td>' +
                       '</tr>' +
                       '<tr>' +
                           '<td><div id="provmethodpie" class="summarypie"></td>' +
                           '<td><div id="nodetypepie" class="summarypie"></div></td>' +
                       '</tr></table>';
    $('#summaryTab').append(summaryTable);
    $('#summaryTab .summarypie').append(createLoader());
    
    $.ajax({
        url : 'lib/cmd.php',
        dataType : 'json',
        data : {
            cmd : 'webrun',
            tgt : '',
            args : 'summary;' + groupName,
            msg : '' 
        },
        
        success:function(data) {
             for (var i in data.rsp) {
                 drawPieSummary(i, data.rsp[i]);
             }
        }
    });
}

/**
 * Get nodes information and draw pie chart
 * 
 * @param index Node index
 * @param valuePair Node information key value pairing
 */
function drawPieSummary(index, valuePair){
    var position = 0;
    var key = '';
    var val = '';
    var chartTitle = '';
    var dataArray = [];
    var tempArray = [];
    var container = $('#summaryTab .summarypie').eq(index);
    
    position = valuePair.indexOf('=');
    chartTitle = valuePair.substr(0, position);
    tempArray = valuePair.substr(position + 1).split(';');
    
    for (var i in tempArray) {
        position = tempArray[i].indexOf(':');
        key = tempArray[i].substr(0, position);
        val = Number(tempArray[i].substr(position + 1));
        dataArray.push([key,val]);
    }
                
    container.empty();
    
    var plot = $.jqplot(container.attr('id'), [dataArray], {
                title: chartTitle,
                seriesDefaults: {
                renderer: $.jqplot.PieRenderer,
                rendererOptions: {
                    padding: 5,
                    fill: true,
                    shadow: true,
                    shadowOffset: 2,
                    shadowDepth: 5,
                    shadowAlpha: 0.07,
                    dataLabels : 'value',
                    showDataLabels: true
                    }
                },
                legend: {
                    show:true,
                    location: 'e'
                }
            });
}

/**
 * Load groups
 * 
 * @param data Data returned from HTTP request
 */
function loadGroups(data) {
    // Remove loader
    $('#groups').find('img').remove();
    
    // Save group in cookie
    var groups = data.rsp;
    setGroupsCookies(data);

    // Create a list of groups
    $('#groups').append('<div class="grouplabel">Groups</div>');
    var grouplist= $('<div class="groupdiv"></div>');
    // Create a link for each group
    for (var i = 0; i < groups.length; i++) {
        grouplist.append('<div>' + groups[i] + '</div>');
    }
    
    $('#groups').append(grouplist);
    
    // Bind the click event
    $('#groups .groupdiv div').bind('click', function(){
        var thisGroup = $(this).text();
        $('#groups .groupdiv div').removeClass('selectgroup');
        $(this).addClass('selectgroup');
        
        // Save selected group into cookie
        $.cookie('selectgrouponnodes', thisGroup, { expires: 7 });
        
        drawNodesArea(thisGroup,'',thisGroup);
    });
    
    // Make a link to add nodes
    $('#groups').append('<div class="actionDiv" id="adddiv"></div>');
    $('#groups #adddiv').append(mkAddNodeLink());
}

/**
 * Empty the nodes area and add three tabs for nodes result
 * 
 * @param targetgroup The name range for nodels command
 * @param cmdargs Filter arguments for nodels command
 * @param message The useful information from the HTTP request
 */
function drawNodesArea(targetgroup, cmdargs, message){
    // Clear nodes division
    $('#nodes').empty();
    
    // Create a tab for this group
    var tab = new Tab('nodesPageTabs');
    setNodesTab(tab);
    tab.init();
    $('#nodes').append(tab.object());
    tab.add('summaryTab', 'Summary', '', false);
    tab.add('nodesTab', 'Nodes', '', false);
    tab.add('graphTab', 'Graphic', '', false);
           
    // Load nodes table when tab is selected
    $('#nodesPageTabs').bind('tabsselect', function(event, ui) {
        // Load summary when tab is selected
        if (!$('#summaryTab').children().length && ui.index == 0) {
            loadPieSummary(targetgroup);
        }
        
        // Load nodes table when tab is selected
        else if (!$('#nodesTab').children().length && ui.index == 1) {
            // Create loader
            $('#nodesTab').append($('<center></center>').append(createLoader()));
                    
            // To improve performance, get all nodes within selected group
            // Get node definitions only for first 50 nodes
            $.ajax( {
                url : 'lib/cmd.php',
                dataType : 'json',
                data : {
                    cmd : 'nodels',
                    tgt : targetgroup,
                    args : cmdargs,
                    msg : message
                },

                /**
                 * Get node definitions for first 50 nodes
                 * 
                 * @param data Data returned from HTTP request
                 */
                success : function(data) {
                    var rsp = data.rsp;
                    var group = data.msg;
                    
                    // Save nodes in a list so it can be accessed later
                    nodesList = new Array();
                    for (var i in rsp) {
                        if (rsp[i][0]) {
                            nodesList.push(rsp[i][0]);
                        }
                    }
                    
                    // Sort nodes list
                    nodesList.sort();
                    
                    // Get first 50 nodes
                    var nodes = '';
                    for (var i = 0; i < nodesList.length; i++) {
                        if (i > 49) {
                            break;
                        }
                        
                        nodes += nodesList[i] + ',';                        
                    }
                                
                    // Remove last comma
                    nodes = nodes.substring(0, nodes.length-1);
                    
                    // Get nodes definitions
                    $.ajax( {
                        url : 'lib/cmd.php',
                        dataType : 'json',
                        data : {
                            cmd : 'lsdef',
                            tgt : '',
                            args : nodes,
                            msg : targetgroup
                        },

                        success : loadNodes
                    });
                    
                }
            });
        }
        
        // Load graphical layout when tab is selected
        else if (!$('#graphTab').children().length && ui.index == 2) {
            // For the graphical tab, check the graphical data first
            createPhysicalLayout(nodesList);
        }
    });
        
    // Get last view (if any)
    // This can be summary, nodes, or graphic
    if ($.cookie('tabindex_history')) {
        var order = $.cookie('tabindex_history').split(',');
        order[0] = parseInt(order[0]);
        order[1] = parseInt(order[1]);
        if (order[0] == 0 || order[1] == 0) {
            // For some reason, you cannot trigger a select of index 0
            loadPieSummary(targetgroup);
        } else if (order[0] == 1 || order[0] == 2) {
            $('#nodesPageTabs').tabs('select', order[0]);
        } else if (order[1] == 1 || order[1] == 2) {
            $('#nodesPageTabs').tabs('select', order[1]);
        } else {
            loadPieSummary(targetgroup);
        }
    } else {
        loadPieSummary(targetgroup);
    }
}

/**
 * Make a link to add nodes
 * 
 * @returns Link to add nodes
 */
function mkAddNodeLink() {
    // Create link to add nodes
    var addNodeLink = $('<a title="Add a node or a node range to xCAT">+ Add Node</a>');
    addNodeLink.click(function() {
        // Create info bar
        var info = createInfoBar('Select the hardware management for the new node range');
        
        // Create form to add node
        var addNodeForm = $('<div class="form"></div>');
        addNodeForm.append(info);
        addNodeForm.append('<div><label for="mgt">Hardware management:</label>'
            + '<select id="mgt" name="mgt">'
                + '<option value="esx">ESX</option>'
                + '<option value="kvm">KVM</option>'
                + '<option value="zvm">z\/VM</option>'
                + '<option value="ipmi">iDataPlex</option>' 
                + '<option value="blade">BladeCenter</option>'
                + '<option value="hmc">System p</option>'    // Documentation refers to 'IBM System p' (where p is NOT capitalized)
            + '</select>'
        + '</div>');
        
        // Create advanced link to set advanced node properties
        var advanced = $('<div></div>');
        var advancedLnk = $('<a>Advanced</a>').css({
            'cursor': 'pointer',
            'color': '#0000FF'
        });
        advancedLnk.click(function() {
            // Get node attributes
            $.ajax( {
                url : 'lib/cmd.php',
                dataType : 'json',
                data : {
                    cmd : 'lsdef',
                    tgt : '',
                    args : '-t;node;-h',
                    msg : ''
                },

                /**
                 * Set node attributes and open dialog
                 * 
                 * @param data Data returned from HTTP request
                 */
                success : function(data) {
                    // Save node attributes
                    setNodeAttrs(data);
                    // Open a dialog to set node attributes
                    openSetAttrsDialog();
                }
            });
            
            // Close dialog
            addNodeForm.dialog('close');
        });
        advanced.append(advancedLnk);
        addNodeForm.append(advanced);
                    
        // Open dialog to add node
        addNodeForm.dialog({
            modal: true,
            width: 400,
            title:'Add node',
            close: function(){$(this).remove();},
            buttons: {
                'Ok': function(){
                    // Get hardware management
                    var mgt = $(this).find('select[name=mgt]').val();                    
                    
                    var plugin;
                    switch(mgt) {
                        case "kvm":
                            plugin = new kvmPlugin();
                            break;
                        case "esx":
                            plugin = new esxPlugin();
                            break;
                        case "blade":
                            plugin = new bladePlugin();
                            break;
                        case "hmc":
                            plugin = new hmcPlugin();
                            break;
                        case "ipmi":
                            plugin = new ipmiPlugin();
                            break;
                        case "zvm":
                            plugin = new zvmPlugin();
                            break;
                    }
                    
                    plugin.addNode();
                    $(this).dialog('close');
                },
                'Cancel': function(){
                    $(this).dialog('close');
                }
            }
        });

    });
    
    // Generate tooltips
    addNodeLink.tooltip({
        position: 'center right',
        offset: [-2, 10],
        effect: 'fade',
        opacity: 0.7,
        predelay: 800
    });
    
    return addNodeLink;
}

/**
 * Load nodes belonging to a given group
 * 
 * @param data Data returned from HTTP request
 */
function loadNodes(data) {
    // Clear the tab before inserting the table
    $('#nodesTab').children().remove();

    // Data returned    
    var rsp = data.rsp;
    // Group name
    var group = data.msg;
    // Hash of Node attributes
    var attrs = new Object();
    // Node attributes
    var headers = new Object();
    
    // Variable to send command and request node status
    var getNodeStatus = true;

    // Clear hash table containing node attributes
    origAttrs = '';
    
    var node, args;
    for (var i in rsp) {
        // Get node name
        if (rsp[i].indexOf('Object name:') > -1) {
            var temp = rsp[i].split(': ');
            node = jQuery.trim(temp[1]);

            // Create a hash for the node attributes
            attrs[node] = new Object();
            i++;
        }

        // Get key and value
        args = rsp[i].split('=', 2);
        var key = jQuery.trim(args[0]);
        var val = jQuery.trim(rsp[i].substring(rsp[i].indexOf('=') + 1));
        
        // Create a hash table
        attrs[node][key] = val;
        headers[key] = 1;
        
        // If node status is available
        if (key == 'status') {
            // Do not request node status
            getNodeStatus = false;
        }
    }
    
    // Add nodes that are not in data returned
    for (var i in nodesList) {
        if (!attrs[nodesList[i]]) {
            // Create attributes list and save node name
            attrs[nodesList[i]] = new Object();
            attrs[nodesList[i]]['node'] = nodesList[i];
        }
    }
    
    // Save attributes in hash table
    origAttrs = attrs;

    // Sort headers
    var sorted = new Array();
    for (var key in headers) {
        // Do not put comments and status in twice
        if (key != 'usercomment' && key != 'status' && key.indexOf('status') < 0) {
            sorted.push(key);
        }
    }
    sorted.sort();

    // Add column for check box, node, ping, power, monitor, and comments
    sorted.unshift('<input type="checkbox" onclick="selectAllCheckbox(event, $(this))">', 
        'node', 
        '<span><a>status</a></span><img src="images/loader.gif"></img>', 
        '<span><a>power</a></span><img src="images/loader.gif" style="display: none;"></img>',
        '<span><a>monitor</a></span><img src="images/loader.gif" style="display: none;"></img>',
        'comments');

    // Create a datatable
    var nodesTable = new DataTable(nodesTableId);
    nodesTable.init(sorted);
    
    // Go through each node
    for (var node in attrs) {
        // Create a row
        var row = new Array();
        
        // Create a check box, node link, and get node status
        var checkBx = '<input type="checkbox" name="' + node + '"/>';
        var nodeLink = $('<a class="node" id="' + node + '">' + node + '</a>').bind('click', loadNode);
        
        // If there is no status attribute for the node, do not try to access hash table
        // else the code will break
        var status = '';
        if (attrs[node]['status']) {
            status = attrs[node]['status'].replace('sshd', 'ping');
        }
            
        // Push in checkbox, node, status, monitor, and power
        row.push(checkBx, nodeLink, status, '', '');
        
        // If the node attributes are known (i.e the group is known)
        if (attrs[node]['groups']) {
            // Put in comments
            var comments = attrs[node]['usercomment'];        
            // If no comments exists, show 'No comments' and set icon image source
            var iconSrc;
            if (!comments) {
                comments = 'No comments';
                iconSrc = 'images/nodes/ui-icon-no-comment.png';
            } else {
                iconSrc = 'images/nodes/ui-icon-comment.png';
            }
                    
            // Create comments icon
            var tipID = node + 'Tip';
            var icon = $('<img id="' + tipID + '" src="' + iconSrc + '"></img>').css({
                'width': '18px',
                'height': '18px'
            });
            
            // Create tooltip
            var tip = createCommentsToolTip(comments);
            var col = $('<span></span>').append(icon);
            col.append(tip);
            row.push(col);
        
            // Generate tooltips
            icon.tooltip({
                position: "center right",
                offset: [-2, 10],
                effect: "fade",    
                opacity: 0.8,
                relative: true,
                delay: 500
            });
        } else {
            // Do not put in comments if attributes are not known
            row.push('');
        }
        
        // Go through each header
        for (var i = 6; i < sorted.length; i++) {
            // Add the node attributes to the row
            var key = sorted[i];
            
            // Do not put comments and status in twice
            if (key != 'usercomment' && key != 'status' && key.indexOf('status') < 0) {
                var val = attrs[node][key];
                if (val) {
                    row.push(val);
                } else {
                    row.push('');
                }
            }
        }

        // Add the row to the table
        nodesTable.add(row);
    }

    // Clear the tab before inserting the table
    $('#nodesTab').children().remove();
    
    // Create info bar for nodes tab
    var info = createInfoBar('Double-click on a cell to edit a node\'s properties.  Click outside the table to save changes.  Hit the Escape key to ignore changes.');
    $('#nodesTab').append(info);

    // Create action bar
    var actionBar = $('<div class="actionBar"></div>');

    /**
     * Create menu for actions to perform against a given node
     */

    // Power on
    var powerOnLnk = $('<a>Power on</a>');
    powerOnLnk.click(function() {
        var tgtNodes = getNodesChecked(nodesTableId);
        if (tgtNodes) {
            powerNode(tgtNodes, 'on');
        }
    });
    
    // Power off
    var powerOffLnk = $('<a>Power off</a>');
    powerOffLnk.click(function() {
        var tgtNodes = getNodesChecked(nodesTableId);
        if (tgtNodes) {
            powerNode(tgtNodes, 'off');
        }
    });
    
    // Turn monitoring on
    var monitorOnLnk = $('<a>Monitor on</a>');
    monitorOnLnk.click(function() {
        var tgtNodes = getNodesChecked(nodesTableId);
        if (tgtNodes) {
            monitorNode(tgtNodes, 'on');
        }
    });

    // Turn monitoring off
    var monitorOffLnk = $('<a>Monitor off</a>');
    monitorOffLnk.click(function() {
        var tgtNodes = getNodesChecked(nodesTableId);
        if (tgtNodes) {
            monitorNode(tgtNodes, 'off');
        }
    });

    // Clone
    var cloneLnk = $('<a>Clone</a>');
    cloneLnk.click(function() {
        var tgtNodes = getNodesChecked(nodesTableId).split(',');
        for (var i in tgtNodes) {
            var mgt = getNodeAttr(tgtNodes[i], 'mgt');

            // Create an instance of the plugin
            var plugin;
            switch(mgt) {
                case "blade":
                    plugin = new bladePlugin();
                    break;
                case "fsp":
                    plugin = new fspPlugin();
                    break;
                case "hmc":
                    plugin = new hmcPlugin();
                    break;
                case "ipmi":
                    plugin = new ipmiPlugin();
                    break;        
                case "ivm":
                    plugin = new ivmPlugin();
                    break;
                case "zvm":
                    plugin = new zvmPlugin();
                    break;
            }
            
            plugin.loadClonePage(tgtNodes[i]);
        }
    });

    // Delete
    var deleteLnk = $('<a>Delete</a>');
    deleteLnk.click(function() {
        var tgtNodes = getNodesChecked(nodesTableId);
        if (tgtNodes) {
            loadDeletePage(tgtNodes);
        }
    });

    // Unlock
    var unlockLnk = $('<a>Unlock</a>');
    unlockLnk.click(function() {
        var tgtNodes = getNodesChecked(nodesTableId);
        if (tgtNodes) {
            loadUnlockPage(tgtNodes);
        }
    });

    // Run script
    var scriptLnk = $('<a>Run script</a>');
    scriptLnk.click(function() {
        var tgtNodes = getNodesChecked(nodesTableId);
        if (tgtNodes) {
            loadScriptPage(tgtNodes);
        }
    });

    // Update
    var updateLnk = $('<a>Update</a>');
    updateLnk.click(function() {
        var tgtNodes = getNodesChecked(nodesTableId);
        if (tgtNodes) {
            loadUpdatenodePage(tgtNodes);
        }
    });

    // Set boot state
    var setBootStateLnk = $('<a>Set boot state</a>');
    setBootStateLnk.click(function() {
        var tgtNodes = getNodesChecked(nodesTableId);
        if (tgtNodes) {
            loadNodesetPage(tgtNodes);
        }
    });

    // Boot to network
    var boot2NetworkLnk = $('<a>Boot to network</a>');
    boot2NetworkLnk.click(function() {
        var tgtNodes = getNodesChecked(nodesTableId);
        if (tgtNodes) {
            loadNetbootPage(tgtNodes);
        }
    });
    
    // Provision node
    var provisionLnk = $('<a>Provision</a>');
    provisionLnk.click(function() {
        var tgtNodes = getNodesChecked(nodesTableId);
        if (tgtNodes){
            // Jump directly to the provision page
            jump2Provision(tgtNodes);
        }
    });

    // Remote console
    var rcons = $('<a>Open console</a>');
    rcons.bind('click', function(event){
        var tgtNodes = getNodesChecked(nodesTableId);
        if (tgtNodes) {
            loadRconsPage(tgtNodes);
        }
    });
    
    // Edit properties
    var editProps = $('<a>Edit properties</a>');
    editProps.bind('click', function(event){
        var tgtNodes = getNodesChecked(nodesTableId).split(',');
        for (var i in tgtNodes) {
            editNodeProps(tgtNodes[i]);
        }
    });
    
    // Install Ganglia
    var installMonLnk = $('<a>Install monitoring</a>');
    installMonLnk.click(function() {
        var tgtNodes = getNodesChecked(nodesTableId);
        if (tgtNodes) {
            installGanglia(tgtNodes);
        }
    });
    
    // Actions
    var actionsLnk = '<a>Actions</a>';
    var actsMenu = createMenu([cloneLnk, deleteLnk, monitorOnLnk, monitorOffLnk, powerOnLnk, powerOffLnk, scriptLnk]);

    // Configurations
    var configLnk = '<a>Configuration</a>';
    var configMenu = createMenu([unlockLnk, updateLnk, editProps, installMonLnk]);

    // Provision
    var provLnk = '<a>Provision</a>';
    var provMenu = createMenu([boot2NetworkLnk, setBootStateLnk, rcons, provisionLnk]);

    // Create an action menu
    var actionsMenu = createMenu([ [ actionsLnk, actsMenu ], [ configLnk, configMenu ],  [ provLnk, provMenu ] ]);
    actionsMenu.superfish();
    actionsMenu.css('display', 'inline-block');
    actionBar.append(actionsMenu);
    
    // Set correct theme for action menu
    actionsMenu.find('li').hover(function() {
        setMenu2Theme($(this));
    }, function() {
        setMenu2Normal($(this));
    });

    // Insert action bar and nodes datatable
    $('#nodesTab').append(nodesTable.object());
        
    // Turn table into a datatable
    var nodesDatatable = $('#' + nodesTableId).dataTable({
        'iDisplayLength': 50,
        'bLengthChange': false,
        "sScrollX": "100%",
        "bAutoWidth": true,
        "fnInitComplete": function() {
            adjustColumnSize(nodesTableId);
        }
    });
    
    // Filter table when enter key is pressed
    $('#' + nodesTableId + '_filter input').unbind();
    $('#' + nodesTableId + '_filter input').bind('keyup', function(e){
        if (e.keyCode == 13) {
            var table = $('#' + nodesTableId).dataTable();
            table.fnFilter($(this).val());
            
            // If there are nodes found, get the node attributes
            if (!$('#' + nodesTableId + ' .dataTables_empty').length) {
                getNodeAttrs(group);
            }
        }
    });
    
    // Load node definitions when next or previous buttons are clicked
    $('#' + nodesTableId + '_next, #' + nodesTableId + '_previous').click(function() {
        getNodeAttrs(group);
    });
    
    /**
     * Change how datatable behaves
     */
    
    // Do not sort ping, power, and comment column
    var cols = $('#' + nodesTableId + ' thead tr th').click(function() {        
        getNodeAttrs(group);
    });
    var checkboxCol = $('#' + nodesTableId + '_wrapper .dataTables_scrollHead .datatable thead tr th:eq(0)');
    var pingCol = $('#' + nodesTableId + '_wrapper .dataTables_scrollHead .datatable thead tr th:eq(2)');
    var powerCol = $('#' + nodesTableId + '_wrapper .dataTables_scrollHead .datatable thead tr th:eq(3)');
    var monitorCol = $('#' + nodesTableId + '_wrapper .dataTables_scrollHead .datatable thead tr th:eq(4)');
    var commentCol = $('#' + nodesTableId + '_wrapper .dataTables_scrollHead .datatable thead tr th:eq(5)');
    checkboxCol.unbind('click');
    pingCol.unbind('click');
    powerCol.unbind('click');
    monitorCol.unbind('click');
    commentCol.unbind('click');
            
    // Create enough space for loader to be displayed
    // Center align power, ping, and comments
    $('#' + nodesTableId + ' td:nth-child(3),td:nth-child(4),td:nth-child(5)').css({
        'text-align': 'center'
    });
    
    // No minimum width for comments column
    $('#' + nodesTableId + ' tbody tr td:nth-child(6)').css('text-align', 'center');
    
    // Instead refresh the node, power, and monitor status
    pingCol.find('span a').click(function() {
        refreshNodeStatus(group, nodesTableId);
    });
    powerCol.find('span a').click(function() {
        refreshPowerStatus(group, nodesTableId);
    });
    monitorCol.find('span a').click(function() {
        refreshGangliaStatus(group, nodesTableId);
    });
    
    // Create a division to hold actions menu
    var menuDiv = $('<div id="' + nodesTableId + '_menuDiv" class="menuDiv"></div>');
    $('#' + nodesTableId + '_wrapper').prepend(menuDiv);
    menuDiv.append(actionBar);    
    $('#' + nodesTableId + '_filter').appendTo(menuDiv);
    
    // Create tooltip for status
    var tooltipConf = {
            position: "center right",
            offset: [-2, 10],
            effect: "fade",    
            opacity: 0.8,
            relative: true,
            predelay: 800
        };

    var pingTip = createStatusToolTip();
    pingCol.find('span').append(pingTip);
    pingCol.find('span a').tooltip(tooltipConf);
    
    // Create tooltip for power
    var powerTip = createPowerToolTip();
    powerCol.find('span').append(powerTip);
    powerCol.find('span a').tooltip(tooltipConf);
    
    // Create tooltip for monitor
    var monitorTip = createMonitorToolTip();
    monitorCol.find('span').append(monitorTip);
    monitorCol.find('span a').tooltip(tooltipConf);
    
    /**
     * Enable editable columns
     */
    // Do not make 1st, 2nd, 3rd, 4th, 5th, or 6th column editable
    $('#' + nodesTableId + ' td:not(td:nth-child(1),td:nth-child(2),td:nth-child(3),td:nth-child(4),td:nth-child(5),td:nth-child(6))').editable(
        function(value, settings) {        
            // If users did not make changes, return the value directly
            // jeditable saves the old value in this.revert
            if ($(this).attr('revert') == value){
                return value;
            }
            
            // Get column index
            var colPos = this.cellIndex;
                        
            // Get row index
            var dTable = $('#' + nodesTableId).dataTable();
            var rowPos = dTable.fnGetPosition(this.parentNode);
            
            // Update datatable
            dTable.fnUpdate(value, rowPos, colPos, false);
            
            // Get table headers
            var headers = $('#' + nodesTableId).parents('.dataTables_scroll').find('.dataTables_scrollHead thead tr:eq(0) th');
                        
            // Get node name
            var node = $(this).parent().find('td a.node').text();
            // Get attribute name
            var attrName = jQuery.trim(headers.eq(colPos).text());
            // Get column value
            var value = $(this).text();
            
            // Build argument
            var args = attrName + '=' + value;
            
            // Send command to change node attributes
            $.ajax( {
                url : 'lib/cmd.php',
                dataType : 'json',
                data : {
                    cmd : 'chdef',
                    tgt : '',
                    args : '-t;node;-o;' + node + ';' + args,
                    msg : 'out=nodesTab;tgt=' + node
                },

                success: showChdefOutput
            });
            
            // Save the data into global origAttrs
            origAttrs[node][attrName] = value;

            return value;
        }, {
            onblur : 'submit',     // Clicking outside editable area submits changes
            type : 'textarea',
            placeholder: ' ',
            event : "dblclick", // Double click and edit
            height : '30px'     // The height of the text area
        });
    
    /**
     * Get the node status and definable node attributes
     */

    // If request to get node status is made
    if (getNodeStatus) {
        var tgt = getNodesShown(nodesTableId);
        
        // Get node status
        $.ajax( {
            url : 'lib/cmd.php',
            dataType : 'json',
            data : {
                cmd : 'nodestat',
                tgt : tgt,
                args : '-u',
                msg : ''
            },
    
            success : loadNodeStatus
        });
    } else {
        // Hide status loader
        var statCol = $('#' + nodesTableId + '_wrapper .dataTables_scrollHead .datatable thead tr th:eq(2)');
        statCol.find('img').hide();
        adjustColumnSize(nodesTableId);
    }
    
    if (undefined == nodeAttrs){
        // Get definable node attributes
        $.ajax( {
            url : 'lib/cmd.php',
            dataType : 'json',
            data : {
                cmd : 'lsdef',
                tgt : '',
                args : '-t;node;-h',
                msg : ''
            },

            success : setNodeAttrs
        });
    }
    
    /**
     * Additional ajax requests need to be made for zVM
     * load advanced information based on hardware architecture 
     */
    advancedLoad(group);
}

/**
 * Get nodes currently shown in datatable
 * 
 * @param tableId Datatable ID
 * @return String of nodes shown
 */
function getNodesShown(tableId) {
    // String of nodes shown
    var shownNodes = '';
    
    // Get rows of shown nodes
    var nodes = $('#' + tableId + ' tbody tr');
                
    // Go through each row
    var cols;
    for (var i = 0; i < nodes.length; i++) {
        // Get second column containing node name
        cols = nodes.eq(i).find('td');
        shownNodes += cols.eq(1).text() + ',';
    }
    
    // Remove last comma
    shownNodes = shownNodes.substring(0, shownNodes.length-1);
    return shownNodes;
}

/**
 * Get attributes for nodes not yet initialized
 * 
 * @param group Group name
 */
function getNodeAttrs(group) {    
    // Get datatable headers and rows
    var headers = $('#' + nodesTableId).parents('.dataTables_scroll').find('.dataTables_scrollHead thead tr:eq(0) th');
    var nodes = $('#' + nodesTableId + ' tbody tr');
    
    // Find group column
    var head, groupsCol;
    for (var i = 0; i < headers.length; i++) {
        head = headers.eq(i).html();
        if (head == 'groups') {
            groupsCol = i;
            break;
        }
    }

    // Check if groups definition is set
    var node, cols;
    var tgtNodes = '';
    for (var i = 0; i < nodes.length; i++) {
        cols = nodes.eq(i).find('td');
        if (!cols.eq(groupsCol).html()) {
            node = cols.eq(1).text();
            tgtNodes += node + ',';
        }
    }
        
    // If there are node definitions to load
    if (tgtNodes) {
        // Remove last comma
        tgtNodes = tgtNodes.substring(0, tgtNodes.length-1);
                
        // Get node definitions
        $.ajax( {
            url : 'lib/cmd.php',
            dataType : 'json',
            data : {
                cmd : 'lsdef',
                tgt : '',
                args : tgtNodes,
                msg : group
            },
    
            success : addNodes2Table
        });
        
        // Create dialog to indicate table is updating
        var update = $('<div id="updatingDialog"></div>');
        update.append(createInfoBar('Updating table <img src="images/loader.gif"/>'));
        
        update.dialog({
            title: 'Updating',
            modal: true,
            width: 300,
            position: 'center'
        });
    }
}

/**
 * Add nodes to datatable
 * 
 * @param data Data returned from HTTP request
 */
function addNodes2Table(data) {
    // Data returned
    var rsp = data.rsp;
    // Group name
    var group = data.msg;
    // Hash of node attributes
    var attrs = new Object();
    // Node attributes
    var headers = $('#' + nodesTableId).parents('.dataTables_scroll').find('.dataTables_scrollHead thead tr th');
    
    // Variable to send command and request node status
    var getNodeStatus = true;
    
    // Go through each attribute
    var node, args;
    for (var i in rsp) {
        // Get node name
        if (rsp[i].indexOf('Object name:') > -1) {
            var temp = rsp[i].split(': ');
            node = jQuery.trim(temp[1]);

            // Create a hash for node attributes
            attrs[node] = new Object();
            i++;
        }

        // Get key and value
        args = rsp[i].split('=', 2);
        var key = jQuery.trim(args[0]);
        var val = jQuery.trim(rsp[i].substring(rsp[i].indexOf('=') + 1, rsp[i].length));
        
        // Create a hash table
        attrs[node][key] = val;
        // Save attributes in original hash table
        origAttrs[node][key] = val;
                
        // If node status is available
        if (key == 'status') {
            // Do not request node status
            getNodeStatus = false;
        }
    }
        
    // Set the first five headers
    var headersCol = new Object();
    headersCol['node'] = 1;
    headersCol['status'] = 2;
    headersCol['power'] = 3;
    headersCol['monitor'] = 4;
    headersCol['comments'] = 5;
    
    // Go through each header
    for (var i = 6; i < headers.length; i++) {
        // Get the column index
        headersCol[headers.eq(i).html()] = i;
    }

    // Go through each node
    var datatable = $('#' + nodesTableId).dataTable();
    var rows = datatable.fnGetData();
    for (var node in attrs) {
        // Get row containing node
        var nodeRowPos = 0;
        for (var i in rows) {
            // If column contains node
            if (rows[i][1].indexOf('>' + node + '<') > -1) {
                nodeRowPos = i;
                break;
            }
        }
                
        // Get node status
        var status = '';
        if (attrs[node]['status']){
            status = attrs[node]['status'].replace('sshd', 'ping');
        }
        
        rows[nodeRowPos][headersCol['status']] = status;
                
        // Go through each header
        for (var key in headersCol) {
            // Do not put comments and status in twice
            if (key != 'usercomment' && key != 'status' && key.indexOf('status') < 0) {
                var val = attrs[node][key];
                if (val) {
                    rows[nodeRowPos][headersCol[key]] = val;
                }
            }
        }
        
        // Update row
        datatable.fnUpdate(rows[nodeRowPos], nodeRowPos, 0, false);
        
        // Insert node comments
        // This is done after datatable is updated because
        // you cannot insert an object using fnUpdate()
        var comments = attrs[node]['usercomment'];
                
        // If no comments exists, show 'No comments' and
        // set icon image source
        var iconSrc;
        if (!comments) {
            comments = 'No comments';
            iconSrc = 'images/nodes/ui-icon-no-comment.png';
        } else {
            iconSrc = 'images/nodes/ui-icon-comment.png';
        }
                
        // Create icon for node comments
        var tipID = node + 'Tip';
        var commentsCol = $('#' + node).parent().parent().find('td').eq(5);
        
        // Create tooltip
        var icon = $('<img id="' + tipID + '" src="' + iconSrc + '"></img>').css({
            'width': '18px',
            'height': '18px'
        });
        
        var tip = createCommentsToolTip(comments);
        var span = $('<span></span>').append(icon);
        span.append(tip);
        commentsCol.append(span);
                
        // Generate tooltips
        icon.tooltip({
            position: "center right",
            offset: [-2, 10],
            effect: "fade",    
            opacity: 0.8,
            relative: true,
            delay: 500
        });
    }
    
    // Enable node link
    $('.node').bind('click', loadNode);

    // Close dialog for updating table
    $('.ui-dialog-content').dialog('close');
    
    /**
     * Enable editable columns
     */
    // Do not make 1st, 2nd, 3rd, 4th, 5th, or 6th column editable
    $('#' + nodesTableId + ' td:not(td:nth-child(1),td:nth-child(2),td:nth-child(3),td:nth-child(4),td:nth-child(5),td:nth-child(6))').editable(
        function(value, settings) {            
             //if users did not do changes, return the value directly
            //jeditable save the old value in this.revert
            if ($(this).attr('revert') == value){
                return value;
            }
            // Get column index
            var colPos = this.cellIndex;
                        
            // Get row index
            var dTable = $('#' + nodesTableId).dataTable();
            var rowPos = dTable.fnGetPosition(this.parentNode);
            
            // Update datatable
            dTable.fnUpdate(value, rowPos, colPos, false);
            
            // Get table headers
            var headers = $('#' + nodesTableId + ' thead tr th');
            
            // Get node name
            var node = $(this).parent().find('td a.node').text();
            // Get attribute name
            var attrName = jQuery.trim(headers.eq(colPos).text());
            // Get column value
            var value = $(this).text();
            
            // Build argument
            var args = attrName + '=' + value;
            
            // Send command to change node attributes
            $.ajax( {
                url : 'lib/cmd.php',
                dataType : 'json',
                data : {
                    cmd : 'chdef',
                    tgt : '',
                    args : '-t;node;-o;' + node + ';' + args,
                    msg : 'out=nodesTab;tgt=' + node
                },

                success: showChdefOutput
            });

            return value;
        }, {
            onblur : 'submit',     // Clicking outside editable area submits changes
            type : 'textarea',
            placeholder: ' ',
            event : 'dblclick',
            height : '30px'     // The height of the text area
        });
    
    // If request to get node status is made
    if (getNodeStatus) {
        // Get node status
        $.ajax( {
            url : 'lib/cmd.php',
            dataType : 'json',
            data : {
                cmd : 'nodestat',
                tgt : group,
                args : '-u',
                msg : ''
            },
    
            success : loadNodeStatus
        });
    } else {
        // Hide status loader
        var statCol = $('#' + nodesTableId + '_wrapper .dataTables_scrollHead .datatable thead tr th:eq(2)');
        statCol.find('img').hide();
    }
    
    /**
     * Additional ajax requests need to be made for zVM
     */
    advancedLoad(group);
    adjustColumnSize(nodesTableId);
}

/**
 * Load the status of Ganglia for a given group
 * 
 * @param data Data returned from HTTP request
 */
function loadGangliaStatus(data) {
    // Get datatable
    var datatable = $('#' + nodesTableId).dataTable();
    var ganglia = data.rsp;
    var rowNum, node, status;

    for ( var i in ganglia) {
        // ganglia[0] = nodeName and ganglia[1] = state
        node = jQuery.trim(ganglia[i][0]);
        status = jQuery.trim(ganglia[i][1]);

        if (node) {
            // Get the row containing the node
            rowNum = findRow(node, '#' + nodesTableId, 1);
    
            // Update the power status column
            datatable.fnUpdate(status, rowNum, 4);
        }
    }

    // Hide Ganglia loader
    var gangliaCol = $('#' + nodesTableId + '_wrapper .dataTables_scrollHead .datatable thead tr th:eq(4)');
    gangliaCol.find('img').hide();
    adjustColumnSize(nodesTableId);
}

/**
 * Refresh the status of Ganglia for each node
 * 
 * @param group Group name
 */
function refreshGangliaStatus(group) {
    // Show ganglia loader
    var gangliaCol = $('#' + nodesTableId + '_wrapper .dataTables_scrollHead .datatable thead tr th:eq(4)');
    gangliaCol.find('img').show();
    
    // Get power status for nodes shown
    var nodes = getNodesShown(nodesTableId);

    // Get the status of Ganglia
    $.ajax( {
        url : 'lib/cmd.php',
        dataType : 'json',
        data : {
            cmd : 'webrun',
            tgt : '',
            args : 'gangliastatus;' + nodes,
            msg : ''
        },

        success : loadGangliaStatus
    });
}

/**
 * Load power status for each node
 * 
 * @param data Data returned from HTTP request
 */
function loadPowerStatus(data) {
    var dTable = $('#' + nodesTableId).dataTable();
    var power = data.rsp;
    var rowPos, node, status, args;

    for (var i in power) {
        // power[0] = nodeName and power[1] = state
        args = power[i].split(':');
        node = jQuery.trim(args[0]);
        status = jQuery.trim(args[1]);
        
        // Get the row containing the node
        rowPos = findRow(node, '#' + nodesTableId, 1);

        // Update the power status column
        dTable.fnUpdate(status, rowPos, 3, false);
    }
    
    // Hide power loader
    var powerCol = $('#' + nodesTableId + '_wrapper .dataTables_scrollHead .datatable thead tr th:eq(3)');
    powerCol.find('img').hide();
    adjustColumnSize(nodesTableId);
}

/**
 * Refresh power status for each node
 * 
 * @param group Group name
 * @param tableId Table to update node status
 */
function refreshPowerStatus(group, tableId) {
    // Show power loader
    var powerCol = $('#' + tableId + '_wrapper .dataTables_scrollHead .datatable thead tr th:eq(3)');
    powerCol.find('img').show();
    
    // Get power status for nodes shown
    var nodes = getNodesShown(tableId);
        
    // Get power status
    $.ajax( {
        url : 'lib/cmd.php',
        dataType : 'json',
        data : {
            cmd : 'rpower',
            tgt : nodes,
            args : 'stat',
            msg : ''
        },

        success : loadPowerStatus
    });
}

/**
 * Load node status for each node
 * 
 * @param data Data returned from HTTP request
 */
function loadNodeStatus(data) {
    var dTable = $('#' + nodesTableId).dataTable();
    var rsp = data.rsp;
    var args, rowPos, node, status;

    // Get all nodes within datatable
    for (var i in rsp) {
        args = rsp[i].split(':');
        
        // args[0] = node and args[1] = status
        node = jQuery.trim(args[0]);
        status = jQuery.trim(args[1]).replace('sshd', 'ping');
        
        // Get row containing node
        rowPos = findRow(node, '#' + nodesTableId, 1);

        // Update ping status column
        dTable.fnUpdate(status, rowPos, 2, false);
    }
    
    // Hide status loader
    var statCol = $('#' + nodesTableId + '_wrapper .dataTables_scrollHead .datatable thead tr th:eq(2)');
    statCol.find('img').hide();
    adjustColumnSize(nodesTableId);
}

/**
 * Refresh ping status for each node
 * 
 * @param group Group name
 * @param tableId Table to update node status
 */
function refreshNodeStatus(group, tableId) {
    // Show ping loader
    var pingCol = $('#' + tableId + '_wrapper .dataTables_scrollHead .datatable thead tr th:eq(2)');
    pingCol.find('img').show();
    
    // Get power status for nodes shown
    var nodes = getNodesShown(tableId);
    
    // Get the node status
    $.ajax( {
        url : 'lib/cmd.php',
        dataType : 'json',
        data : {
            cmd : 'nodestat',
            tgt : nodes,
            args : '-u',
            msg : ''
        },

        success : loadNodeStatus
    });
}

/**
 * Load inventory for given node
 * 
 * @param e Windows event
 */
function loadNode(e) {
    if (!e) {
        e = window.event;
    }
    
    // Get node that was clicked
    var node = (e.target) ? e.target.id : e.srcElement.id;
    var mgt = getNodeAttr(node, 'mgt');
        
    // Create an instance of the plugin
    var plugin;
    switch(mgt) {
        case "blade":
            plugin = new bladePlugin();
            break;
        case "fsp":
            plugin = new fspPlugin();
            break;
        case "hmc":
            plugin = new hmcPlugin();
            break;
        case "ipmi":
            plugin = new ipmiPlugin();
            break;        
        case "ivm":
            plugin = new ivmPlugin();
            break;
        case "zvm":
            plugin = new zvmPlugin();
            break;
    }

    // Get tab area where a new tab will be inserted
    var myTab = getNodesTab();
    var inst = 0;
    var newTabId = 'nodeTab' + inst;
    while ($('#' + newTabId).length) {
        // If one already exists, generate another one
        inst = inst + 1;
        newTabId = 'nodeTab' + inst;
    }
    // Reset node process
    $.cookie(node + 'Processes', 0);

    // Add new tab, only if one does not exist
    var loader = createLoader(newTabId + 'TabLoader');
    loader = $('<center></center>').append(loader);
    myTab.add(newTabId, node, loader, true);

    // Get node inventory
    var msg = 'out=' + newTabId + ',node=' + node;
    $.ajax( {
        url : 'lib/cmd.php',
        dataType : 'json',
        data : {
            cmd : 'rinv',
            tgt : node,
            args : 'all',
            msg : msg
        },

        success : plugin.loadInventory
    });

    // Select new tab
    myTab.select(newTabId);
}

/**
 * Unlock a node by setting the ssh keys
 * 
 * @param tgtNodes Nodes to unlock
 */
function loadUnlockPage(tgtNodes) {
    // Get nodes tab
    var tab = getNodesTab();

    // Generate new tab ID
    var instance = 0;
    var newTabId = 'unlockTab' + instance;
    while ($('#' + newTabId).length) {
        // If one already exists, generate another one
        instance = instance + 1;
        newTabId = 'unlockTab' + instance;
    }

    var unlockForm = $('<div class="form"></div>');

    // Create status bar, hide on load
    var statBarId = 'unlockStatusBar' + instance;
    var statusBar = createStatusBar(statBarId).hide();
    unlockForm.append(statusBar);

    // Create loader
    var loader = createLoader('');
    statusBar.find('div').append(loader);

    // Create info bar
    var infoBar = createInfoBar('Give the root password for this node range to setup its SSH keys.');
    unlockForm.append(infoBar);

    unlockForm.append('<div><label>Target node range:</label><input type="text" id="node" name="node" readonly="readonly" value="' + tgtNodes + '" title="The node or node range to unlock"/></div>');
    unlockForm.append('<div><label>Password:</label><input type="password" id="password" name="password" title="The root password to unlock this node"/></div>');

    // Generate tooltips
    unlockForm.find('div input[title]').tooltip({
        position: "center right",
        offset: [-2, 10],
        effect: "fade",
        opacity: 0.7,
        predelay: 800,
        events : {
            def : "mouseover,mouseout",
            input : "mouseover,mouseout",
            widget : "focus mouseover,blur mouseout",
            tooltip : "mouseover,mouseout"
        }
    });
    
    /**
     * Ok
     */
    var okBtn = createButton('Ok');
    okBtn.click(function() {
        // Remove any warning messages
        $(this).parent().parent().find('.ui-state-error').remove();
        
        // If a password is given
        var password = $('#' + newTabId + ' input[name=password]').css('border', 'solid #BDBDBD 1px');
        if (password.val()) {
            // Setup SSH keys
            $.ajax( {
                url : 'lib/cmd.php',
                dataType : 'json',
                data : {
                    cmd : 'webrun',
                    tgt : '',
                    args : 'unlock;' + tgtNodes + ';' + password.val(),
                    msg : 'out=' + statBarId + ';cmd=unlock;tgt=' + tgtNodes
                },
    
                success : updateStatusBar
            });
    
            // Show status bar
            statusBar.show();
    
            // Disable all inputs and Ok button
            $('#' + newTabId + ' input').attr('disabled', 'disabled');
            $(this).attr('disabled', 'true');
        } else {
            // Show warning message
            var warn = createWarnBar('You are missing some values!');
            warn.prependTo($(this).parent().parent());
            password.css('border', 'solid #FF0000 1px');
        }
    });

    unlockForm.append(okBtn);
    tab.add(newTabId, 'Unlock', unlockForm, true);
    tab.select(newTabId);
}

/**
 * Load script page
 * 
 * @param tgtNodes Targets to run script against
 */
function loadScriptPage(tgtNodes) {
    // Get nodes tab
    var tab = getNodesTab();

    // Generate new tab ID
    var inst = 0;
    var newTabId = 'scriptTab' + inst;
    while ($('#' + newTabId).length) {
        // If one already exists, generate another one
        inst = inst + 1;
        newTabId = 'scriptTab' + inst;
    }

    // Open new tab
    // Create remote script form
    var scriptForm = $('<div class="form"></div>');

    // Create status bar
    var barId = 'scriptStatusBar' + inst;
    var statBar = createStatusBar(barId);
    statBar.hide();
    scriptForm.append(statBar);

    // Create loader
    var loader = createLoader('scriptLoader' + inst);
    statBar.find('div').append(loader);

    // Create info bar
    var infoBar = createInfoBar('Load a script to run against this node range.');
    scriptForm.append(infoBar);

    // Target node or group
    var tgt = $('<div><label for="target">Target node range:</label><input type="text" name="target" value="' + tgtNodes + '" title="The node or node range to run a given script against"/></div>');
    scriptForm.append(tgt);

    // Upload file
    var upload = $('<form action="lib/upload.php" method="post" enctype="multipart/form-data"></form>');
    var label = $('<label for="file">Remote file:</label>');
    var file = $('<input type="file" name="file" id="file"/>');
    var subBtn = createButton('Load');
    upload.append(label);
    upload.append(file);
    upload.append(subBtn);
    scriptForm.append(upload);
    
    // Generate tooltips
    scriptForm.find('div input[title]').tooltip({
        position: "center right",
        offset: [-2, 10],
        effect: "fade",
        opacity: 0.7,
        predelay: 800,
        events : {
            def : "mouseover,mouseout",
            input : "mouseover,mouseout",
            widget : "focus mouseover,blur mouseout",
            tooltip : "mouseover,mouseout"
        }
    });

    // Script
    var script = $('<div><label>Script:</label><textarea/>');
    scriptForm.append(script);

    // Ajax form options
    var options = {
        // Output to text area
        target : '#' + newTabId + ' textarea'
    };
    upload.ajaxForm(options);

    /**
     * Run
     */
    var runBtn = createButton('Run');
    runBtn.click(function() {
        // Remove any warning messages
        $(this).parent().parent().find('.ui-state-error').remove();
        
        // Get script to run
        var textarea = $('#' + newTabId + ' textarea').css('border', 'solid #BDBDBD 1px');
        
        // If no inputs are empty
        if (textarea.val()) {
            // Run script
            runScript(inst);
        } else {
            // Show warning message
            var warn = createWarnBar('You are missing some values');
            warn.prependTo($(this).parent().parent());
            textarea.css('border', 'solid #FF0000 1px');
        }
    });
    scriptForm.append(runBtn);

    // Append to discover tab
    tab.add(newTabId, 'Script', scriptForm, true);

    // Select new tab
    tab.select(newTabId);
}

/**
 * Sort a list
 * 
 * @return Sorted list
 */
jQuery.fn.sort = function() {
    return this.pushStack([].sort.apply(this, arguments), []);
};

function sortAlpha(a, b) {
    return a.innerHTML > b.innerHTML ? 1 : -1;
};

/**
 * Power on a given node
 * 
 * @param node Node to power on or off
 * @param power2 Power node to given state
 */
function powerNode(node, power2) {
    // Show power loader
    var powerCol = $('#' + nodesTableId + '_wrapper .dataTables_scrollHead .datatable thead tr th:eq(3)');
    powerCol.find('img').show();
    
    node = node.replace('Power', '');
    $.ajax( {
        url : 'lib/cmd.php',
        dataType : 'json',
        data : {
            cmd : 'rpower',
            tgt : node,
            args : power2,
            msg : node
        },

        success : updatePowerStatus
    });
}

/**
 * Load delete node page
 * 
 * @param tgtNodes Nodes to delete
 */
function loadDeletePage(tgtNodes) {
    // Get nodes tab
    var myTab = getNodesTab();

    // Generate new tab ID
    var inst = 0;
    newTabId = 'deleteTab' + inst;
    while ($('#' + newTabId).length) {
        // If one already exists, generate another one
        inst = inst + 1;
        newTabId = 'deleteTab' + inst;
    }

    // Create status bar, hide on load
    var statBarId = 'deleteStatusBar' + inst;
    var statBar = createStatusBar(statBarId).hide();

    // Create loader
    var loader = createLoader('');
    statBar.find('div').append(loader);
    statBar.hide();

    // Create target nodes string
    var tgtNodesStr = '';
    var nodes = tgtNodes.split(',');
    // Loop through each node
    for (var i in nodes) {
        // If it is the 1st and only node
        if (i == 0 && i == nodes.length - 1) {
            tgtNodesStr += nodes[i];
        }
        // If it is the 1st node of many nodes
        else if (i == 0 && i != nodes.length - 1) {
            // Append a comma to the string
            tgtNodesStr += nodes[i] + ', ';
        } else {
            // If it is the last node
            if (i == nodes.length - 1) {
                // Append nothing to the string
                tgtNodesStr += nodes[i];
            } else {
                // Append a comma to the string
                tgtNodesStr += nodes[i] + ', ';
            }
        }
    }

    // Create delete form
    var deleteForm = $('<div class="form"></div>');
    deleteForm.append(statBar);
    deleteForm.append(statBar);
    
    // Confirm delete
    var instr = $('<p>Are you sure you want to delete ' + tgtNodesStr + '?</p>').css('word-wrap', 'break-word');
    deleteForm.append(instr);

    /**
     * Delete
     */
    var deleteBtn = createButton('Delete');
    deleteBtn.click(function() {
        // Delete the virtual server
        $.ajax( {
            url : 'lib/cmd.php',
            dataType : 'json',
            data : {
                cmd : 'rmvm',
                tgt : tgtNodes,
                args : '',
                msg : 'out=' + statBarId + ';cmd=rmvm;tgt=' + tgtNodes
            },

            success : updateStatusBar
        });

        // Show status bar loader
        statBar.show();

        // Disable delete button
        $(this).attr('disabled', 'true');
    });
    
    /**
     * Cancel
     */
    var cancelBtn = createButton('Cancel');
    cancelBtn.bind('click', function(){
        myTab.remove($(this).parent().parent().attr('id'));
    });

    deleteForm.append(deleteBtn);
    deleteForm.append(cancelBtn);
    myTab.add(newTabId, 'Delete', deleteForm, true);

    myTab.select(newTabId);
}

/**
 * Update status bar of a given tab
 * 
 * @param data Data returned from HTTP request
 */
function updateStatusBar(data) {
    // Get ajax response
    var rsp = data.rsp;
    var args = data.msg.split(';');
    var statBarId = args[0].replace('out=', '');
    var cmd = args[1].replace('cmd=', '');
    var tgts = args[2].replace('tgt=', '').split(',');

    if (cmd == 'unlock' || cmd == 'updatenode') {
        // Hide loader
        $('#' + statBarId).find('img').hide();

        // Write ajax response to status bar
        var prg = writeRsp(rsp, '');    
        $('#' + statBarId).find('div').append(prg);    
    } else if (cmd == 'rmvm') {
        // Get data table
        var dTable = $('#' + nodesTableId).dataTable();
        var failed = false;

        // Hide loader
        $('#' + statBarId).find('img').hide();

        // Write ajax response to status bar
        var prg = writeRsp(rsp, '');
        $('#' + statBarId).find('div').append(prg);    
        
        // If there was an error, do not continue
        if (prg.html().indexOf('Error') > -1) {
            failed = true;
        }

        // Update data table
        var rowPos;
        for (var i in tgts) {
            if (!failed) {
                // Get row containing the node link and delete it
                rowPos = findRow(tgts[i], '#' + nodesTableId, 1);
                dTable.fnDeleteRow(rowPos);
            }
        }
    } else if (cmd == 'xdsh') {
        // Hide loader
        $('#' + statBarId).find('img').hide();
        
        // Write ajax response to status bar
        var prg = $('<pre></pre>');
        for (var i in rsp) {
            for (var j in tgts) {
                rsp[i] = rsp[i].replace(new RegExp(tgts[j] + ':', 'g'), '');
            }

            prg.append(rsp[i]);
            prg.append('<br>');    
        }
        $('#' + statBarId).find('div').append(prg);    
        
        // Enable fields
        $('#' + statBarId).parent().find('input').removeAttr('disabled');
        $('#' + statBarId).parent().find('textarea').removeAttr('disabled');
        
        // Enable buttons
        $('#' + statBarId).parent().find('button').removeAttr('disabled');
    } else {
        // Hide loader
        $('#' + statBarId).find('img').hide();
        
        // Write ajax response to status bar
        var prg = writeRsp(rsp, '[A-Za-z0-9._-]+:');    
        $('#' + statBarId).find('div').append(prg);    
    }
}

/**
 * Update power status of a node in the datatable
 * 
 * @param data Data from HTTP request
 */
function updatePowerStatus(data) {
    // Hide power loader
    var powerCol = $('#' + nodesTableId + '_wrapper .dataTables_scrollHead .datatable thead tr th:eq(3)');
    powerCol.find('img').hide();
    
    // Get datatable
    var dTable = $('#' + nodesTableId).dataTable();

    // Get xCAT response
    var rsp = data.rsp;
    // Loop through each line
    var node, status, rowPos, strPos;
    for (var i in rsp) {
        // Get node name
        node = rsp[i].split(":")[0];

        // If there is no error
        if (rsp[i].indexOf("Error") < 0 || rsp[i].indexOf("Failed") < 0) {
            // Get the row containing the node link
            rowPos = findRow(node, '#' + nodesTableId, 1);

            // If it was power on, then the data return would contain "Starting"
            strPos = rsp[i].indexOf("Starting");
            if (strPos > -1) {
                status = 'on';
            } else {
                status = 'off';
            }

            // Update the power status column
            dTable.fnUpdate(status, rowPos, 3, false);
        } else {
            // Power on/off failed
            alert(rsp[i]);
        }
    }
    
    // Adjust datatable column size
    adjustColumnSize(nodesTableId);
}

/**
 * Run a script
 * 
 * @param inst Remote script tab instance
 */
function runScript(inst) {
    // Get tab ID
    var tabId = 'scriptTab' + inst;
    // Get node name
    var tgts = $('#' + tabId + ' input[name=target]').val();
    // Get script
    var script = $('#' + tabId + ' textarea').val();
    
    var statBarId = 'scriptStatusBar' + inst;
    $('#' + statBarId).show();                    // Show status bar
    $('#' + statBarId + ' img').show();            // Show loader
    $('#' + statBarId + ' p').remove();            // Clear status bar

    // Disable all fields
    $('#' + tabId + ' input').attr('disabled', 'true');
    $('#' + tabId + ' textarea').attr('disabled', 'true');
    
    // Disable buttons
    $('#' + tabId + ' button').attr('disabled', 'true');

    // Run script
    $.ajax( {
        url : 'lib/zCmd.php',
        dataType : 'json',
        data : {
            cmd : 'xdsh',
            tgt : tgts,
            args : '-e',
            att : script,
            msg : 'out=scriptStatusBar' + inst + ';cmd=xdsh;tgt=' + tgts
        },

        success : updateStatusBar
    });
}

/**
 * Get an attribute of a given node
 * 
 * @param node The node
 * @param attrName The attribute
 * @return The attribute of the node
 */
function getNodeAttr(node, attrName) {
    // Get the row
    var row = $('[id=' + node + ']').parents('tr');

    // Search for the column containing the attribute
    var attrCol;
    
    var cols = row.parents('.dataTables_scroll').find('.dataTables_scrollHead thead tr:eq(0) th');
    // Loop through each column
    for (var i in cols) {
        // Find column that matches the attribute
        if (cols.eq(i).html() == attrName) {
            attrCol = cols.eq(i);
            break;
        }
    }
    
    // If the column containing the attribute is found
    if (attrCol) {
        // Get the attribute column index
        var attrIndex = attrCol.index();

        // Get the attribute for the given node
        var attr = row.find('td:eq(' + attrIndex + ')');
        return attr.text();
    } else {
        return '';
    }
}

/**
 * Set a cookie for the OS images
 * 
 * @param data Data from HTTP request
 */
function setOSImageCookies(data) {
    // Get response
    var rsp = data.rsp;

    var imageNames = new Array;
    var profilesHash = new Object();
    var osVersHash = new Object();
    var osArchsHash = new Object();
    var imagePos = 0;
    var profilePos = 0;
    var osversPos = 0;
    var osarchPos = 0;
    
    // Get column value
    var colNameArray = rsp[0].substr(1).split(',');
    for (var i in colNameArray){
        switch (colNameArray[i]){
            case 'imagename':
                imagePos = i;
                break;
            case 'profile':
                profilePos = i;
                break;
            case 'osvers':
                osversPos = i;
                break;
            case 'osarch':
                osarchPos = i;
                break;
            default :
                break;
        }
    }
    
    // Go through each index
    for (var i = 1; i < rsp.length; i++) {
        // Get image name
        var cols = rsp[i].split(',');
        var osImage = cols[imagePos].replace(new RegExp('"', 'g'), '');
        var profile = cols[profilePos].replace(new RegExp('"', 'g'), '');
        var osVer = cols[osversPos].replace(new RegExp('"', 'g'), '');
        var osArch = cols[osarchPos].replace(new RegExp('"', 'g'), '');
        
        imageNames.push(osImage);
        profilesHash[profile] = 1;
        osVersHash[osVer] = 1;
        osArchsHash[osArch] = 1;
    }

    // Save image names in a cookie
    $.cookie('imagenames', imageNames);

    // Save profiles in a cookie
    var tmp = new Array;
    for (var key in profilesHash) {
        tmp.push(key);
    }
    $.cookie('profiles', tmp);

    // Save OS versions in a cookie
    tmp = new Array;
    for (var key in osVersHash) {
        tmp.push(key);
    }
    $.cookie('osvers', tmp);

    // Save OS architectures in a cookie
    tmp = new Array;
    for (var key in osArchsHash) {
        tmp.push(key);
    }
    $.cookie('osarchs', tmp);
}

/**
 * Set a cookie for the groups
 * 
 * @param data Data from HTTP request
 */
function setGroupsCookies(data) {
    var rsp = data.rsp;
    $.cookie('groups', rsp);
}

/**
 * Find the row index containing a column with a given string
 * 
 * @param str String to search for
 * @param table Table to check
 * @param col Column to find string under
 * @return The row index containing the search string
 */
function findRow(str, table, col){    
    // Get datatable
    var dTable = $(table).dataTable();
    var rows = dTable.fnGetData();
    
    // Loop through each row
    for (var i in rows) {
        // If the column contains the search string
        if (rows[i][col].indexOf(str) > -1) {
            return i;
        }
    }
    
    return -1;
}

/**
 * Select all checkboxes in the datatable
 * 
 * @param event Event on element
 * @param obj Object triggering event
 */
function selectAllCheckbox(event, obj) {
    // Get datatable ID
    // This will ascend from <input> <td> <tr> <thead> <table>
    var tableObj = obj.parents('.dataTables_scroll').find('.dataTables_scrollBody');
    var status = obj.attr('checked');
    tableObj.find(' :checkbox').attr('checked', status);
    event.stopPropagation();
}

/**
 * Load rcons page
 * 
 * @param tgtNodes Targets to run rcons against
 */
function loadRconsPage(tgtNodes){
    var hostName = window.location.host;
    var urlPath = window.location.pathname;
    var redirectUrl = 'https://';
    var pos = 0;
    
    // We only support one node
    if (-1 != tgtNodes.indexOf(',')){
        alert("You can only open one console at a time!");
        return;
    }
    
    redirectUrl += hostName;
    pos = urlPath.lastIndexOf('/');
    redirectUrl += urlPath.substring(0, pos + 1);
    redirectUrl += 'rcons.php';
    
    // Open the rcons page
    window.open(redirectUrl + "?rconsnd=" + tgtNodes, '', "toolbar=no,location=no,directories=no,status=no,menubar=no,scrollbars=yes,resizable=no,width=590,height=436");
}

/**
 * Create a tool tip for comments
 * 
 * @param comment Comments to be placed in a tool tip
 * @return Tool tip
 */
function createCommentsToolTip(comment) {
    // Create tooltip container
    var toolTip = $('<div class="tooltip"></div>');
    // Create textarea to hold comment
    var txtArea = $('<textarea>' + comment + '</textarea>').css({
        'font-size': '10px',
        'height': '50px',
        'width': '200px',
        'background-color': '#000',
        'color': '#fff',
        'border': '0px',
        'display': 'block'
    });
    
    // Create links to save and cancel changes
    var lnkStyle = {
        'color': '#58ACFA',
        'font-size': '10px',
        'display': 'inline-block',
        'padding': '5px',
        'float': 'right'
    };
    
    var saveLnk = $('<a>Save</a>').css(lnkStyle).hide();
    var cancelLnk = $('<a>Cancel</a>').css(lnkStyle).hide();
    var infoSpan = $('<span>Click to edit</span>').css(lnkStyle);
    
    // Save changes onclick
    saveLnk.bind('click', function(){
        // Get node and comment
        var node = $(this).parent().parent().find('img').attr('id').replace('Tip', '');
        var comments = $(this).parent().find('textarea').val();
        
        // Save comment
        $.ajax( {
            url : 'lib/cmd.php',
            dataType : 'json',
            data : {
                cmd : 'chdef',
                tgt : '',
                args : '-t;node;-o;' + node + ';usercomment=' + comments,
                msg : 'out=nodesTab;tgt=' + node
            },
            
            success: showChdefOutput
        });
        
        // Hide cancel and save links
        $(this).hide();
        cancelLnk.hide();
    });
        
    // Cancel changes onclick
    cancelLnk.bind('click', function(){
        // Get original comment and put it back
        var orignComments = $(this).parent().find('textarea').text();
        $(this).parent().find('textarea').val(orignComments);
        
        // Hide cancel and save links
        $(this).hide();
        saveLnk.hide();
        infoSpan.show();
    });
    
    // Show save link when comment is edited
    txtArea.bind('click', function(){
        saveLnk.show();
        cancelLnk.show();
        infoSpan.hide();
    });
        
    toolTip.append(txtArea);
    toolTip.append(cancelLnk);
    toolTip.append(saveLnk);
    toolTip.append(infoSpan);
    
    return toolTip;
}

/**
 * Create a tool tip for node status
 * 
 * @return Tool tip
 */
function createStatusToolTip() {
    // Create tooltip container
    var toolTip = $('<div class="tooltip"></div>').css({
        'width': '150px',
        'font-weight': 'normal'
    });
    
    // Create info text
    var info = $('<p></p>').css({
        'white-space': 'normal'
    });
    info.append('Click here to refresh the node status. To configure the xCAT monitor, ');
    
    // Create link to turn on xCAT monitoring
    var monitorLnk = $('<a>click here</a>').css({
        'color': '#58ACFA',
        'font-size': '10px'
    });
    
    // Open dialog to configure xCAT monitor
    monitorLnk.bind('click', function(){
        // Check if xCAT monitor is enabled
        $.ajax( {
            url : 'lib/cmd.php',
            dataType : 'json',
            data : {
                cmd : 'monls',
                tgt : '',
                args : 'xcatmon',
                msg : ''
            },

            success : openConfXcatMon
        });
    });
    
    info.append(monitorLnk);
    toolTip.append(info);
    
    return toolTip;
}

/**
 * Create a tool tip for power status
 * 
 * @return Tool tip
 */
function createPowerToolTip() {
    // Create tooltip container
    var toolTip = $('<div class="tooltip">Click here to refresh the power status</div>').css({
        'width': '150px',
        'white-space': 'normal',
        'font-weight': 'normal'
    });
    return toolTip;
}

/**
 * Create a tool tip for monitoring status
 * 
 * @return Tool tip
 */
function createMonitorToolTip() {
    // Create tooltip container
    var toolTip = $('<div class="tooltip">Click here to refresh the monitoring status</div>').css({
        'width': '150px',
        'white-space': 'normal',
        'font-weight': 'normal'
    });    
    return toolTip;
}

/**
 * Open dialog to configure xCAT monitor
 * 
 * @param data Data returned from HTTP request
 */
function openConfXcatMon(data) {
    // Create info bar
    var info = createInfoBar('Configure the xCAT monitor. Select to enable or disable the monitor below.');
    var dialog = $('<div></div>');
    dialog.append(info);
    
    // Create status area
    var statusArea = $('<div></div>').css('padding-top', '10px');
    var label = $('<label>Status:</label>');
    statusArea.append(label);
    
    // Get xCAT monitor status
    var status = data.rsp[0];
    var buttons;
    // If xCAT monitor is disabled
    if (status.indexOf('not-monitored') > -1) {
        status = $('<span>Disabled</span>').css('padding', '0px 5px');
        statusArea.append(status);
        
        // Create enable and cancel buttons
        buttons = {
            "Enable": function(){
                // Enable xCAT monitor
                $.ajax({
                    url : 'lib/cmd.php',
                    dataType : 'json',
                    data : {
                        cmd : 'monstart',
                        tgt : '',
                        args : 'xcatmon',
                        msg : ''
                    },
    
                    success : function(data){
                        openDialog('info', data.rsp[0]);
                    }
                });
                $(this).dialog("close");
            },
            "Cancel": function(){ 
                $(this).dialog("close");
            }
        };
    } else {
        status = $('<span>Enabled</span>').css('padding', '0px 5px');
        statusArea.append(status);
        
        // Create disable and cancel buttons
        buttons = {
            "Disable": function(){
                // Disable xCAT monitor
                $.ajax({
                    url : 'lib/cmd.php',
                    dataType : 'json',
                    data : {
                        cmd : 'monstop',
                        tgt : '',
                        args : 'xcatmon',
                        msg : ''
                    },
    
                    success : function(data){
                        openDialog('info', data.rsp[0]);
                    }
                });
                $(this).dialog("close");
            },
            "Cancel": function(){ 
                $(this).dialog("close");
            }
        };
    }
    
    dialog.append(statusArea);
    
    // Open dialog
    dialog.dialog({
        modal: true,
        width: 500,
        buttons: buttons
    });
}

/**
 * Show chdef output
 * 
 * @param data Data returned from HTTP request
 */
function showChdefOutput(data) {
    // Get output
    var out = data.rsp;
    var args = data.msg.split(';');
    var tabID = args[0].replace('out=', '');
    var tgt = args[1].replace('tgt=', '');
    
    // Find info bar on nodes tab, if any
    var info = $('#' + tabID).find('.ui-state-highlight');
    if (!info.length) {
        // Create info bar if one does not exist
        info = createInfoBar('');
        $('#' + tabID).append(info);
    }
        
    // Go through output and append to paragraph
    var prg = $('<p></p>');
    for (var i in out) {
        prg.append(tgt + ': ' + out[i] + '<br>');
    }
    
    info.append(prg);
}

/**
 * Set node attributes
 * 
 * @param data Data returned from HTTP request
 */
function setNodeAttrs(data) {
    // Clear hash table containing definable node attributes
    nodeAttrs = new Array();
    
    // Get definable attributes
    var attrs = data.rsp[2].split(/\n/);

    // Go through each line
    var attr, key, descr;
    for (var i in attrs) {
        attr = attrs[i];
        
        // If the line is not empty
        if (attr) {
            // If the line has the attribute name
            if (attr.indexOf(':') && attr.indexOf(' ')) {
                // Get attribute name and description
                key = jQuery.trim(attr.substring(0, attr.indexOf(':')));
                descr = jQuery.trim(attr.substring(attr.indexOf(':') + 1));
                
                // Remove arrow brackets
                descr = descr.replace(new RegExp('<|>', 'g'), '');
                
                // Set hash table where key = attribute name and value = description
                nodeAttrs[key] = descr;
            } else {
                // Remove arrow brackets
                attr = attr.replace(new RegExp('<|>', 'g'), '');
                
                // Append description to hash table
                nodeAttrs[key] = nodeAttrs[key] + '\n' + attr;
            }
        } // End of if
    } // End of for
}

/**
 * Load set node properties page
 * 
 * @param tgtNode Target node to set properties
 */
function editNodeProps(tgtNode) {
    // Get nodes tab
    var tab = getNodesTab();

    // Generate new tab ID
    var inst = 0;
    var newTabId = 'editPropsTab' + inst;
    while ($('#' + newTabId).length) {
        // If one already exists, generate another one
        inst = inst + 1;
        newTabId = 'editPropsTab' + inst;
    }

    // Open new tab
    // Create set properties form
    var editPropsForm = $('<div class="form"></div>');

    // Create info bar
    var infoBar = createInfoBar('Choose the properties you wish to change on the node. When you are finished, click Save.');
    editPropsForm.append(infoBar);

    // Create an input for each definable attribute
    var div, label, input, descr, value;
    // Set node attribute
    origAttrs[tgtNode]['node'] = tgtNode;
    for (var key in nodeAttrs) {
        // If an attribute value exists
        if (origAttrs[tgtNode][key]) {
            // Set the value
            value = origAttrs[tgtNode][key];
        } else {
            value = '';
        }
        
        // Create label and input for attribute
        div = $('<div></div>').css('display', 'inline-table');        
        label = $('<label>' + key + ':</label>').css('vertical-align', 'middle');
        input = $('<input type="text" value="' + value + '" title="' + nodeAttrs[key] + '"/>').css('margin-top', '5px');
        
        // Change border to blue onchange
        input.bind('change', function(event) {
            $(this).css('border-color', 'blue');
        });
        
        div.append(label);
        div.append(input);
        editPropsForm.append(div);
    }

    // Change style for last division
    div.css({
        'display': 'block',
        'margin': '0px 0px 10px 0px'
    });
    
    // Generate tooltips
    editPropsForm.find('div input[title]').tooltip({
        position: "center right",
        offset: [-2, 10],
        effect: "fade",
        opacity: 0.8,
        delay: 0,
        predelay: 800,
        events: {
              def:     "mouseover,mouseout",
              input:   "mouseover,mouseout",
              widget:  "focus mouseover,blur mouseout",
              tooltip: "mouseover,mouseout"
        }
    });

    // Save changes
    var saveBtn = createButton('Save');
    saveBtn.click(function() {    
        // Get all inputs
        var inputs = $('#' + newTabId + ' input');
        
        // Go through each input
        var args = '';
        var attrName, attrVal;
        inputs.each(function(){
            // If the border color is blue
            if ($(this).css('border-left-color') == 'rgb(0, 0, 255)') {
                // Change border color back to normal
                $(this).css('border-color', '');
                
                // Get attribute name and value
                attrName = $(this).parent().find('label').text().replace(':', '');
                attrVal = $(this).val();
                
                // Build argument string
                if (args) {
                    // Handle subsequent arguments
                    args += ';' + attrName + '=' + attrVal;
                } else {
                    // Handle the 1st argument
                    args += attrName + '=' + attrVal;
                }
            }
        });
        
        // Send command to change node attributes
        $.ajax( {
            url : 'lib/cmd.php',
            dataType : 'json',
            data : {
                cmd : 'chdef',
                tgt : '',
                args : '-t;node;-o;' + tgtNode + ';' + args,
                msg : 'out=' + newTabId + ';tgt=' + tgtNode
            },

            success: showChdefOutput
        });
    });
    editPropsForm.append(saveBtn);
    
    // Cancel changes
    var cancelBtn = createButton('Cancel');
    cancelBtn.click(function() {
        // Close the tab
        tab.remove($(this).parent().parent().attr('id'));
    });
    editPropsForm.append(cancelBtn);

    // Append to discover tab
    tab.add(newTabId, 'Edit', editPropsForm, true);

    // Select new tab
    tab.select(newTabId);
}

/**
 * Open set node attributes dialog
 */
function openSetAttrsDialog() {
    // Open new tab
    // Create set properties form
    var setPropsForm = $('<div class="form"></div>');

    // Create info bar
    var infoBar = createInfoBar('Choose the properties you wish to change on the node. When you are finished, click Save.');
    setPropsForm.append(infoBar);
    
    // Create an input for each definable attribute
    var div, label, input, descr, value;
    for (var key in nodeAttrs) {
        value = '';
        
        // Create label and input for attribute
        div = $('<div></div>').css('display', 'inline');
        label = $('<label>' + key + ':</label>').css('vertical-align', 'middle');
        input = $('<input type="text" value="' + value + '" title="' + nodeAttrs[key] + '"/>').css('margin-top', '5px');
        
        // Change border to blue onchange
        input.bind('change', function(event) {
            $(this).css('border-color', 'blue');
        });
        
        div.append(label);
        div.append(input);
        setPropsForm.append(div);
    }
    
    // Change style for last division
    div.css({
        'display': 'block',
        'margin': '0px 0px 10px 0px'
    });
    
    // Generate tooltips
    setPropsForm.find('div input[title]').tooltip({
        position: "center right",
        offset: [-2, 10],
        effect: "fade",
        opacity: 0.8,
        delay: 0,
        predelay: 800,
        events: {
              def:     "mouseover,mouseout",
              input:   "mouseover,mouseout",
              widget:  "focus mouseover,blur mouseout",
              tooltip: "mouseover,mouseout"
        },

        // Change z index to show tooltip in front
        onBeforeShow: function() {
            this.getTip().css('z-index', $.topZIndex());
        }
    });
    
    // Enable vertical scroll
    setPropsForm.css('overflow', 'auto');
        
    // Open form as a dialog
    setPropsForm.dialog({
        title: 'Set attributes',
        modal: true,
        close: function(){
            $(this).remove();
        },
        height: 400,
        width: 700,
        buttons: {
            "Save": function() {
                // Remove any warning messages
                $(this).find('.ui-state-error').remove();
                
                // Get all inputs
                var inputs = $(this).find('input');
                
                // Go through each input
                var args = '';
                var tgtNode, attrName, attrVal;
                inputs.each(function(){
                    // If the border color is blue
                    if ($(this).css('border-left-color') == 'rgb(0, 0, 255)') {
                        // Change border color back to normal
                        $(this).css('border-color', '');
                        
                        // Get attribute name and value
                        attrName = $(this).parent().find('label').text().replace(':', '');
                        attrVal = $(this).val();
                        
                        // Get node name
                        if (attrName == 'node') {
                            tgtNode = attrVal;
                        } else {
                            // Build argument string
                            if (args) {
                                // Handle subsequent arguments
                                args += ';' + attrName + '=' + attrVal;
                            } else {
                                // Handle the 1st argument
                                args += attrName + '=' + attrVal;
                            }
                        }
                    }
                });
                
                // Send command to change node attributes
                $.ajax( {
                    url : 'lib/cmd.php',
                    dataType : 'json',
                    data : {
                        cmd : 'chdef',
                        tgt : '',
                        args : '-t;node;-o;' + tgtNode + ';' + args,
                        msg : 'node=' + tgtNode
                    },

                    /**
                     * Show results
                     * 
                     * @param data
                     *            Data returned from HTTP request
                     * @return Nothing
                     */
                    success: function(data) {
                        // Get output
                        var out = data.rsp;
                        var node = data.msg.replace('node=', '');
                        
                        // Go through output and append to paragraph
                        var msg = '';
                        for (var i in out) {
                            if (!msg) {
                                msg = node + ': ' + out[i];
                            } else {
                                msg += '<br>' + node + ': ' + out[i];
                            }
                        }
                        
                        openDialog('info', msg);
                    }
                });
                
                // Close dialog
                $(this).dialog( "close" );
            },
            "Cancel": function(){
                $(this).dialog( "close" );
            }
        }
    });
}

/**
 * Turn on monitoring for a given node
 * 
 * @param node Node to monitor on or off
 * @param monitor Monitor state, on or off
 */
function monitorNode(node, monitor) {
    // Show ganglia loader
    var gangliaCol = $('#' + nodesTableId + '_wrapper .dataTables_scrollHead .datatable thead tr th:eq(4)');
    gangliaCol.find('img').show();
    
    if (monitor == 'on') {
        // Append loader to warning bar
        var warningBar = $('#nodesTab').find('.ui-state-error p');
        if (warningBar.length) {
            warningBar.append(createLoader(''));
        }

        if (node) {
            // Check if ganglia RPMs are installed
            $.ajax( {
                url : 'lib/cmd.php',
                dataType : 'json',
                data : {
                    cmd : 'webrun',
                    tgt : '',
                    args : 'gangliacheck;' + node,
                    msg : node    // Node range will be passed along in data.msg
                },

                /**
                 * Start ganglia on a given node range
                 * 
                 * @param data Data returned from HTTP request
                 */
                success : function(data) {
                    // Get response
                    var out = data.rsp[0].split(/\n/);

                    // Go through each line
                    var warn = false;
                    var warningMsg = '';
                    for (var i in out) {
                        // If an RPM is not installed
                        if (out[i].indexOf('not installed') > -1) {
                            warn = true;
                            
                            if (warningMsg) {
                                warningMsg += '<br>' + out[i];
                            } else {
                                warningMsg = out[i];
                            }
                        }
                    }
                    
                    // If there are warnings
                    if (warn) {
                        // Create warning bar
                        var warningBar = createWarnBar(warningMsg);
                        warningBar.css('margin-bottom', '10px');
                        warningBar.prependTo($('#nodesTab'));
                    } else {
                        $.ajax( {
                            url : 'lib/cmd.php',
                            dataType : 'json',
                            data : {
                                cmd : 'webrun',
                                tgt : '',
                                args : 'gangliastart;' + data.msg + ';-r',
                                msg : data.msg
                            },

                            success : function(data) {
                                // Remove any warnings
                                $('#nodesTab').find('.ui-state-error').remove();
                                                                
                                // Update datatable
                                $.ajax( {
                                    url : 'lib/cmd.php',
                                    dataType : 'json',
                                    data : {
                                        cmd : 'webrun',
                                        tgt : '',
                                        args : 'gangliastatus;' + data.msg,
                                        msg : ''
                                    },

                                    success : loadGangliaStatus
                                });
                            }
                        });
                    } // End of if (warn)
                } // End of function(data)
            });
        } else {
            $.ajax( {
                url : 'lib/cmd.php',
                dataType : 'json',
                data : {
                    cmd : 'webrun',
                    tgt : '',
                    args : 'gangliastart',
                    msg : ''
                },

                success : function(data) {
                    // Remove any warnings
                    $('#nodesTab').find('.ui-state-error').remove();
                }
            });
        } // End of if (node)
    } else {
        var args;
        if (node) {
            args = 'gangliastop;' + node + ';-r';
        } else {
            args = 'gangliastop';
        }

        $.ajax( {
            url : 'lib/cmd.php',
            dataType : 'json',
            data : {
                cmd : 'webrun',
                tgt : '',
                args : args,
                msg : ''
            },

            success : function(data) {
                // Hide ganglia loader
                var gangliaCol = $('#' + nodesTableId + '_wrapper .dataTables_scrollHead .datatable thead tr th:eq(4)');
                gangliaCol.find('img').hide();
            }
        });
    }
}

/**
 * Install Ganglia on a given node
 * 
 * @param node Node to install Ganglia on
 */
function installGanglia(node) {
    var iframe = createIFrame('lib/cmd.php?cmd=webrun&tgt=&args=installganglia;' + node + '&msg=' + node + '&opts=flush');
    iframe.prependTo($('#nodesTab'));
    
    // Turn on Ganglia for node
    monitorNode(node, 'on');
}

/**
 * After nodes are loaded, load more information based on different hardware architectures 
 * 
 * @param group Group name
 */
function advancedLoad(group){
    var tempIndex = 0;
    var tableHeaders = $('#' + nodesTableId).parents('.dataTables_scroll').find('.dataTables_scrollHead thead tr:eq(0) th');
    var colNameHash = new Object();
    var colName = '';
    var archCol = 0, hcpCol = 0;
    
    // Find out the column name and their index
    for (tempIndex = 0; tempIndex < tableHeaders.size(); tempIndex++){
        var header = tableHeaders.eq(tempIndex);
        // Skip headers that are links, e.g. status, power, and monitor
        if (header.find('a').size() > 0){
            continue;
        }
        
        colName = header.text();
        
        if (colName) {
            colNameHash[colName] = tempIndex;
        }
    }
    
    // If there is no arch column, exit because you cannot distinguish hardware type
    if (!colNameHash['arch']) {
        return;
    }
    
    if (!colNameHash['hcp']) {
        return;
    }
    archCol = colNameHash['arch'];
    hcpCol = colNameHash['hcp'];
    
    // Get hardware control point
    var rows = $('#' + nodesTableId + ' tbody tr');
    var hcps = new Object();
    var rowsNum = rows.size();
    for (var j = 0; j < rowsNum; j++) {
        var val = rows.eq(j).find('td').eq(hcpCol).html();
        var archval = rows.eq(j).find('td').eq(archCol).html();
        if (-1 == archval.indexOf('390')){
            continue;
        }
        hcps[val] = 1;
    }

    var args;
    for (var h in hcps) {
        // Get node without domain name
        args = h.split('.');
        
        // If there are no disk pools or network names cookie for this hcp
        if (!$.cookie(args[0] + 'diskpools') || !$.cookie(args[0] + 'networks')) {
            // Check if SMAPI is online
            $.ajax( {
                url : 'lib/cmd.php',
                dataType : 'json',
                data : {
                    cmd : 'lsvm',
                    tgt : args[0],
                    args : '',
                    msg : 'group=' + group + ';hcp=' + args[0]
                },

                // Load hardware control point specific info
                // Get disk pools and network names
                success : loadHcpInfo
            });     
        }
    } // End of for
}

/**
 * Jump to provision page on-click
 * 
 * @param tgtNodes Target nodes
 */
function jump2Provision(tgtNodes){
    var nodeArray = tgtNodes.split(',');
    var nodeName = '';
    var index = 0;
    var archType = '';
    var errorMsg = '';
    var master = '';
    var tftpserver = '';
    var nfsserver = '';
    var diaDiv = $('<div title="Provision" class="form" id="deployDiv"></div>');
    
    // Check the first node's arch type
    for (index in nodeArray){
        nodeName = nodeArray[index];
        
        // Skip if node does not have arch
        if (!origAttrs[nodeName]['arch']){
            errorMsg = 'Nodes should have arch defined! ';
            break;
        }
        
        if (index == 0) {
            archType = origAttrs[nodeName]['arch'];
        }
        
        // Skip if nodes do not have same arch
        if (archType != origAttrs[nodeName]['arch']){
            errorMsg = 'Nodes should belong to the same arch!<br/>';
            break;
        }
    }

    // Skip if nodes do not have MAC address
    for (index in nodeArray){
        if (!origAttrs[nodeName]['mac'] || !origAttrs[nodeName]['ip']){
            errorMsg += 'Nodes should have the IP and MAC addresses defined!<br/>';
            break;
        }
    }
    
    if (archType.indexOf('390') != -1) {
        errorMsg += 'Please use the provision page';
    }
    
    // Open dialog to show error message
    if (errorMsg){
        diaDiv.append(createWarnBar(errorMsg));
        diaDiv.dialog({
            modal: true,
            close: function(){
                $(this).remove();
            },
            width: 400,
            buttons: {
                'Close': function(){
                    $(this).dialog('destroy');
                }
            }
        });
        
        return;
    }
    
    if (origAttrs[nodeName]['xcatmaster']) {
        master = origAttrs[nodeName]['xcatmaster'];
    }
    
    if (origAttrs[nodeName]['tftpserver']) {
        tftpserver = origAttrs[nodeName]['tftpserver'];
    }
    
    if (origAttrs[nodeName]['nfsserver']) {
        nfsserver = origAttrs[nodeName]['nfsserver'];
    }
    
    window.location.href = 'provision.php?nodes=' + tgtNodes + '&arch=' + archType + '&master=' + master +
                           '&tftpserver=' + tftpserver + '&nfsserver=' + nfsserver;
}