1
0
mirror of https://github.com/OpenNebula/one.git synced 2025-01-26 10:03:37 +03:00

Merge remote-tracking branch 'origin/feature-1607' into feature-2854

This commit is contained in:
Daniel Molina 2014-05-08 17:00:28 +02:00
commit 2dc10c2408
12 changed files with 1477 additions and 6 deletions

View File

@ -1498,6 +1498,9 @@ src/sunstone/public/vendor/flot/jquery.flot.navigate.min.js \
src/sunstone/public/vendor/flot/jquery.flot.pie.min.js \
src/sunstone/public/vendor/flot/jquery.flot.resize.min.js \
src/sunstone/public/vendor/flot/jquery.flot.stack.min.js \
src/sunstone/public/vendor/flot/jquery.flot.tooltip.min.js \
src/sunstone/public/vendor/flot/jquery.flot.JUMlib.min.js \
src/sunstone/public/vendor/flot/jquery.flot.gantt.min.js \
src/sunstone/public/vendor/flot/LICENSE.txt \
src/sunstone/public/vendor/flot/NOTICE"

View File

@ -368,6 +368,30 @@ class SunstoneServer < CloudServer
return [200, {:monitoring => result}.to_json]
end
def get_vm_accounting(options)
pool = VirtualMachinePool.new(@client)
filter_flag = options[:userfilter] ? options[:userfilter].to_i : VirtualMachinePool::INFO_ALL
start_time = options[:start_time] ? options[:start_time].to_i : -1
end_time = options[:end_time] ? options[:end_time].to_i : -1
acct_options = {
:start_time => start_time,
:end_time => end_time,
:group => options[:group]
}
rc = pool.accounting(filter_flag, acct_options)
if OpenNebula.is_error?(rc)
error = Error.new(rc.message)
return [500, error.to_json]
end
return [200, rc.to_json]
end
private
############################################################################

View File

