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

/**
 * 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
 * 
 * @return Nothing
 */
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
 * @return Nothing
 */
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
 * @return Nothing
 */
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
 * @return Nothing
 */
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
 * @return Nothing
 */
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
                 * @return Nothing
                 */
                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
				 * @return Nothing
				 */
				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
 * @return Nothing
 */
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
 * @return Nothing
 */
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" class="ui-state-highlight ui-corner-all">' 
				+ '<p><span class="ui-icon ui-icon-info"></span> Updating table <img src="images/loader.gif"/></p>'
			+'</div>');
		update.dialog({
			modal: true,
			width: 300,
			position: 'center'
		});
	}
}

/**
 * Add nodes to datatable
 * 
 * @param data
 *            Data returned from HTTP request
 * @return Nothing
 */
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 + ' 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 four headers
	var headersCol = new Object();
	headersCol['node'] = 1;
	headersCol['status'] = 2;
	headersCol['power'] = 3;
	headersCol['comments'] = 4;
	
	// Go through each header
	for (var i = 5; 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;
		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(4);
		
		// 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
 * @return Nothing
 */
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
 * @return Nothing
 */
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
 * @return Nothing
 */
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
 * @return Nothing
 */
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
 * @return Nothing
 */
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
 * @return Nothing
 */
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
 * @return Nothing
 */
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
 * @return Nothing
 */
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
 * @return Nothing
 */
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
 * @return Nothing
 */
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
 * @return Nothing
 */
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
 * @return Nothing
 */
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);
			}
		}
		
		// Adjust column size
		adjustColumnSize(nodesTableId);
	} 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
 * @return Nothing
 */
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
 * @return Nothing
 */
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
 * @return Nothing
 */
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
 * @return Nothing
 */
function setGroupsCookies(data) {
	var rsp = data.rsp;
	$.cookie('groups', rsp);
}

/**
 * Get nodes that are checked in a given datatable
 * 
 * @param datatableId
 *            The datatable ID
 * @return Nodes that were checked
 */
function getNodesChecked(datatableId) {
	var tgts = '';

	// Get nodes that were checked
	var nodes = $('#' + datatableId + ' input[type=checkbox]:checked');
	for (var i in nodes) {
		var tgtNode = nodes.eq(i).attr('name');
		
		if (tgtNode){
			tgts += tgtNode;
			
			// Add a comma at the end
			if (i < nodes.length - 1) {
				tgts += ',';
			}
		}
	}

	return tgts;
}

/**
 * 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){
	var dTable, rows, cols;
	
	// Get datatable
	dTable = $(table).dataTable();
	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
 * @return Nothing
 */
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
 * @return Nothing
 */
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 += 'rconsShow.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=670,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
 * @return Nothing
 */
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
 * @return Nothing
 */
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
 * @return Nothing
 */
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
 * @return Nothing
 */
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
 * 
 * @return Nothing
 */
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,
		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
 * @return Nothing
 */
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
				 * @return Nothing
				 */
				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
 * @return Nothing
 */
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
 * @return Nothing
 */
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 onclick
 * 
 * @param tgtNodes
 * 			Target nodes
 * @return Nothing
 */
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 (only supported for Linux)" 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 (0 == index) {
            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,
            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;
}