diff --git a/src/sunstone/public/js/one-ui_views.js b/src/sunstone/public/js/one-ui_views.js index fb67fc85b3..c2a2e9d53e 100644 --- a/src/sunstone/public/js/one-ui_views.js +++ b/src/sunstone/public/js/one-ui_views.js @@ -21,25 +21,28 @@ var dataTable_hosts=null; //~ var dataTable_clusters=null; -var dataTable_vMachines=null; -var dataTable_vNetworks=null; -var dataTable_users=null; -var dataTable_images=null; -var host_list_json = {}; -var cluster_list_json = {}; -var vmachine_list_json = {}; -var network_list_json = {}; -var user_list_json = {}; -var image_list_json = {}; -var hosts_select=""; -var clusters_select=""; -var vnetworks_select=""; -var images_select=""; +//~ var dataTable_vMachines=null; +//~ var dataTable_vNetworks=null; +//~ var dataTable_users=null; +//~ var dataTable_images=null; +//~ var host_list_json = {}; +//~ var cluster_list_json = {}; +//~ var vmachine_list_json = {}; +//~ var network_list_json = {}; +//~ var user_list_json = {}; +//~ var image_list_json = {}; +//~ var hosts_select=""; +//~ var clusters_select=""; +//~ var vnetworks_select=""; +//~ var images_select=""; var cookie = {}; var username = ''; var uid = ''; var spinner = '<img src="/images/ajax-loader.gif" alt="retrieving" class="loading_img"/>'; + + + /*###################################################################### * DOCUMENT READY FUNCTIONS * ###################################################################*/ @@ -229,12 +232,12 @@ function initDataTables(){ } //Adds a listener to checks all the elements of a table -function initCheckAllBoxes(){ +function initCheckAllBoxes(datatable){ //not showing nice in that position //$('.check_all').button({ icons: {primary : "ui-icon-check" }, // text : true}); - $('.check_all').css({"border":"2px"}); - $('.check_all').click(function(){ + $('.check_all',datatable).css({"border":"2px"}); + $('.check_all',datatable).click(function(){ if ($(this).attr("checked")) { $('tbody input:checkbox', $(this).parents("table")).each(function(){ @@ -761,6 +764,9 @@ function actionButtonListener(){ }); } + + + function preloadTables(){ dataTable_hosts.fnClearTable(); addElement([ @@ -2180,17 +2186,14 @@ function setupTips(){ //Crawls the user dataTable for that. If such user is not found, //we return the uid. function getUserName(uid){ + nodes = dataTable_users.fnGetData(); user = "uid "+uid; - if (dataTable_users != null){ - nodes = dataTable_users.fnGetData(); - $.each(nodes,function(){ - if (uid == this[1]) { - user = this[2]; - return false; - } - }); - }; - + $.each(nodes,function(){ + if (uid == this[1]) { + user = this[2]; + return false; + } + }); return user; } @@ -2556,13 +2559,13 @@ function emptyDashboard(){ $("#dashboard .value_td span").html(spinner); } -function updateDashboard(what){ +function updateDashboard(what,json_info){ db = $('#dashboard'); switch (what){ case "hosts": - total_hosts=host_list_json.length; + total_hosts=json_info.length; active_hosts=0; - $.each(host_list_json,function(){ + $.each(json_info,function(){ if (parseInt(this.HOST.STATE) < 3){ active_hosts++;} }); @@ -2570,14 +2573,14 @@ function updateDashboard(what){ $('#active_hosts',db).html(active_hosts); break; case "clusters": - total_clusters=cluster_list_json.length; + total_clusters=json_info.length; $('#total_clusters',db).html(total_clusters); break; case "vms": - total_vms=vmachine_list_json.length; + total_vms=json_info.length; running_vms=0; failed_vms=0; - $.each(vmachine_list_json,function(){ + $.each(json_info,function(){ vm_state = parseInt(this.VM.STATE); if (vm_state == 3){ running_vms++; @@ -2592,8 +2595,8 @@ function updateDashboard(what){ break; case "vnets": public_vnets=0; - total_vnets=network_list_json.length; - $.each(network_list_json,function(){ + total_vnets=json_info.length; + $.each(json_info,function(){ if (parseInt(this.VNET.PUBLIC)){ public_vnets++;} }); @@ -2601,13 +2604,13 @@ function updateDashboard(what){ $('#public_vnets',db).html(public_vnets); break; case "users": - total_users=user_list_json.length; + total_users=json_info.length; $('#total_users',db).html(total_users); break; case "images": - total_images=image_list_json.length; + total_images=json_info.length; public_images=0; - $.each(image_list_json,function(){ + $.each(json_info,function(){ if (parseInt(this.IMAGE.PUBLIC)){ public_images++;} }); diff --git a/src/sunstone/public/js/plugins/hosts-tab.js b/src/sunstone/public/js/plugins/hosts-tab.js new file mode 100644 index 0000000000..a1abcefe54 --- /dev/null +++ b/src/sunstone/public/js/plugins/hosts-tab.js @@ -0,0 +1,444 @@ +/* -------------------------------------------------------------------------- */ +/* Copyright 2002-2011, OpenNebula Project Leads (OpenNebula.org) */ +/* */ +/* Licensed under the Apache License, Version 2.0 (the "License"); you may */ +/* not use this file except in compliance with the License. You may obtain */ +/* a copy of the License at */ +/* */ +/* http://www.apache.org/licenses/LICENSE-2.0 */ +/* */ +/* Unless required by applicable law or agreed to in writing, software */ +/* distributed under the License is distributed on an "AS IS" BASIS, */ +/* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. */ +/* See the License for the specific language governing permissions and */ +/* limitations under the License. */ +/* -------------------------------------------------------------------------- */ + +/*Host tab plugin*/ + +var host_tab_content = +'<form id="form_hosts" action="javascript:alert(\'js errors?!\')">\ + <div class="action_blocks">\ + </div>\ +<table id="datatable_hosts" class="display">\ + <thead>\ + <tr>\ + <th class="check"><input type="checkbox" class="check_all" value="">All</input></th>\ + <th>ID</th>\ + <th>Name</th>\ + <th>Cluster</th>\ + <th>Running VMs</th>\ + <th>CPU Use</th>\ + <th>Memory use</th>\ + <th>Status</th>\ + </tr>\ + </thead>\ + <tbody id="tbodyhosts">\ + </tbody>\ +</table>\ +</form>'; + +var hosts_select=""; +var host_list_json = {}; + + +//Setup actions +var host_actions = { + "Host.create" : { + type: "create", + call : OpenNebula.Host.create, + callback = addHostElement, + error : onError, + notify:true, + condition: true + }, + + "Host.enable" = { + type: "multiple", + call : OpenNebula.Host.enable, + callback = host_update_callback, + error : onError, + notify:true, + condition:true + }, + + "Host.disable" = { + type: "multiple", + call : OpenNebula.Host.disable, + callback = host_update_callback, + error : onError, + notify:true, + condition:true + }, + + "Host.delete" = { + type: "multiple", + call : OpenNebula.Host.create, + callback = deleteHostElement, + error : onError, + notify:true, + condition:true + }, + + "Host.list" = { + type: "custom", + call : function() { + OpenNebula.Host.list({success: updateHostsView, error: onError}); + OpenNebula.Cluster.list({success: updateClustersView, error: onError}); + } + callback: null, + error: onError, + notify:true, + condition:true + }, + + "Cluster.create" = { + type: "create", + call : OpenNebula.Cluster.create, + callback = addClusterElement + error : onError, + notify:true, + condition : true + }, + + "Cluster.delete" = { + type: "multiple", + call : OpenNebula.Host.create, + callback = addHostElement, + error : onError, + notify:true, + condition:true + }, + + "Cluster.addhost" = { + type: "confirm_with_select", + select: cluster_select, + tip: "Select the cluster in which you would like to place the hosts", + call : OpenNebula.Cluster.addhost, + callback = updateHostElement, + error : onError, + notify:true, + condition : true + }, + + "Cluster.removehost" = { + type: "multiple", + call : OpenNebula.Cluster.removehost, + callback = deleteHostElement, + error : onError, + notify:true, + condition:true + } + }; + + +var host_buttons = [ + { + type: "create", + text: "+ New host", + action: "Host.create", + condition : true + }, + { + type: "action", + text: "Enable", + action: "Host.enable", + condition : true + }, + { + type: "action", + text: "Disable", + action: "Host.disable", + condition : true + }, + { + type: "create", + text: "+ New Cluster", + action: "Cluster.create", + condition : true + }, + { + type: "action", + text: "Delete cluster", + action: "Cluster.delete", + condition : true + } + { + type: "select", + action: [{ text: "Add host to cluster", + value: "Cluster.addhost", + condition: true}, + { text: "Remove host from cluster", + value: "Cluster.removehost", + condition: true}], + condition : true + }, + { + type: "action", + text: "Delete host", + value: "Host.delete", + condition : true + }]}; + +for (action in host_actions){ + Sunstone.addAction(action,host_actions[action]); +} + +Sunstone.addMainTab(hosts_tab_content,'hosts_tab'); + +$.each(host_buttons,function(){ + Sunstone.addButton(this,'#hosts_tab'); +} + + +//Setup tab + + +//Plugin functions +function host_update_callback(req){ + OpenNebula.Host.show({data:{id:req.request.data[0]},success: updateHostElement,error: onError}); +} + +function hostElementArray(host_json){ + host = host_json.HOST; + acpu = parseInt(host.HOST_SHARE.MAX_CPU); + if (!acpu) {acpu=100}; + acpu = acpu - parseInt(host.HOST_SHARE.CPU_USAGE); + + total_mem = parseInt(host.HOST_SHARE.MAX_MEM); + free_mem = parseInt(host.HOST_SHARE.FREE_MEM); + + if (total_mem == 0) { + ratio_mem = 0; + } else { + ratio_mem = Math.round(((total_mem - free_mem) / total_mem) * 100); + } + + + total_cpu = parseInt(host.HOST_SHARE.MAX_CPU); + used_cpu = Math.max(total_cpu - parseInt(host.HOST_SHARE.USED_CPU),acpu); + + if (total_cpu == 0) { + ratio_cpu = 0; + } else { + ratio_cpu = Math.round(((total_cpu - used_cpu) / total_cpu) * 100); + } + + pb_mem = +'<div style="height:10px" class="ratiobar ui-progressbar ui-widget ui-widget-content ui-corner-all" role="progressbar" aria-valuemin="0" aria-valuemax="100" aria-valuenow="'+ratio_mem+'">\ + <div class="ui-progressbar-value ui-widget-header ui-corner-left ui-corner-right" style="width: '+ratio_mem+'%;"/>\ + <span style="position:relative;left:45px;top:-4px;font-size:0.6em">'+ratio_mem+'%</span>\ + </div>\ +</div>'; + + pb_cpu = +'<div style="height:10px" class="ratiobar ui-progressbar ui-widget ui-widget-content ui-corner-all" role="progressbar" aria-valuemin="0" aria-valuemax="100" aria-valuenow="'+ratio_cpu+'">\ + <div class="ui-progressbar-value ui-widget-header ui-corner-left ui-corner-right" style="width: '+ratio_cpu+'%;"/>\ + <span style="position:relative;left:45px;top:-4px;font-size:0.6em">'+ratio_cpu+'%</span>\ + </div>\ +</div>'; + + + return [ '<input type="checkbox" id="host_'+host.ID+'" name="selected_items" value="'+host.ID+'"/>', + host.ID, + host.NAME, + host.CLUSTER, + host.HOST_SHARE.RUNNING_VMS, //rvm + pb_cpu, + pb_mem, + OpenNebula.Helper.resource_state("host",host.STATE) ]; + + + //~ return [ '<input type="checkbox" id="host_'+host.ID+'" name="selected_items" value="'+host.ID+'"/>', + //~ host.ID, + //~ host.NAME, + //~ host.CLUSTER, + //~ host.HOST_SHARE.RUNNING_VMS, //rvm + //~ host.HOST_SHARE.MAX_CPU, //tcpu + //~ parseInt(host.HOST_SHARE.MAX_CPU) - parseInt(host.HOST_SHARE.USED_CPU), //fcpu + //~ acpu, + //~ humanize_size(host.HOST_SHARE.MAX_MEM), + //~ humanize_size(host.HOST_SHARE.FREE_MEM), + //~ OpenNebula.Helper.resource_state("host",host.STATE) ]; +} + + +function hostInfoListener(){ + $('#tbodyhosts tr').live("click",function(e){ + + //do nothing if we are clicking a checkbox! + if ($(e.target).is('input')) {return true;} + + popDialogLoading(); + aData = dataTable_hosts.fnGetData(this); + id = $(aData[0]).val(); + OpenNebula.Host.show({data:{id:id},success: updateHostInfo,error: onError}); + return false; + }); +} + +function updateHostSelect(host_list){ + + //update select helper + hosts_select=""; + hosts_select += "<option value=\"\">Select a Host</option>"; + $.each(host_list, function(){ + hosts_select += "<option value=\""+this.HOST.ID+"\">"+this.HOST.NAME+"</option>"; + }); + + //update static selectors + $('#vm_host').html(hosts_select); +} + + +function updateHostElement(request, host_json){ + id = host_json.HOST.ID; + element = hostElementArray(host_json); + updateSingleElement(element,dataTable_hosts,'#host_'+id); +} + +function deleteHostElement(req){ + deleteElement(dataTable_hosts,'#host_'+req.request.data); +} + +function addHostElement(request,host_json){ + id = host_json.HOST.ID; + element = hostElementArray(host_json); + addElement(element,dataTable_hosts); +} + +function updateHostsView (request,host_list){ + host_list_json = host_list; + host_list_array = [] + + $.each(host_list,function(){ + //Grab table data from the host_list + host_list_array.push(hostElementArray(this)); + }); + + updateView(host_list_array,dataTable_hosts); + updateHostSelect(host_list); + updateDashboard("hosts",host_list_json); +} + +function updateHostInfo(request,host){ + host_info = host.HOST + rendered_info = +'<div id="host_informations">\ + <ul>\ + <li><a href="#info_host">Host information</a></li>\ + <li><a href="#host_template">Host template</a></li>\ + </ul>\ + <div id="info_host">\ + <table id="info_host_table" class="info_table">\ + <thead>\ + <tr><th colspan="2">Host information - '+host_info.NAME+'</th></tr>\ + </thead>\ + <tr>\ + <td class="key_td">ID</td>\ + <td class="value_td">'+host_info.ID+'</td>\ + </tr>\ + <tr>\ + <td class="key_td">State</td>\ + <td class="value_td">'+OpenNebula.Helper.resource_state("host",host_info.STATE)+'</td>\ + </tr>\ + <tr>\ + <td class="key_td">Cluster</td>\ + <td class="value_td">'+host_info.CLUSTER+'</td>\ + </tr>\ + <tr>\ + <td class="key_td">IM MAD</td>\ + <td class="value_td">'+host_info.IM_MAD+'</td>\ + </tr>\ + <tr>\ + <td class="key_td">VM MAD</td>\ + <td class="value_td">'+host_info.VM_MAD+'</td>\ + </tr>\ + <tr>\ + <td class="key_td">TM MAD</td>\ + <td class="value_td">'+host_info.TM_MAD+'</td>\ + </tr>\ + </table>\ + <table id="host_shares_table" class="info_table">\ + <thead>\ + <tr><th colspan="2">Host shares</th></tr>\ + </thead>\ + <tr>\ + <td class="key_td">Max Mem</td>\ + <td class="value_td">'+humanize_size(host_info.HOST_SHARE.MAX_MEM)+'</td>\ + </tr>\ + <tr>\ + <td class="key_td">Used Mem (real)</td>\ + <td class="value_td">'+humanize_size(host_info.HOST_SHARE.USED_MEM)+'</td>\ + </tr>\ + <tr>\ + <td class="key_td">Used Mem (allocated)</td>\ + <td class="value_td">'+humanize_size(host_info.HOST_SHARE.MAX_USAGE)+'</td>\ + </tr>\ + <tr>\ + <td class="key_td">Used CPU (real)</td>\ + <td class="value_td">'+host_info.HOST_SHARE.USED_CPU+'</td>\ + </tr>\ + <tr>\ + <td class="key_td">Used CPU(allocated)</td>\ + <td class="value_td">'+host_info.HOST_SHARE.CPU_USAGE+'</td>\ + </tr>\ + <tr>\ + <td class="key_td">Running VMs</td>\ + <td class="value_td">'+host_info.HOST_SHARE.RUNNING_VMS+'</td>\ + </tr>\ + </table>\ + </div>\ + <div id="host_template">\ + <table id="host_template_table" class="info_table">\ + <thead><tr><th colspan="2">Host template</th></tr></thead>'+ + prettyPrintJSON(host_info.TEMPLATE)+ + '</table>\ + </div>\ +</div>'; + popDialog(rendered_info); + $('#host_informations').tabs(); + +} + +//Document ready +$(document).ready(){ + + //prepare host datatable + var dataTable_hosts = $("#datatable_hosts").dataTable({ + "bJQueryUI": true, + "bSortClasses": false, + "bAutoWidth":false, + "sPaginationType": "full_numbers", + "aoColumnDefs": [ + { "bSortable": false, "aTargets": ["check"] }, + { "sWidth": "60px", "aTargets": [0,4] }, + { "sWidth": "35px", "aTargets": [1] }, + { "sWidth": "120px", "aTargets": [5,6] } + ] + }); + + //preload it + dataTable_hosts.fnClearTable(); + addElement([ + spinner, + '','','','','','',''],dataTable_hosts); + OpenNebula.Host.list({success: updateHostsView,error: onError}); + + //set refresh interval + setInterval(function(){ + nodes = $('input:checked',dataTable_hosts.fnGetNodes()); + filter = $("#datatable_hosts_filter input").attr("value"); + if (!nodes.length && !filter.length){ + OpenNebula.Host.list({timeout: true, success: updateHostsView,error: onError}); + } + },interval); + + initCheckAllBoxes(dataTable_hosts); + + //.action button listener + $('#hosts_tab .action_button').click(function(){ + Sunstone.runActionOnDatatableNodes($(this).val(),dataTable_hosts); + } + +} diff --git a/src/sunstone/public/js/sunstone-util.js b/src/sunstone/public/js/sunstone-util.js new file mode 100644 index 0000000000..5baa070b62 --- /dev/null +++ b/src/sunstone/public/js/sunstone-util.js @@ -0,0 +1,260 @@ +/* -------------------------------------------------------------------------- */ +/* Copyright 2002-2011, OpenNebula Project Leads (OpenNebula.org) */ +/* */ +/* Licensed under the Apache License, Version 2.0 (the "License"); you may */ +/* not use this file except in compliance with the License. You may obtain */ +/* a copy of the License at */ +/* */ +/* http://www.apache.org/licenses/LICENSE-2.0 */ +/* */ +/* Unless required by applicable law or agreed to in writing, software */ +/* distributed under the License is distributed on an "AS IS" BASIS, */ +/* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. */ +/* See the License for the specific language governing permissions and */ +/* limitations under the License. */ +/* -------------------------------------------------------------------------- */ + + +/* Some useful functions for Sunstone default plugins */ + +function emptyDashboard(){ + $("#dashboard .value_td span").html(spinner); +} + +function updateDashboard(what,json_info){ + db = $('#dashboard'); + switch (what){ + case "hosts": + total_hosts=json_info.length; + active_hosts=0; + $.each(json_info,function(){ + if (parseInt(this.HOST.STATE) < 3){ + active_hosts++;} + }); + $('#total_hosts',db).html(total_hosts); + $('#active_hosts',db).html(active_hosts); + break; + case "clusters": + total_clusters=json_info.length; + $('#total_clusters',db).html(total_clusters); + break; + case "vms": + total_vms=json_info.length; + running_vms=0; + failed_vms=0; + $.each(json_info,function(){ + vm_state = parseInt(this.VM.STATE); + if (vm_state == 3){ + running_vms++; + } + else if (vm_state == 7) { + failed_vms++; + } + }); + $('#total_vms',db).html(total_vms); + $('#running_vms',db).html(running_vms); + $('#failed_vms',db).html(failed_vms); + break; + case "vnets": + public_vnets=0; + total_vnets=json_info.length; + $.each(json_info,function(){ + if (parseInt(this.VNET.PUBLIC)){ + public_vnets++;} + }); + $('#total_vnets',db).html(total_vnets); + $('#public_vnets',db).html(public_vnets); + break; + case "users": + total_users=json_info.length; + $('#total_users',db).html(total_users); + break; + case "images": + total_images=json_info.length; + public_images=0; + $.each(json_info,function(){ + if (parseInt(this.IMAGE.PUBLIC)){ + public_images++;} + }); + $('#total_images',db).html(total_images); + $('#public_images',db).html(public_images); + break; + } +} + +function pad(number,length) { + var str = '' + number; + while (str.length < length) + str = '0' + str; + return str; +} + +function pretty_time(time_seconds) +{ + var d = new Date(); + d.setTime(time_seconds*1000); + + var secs = pad(d.getSeconds(),2); + var hour = pad(d.getHours(),2); + var mins = pad(d.getMinutes(),2); + var day = pad(d.getDate(),2); + var month = pad(d.getMonth(),2); + var year = d.getFullYear(); + + return hour + ":" + mins +":" + secs + " " + month + "/" + day + "/" + year; +} + +function humanize_size(value) { + if (typeof(value) === "undefined") { + value = 0; + } + var binarySufix = ["K", "M", "G", "T" ]; + var i=0; + while (value > 1024 && i < 3){ + value = value / 1024; + i++; + } + value = Math.round(value * 10) / 10; + + if (value - Math.round(value) == 0) { + value = Math.round(value); + } + + var st = value + binarySufix[i]; + return st; +} + +function deleteElement(data_table,tag){ + tr = $(tag).parents('tr')[0]; + data_table.fnDeleteRow(tr); + $('input',data_table).trigger("change"); +} + +function tableCheckboxesListener(dataTable){ + + context = dataTable.parents('form'); + last_action_b = $('.last_action_button',context); + $('.top_button, .list_button',context).button("disable"); + if (last_action_b.length && last_action_b.val().length){ + last_action_b.button("disable"); + }; + $('.new_button',context).button("enable"); + + //listen to changes + $('input',dataTable).live("change",function(){ + dataTable = $(this).parents('table').dataTable(); + context = dataTable.parents('form'); + last_action_b = $('.last_action_button',context); + nodes = dataTable.fnGetNodes(); + total_length = nodes.length; + checked_length = $('input:checked',nodes).length; + + if (total_length == checked_length){ + $('.check_all',dataTable).attr("checked","checked"); + } else { + $('.check_all',dataTable).removeAttr("checked"); + } + + if (checked_length){ + $('.top_button, .list_button',context).button("enable"); + if (last_action_b.length && last_action_b.val().length){ + last_action_b.button("enable"); + }; + $('.new_button',context).button("enable"); + } else { + $('.top_button, .list_button',context).button("disable"); + last_action_b.button("disable"); + $('.new_button',context).button("enable"); + } + }); + +} + +// Updates a data_table, with a 2D array containing +// Does a partial redraw, so the filter and pagination are kept +function updateView(item_list,data_table){ + if (data_table!=null) { + data_table.fnClearTable(); + data_table.fnAddData(item_list); + data_table.fnDraw(false); + }; +} + +function updateSingleElement(element,data_table,tag){ + tr = $(tag).parents('tr')[0]; + position = data_table.fnGetPosition(tr); + data_table.fnUpdate(element,position,0); + $('input',data_table).trigger("change"); + +} + +// Returns an string in the form key=value key=value ... +// Does not explore objects in depth. +function stringJSON(json){ + str = "" + for (field in json) { + str+= field + '=' + json[field] + ' '; + } + return str; +} + +// Returns the running time data +function str_start_time(vm){ + return pretty_time(vm.STIME); +} + + +//Notifications +function notifySubmit(action, args, extra_param){ + var action_text = action.replace(/OpenNebula\./,'').replace(/\./,' '); + + var msg = "<h1>Submitted</h1>"; + msg += action_text + ": " + args; + if (extra_param != null) + msg += " >> " + extra_param; + + $.jGrowl(msg, {theme: "jGrowl-notify-submit"}); +} + +function notifyError(msg){ + msg = "<h1>Error</h1>" + msg; + + $.jGrowl(msg, {theme: "jGrowl-notify-error", sticky: true }); +} + +// Returns an HTML string with the json keys and values in the form +// key: value<br /> +// It recursively explores objects, and flattens their contents in +// the result. +function prettyPrintJSON(template_json){ + str = "" + for (field in template_json) { + if (typeof template_json[field] == 'object'){ + str += prettyPrintJSON(template_json[field]) + '<tr><td></td><td></td></tr>'; + } else { + str += '<tr><td class="key_td">'+field+'</td><td class="value_td">'+template_json[field]+'</td></tr>'; + }; + }; + return str; +} + +//Adds a listener to checks all the elements of a table +function initCheckAllBoxes(datatable){ + //not showing nice in that position + //$('.check_all').button({ icons: {primary : "ui-icon-check" }, + // text : true}); + $('.check_all',datatable).css({"border":"2px"}); + $('.check_all',datatable).click(function(){ + if ($(this).attr("checked")) { + $('tbody input:checkbox', + $(this).parents("table")).each(function(){ + $(this).attr("checked","checked"); + }); + + } else { + $('tbody input:checkbox', + $(this).parents("table")).each(function(){ + $(this).removeAttr("checked"); + }); } + }); +} diff --git a/src/sunstone/public/js/sunstone.js b/src/sunstone/public/js/sunstone.js new file mode 100644 index 0000000000..7c631040af --- /dev/null +++ b/src/sunstone/public/js/sunstone.js @@ -0,0 +1,472 @@ +/* -------------------------------------------------------------------------- */ +/* Copyright 2002-2011, OpenNebula Project Leads (OpenNebula.org) */ +/* */ +/* Licensed under the Apache License, Version 2.0 (the "License"); you may */ +/* not use this file except in compliance with the License. You may obtain */ +/* a copy of the License at */ +/* */ +/* http://www.apache.org/licenses/LICENSE-2.0 */ +/* */ +/* Unless required by applicable law or agreed to in writing, software */ +/* distributed under the License is distributed on an "AS IS" BASIS, */ +/* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. */ +/* See the License for the specific language governing permissions and */ +/* limitations under the License. */ +/* -------------------------------------------------------------------------- */ + + +//TODOs: handle confirm and confirm with select dialogs + +var cookie = {}; +var username = ''; +var uid = ''; +var spinner = '<img src="/images/ajax-loader.gif" alt="retrieving" class="loading_img"/>'; + + +var Sunstone = { + + + "addAction" : function (name,action_obj) { + SunstoneCfg.config.actions[name] = action_obj; + }, + + "updateAction" : function(action,new_action) { + + }, + + "removeAction" : function(action) { + + }, + + "addMainTab" : function(tab_id,title_arg,content_arg) { + SunstoneCfg["tabs"][tab_id] = {title: title_arg, + content: content_arg}; + }, + + "updateMainTab" : function(tab_id,new_content){ + + }, + + "removeMainTab" : function(tab_id) { + + }, + + "runAction" : function(action, data_arg, extra_param){ + + var actions = Sunstone.actions; + if (!actions[action]){ + notifyError("Action "+action+" not defined"); + return; + } + + var action_cfg = actions[action]; + + var call = action_cfg.run; + var callback = action_cfg.callback; + var err = action_cfg.callback; + var notify = action_cfg.notify; + + + //We ease the use of: + // * Create call + // * Confirm and confirm with select calls + // * Calls on multiple elements + // * Other calls + switch (actions[action].type){ + + case "create","single": + call({data:data_arg, success: callback,error:err}); + break; + case "confirm": + tip = actions.tip; + //popup confirm dialog (action,tip). + break; + case "confirm_with_select": + tip = OpenNebula.Views.Actions[action].tip; + select = OpenNebula.Views.Actions[action].select; + //popup confirm dialog with select(action,tip,select) + break; + case "list": + call({success: callback, error:err}); + break; + case "multiple": + //run on the list of nodes that come on the data + $.each(data_arg,function(){ + if (extra_param){ + call(this,callback,error) + } else { + call(this,extra_param,callback,error) + } + }); + break; + default: + //we have supposedly altered an action and we want it to do + //something completely different + call(data,extra_param); + } + + }, + + "runActionOnDatatableNodes": function(action,datatable){ + if (dataTable != null){ + + //Which rows of the datatable are checked? + var nodes = $('input:checked',dataTable.fnGetNodes()); + var data = []; + $.each(nodes,function(){ + data.push($(this).val()); + } + runAction(action,data); + + } else { + notifyError("Unknown datatable"); + }; + }//meter coma y seguir aquĆ + +} + +var SunstoneCfg = { + "config" = { + + "actions" : { + + "VM.create" = { + + }, + + "VM.deploy" = { + + }, + + "VM.migrate" = { + + }, + + "VM.livemigrate" = { + + }, + + "VM.hold" = { + + }, + + "VM.release" = { + + }, + + "VM.suspend" = { + + }, + + "VM.resume" = { + + }, + + "VM.stop" = { + + }, + + "VM.restart" = { + + }, + + "VM.shutdown" = { + + }, + + "VM.cancel" = { + + }, + + "VM.delete" = { + + }, + + "Network.publish" = { + + }, + + "Network.unpublish" = { + + }, + + "Network.delete" = { + + }, + + "User.create" = { + + }, + + "User.delete" = { + + }, + + "Image.enable" = { + + }, + + "Image.disable" = { + + }, + + "Image.persistent" = { + + }, + + "Image.nonpersistent" = { + + }, + + "Image.publish" = { + + }, + + "Image.unpublish" = { + + }, + + "Image.delete" = { + + } + }, + + "tabs" = { + + }, + + "info_panels" = { + + } + + } + +} + +//plugins have done their jobs when we execute this +$(document).ready(){ + readCookie(); + setLogin(); + insertTabs(); + insertButtons(); + + + initListButtons(); + setupCreateDialogs(); //listener for create + setupTips(); + + //action button listener! -> plugins deal with that + + + $('button').button(); + $('div#select_helpers').hide(); + emptyDashboard(); + + $(".ui-widget-overlay").live("click", function (){ + $("div:ui-dialog:visible").dialog("close"); + }); + + //Dashboard link listener + $("#dashboard_table h3 a").live("click", function (){ + var tab = $(this).attr('href'); + showTab(tab); + return false; + }); + + //Close select lists... + $('*:not(.action_list,.list_button)').click(function(){ + $('.action_list:visible').hide(); + }); + +} + + +//reads the cookie and places its info in the 'cookie' var +function readCookie(){ + $.each(document.cookie.split("; "), function(i,e){ + var e_split = e.split("="); + var key = e_split[0]; + var value = e_split[1]; + cookie[key] = value; + }); +} + +//sets the user info in the top bar and creates a listner in the signout button +function setLogin(){ + username = cookie["one-user"]; + uid = cookie["one-user_id"]; + + $("#user").html(username); + $("#logout").click(function(){ + OpenNebula.Auth.logout({success:function(){ + window.location.href = "/login"; + } + }); + return false; + }); +} + + +function insertTabs(){ + var tab_info; + for (tab in SunstoneCfg["tabs"]){ + tab_info = SunstoneCfg["tabs"].tab; + $("div.inner_center").append('<div id="'+tab_info.tab_id+'" class="tab"></div>'); + $('div#'+tab_info.tab_id).html(tab_info.content); + + $('ul#navigation').append('<li><a href="'+tab_info.tab_id+'">'+tab+'</a></li>'); + + } +} + + +function insertButtons(){ + var buttons; + var tab_id; + var button_code; + + for (tab in SunstoneCfg["tabs"]){ + buttons = SunstoneCfg["tabs"][tab].buttons; + content = SunstoneCfg["tabs"][tab].content; + if ($('div#'+tab+' .action_blocks').length){ + $.each(buttons,function(){ + button_code = ""; + if (!this.condition()) { return true }; + + switch (this.type) { + case "action": + button_code = '<button class="action_button top_button" value="'+this.action+'">'+this.text+'</button>'; + break; + case "create": + button_code = '<button class="create top_button" value="'+this.action+'">'+this.text+'</button>'; + break; + case "select": + button_code = '<select class="multi_action_slct">'; + $.each(this.action,function(){ + if (this.condition()){ + button_code += '<option value="'+this.value+'">'+this.text+'</option>'; + }; + }); + button_code = '</select>'; + break; + + } + $('div#'+tab+' .action_blocks').append(button_code); + + }); + } + } + +} + +//Converts selects into buttons which show a of actions when clicked +function initListButtons(){ + + //for each multi_action select + $('.multi_action_slct').each(function(){ + //prepare replacement buttons + buttonset = $('<div style="display:inline-block;" class="top_button"></div'); + button1 = $('<button class="last_action_button action_button confirm_button confirm_with_select_button" value="">Previous action</button>').button(); + button1.attr("disabled","disabled"); + button2 = $('<button class="list_button" value="">See more</button>').button({ + text:false, + icons: { primary: "ui-icon-triangle-1-s" } + }); + buttonset.append(button1); + buttonset.append(button2); + buttonset.buttonset(); + + //prepare list + options = $('option', $(this)); + list = $('<ul class="action_list"></ul>'); + $.each(options,function(){ + classes = $(this).attr("class"); + item = $('<li></li>'); + a = $('<a href="#" class="'+classes+'" value="'+$(this).val()+'">'+$(this).text()+'</a>'); + a.val($(this).val()); + item.html(a); + list.append(item); + }); + list.css({ + "display":"none" + }); + + + //replace the select and insert the buttons + $(this).before(buttonset); + $(this).parents('.action_blocks').append(list); + $(this).remove(); + //$(this).replaceWith(list); + + }); + + + //listen for events on this buttons and list + + //enable run the last action button + $('.action_list li a').click(function(){ + //enable run last action button + prev_action_button = $('.last_action_button',$(this).parents('.action_blocks')); + prev_action_button.val($(this).val()); + prev_action_button.removeClass("confirm_with_select_button"); + prev_action_button.removeClass("confirm_button"); + prev_action_button.removeClass("action_button"); + prev_action_button.addClass($(this).attr("class")); + prev_action_button.button("option","label",$(this).text()); + prev_action_button.button("enable"); + $(this).parents('ul').hide("blind",100); + //return false; + }); + + + //Show the list of actions in place + $('.list_button').click(function(){ + $('.action_list',$(this).parents('.action_blocks')).css({ + "left": $(this).prev().position().left, + "top": $(this).prev().position().top+13, + "width": $(this).parent().outerWidth()-11 + }); + $('.action_list',$(this).parents('.action_blocks')).toggle("blind",100); + return false; + }); +} + +//Sets up all the "+ New Thing" dialogs. +function setupCreateDialogs(){ + createHostDialog(); + createClusterDialog(); + createVMachineDialog(); + createVNetworkDialog(); + createUserDialog(); + createImageDialog(); + + //Todo listener on "create" class to trigger the right dialog. +} + +//Replaces all class"tip" divs with an information icon that +//displays the tip information on mouseover. +function setupTips(){ + $('div.tip').each(function(){ + tip = $(this).html(); + $(this).html('<span class="ui-icon ui-icon-info info_icon"></span>'); + $(this).append('<span class="tipspan"></span>'); + + $(this).append('<span class="ui-icon ui-icon-alert man_icon" />'); + + + $('span.tipspan',this).html(tip); + $(this).parent().append('<div class="clear"></div>'); + $('span.tipspan',this).hide(); + $('span.info_icon',this).hover(function(e){ + var top, left; + top = e.pageY - 15;// - $(this).parents('#create_vm_dialog').offset().top - 15; + left = e.pageX + 15;// - $(this).parents('#create_vm_dialog').offset().left; + $(this).next().css( + {"top":top+"px", + "left":left+"px"}); + $(this).next().fadeIn(); + },function(){ + $(this).next().fadeOut(); + }); + }); +}