@ -595,7 +595,32 @@ var OpenNebula = {
callback_error(request, OpenNebula.Error(response)) : null;
}
});
}
},
"accounting": function(params, resource, path){
var callback = params.success;
var callback_error = params.error;
var data = params.data;
var method = "accounting";
var request = OpenNebula.Helper.request(resource,method, data);
var url = path ? path : resource.toLowerCase() + "/accounting";
$.ajax({
url: url,
type: "GET",
data: data,
dataType: "json",
success: function(response){
return callback ? callback(request, response) : null;
},
error: function(response){
return callback_error ?
callback_error(request, OpenNebula.Error(response)) : null;
}
});
},
},
"Auth": {
@ -984,7 +1009,10 @@ var OpenNebula = {
"recover" : function(params){
var action_obj = {"with": params.data.extra_param};
OpenNebula.Action.simple_action(params,OpenNebula.VM.resource,"recover",action_obj);
}
},
"accounting": function(params){
OpenNebula.Action.accounting(params,OpenNebula.VM.resource);
},
},
"Group": {

View File

@ -703,9 +703,16 @@ function updateGroupInfo(request,group){
</div>'
};
var accounting_tab = {
title: tr("Accounting"),
icon: "fa-bar-chart-o",
content: '<div id="group_accounting"></div>'
};
Sunstone.updateInfoPanelTab("group_info_panel","group_info_tab",info_tab);
Sunstone.updateInfoPanelTab("group_info_panel","group_quotas_tab",quotas_tab);
Sunstone.updateInfoPanelTab("group_info_panel","group_providers_tab",providers_tab);
Sunstone.updateInfoPanelTab("group_info_panel","group_accouning_tab",accounting_tab);
Sunstone.popUpInfoPanel("group_info_panel", 'groups-tab');
$("#add_rp_button", $("#group_info_panel")).click(function(){
@ -715,6 +722,11 @@ function updateGroupInfo(request,group){
return false;
});
accountingGraphs(
$("#group_accounting","#group_info_panel"),
{ fixed_group: info.ID,
init_group_by: "user" });
}
function setup_group_resource_tab_content(zone_id, zone_section, str_zone_tab_id, str_datatable_id, selected_group_clusters, group) {

View File

@ -343,9 +343,10 @@ var provision_user_info = '<div id="provision_user_info" class="hidden section_c
'<div class="row">'+
'<div class="large-10 large-centered columns">'+
'<dl class="tabs text-center" data-tab style="width: 100%">'+
'<dd class="active" style="width: 33%;box-shadow: 0px 1px #dfdfdf;"><a href="#provision_info_ssh_key"><i class="fa fa-fw fa-lg fa-key"/>&emsp;'+ tr("SSH Key") +'</a></dd>'+
'<dd style="width: 33%;box-shadow: 0px 1px #dfdfdf;"><a href="#provision_info_quotas"><i class="fa fa-fw fa-lg fa-align-left"/>&emsp;'+ tr("Quotas") +'</a></dd>'+
'<dd style="width: 33%;box-shadow: 0px 1px #dfdfdf;"><a href="#provision_info_settings"><i class="fa fa-fw fa-lg fa-cogs"/>&emsp;'+ tr("Settings") +'</a></dd>'+
'<dd class="active" style="width: 25%;box-shadow: 0px 1px #dfdfdf;"><a href="#provision_info_ssh_key"><i class="fa fa-fw fa-lg fa-key"/>&emsp;'+ tr("SSH Key") +'</a></dd>'+
'<dd style="width: 25%;box-shadow: 0px 1px #dfdfdf;"><a href="#provision_info_acct"><i class="fa fa-fw fa-lg fa-bar-chart-o"/>&emsp;'+ tr("Accounting") +'</a></dd>'+
'<dd style="width: 25%;box-shadow: 0px 1px #dfdfdf;"><a href="#provision_info_quotas"><i class="fa fa-fw fa-lg fa-align-left"/>&emsp;'+ tr("Quotas") +'</a></dd>'+
'<dd style="width: 25%;box-shadow: 0px 1px #dfdfdf;"><a href="#provision_info_settings"><i class="fa fa-fw fa-lg fa-cogs"/>&emsp;'+ tr("Settings") +'</a></dd>'+
'</dl>'+
'<br>'+
'</div>'+
@ -406,6 +407,12 @@ var provision_user_info = '<div id="provision_user_info" class="hidden section_c
'</div>'+
'</form>'+
'</div>'+
'<div class="content" id="provision_info_acct">'+
'<div class="row">'+
'<div id="provision_user_info_acct_div" class="large-9 large-centered columns">'+
'</div>'+
'</div>'+
'</div>'+
'<div class="content" id="provision_info_quotas">'+
'<div class="row">'+
'<div id="provision_user_info_quotas_div" class="large-9 large-centered columns">'+
@ -1261,6 +1268,11 @@ function show_provision_user_info_callback(request, response) {
$('#provision_new_language option[value="'+config['user_config']["lang"]+'"]').attr('selected','selected');
$('#provision_user_views_select option[value="'+config['user_config']["default_view"]+'"]').attr('selected','selected');
accountingGraphs(
$("#provision_user_info_acct_div"),
{ fixed_user: info.ID,
fixed_group_by: "vm" });
}
function show_provision_create_vm() {

View File

@ -715,10 +715,23 @@ function updateUserInfo(request,user){
content : quotas_html
};
var accounting_tab = {
title: tr("Accounting"),
icon: "fa-bar-chart-o",
content: '<div id="user_accounting"></div>'
};
Sunstone.updateInfoPanelTab("user_info_panel","user_info_tab",info_tab);
Sunstone.updateInfoPanelTab("user_info_panel","user_quotas_tab",quotas_tab);
Sunstone.updateInfoPanelTab("user_info_panel","user_accouning_tab",accounting_tab);
//Sunstone.updateInfoPanelTab("user_info_panel","user_acct_tab",acct_tab);
Sunstone.popUpInfoPanel("user_info_panel", 'users-tab');
accountingGraphs(
$("#user_accounting","#user_info_panel"),
{ fixed_user: info.ID,
init_group_by: "vm" });
};
// Used also from groups-tabs.js

View File

@ -3902,3 +3902,639 @@ $(document).ready(function(){
}
})
});
// Format time in UTC, YYYY/MM/DD
// time is in ms
function time_UTC(time){
var d = new Date(time);
return d.getUTCFullYear() + '/' + (d.getUTCMonth()+1) + '/' + d.getUTCDate();
}
// div is a jQuery selector
// The following options can be set:
// fixed_user fix an owner user ID
// fixed_group fix an owner group ID
// init_group_by "user", "group", "vm". init the group-by selector
// fixed_group_by "user", "group", "vm". set a fixed group-by selector
function accountingGraphs(div, opt){
if(div.html() != ""){
return false;
}
div.html(
'<div class="row">\
<div class="large-3 columns">\
'+tr("Time range")+'\
</div>\
<div class="large-4 left columns">\
<input id="acct_start_time" type="text" placeholder="2013/12/30"/>\
</div>\
<div class="large-4 left columns">\
<input id="acct_end_time" type="text" placeholder="'+tr("Today")+'"/>\
</div>\
<div class="large-1 left columns">\
<button class="button radius left success" id="acct_submit" type="submit">'+tr("Go")+'</button>\
</div>\
</div>\
<div class="row" id="acct_owner">\
<div class="large-7 text-right columns">\
<input id="acct_owner_all" type="radio" name="acct_owner" value="acct_owner_all" checked/><label for="acct_owner_all">' + tr("All") + '</label>\
<input id="acct_owner_group" type="radio" name="acct_owner" value="acct_owner_group" /><label for="acct_owner_group">' + tr("Group") + '</label>\
<input id="acct_owner_user" type="radio" name="acct_owner" value="acct_owner_user" /><label for="acct_owner_user">' + tr("User") + '</label>\
</div>\
<div class="large-5 left columns">\
<div id="acct_owner_select"/>\
</div>\
</div>\
<div class="row" id="acct_group_by_row">\
<div class="large-3 columns">\
<label for="acct_group_by">' + tr("Group by") + '</label>\
</div>\
<div class="large-4 left columns">\
<select id="acct_group_by" name="acct_group_by">\
<option value="user">' + tr("User") + '</option>\
<option value="group">' + tr("Group") + '</option>\
<option value="vm">' + tr("VM") + '</option>\
</select>\
</div>\
</div>\
<div class="row">\
</div>\
<div id="acct_placeholder">\
<div class="row">\
<div class="large-8 large-centered columns">\
<div class="text-center">\
<span class="fa-stack fa-5x" style="color: #dfdfdf">\
<i class="fa fa-cloud fa-stack-2x"></i>\
<i class="fa fa-bar-chart-o fa-stack-1x fa-inverse"></i>\
</span>\
<div id="acct_no_data" class="hidden">\
<br>\
<p style="font-size: 18px; color: #999">'+
tr("There are no accounting records")+
'</p>\
</div>\
</div>\
</div>\
</div>\
</div>\
<div id="acct_content" class="hidden">\
<div class="row">\
<div class="row graph_legend">\
<h3 class="subheader"><small>'+tr("CPU hours")+'</small></h3>\
</div>\
<div class="row">\
<div class="large-12 columns centered graph" id="acct_cpu_graph" style="height: 200px;">\
</div>\
</div>\
</div>\
<div class="row">\
<div class="row graph_legend">\
<h3 class="subheader"><small>'+tr("Memory GB hours")+'</small></h3>\
</div>\
<div class="row">\
<div class="large-12 columns centered graph" id="acct_mem_graph" style="height: 200px;">\
</div>\
</div>\
</div>\
<div class="row">\
<div class="row graph_legend">\
<h3 class="subheader"><small>'+tr("CPU hours")+'</small></h3>\
</div>\
<div style="overflow:auto">\
<table id="acct_cpu_datatable" class="datatable twelve">\
<thead>\
<tr>\
<th>'+tr("Date")+'</th>\
</tr>\
</thead>\
<tbody id="tbody_acct_cpu_datatable">\
</tbody>\
</table>\
</div>\
</div>\
<div class="row">\
<div class="row graph_legend">\
<h3 class="subheader"><small>'+tr("Memory GB hours")+'</small></h3>\
</div>\
<div style="overflow:auto">\
<table id="acct_mem_datatable" class="datatable twelve">\
<thead>\
<tr>\
<th>'+tr("Date")+'</th>\
</tr>\
</thead>\
<tbody id="tbody_acct_mem_datatable">\
</tbody>\
</table>\
</div>\
</div>\
</div>');
if (opt == undefined){
opt = {};
}
//--------------------------------------------------------------------------
// Init start time to 1st of last month
//--------------------------------------------------------------------------
var d = new Date();
d.setDate(1);
d.setMonth(d.getMonth() - 1);
$("#acct_start_time", div).val(d.getFullYear() + '/' + (d.getMonth()+1) + '/' + d.getDate());
//--------------------------------------------------------------------------
// VM owner: all, group, user
//--------------------------------------------------------------------------
if (opt.fixed_user != undefined || opt.fixed_group != undefined){
$("#acct_owner", div).hide();
/*
$("input[name='acct_owner']", div).attr("disabled", "disabled");
$("#acct_owner_select", div).show();
if(opt.fixed_user != undefined){
var text = tr("User") +" " + opt.fixed_user;
$("input[value='acct_owner_user']", div).attr("checked", "checked");
} else {
var text = tr("Group") + " " + opt.fixed_group;
$("input[value='acct_owner_group']", div).attr("checked", "checked");
}
$("#acct_owner_select", div).text(text);
*/
} else {
$("input[name='acct_owner']", div).change(function(){
var value = $(this).val();
switch (value){
case "acct_owner_all":
$("#acct_owner_select", div).hide();
break;
case "acct_owner_group":
$("#acct_owner_select", div).show();
insertSelectOptions("#acct_owner_select", div, "Group");
break;
case "acct_owner_user":
$("#acct_owner_select", div).show();
insertSelectOptions("#acct_owner_select", div, "User", -1, false,
'<option value="-1">'+tr("<< me >>")+'</option>');
break;
}
});
}
//--------------------------------------------------------------------------
// Init group by select
//--------------------------------------------------------------------------
if(opt.init_group_by != undefined){
$("#acct_group_by", div).val(opt.init_group_by);
}else if(opt.fixed_group_by != undefined){
$("#acct_group_by", div).val(opt.fixed_group_by);
$("#acct_group_by_row", div).hide();
}
//--------------------------------------------------------------------------
// Submit request
//--------------------------------------------------------------------------
$("#acct_submit", div).on("click", function(){
var start_time = -1;
var end_time = -1;
var v = $("#acct_start_time", div).val();
if (v == ""){
notifyError(tr("Time range start is mandatory"));
return false;
}else{
start_time = Date.parse(v+' UTC');
if (isNaN(start_time)){
notifyError(tr("Time range start is not a valid date. It must be YYYY/MM/DD"));
return false;
}
// ms to s
start_time = start_time / 1000;
}
var v = $("#acct_end_time", div).val();
if (v != ""){
end_time = Date.parse(v+' UTC');
if (isNaN(end_time)){
notifyError(tr("Time range end is not a valid date. It must be YYYY/MM/DD"));
return false;
}
// ms to s
end_time = end_time / 1000;
}
var options = {
"start_time": start_time,
"end_time": end_time
};
if (opt.fixed_user != undefined){
options.userfilter = opt.fixed_user;
} else if (opt.fixed_group != undefined){
options.group = opt.fixed_group;
} else {
var select_val = $("#acct_owner_select .resource_list_select", div).val();
switch ($("input[name='acct_owner']:checked", div).val()){
case "acct_owner_all":
break;
case "acct_owner_group":
if(select_val != ""){
options.group = select_val;
}
break;
case "acct_owner_user":
if(select_val != ""){
options.userfilter = select_val;
}
break;
}
}
OpenNebula.VM.accounting({
// timeout: true,
success: function(req, response){
fillAccounting(div, req, response);
},
error: onError,
data: options
});
});
}
function fillAccounting(div, req, response) {
var options = req.request.data[0];
//--------------------------------------------------------------------------
// Time slots
//--------------------------------------------------------------------------
// start_time is mandatory
var start = new Date(options.start_time * 1000);
var end = new Date();
if(options.end_time != undefined && options.end_time != -1){
var end = new Date(options.end_time * 1000)
}
// granularity of 1 day
var times = [];
var tmp_time = start;
// End time is the start of the last time slot. We use <=, to
// add one extra time step
while (tmp_time <= end) {
times.push(tmp_time.getTime());
// day += 1
tmp_time.setUTCDate( tmp_time.getUTCDate() + 1 );
}
//--------------------------------------------------------------------------
// Flot options
//--------------------------------------------------------------------------
var options = {
colors: ["#0098C3","#0A00C2","#AB00C2","#C20037","#C26B00","#78C200","#00C22A","#00B8C2"],
xaxis : {
mode: "time",
timeformat: "%y/%m/%d",
color: "#999",
size: 8,
minTickSize: [1, "day"]
},
yaxis : { labelWidth: 50,
min: 0,
color: "#999",
size: 8
},
series: {
bars: {
show: true,
lineWidth: 1,
fill: true,
barWidth: 24*60*60*1000 * 0.8,
align: "center"
},
stack: true
},
legend : {
show : false
},
grid: {
borderWidth: 1,
borderColor: "#cfcfcf",
hoverable: true
},
tooltip: true,
tooltipOpts: {
content: "%x | %s | %y"
}
};
//--------------------------------------------------------------------------
// Group by
//--------------------------------------------------------------------------
// TODO: Allow to change group by dynamically, instead of calling oned again
switch ($("#acct_group_by", div).val()){
case "user":
var group_by_fn = function(history){
return history.VM.UID;
}
var group_by_prefix = tr("User")+" ";
break;
case "group":
var group_by_fn = function(history){
return history.VM.GID;
}
var group_by_prefix = tr("Group")+" ";
break;
case "vm":
var group_by_fn = function(history){
return history.OID;
}
var group_by_prefix = tr("VM")+" ";
break;
}
//--------------------------------------------------------------------------
// Filter history entries
//--------------------------------------------------------------------------
// TODO filter
// True to proccess, false to discard
var filter_by_fn = function(history){
// return history.OID == 3605 || history.OID == 2673;
return true;
}
//--------------------------------------------------------------------------
// Process data series for flot
//--------------------------------------------------------------------------
var series = {};
$("#acct_no_data", div).hide();
if(response.HISTORY_RECORDS == undefined){
$("#acct_placeholder", div).show();
$("#acct_content", div).hide();
$("#acct_no_data", div).show();
return false;
}
$.each(response.HISTORY_RECORDS.HISTORY, function(index, history){
/*
if(!filter_by_fn(history)){
return true; //continue
}
*/
var group_by = group_by_fn(history);
if (series[group_by] == undefined){
series[group_by] = {};
series[group_by][times[0]] = {};
series[group_by][times[times.length-2]] = {};
series[group_by][times[0]].CPU_HOURS = 0;
series[group_by][times[times.length-2]].CPU_HOURS = 0;
series[group_by][times[0]].MEM_HOURS = 0;
series[group_by][times[times.length-2]].MEM_HOURS = 0;
}
var serie = series[group_by];
for (var i = 0; i<times.length-1; i++){
var t = times[i];
var t_next = times[i+1];
if( (history.ETIME*1000 > t || history.ETIME == 0) &&
(history.STIME != 0 && history.STIME*1000 <= t_next) ) {
var stime = t;
if(history.STIME != 0){
stime = Math.max(t, history.STIME*1000);
}
var etime = t_next;
if(history.ETIME != 0){
etime = Math.min(t_next, history.ETIME*1000);
}
var n_hours = (etime - stime) / 1000 / 60 / 60;
if(serie[t] == undefined){
serie[t] = {};
serie[t].CPU_HOURS = 0;
serie[t].MEM_HOURS = 0;
}
// --- cpu ---
var val = parseFloat(history.VM.TEMPLATE.CPU) * n_hours;
if (!isNaN(val)){
serie[t].CPU_HOURS += val;
}
// --- mem ---
var val = parseInt(history.VM.TEMPLATE.MEMORY)/1024 * n_hours;
if (!isNaN(val)){
serie[t].MEM_HOURS += val;
}
}
}
});
//--------------------------------------------------------------------------
// Create series, draw plots
//--------------------------------------------------------------------------
var cpu_plot_series = [];
var mem_plot_series = [];
$.each(series, function(key, val){
var cpu_data = [];
var mem_data = [];
$.each(val, function(time,num){
cpu_data.push([parseInt(time),num.CPU_HOURS]);
mem_data.push([parseInt(time),num.MEM_HOURS]);
});
cpu_plot_series.push(
{
label: group_by_prefix+key,
data: cpu_data
});
mem_plot_series.push(
{
label: group_by_prefix+key,
data: mem_data
});
});
var cpu_plot = $.plot($("#acct_cpu_graph", div), cpu_plot_series, options);
var mem_plot = $.plot($("#acct_mem_graph", div), mem_plot_series, options);
//--------------------------------------------------------------------------
// Init dataTables
//--------------------------------------------------------------------------
$("#acct_cpu_datatable",div).dataTable().fnClearTable();
$("#acct_cpu_datatable",div).dataTable().fnDestroy();
$("#acct_cpu_datatable thead",div).remove();
$("#acct_cpu_datatable",div).width("1px");
$("#acct_mem_datatable",div).dataTable().fnClearTable();
$("#acct_mem_datatable",div).dataTable().fnDestroy();
$("#acct_mem_datatable thead",div).remove();
$("#acct_mem_datatable",div).width("1px");
cpu_plot_data = cpu_plot.getData();
mem_plot_data = mem_plot.getData();
var thead =
'<thead>\
<tr>\
<th>'+tr("Date UTC")+'</th>\
<th>'+tr("Total")+'</th>';
$.each(cpu_plot_data, function(i, serie){
thead += '<th style="border-bottom: '+serie.color+' 4px solid !important;'+
' border-left: 10px solid white; border-right: 5px solid white">'+
serie.label+'</th>';
});
thead += '</tr></thead>';
$("#acct_cpu_datatable",div).append(thead);
thead =
'<thead>\
<tr>\
<th>'+tr("Date UTC")+'</th>\
<th>'+tr("Total")+'</th>';
$.each(mem_plot_data, function(i, serie){
thead += '<th style="border-bottom: '+serie.color+' 4px solid !important;'+
' border-left: 10px solid white; border-right: 5px solid white">'+
serie.label+'</th>';
});
thead += '</tr></thead>';
$("#acct_mem_datatable",div).append(thead);
var cpu_dataTable_data = [];
var mem_dataTable_data = [];
for (var i = 0; i<times.length-1; i++){
var t = times[i];
var cpu_row = [];
var mem_row = [];
var time_st = time_UTC(t);
cpu_row.push(time_st);
mem_row.push(time_st);
cpu_row.push(0);
mem_row.push(0);
var cpu_total = 0;
var mem_total = 0;
$.each(series, function(key, val){
var v = val[t];
if(v != undefined){
var cpu_v = (v.CPU_HOURS * 100).toFixed() / 100;
var mem_v = (v.MEM_HOURS * 100).toFixed() / 100;
cpu_total += cpu_v;
mem_total += mem_v;
cpu_row.push(cpu_v);
mem_row.push(mem_v);
} else {
cpu_row.push(0);
mem_row.push(0);
}
});
cpu_row[1] = cpu_total;
mem_row[1] = mem_total;
cpu_dataTable_data.push(cpu_row);
mem_dataTable_data.push(mem_row);
}
var acct_cpu_dataTable = $("#acct_cpu_datatable",div).dataTable({
"bSortClasses" : false,
"bDeferRender": true,
"aoColumnDefs": [
{ "bSortable": false, "aTargets": ['_all'] },
]
});
var acct_mem_dataTable = $("#acct_mem_datatable",div).dataTable({
"bSortClasses" : false,
"bDeferRender": true,
"aoColumnDefs": [
{ "bSortable": false, "aTargets": ['_all'] },
]
});
acct_cpu_dataTable.fnAddData(cpu_dataTable_data);
acct_mem_dataTable.fnAddData(mem_dataTable_data);
$("#acct_placeholder", div).hide();
$("#acct_content", div).show();
}

View File

@ -0,0 +1,520 @@
/*
* The MIT License
Copyright (c) 2012, 2013 by Juergen Marsch
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.
*/
(function ($) {
"use strict";
var pluginName = "JUMlib", pluginVersion = "0.5";
function between(v,limit1,limit2){
if(limit2 > limit1){ return (v >= limit1 && v <= limit2); }
else{ return(v >=limit2 && v <= limit1); }
}
function getMinMax(data,dataIndex){
var mn,mx,df,dI;
if(dataIndex){ dI = dataIndex} else{ dI = 1;}
mn = Number.POSITIVE_INFINITY;
mx = Number.NEGATIVE_INFINITY;
for(var i = 0; i < data.length; i++){
mn = Math.min(mn, data[i][dI]);
mx = Math.max(mx, data[i][dI]);
}
df = mx - mn;
return {min: mn, max: mx, diff: df};
}
function showHover(event,pos,item,showAlways,createText){
var txt;
if (item) {
var data = item.series.data[item.dataIndex];
if(createText){ txt = createText(data);}
else {
txt = "X:" + data[0] + "<br>Y:" + data[1];
if(data.length > 2) { for(var i = 2; i < data.length; i++){ txt += "<br>" + data[i]; } }
}
showTooltip(pos.pageX, pos.pageY,txt);
}
else {
if(showAlways === true){
txt = pos.x1 + " / " + pos.y1;
showTooltip(pos.pageX,pos.pageY,txt);
}
else {$("#FLOTtooltip").remove();}
}
}
function showTooltip(x, y, contents){
$("#FLOTtooltip").remove();
$('<div id="FLOTtooltip">' + contents + '</div>').css(
{ position: 'absolute',display: 'none',top: y + 5,left: x + 5,
border: '1px solid #fdd',padding: '2px','background-color': '#fee',opacity: 0.80
}).appendTo("body").fadeIn(200);
}
function loadScripts(scripts,maxWait,callback){
var loadedScripts = {},defs = [];
for(var i = 0; i < scripts.length; i++){ defs.push(loadScript(scripts[i].path,scripts[i].name)); }
$.when.apply(null,defs).then(function(){callback(loadedScripts);});
function loadScript(path,name){
var dfd = $.Deferred(),t;
t = setInterval(function(){clearInterval(t);dfd.reject();},maxWait);
$.getScript(path).done(loaded).fail(errorFound);
return dfd.promise();
function loaded(){ loadedScripts[name] = true;dfd.resolve();}
function errorFound(e,f,g){console.log(path,e); loadedScripts[name] = false;dfd.reject();}
}
}
function loadJSON(scripts,maxWait,callback){
var loadedJSON = {},defs = [];
for(var i = 0;i < scripts.length; i++){ defs.push(loadJSON(scripts[i].path,scripts[i].name)); }
$.when.apply(null,defs).then(function(){ callback(loadedJSON);});
function loadJSON(path,name){
var dfd = $.Deferred(),t;
t = setInterval(function(){clearInterval(t);dfd.reject();},maxWait);
$.getJSON(path,function(data){
loadedJSON[name] = {data:data,name:name,loaded:true};
}).done(loaded).fail(errorFound);
return dfd.promise();
function loaded(){ dfd.resolve(); }
function errorFound(e,f,g){ loadedJSON[name] = {name:name,loaded:false};dfd.reject();}
}
}
function createQuartile(data, index, indexName){
var q0 = [], q1 = [],q2 = [],q3 = [],q4 = [], v = [], i1, i2, i3, i4, p;
i1 = (0.25 * data.length).toFixed(0); i2 = (0.5 * data.length).toFixed(0);
i3 = (0.75 * data.length).toFixed(0); i4 = data.length - 1;
for (var j = 0; j < data[0].length; j++){
p = [];
for (var i = 0; i < data.length; i++) { p.push(data[i][j]); }
p.sort(function(a,b){return a - b;} );
q1.push([j,p[i1]]); q2.push([j,p[i2]]); q3.push([j,p[i3]]); q4.push([j,p[i4]]);
q0.push([j,p[0]]); v.push([j,data[index][j]]);
}
var r = [ { data: q4}, { data: q3}, { data: q2}, { data: q1}, {data: q0, color: "#ffffff" },
{label: indexName, points: {show:true}, lines: { fill: null, steps: false}, data: v}];
return r;
}
function createPercentile(data, index, indexName, percentiles){
var percentile = [], val = [], indexes = [], p,j,i;
if(percentiles.length){
indexes.push([0]);
percentile.push([]);
for(j = 0;j < percentiles.length;j++){
indexes.push(parseInt(data.length * percentiles[j],0));
percentile.push([]);
}
indexes.push(data.length - 1);
}
else{
for(j = 0;j < percentiles; j++){
indexes.push(parseInt(data.length / percentiles * j,0));
percentile.push([]);
}
indexes.push(data.length - 1);
}
percentile.push([]);
for(j = 0; j < data[0].length; j++){
p = [];
for(i = 0; i < data.length; i++){ p.push(data[i][j]); }
p.sort(function(a,b){return a-b;});
for(i = 0; i < percentile.length; i++ ) { percentile[i].push([j,p[indexes[i]]]); }
val.push([j,data[index][j]]);
}
var r = [];
for(j = percentile.length - 1; j > 0 ; j--){ r.push({ data: percentile[j] });}
r.push({data: percentile[0], color:"#ffffff"});
r.push({label: indexName, points: {show: true}, lines: { fill: null, steps: false}, data:val});
return r;
}
function createSimiliarity(data1, data2, mode){
var r = [];
var d1 = normalize(data1);
var d2 = normalize(data2);
var d;
for (var i = 0; i < d1.length; i++){
switch (mode){
case "diff":
d = d1[i][1] - d2[i][1];
break;
case "abs":
d = Math.abs(d1[i][1] - d2[i][1]);
break;
default:
d = 0;
}
r.push([d1[i][0],d]);
}
return r;
}
function createWaterfall(data, colors){ //convert waterfalldata to 4 Bars, d1 for fixed bars, d2 for invisible bar
var d1 = [], d2 = [], d3 = [], d4 = [], p = 0, mn = Number.POSITIVE_INFINITY,i; // d3 for negative bars and d4 for positive bars
var dx = data;
for(i = 0; i < dx.length; i++) {
if(dx[i][2]){
if(typeof dx[i][1] === "undefined") { d1.push([i,p]); }
else { d1.push([i,dx[i][1]]); p = dx[i][1]; }
}
else{
if(dx[i][1] > 0) { d4.push([i,- dx[i][1]]);p = p + dx[i][1];d2.push([i,p]);d3.push([i,0]);}
else {d3.push([i,- dx[i][1]]);p = p + dx[i][1];d2.push([i,p]);}
}
mn = Math.min(mn,p);
}
var ticks = [];
for(i = 0; i < data.length; i++){ ticks.push([i,data[i][0]]);}
var dr = {
data: [
{ data: d1, color: colors.fixed },
{ data: d2, bars: { show: false }, lines: { show: false } },
{ data: d3, color: colors.negative },
{ data: d4, color: colors.positive}
],
ticks: ticks,
yaxismin: mn
};
return dr;
}
function avg(data,range){
var r = [],rd = [],i1,s;
for(var i = 0; i < data.length; i++){
if(i < range){ i1 = 0;} else{ i1 = i - range + 1;}
rd = [];
rd.push(data[i][0]);
for(var k = 1; k < data[i].length; k++){
s = 0;
for(var j = i1; j <= i; j++){ s += data[j][k];}
rd.push(s / (i - i1 + 1));
}
r.push(rd);
}
return r;
}
function max(data,range){
var r = [], rd = [], i1, mx;
for(var i = 0; i < data.length; i++){
if(i < range) {i1 = 0;} else {i1 = i - range + 1;}
rd = [];
rd.push(data[i][0]);
for(var k = 1; k < data[i].length; k++){
mx = - Number.MAX_VALUE;
for(var j = i1; j <= i; j++){ if(data[j][k] > mx){ mx = data[j][k]; } }
rd.push(mx);
}
r.push(rd);
}
return r;
}
function min(data,range){
var r = [], rd = [], i1, mn;
for(var i = 0; i < data.length; i++){
if(i < range){ i1 = 0;} else{ i1 = i - range + 1;}
rd = [];
rd.push(data[i][0]);
for(var k = 1; k < data[i].length; k++){
mn = Number.MAX_VALUE;
for(var j = i1; j <= i; j++){ if(data[j][k] < mn){ mn = data[j][k]; } }
rd.push(mn);
}
r.push(rd);
}
return r;
}
function sort(data,sortOrder,sortfnc){
var d = [];
for(var i = 0; i < data.length; i++){ d.push(data[i]);}
if(sortfnc){ d.sort(sortfnc); }
else{
if (sortOrder === "a"){ d.sort(mysorta); } else { d.sort(mysortd); }
}
return d;
function mysorta(a,b){ return a[1] - b[1]; }
function mysortd(a,b){ return b[1] - a[1]; }
}
function sortTicks(data,sortOrder,sortfnc){
var d = sort(data,sortOrder,sortfnc);
for(var i = 0; i < d.length; i++){d[i][0] = i; }
return { data:d, ticks:getTicks(d) };
}
function pareto(data,otherLabel,showOthers,topN,topPercent){
var d = [],othersLabel="Others",showothers = true,i,dn = [];
d = sort(data,"d");
if(otherLabel.length > 0){ othersLabel = otherLabel;}
showothers = showOthers;
if(topN){
var s;
for(i = 0;i < topN; i++){ dn.push(d[i]); }
s = 0;
for(i = topN;i < d.length; i++){ s+=d[i][1]; }
if(showothers){ dn.push([topN, s,othersLabel]);}
d = dn;
}
else if(topPercent){
var datasum = 0, datar = 0, datao = 0,j;
for(i = 0; i < d.length;i++){ datasum += d[i][1];}
datasum = datasum * topPercent / 100;
for(i = 0; i < d.length; i++){
if (datar < datasum) {
dn.push(d[i]);
datar += d[i][1];
j = i;
}
else{ datao += d[i][1]; }
}
j++;
if(showothers){ dn.push([j,datao,othersLabel]);}
d = dn;
}
for(i = 0; i < d.length; i++){d[i][0] = i; }
return { data:d, ticks:getTicks(d) };
}
function getTicks(d){
var t = [];
for(var i = 0; i < d.length; i++){
if(d[i][2]){ t.push([d[i][0], d[i][2]]);} else{ t.push(d[i][0], d[i][0]);}
}
return t;
}
function normalize(data){
var minmax, d;var r = [];
minmax = getMinMax(data);
for(var i = 0; i < data.length; i++){
d = (data[i][1] - minmax.min) / minmax.diff * 100;
r.push([data[i][0],d]);
}
return r;
}
function combineData(data,ticks){
var r = [];
for(var i = 0; i < data.length; i++){
var s = [];
for(var j = 0; j < data[i].length; j++){
var d = [ ticks[j], data[i][j] ];
s.push(d);
}
r.push(s);
}
return r;
}
function createFont(placeholder){
var f = {
style: placeholder.css("font-style"),
size: Math.round(0.8 * (+placeholder.css("font-size").replace("px", "") || 13)),
variant: placeholder.css("font-variant"),
weight: placeholder.css("font-weight"),
family: placeholder.css("font-family")
};
return f;
}
function createColors(options,neededColors){
// this is copied code from jquery.flot.js in fillInSeriesOptions
var c, colors = [], colorPool = options.colors,
colorPoolSize = colorPool.length, variation = 0;
for(var i = 0; i < colorPoolSize; i++){ colors[i] = colorPool[i]; }
if(colorPoolSize < neededColors){
for (i = colorPoolSize; i < neededColors; i++) {
c = $.color.parse(colorPool[i % colorPoolSize] || "#666");
if (i % colorPoolSize === 0 && i) {
if (variation >= 0) {
if (variation < 0.5) { variation = -variation - 0.2; } else{ variation = 0;}
}
else {variation = -variation;}
}
colors[i] = c.scale('rgb', 1 + variation).toString();
}
}
return colors;
}
function getColor(colorData){ //based on a patch from Martin Thorsen Ranang from Nov 2012
var c;
if(typeof colorData === "object"){
if(typeof colorData.dataIndex !== "undefined"){
if(typeof colorData.serie.data[colorData.dataIndex].color !== "undefined"){
c = getColorL(colorData.serie.data[colorData.dataIndex].color);
}
else{ c = colorData.colors[colorData.dataIndex]; }
}
else{
if(typeof colorData.serieIndex !== "undefined"){
if(typeof colorData.serie.color !== "undefined"){ c = getColorL(colorData.serie.color);}
else{ c = colorData.colors[colorData.serieIndex];}
}
else{
if(typeof colorData.color !== "undefined"){c = getColorL(colorData.color);}
else {c = "darkgreen"; }
}
}
}
else{ c = getColorL(colorData); }
return c;
function getColorL(color){
var c;
if(typeof color === "object"){
if(typeof color.image !== "undefined"){
c = colorData.ctx.createPattern(color.image,color.repeat);
}
else{
if(colorData.radius){
c = colorData.ctx.createRadialGradient(colorData.left,colorData.top,0,
colorData.left,colorData.top,colorData.radius);
}
else { c = colorData.ctx.createLinearGradient(0,0,colorData.width,colorData.height);}
for(var i = 0; i < color.colors.length; i++){
var cl = color.colors[i];
if(typeof cl !== "string"){
var co = $.color.parse(colorData.defaultColor);
if(color.brightness !== null){ cl = co.scale("rgb",color.brightness);}
if(color.opacity !== null){ co *= color.opacity;}
cl = co.toString();
}
c.addColorStop(i / (color.colors.length - 1),cl);
}
}
}
else{ if(typeof color === "string"){ c = color; } else { c = colorData.colors[color]; } }
return c;
}
}
function loadImages(images,maxWait,callback){
var loadedImg = {},defs = [];
for(var i = 0; i < images.length; i++){ defs.push(loadImage(images[i])); }
$.when.apply(null,defs).then(function(){callback(loadedImg);});
function loadImage(img){
var dfd = $.Deferred(),t,url;
url = img.path + img.name + "." + img.type;
t = setInterval(function(){clearInterval(t);dfd.reject();},maxWait);
$('<img />').attr('src',url).load(loaded).error(errorFound);
return dfd.promise();
function loaded(){ loadedImg[img.name] = this;dfd.resolve();}
function errorFound(e,f,g){console.log(url,e); loadedImg[img.name] = null;dfd.reject();}
}
}
function getCanvases(placeholder){
var canvases = {
background:$(placeholder).children(".flot-background"),
base:$(placeholder).children(".flot-base"),
overlay:$(placeholder).children(".flot-overlay")
};
return canvases;
}
function extendEmpty(org,ext){
for(var i in ext){
if(!org[i]){ org[i] = ext[i];}
else{
if(typeof ext[i] === "object"){
extendEmpty(org[i],ext[i]);
}
}
}
}
function drawLines(plot,lines){
var offset,series,ctx;
offset = plot.getPlotOffset();
series = plot.getData();
ctx = plot.getCanvas().getContext("2d");
ctx.translate(offset.left,offset.top);
for(var i = 0; i < lines.length; i++){
var from = series[lines[i].from.seriesIndex], to = series[lines[i].to.seriesIndex];
var fl = lines[i].from, tl = lines[i].to;
if(!fl.dataFieldX){ fl.dataFieldX = 0;}
if(!fl.dataFieldY){ fl.dataFieldY = 1;}
if(!tl.dataFieldX){ tl.dataFieldX = 0;}
if(!tl.dataFieldY){ tl.dataFieldY = 1;}
var fromPos = {xaxis:from.xaxis, yaxis:from.yaxis, x:from.data[fl.dataIndex][fl.dataFieldX], y:from.data[fl.dataIndex][fl.dataFieldY]},
toPos = {xaxis:to.xaxis, yaxis:to.yaxis, x:to.data[tl.dataIndex][tl.dataFieldX], y:to.data[tl.dataIndex][tl.dataFieldY]},
lineStyle = {strokeStyle:"red", lineWidth:5};
drawLine(plot,ctx,fromPos,toPos,lineStyle);
}
}
function drawLine(plot,ctx,fromPos,toPos,lineStyle){
var xf,yf,xt,yt;
xf = fromPos.xaxis.p2c(fromPos.x);
yf = fromPos.yaxis.p2c(fromPos.y);
xt = toPos.xaxis.p2c(toPos.x);
yt = toPos.yaxis.p2c(toPos.y);
ctx.beginPath();
ctx.strokeStyle = lineStyle.strokeStyle;
ctx.lineWidth = lineStyle.lineWidth;
ctx.moveTo(xf,yf);
ctx.lineTo(xt,yt);
ctx.stroke();
}
function drawRect(plot,ctx,xaxis,yaxis,boxDim,boxStyle){
var boxC = {x: xaxis.p2c(boxDim.x),y: yaxis.p2c(boxDim.y),
width: xaxis.p2c((boxDim.x + boxDim.width)) - xaxis.p2c(boxDim.x),
height: yaxis.p2c(boxDim.y + boxDim.height) - yaxis.p2c(boxDim.y)};
switch(boxStyle.mode){
case "image": drawImage(plot,ctx,boxC,boxStyle); break;
case "color": drawColor(plot,ctx,boxC,boxStyle); break;
case "userdefined": boxStyle.boxDraw(plot,ctx,boxC,boxStyle); break;
default: drawColor(plot,ctx,boxC,boxStyle);
}
function drawImage(plot,ctx,boxC,boxStyle){
var image = boxStyle.image;
if(typeof image !== "undefined"){ctx.drawImage(image,boxC.x,boxC.y,boxC.width(),boxC.height());}
}
function drawColor(plot,ctx,boxC,boxStyle){
color = getColor({ctx:ctx,color:boxStyle.color,left:boxC.x,top:boxC.y,height:boxC.height,width:boxC.width});
ctx.fillStyle = color;
ctx.fillRect(boxC.x,boxC.y,boxC.width,boxC.height);
}
}
function drawCircle(plot,ctx,xaxis,yaxis,circle,circleStyle){
var circleC = {x: xaxis.p2c(boxDim.x),y: yaxis.p2c(boxDim.y)}
}
$.plot.JUMlib = {};
$.plot.JUMlib.library = {};
$.plot.JUMlib.library.between = between;
$.plot.JUMlib.library.getMinMax = getMinMax;
$.plot.JUMlib.library.showHover = showHover;
$.plot.JUMlib.library.showTooltip = showTooltip;
$.plot.JUMlib.library.loadScripts = loadScripts;
$.plot.JUMlib.library.loadJSON = loadJSON;
$.plot.JUMlib.prepareData = {};
$.plot.JUMlib.prepareData.createQuartile = createQuartile;
$.plot.JUMlib.prepareData.createPercentile = createPercentile;
$.plot.JUMlib.prepareData.createSimiliarity = createSimiliarity;
$.plot.JUMlib.prepareData.createWaterfall = createWaterfall;
$.plot.JUMlib.prepareData.avg = avg;
$.plot.JUMlib.prepareData.max = max;
$.plot.JUMlib.prepareData.min = min;
$.plot.JUMlib.prepareData.sort = sort;
$.plot.JUMlib.prepareData.sortTicks = sortTicks;
$.plot.JUMlib.prepareData.pareto = pareto;
$.plot.JUMlib.prepareData.normalize = normalize;
$.plot.JUMlib.prepareData.combineData = combineData;
$.plot.JUMlib.data = {};
$.plot.JUMlib.data.createFont = createFont;
$.plot.JUMlib.data.createColors = createColors;
$.plot.JUMlib.data.getColor = getColor;
$.plot.JUMlib.data.loadImages = loadImages;
$.plot.JUMlib.data.getCanvases = getCanvases;
$.plot.JUMlib.data.extendEmpty = extendEmpty;
$.plot.JUMlib.drawing = {};
$.plot.JUMlib.drawing.drawLine = drawLine;
$.plot.JUMlib.drawing.drawRect = drawRect;
$.plot.JUMlib.drawing.drawLines = drawLines;
$.plot.plugins.push({
init: function(){},
options: { },
name: pluginName,
version: pluginVersion
});
})(jQuery);

View File

@ -0,0 +1,201 @@
/*
* The MIT License
Copyright (c) 2010, 2011, 2012, 2013 by Juergen Marsch
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.
*/
(function ($) {
"use strict";
var pluginName = "gantt", pluginVersion = "0.3";
var options = {
series: {
gantt: {
active: false,
show: false,
connectSteps: { show: false, lineWidth:2, color:"rgb(0,0,0)" },
barHeight: 0.6,
highlight: { opacity: 0.5 },
drawstep: drawStepDefault
}
}
};
var replaceOptions = { series:{ lines: { show:false } } };
var defaultOptions = {
series:{
editMode: 'y', //could be none, x, y, xy, v
nearBy:{
distance: 6,
findItem: null,
findMode: "circle",
drawHover: null
}
}
};
function drawStepDefault(ctx,series,data,x,y,x2,color, isHighlight){
if(isHighlight === false){
ctx.beginPath();
ctx.lineWidth = series.gantt.barheight;
ctx.strokeStyle = "rgb(0,0,0)";
ctx.moveTo(x, y);
ctx.lineTo(x2, y);
ctx.stroke();
}
ctx.beginPath();
ctx.strokeStyle = color;
ctx.lineWidth = series.gantt.barheight - 2;
ctx.lineCap = "butt";
ctx.moveTo(x + 1, y);
ctx.lineTo(x2 - 1, y);
ctx.stroke();
}
function init(plot) {
var offset = null, opt = null, series = null,canvas,target,axes,data;
plot.hooks.processOptions.push(processOptions);
function processOptions(plot,options){
if (options.series.gantt.active){
$.extend(true,options,replaceOptions);
$.plot.JUMlib.data.extendEmpty(options,defaultOptions);
opt = options;
plot.hooks.processRawData.push(processRawData);
plot.hooks.draw.push(draw);
}
}
function processRawData(plot,s,data,datapoints){
if(s.gantt.show === true){
s.nearBy.findItem = findNearbyItemGantt;
s.nearBy.drawHover = drawHoverGantt;
}
}
function draw(plot, ctx){
var serie;
canvas = plot.getCanvas();
target = $(canvas).parent();
axes = plot.getAxes();
offset = plot.getPlotOffset();
data = plot.getData();
for (var i = 0; i < data.length; i++){
serie = data[i];
serie.gantt.barheight = axes.yaxis.p2c(1) / (axes.yaxis.max - axes.yaxis.min) * serie.gantt.barHeight;
if (serie.gantt.show) {
series = serie;
for (var j = 0; j < serie.data.length; j++){drawData(ctx,serie, serie.data[j], serie.color,false); }
if(serie.gantt.connectSteps.show){ drawConnections(ctx,serie); }
}
}
}
function drawData(ctx,series,data,color,isHighlight){
var x,y,x2;
x = offset.left + axes.xaxis.p2c(data[0]);
x = Math.min(Math.max(offset.left,x),offset.left + plot.width());
y = offset.top + axes.yaxis.p2c(data[1]);
x2 = offset.left + axes.xaxis.p2c(data[2]);
x2 = Math.min(Math.max(x2,offset.left),offset.left + plot.width());
if(x2 > offset.left || x > offset.left){
if (x < (offset.left + plot.width()) || x2 < (offset.left + plot.width())){
if (data.length === 4) {drawStepDefault(ctx, series, data, x, y, x2, color, isHighlight);}
else{ series.gantt.drawstep(ctx,series,data,x,y,x2,color,isHighlight);}
}
}
}
function drawConnections(ctx,series){
for(var i = 0; i < series.data.length; i++){
for(var j = 0; j < series.data.length; j++){
if(series.data[i][2] == series.data[j][0]){
var x = offset.left + axes.xaxis.p2c(series.data[i][2]),
y = offset.top + axes.yaxis.p2c(series.data[i][1]),
y2 = offset.top + axes.yaxis.p2c(series.data[j][1]);
drawConnection(ctx,x,y,y2,series.gantt.connectSteps.lineWidth,series.gantt.connectSteps.color);
}
}
}
}
function drawConnection(ctx,x,y,y2,lineWidth,color){
ctx.beginPath();
ctx.lineWidth = lineWidth;
ctx.strokeStyle = color;
ctx.moveTo(x, y);
ctx.lineTo(x, y2);
ctx.stroke();
}
function findNearbyItemGantt(mouseX, mouseY,i,serie){
var item = null;
if(opt.series.justEditing){
if(opt.series.justEditing[1].seriesIndex === i){item = findNearbyItemEdit(mouseX,mouseY,i,serie);}
}
else{
if(opt.grid.editable){ item = findNearbyItemForEdit(mouseX,mouseY,i,serie);}
else{ item = findNearbyItem(mouseX,mouseY,i,serie);}
}
return item;
function findNearbyItem(mouseX,mouseY,i,serie){
var item = null;
if(serie.gantt.show){
for(var j = 0; j < serie.data.length; j++){
var dataitem = serie.data[j];
var dx = serie.xaxis.p2c(dataitem[0]),dx2 = serie.xaxis.p2c(dataitem[2]),
dy = Math.abs(serie.yaxis.p2c(dataitem[1]) - mouseY);
if(dy <= serie.gantt.barheight / 2){ if(between(mouseX,dx,dx2)){ item = [i,j]; } }
}
}
return item;
}
function findNearbyItemForEdit(mouseX,mouseY,i,serie){
var item = null;
if(serie.gantt.show){
for(var j = 0; j < serie.data.length; j++){
var dataitem = serie.data[j];
var dx = serie.xaxis.p2c(dataitem[0]),dx2 = serie.xaxis.p2c(dataitem[2]),
dy = Math.abs(serie.yaxis.p2c(dataitem[1]) - mouseY);
if(dy <= serie.gantt.barheight / 2){
if(between(mouseX,dx,dx2)){ item = [i,j]; serie.editMode = 'y'; serie.nearBy.findMode = 'vertical';serie.nearBy.width = dataitem[2]-dataitem[0];}
if(between(mouseX,dx,dx + serie.nearBy.distance)) { item = [i,[j,1]];serie.editMode = 'x'; serie.nearBy.findMode = 'horizontal'; }
if(between(mouseX,dx2,dx2 + serie.nearBy.distance)) { item = [i,[j,2]];serie.editMode = 'x'; serie.nearBy.findMode = 'horizontal'; }
}
}
}
return item;
}
function findNearbyItemEdit(mouseX,mouseY,i,serie){
var item = null;
var j = opt.series.justEditing[1].dataIndex;
var dataitem = serie.data[j];
if(j.length){item = [i,j];}else{item = [i,j];}
return item;
}
}
function drawHoverGantt(octx,serie,dataIndex){
var data;
octx.save();
octx.translate(-offset.left,-offset.top);
var c = "rgba(255,255,255, " + serie.gantt.highlight.opacity + ")";
if(dataIndex.length){ data = serie.data[dataIndex[0]];} else{ data = serie.data[dataIndex];}
drawData(octx,serie,data,c,true);
octx.restore();
}
}
var between = $.plot.JUMlib.library.between;
$.plot.plugins.push({
init: init,
options: options,
name: pluginName,
version: pluginVersion
});
})(jQuery);

File diff suppressed because one or more lines are too long

View File

@ -374,7 +374,7 @@ get '/vm/:id/log' do
end
##############################################################################
# Accounting & Monitoring
# Monitoring
##############################################################################
get '/:resource/monitor' do
@ -399,6 +399,15 @@ get '/:resource/:id/monitor' do
params[:monitor_resources])
end
##############################################################################
# Accounting
##############################################################################
get '/vm/accounting' do
@SunstoneServer.get_vm_accounting(params)
end
##############################################################################
# Marketplace
##############################################################################

View File

@ -13,6 +13,7 @@
<script src="vendor/flot/jquery.flot.pie.min.js?v=<%= OpenNebula::VERSION %>" type="text/javascript"></script>
<script src="vendor/flot/jquery.flot.resize.min.js?v=<%= OpenNebula::VERSION %>" type="text/javascript"></script>
<script src="vendor/flot/jquery.flot.stack.min.js?v=<%= OpenNebula::VERSION %>" type="text/javascript"></script>
<script src="vendor/flot/jquery.flot.tooltip.min.js?v=<%= OpenNebula::VERSION %>" type="text/javascript"></script>
<script src="vendor/fileuploader/fileuploader.js?v=<%= OpenNebula::VERSION %>" type="text/javascript"></script>
<script src="vendor/4.0/nouislider/jquery.nouislider.min.js?v=<%= OpenNebula::VERSION %>" type="text/javascript"></script>
<script src="vendor/4.0/jdpicker_1.1/jquery.jdpicker.js"?v=<%= OpenNebula::VERSION %> type="text/javascript"></script>