diff --git a/pandora_console/extensions/realtime_graphs.php b/pandora_console/extensions/realtime_graphs.php index e8cac701bb..a723844d40 100644 --- a/pandora_console/extensions/realtime_graphs.php +++ b/pandora_console/extensions/realtime_graphs.php @@ -39,14 +39,40 @@ function pandora_realtime_graphs () { $legend = ''; $long_index = array(); $no_data_image = ''; - + $canvas = '
'; $canvas .= '
'; - $canvas .= area_graph($interactive_graph, $chart, 800, 300, $color, $legend, $long_index, $no_data_image, "", "", "", - "", '', '', '', 1, array(), array(), 0, 0, '', false, '', false); + + $width = 800; + $height = 300; + + $data_array['realtime']['data'][0][0] = time() - 10; + $data_array['realtime']['data'][0][1] = 0; + $data_array['realtime']['data'][1][0] = time(); + $data_array['realtime']['data'][1][1] = 0; + $data_array['realtime']['color'] = 'green'; + + $params =array( + 'agent_module_id' => false, + 'period' => 300, + 'width' => $width, + 'height' => $height, + 'unit' => $unit, + 'only_image' => $only_image, + 'homeurl' => $homeurl, + 'type_graph' => 'area', + 'font' => $config['fontpath'], + 'font-size' => $config['font_size'], + 'array_data_create' => $data_array, + 'show_legend' => false, + 'show_menu' => false + ); + + $canvas .= grafico_modulo_sparse($params); + $canvas .= '
'; echo $canvas; - + $table->width = '100%'; $table->id = 'table-form'; $table->class = 'databox filters'; @@ -62,7 +88,7 @@ function pandora_realtime_graphs () { $table->style['snmp_oid'] = 'font-weight: bold;'; $table->style['snmp_oid'] = 'font-weight: bold;'; $table->data = array (); - + $graph_fields['cpu_load'] = __('%s Server CPU', get_product_name()); $graph_fields['pending_packets'] = __('Pending packages from %s Server', get_product_name()); $graph_fields['disk_io_wait'] = __('%s Server Disk IO Wait', get_product_name()); @@ -70,19 +96,19 @@ function pandora_realtime_graphs () { $graph_fields['mysql_load'] = __('%s Server MySQL load', get_product_name()); $graph_fields['server_load'] = __('%s Server load', get_product_name()); $graph_fields['snmp_interface'] = __('SNMP Interface throughput'); - + $graph = get_parameter('graph', 'cpu_load'); $refresh = get_parameter('refresh', '1000'); - + if ($graph != 'snmp_module') { $data['graph'] = __('Graph') . '  ' . html_print_select ($graph_fields, 'graph', $graph, '', '', 0, true); } - $refresh_fields[1000] = human_time_description_raw(1, true, 'large'); - $refresh_fields[5000] = human_time_description_raw(5, true, 'large'); + $refresh_fields[1000] = human_time_description_raw(1, true, 'large'); + $refresh_fields[5000] = human_time_description_raw(5, true, 'large'); $refresh_fields[10000] = human_time_description_raw(10, true, 'large'); $refresh_fields[30000] = human_time_description_raw(30, true, 'large'); - + if ($graph == 'snmp_module') { $agent_alias = get_parameter('agent_alias', ''); $module_name = get_parameter('module_name', ''); @@ -109,7 +135,7 @@ function pandora_realtime_graphs () { $snmp_ver = get_parameter('snmp_ver', ''); $data = array(); - + $data['snmp_address'] = __('Target IP') . '  ' . html_print_input_text ('ip_target', $snmp_address, '', 50, 255, true); $table->colspan[1]['snmp_address'] = 2; @@ -122,7 +148,7 @@ function pandora_realtime_graphs () { $snmp_versions['1'] = '1'; $snmp_versions['2'] = '2'; $snmp_versions['2c'] = '2c'; - + $data = array(); $data['snmp_oid'] = __('OID') . '  ' . html_print_input_text ('snmp_oid', $snmp_oid, '', 100, 255, true); $table->colspan[2]['snmp_oid'] = 2; @@ -140,7 +166,7 @@ function pandora_realtime_graphs () { } snmp_browser_print_container (false, '100%', '60%', 'none'); } - + // Print the relative path to AJAX calls: html_print_input_hidden('rel_path', get_parameter('rel_path', '')); @@ -148,7 +174,7 @@ function pandora_realtime_graphs () { echo '
'; html_print_table($table); echo '
'; - + // Define a custom action to save the OID selected in the SNMP browser to the form html_print_input_hidden ('custom_action', urlencode (base64_encode(' ')), false); html_print_input_hidden ('incremental_base', '0'); @@ -156,7 +182,7 @@ function pandora_realtime_graphs () { echo ''; echo ''; echo ''; - + // Store servers timezone offset to be retrieved from js set_js_value('timezone_offset', date('Z', time())); } diff --git a/pandora_console/godmode/reporting/reporting_builder.item_editor.php b/pandora_console/godmode/reporting/reporting_builder.item_editor.php index fa75a7b452..3831fb9b33 100755 --- a/pandora_console/godmode/reporting/reporting_builder.item_editor.php +++ b/pandora_console/godmode/reporting/reporting_builder.item_editor.php @@ -2919,7 +2919,7 @@ function chooseType() { case 'simple_graph': $("#row_time_compare_overlapped").show(); - $("#row_only_avg").show(); + // $("#row_only_avg").show(); $("#row_fullscale").show(); if ($("#checkbox-percentil").prop("checked")) $("#row_percentil").show(); @@ -2930,7 +2930,6 @@ function chooseType() { $("#row_agent").show(); $("#row_module").show(); $("#row_period").show(); - $("#row_show_in_two_columns").show(); $("#row_show_in_landscape").show(); $("#row_time_compare_overlapped").show(); $("#row_historical_db_check").hide(); diff --git a/pandora_console/include/ajax/graph.ajax.php b/pandora_console/include/ajax/graph.ajax.php index ed1651ee3c..a4f5bc80a7 100644 --- a/pandora_console/include/ajax/graph.ajax.php +++ b/pandora_console/include/ajax/graph.ajax.php @@ -22,7 +22,7 @@ $get_graphs = (bool)get_parameter('get_graphs_container'); if ($save_custom_graph) { $return = array(); - + $id_modules = (array)get_parameter('id_modules', array()); $name = get_parameter('name', ''); $description = get_parameter('description', ''); @@ -32,87 +32,90 @@ if ($save_custom_graph) { $events = get_parameter('events', 0); $period = get_parameter('period', 0); $fullscale = get_parameter('fullscale', 0); - + $result = (bool)custom_graphs_create($id_modules, $name, $description, $stacked, $width, $height, $events, $period, 0, 0, false, $fullscale); - - + $return['correct'] = $result; - + echo json_encode($return); return; } if ($print_custom_graph) { ob_clean(); - - $id_graph = (int) get_parameter('id_graph'); - $height = (int) get_parameter('height', CHART_DEFAULT_HEIGHT); - $width = (int) get_parameter('width', CHART_DEFAULT_WIDTH); - $period = (int) get_parameter('period', SECONDS_5MINUTES); - $stacked = (int) get_parameter('stacked', CUSTOM_GRAPH_LINE); - $date = (int) get_parameter('date', time()); - $only_image = (bool) get_parameter('only_image'); - $background_color = (string) get_parameter('background_color', 'white'); - $modules_param = get_parameter('modules_param', array()); - $homeurl = (string) get_parameter('homeurl'); - $name_list = get_parameter('name_list', array()); - $unit_list = get_parameter('unit_list', array()); - $show_last = (bool) get_parameter('show_last', true); - $show_max = (bool) get_parameter('show_max', true); - $show_min = (bool) get_parameter('show_min', true); - $show_avg = (bool) get_parameter('show_avg', true); - $ttl = (int) get_parameter('ttl', 1); - $dashboard = (bool) get_parameter('dashboard'); - $vconsole = (bool) get_parameter('vconsole'); - $fullscale = (bool) get_parameter('fullscale'); - echo custom_graphs_print($id_graph, $height, $width, $period, $stacked, - true, $date, $only_image, $background_color, $modules_param, - $homeurl, $name_list, $unit_list, $show_last, $show_max, - $show_min, $show_avg, $ttl, $dashboard, $vconsole); + $params =array( + 'period' => (int) get_parameter('period', SECONDS_5MINUTES), + 'width' => (int) get_parameter('width', CHART_DEFAULT_WIDTH), + 'height' => (int) get_parameter('height', CHART_DEFAULT_HEIGHT), + 'unit_name' => get_parameter('unit_list', array()), + 'date' => (int) get_parameter('date', time()), + 'only_image' => (bool) get_parameter('only_image', false), + 'homeurl' => (string) get_parameter('homeurl', ''), + 'ttl' => (int) get_parameter('ttl', 1), + 'dashboard' => (bool) get_parameter('dashboard', false), + 'vconsole' => (bool) get_parameter('vconsole', false), + 'fullscale' => (bool) get_parameter('fullscale', false), + 'backgroundColor' => (string) get_parameter('background_color', 'white'), + 'show_alerts' => (bool) get_parameter('show_alerts'), + 'show_events' => (bool) get_parameter('show_events'), + 'type_graph' => get_parameter('type_g', $config['type_module_charts']), + ); + + $params_combined = array( + 'stacked' => (int) get_parameter('stacked', CUSTOM_GRAPH_LINE), + 'labels' => get_parameter('name_list', array()), + 'modules_series' => get_parameter('modules_param', array()), + 'id_graph' => (int) get_parameter('id_graph', 0), + 'return' => 1 + ); + + echo graphic_combined_module( + get_parameter('modules_param', array()), + $params, + $params_combined + ); return; } if ($print_sparse_graph) { ob_clean(); - - $agent_module_id = (int) get_parameter('agent_module_id'); - $period = (int) get_parameter('period', SECONDS_5MINUTES); - $show_events = (bool) get_parameter('show_events'); - $width = (int) get_parameter('width', CHART_DEFAULT_WIDTH); - $height = (int) get_parameter('height', CHART_DEFAULT_HEIGHT); - $title = (string) get_parameter('title'); - $unit_name = (string) get_parameter('unit_name'); - $show_alerts = (bool) get_parameter('show_alerts'); - $avg_only = (int) get_parameter('avg_only'); - $pure = (bool) get_parameter('pure'); - $date = (int) get_parameter('date', time()); - $unit = (string) get_parameter('unit'); - $baseline = (int) get_parameter('baseline'); - $return_data = (int) get_parameter('return_data'); - $show_title = (bool) get_parameter('show_title', true); - $only_image = (bool) get_parameter('only_image'); - $homeurl = (string) get_parameter('homeurl'); - $ttl = (int) get_parameter('ttl', 1); - $projection = (bool) get_parameter('projection'); - $adapt_key = (string) get_parameter('adapt_key'); - $compare = (bool) get_parameter('compare'); - $show_unknown = (bool) get_parameter('show_unknown'); - $menu = (bool) get_parameter('menu', true); - $background_color = (string) get_parameter('background_color', 'white'); - $percentil = get_parameter('percentil', null); - $dashboard = (bool) get_parameter('dashboard'); - $vconsole = (bool) get_parameter('vconsole'); - $type_g = get_parameter('type_g', $config['type_module_charts']); - $fullscale = get_parameter('fullscale', 0); - - echo grafico_modulo_sparse($agent_module_id, $period, $show_events, - $width, $height , $title, $unit_name, $show_alerts, $avg_only, - $pure, $date, $unit, $baseline, $return_data, $show_title, - $only_image, $homeurl, $ttl, $projection, $adapt_key, $compare, - $show_unknown, $menu, $backgroundColor, $percentil, - $dashboard, $vconsole, $type_g, $fullscale); + $params =array( + 'agent_module_id' => (int)get_parameter('agent_module_id'), + 'period' => (int) get_parameter('period', SECONDS_5MINUTES), + 'show_events' => (bool) get_parameter('show_events'), + 'title' => (string) get_parameter('title'), + 'unit_name' => (string) get_parameter('unit_name'), + 'show_alerts' => (bool) get_parameter('show_alerts'), + 'avg_only' => (int) get_parameter('avg_only'), + 'pure' => (bool) get_parameter('pure'), + 'date' => (int) get_parameter('date', time()), + 'unit' => (string) get_parameter('unit'), + 'baseline' => (int) get_parameter('baseline'), + 'return_data' => (int) get_parameter('return_data'), + 'show_title' => (bool) get_parameter('show_title', true), + 'only_image' => (bool) get_parameter('only_image'), + 'homeurl' => (string) get_parameter('homeurl'), + 'ttl' => (int) get_parameter('ttl', 1), + 'projection' => (bool) get_parameter('projection'), + 'adapt_key' => (string) get_parameter('adapt_key'), + 'compare' => (bool) get_parameter('compare'), + 'show_unknown' => (bool) get_parameter('show_unknown'), + 'menu' => (bool) get_parameter('menu', true), + 'backgroundColor' => (string) get_parameter('background_color', 'white'), + 'percentil' => get_parameter('percentil', null), + 'dashboard' => (bool) get_parameter('dashboard'), + 'vconsole' => (bool) get_parameter('vconsole'), + 'type_graph' => get_parameter('type_g', $config['type_module_charts']), + 'fullscale' => get_parameter('fullscale', 0), + 'id_widget_dashboard' => false, + 'force_interval' => '', + 'time_interval' => 300, + 'array_data_create' => 0 + ); + + echo grafico_modulo_sparse($params); return; } @@ -124,7 +127,7 @@ if ($get_graphs){ if (!empty($result_items)){ $hash = get_parameter('hash',0); $period = get_parameter('time',0); - + $periods = array (); $periods[1] = __('none'); $periods[SECONDS_1HOUR] = __('1 hour'); @@ -137,7 +140,7 @@ if ($get_graphs){ $periods[SECONDS_1WEEK] = __('1 week'); $periods[SECONDS_15DAYS] = __('15 days'); $periods[SECONDS_1MONTH] = __('1 month'); - + $table = ''; $single_table = ""; $single_table .= ""; @@ -151,7 +154,7 @@ if ($get_graphs){ $single_table .= ""; $single_table .= ""; $single_table .= "
"; - + $table .= $single_table; $contador = $config['max_graph_container']; foreach ($result_items as $key => $value) { @@ -159,9 +162,9 @@ if ($get_graphs){ if($period > 1){ $value['time_lapse'] = $period; } - + $type_graph = ($value['type_graph'])? "line" : "area"; - + switch ($value['type']) { case 'simple_graph': if ($contador > 0) { @@ -170,60 +173,23 @@ if ($get_graphs){ $sql_alias = db_get_all_rows_sql("SELECT alias from tagente WHERE id_agente = ". $sql_modulo[0]['id_agente']); $table .= "

AGENT " .$sql_alias[0]['alias']." MODULE ".$sql_modulo[0]['nombre']."


"; - $table .= grafico_modulo_sparse( - $value['id_agent_module'], - $value['time_lapse'], - 0, - 1000, - 300, - '', - '', - false, - $value['only_average'], - false, - 0, - '', - 0, - 0, - 1, - false, - ui_get_full_url(false, false, false, false), - 1, - false, - 0, - false, - false, - 1, - 'white', - null, - false, - false, - $type_graph, - $value['fullscale']); + + $params =array( + 'agent_module_id' => $value['id_agent_module'], + 'period' => $value['time_lapse'], + 'avg_only' => $value['only_average'], + 'homeurl' => ui_get_full_url(false, false, false, false), + 'type_graph' => $type_graph, + 'fullscale' => $value['fullscale'] + ); + + $table .= grafico_modulo_sparse($params); $contador --; } // $table .= "
"; break; case 'custom_graph': if ($contador > 0) { - $graph = db_get_all_rows_field_filter('tgraph', 'id_graph',$value['id_graph']); - - $sources = db_get_all_rows_field_filter('tgraph_source', 'id_graph',$value['id_graph']); - $modules = array (); - $weights = array (); - $labels = array (); - foreach ($sources as $source) { - array_push ($modules, $source['id_agent_module']); - array_push ($weights, $source['weight']); - if ($source['label'] != ''){ - $item['type'] = 'custom_graph'; - $item['id_agent'] = agents_get_module_id($source['id_agent_module']); - $item['id_agent_module'] = $source['id_agent_module']; - $labels[$source['id_agent_module']] = reporting_label_macro($item, $source['label']); - } - } - - $homeurl = ui_get_full_url(false, false, false, false); $graph_conf = db_get_row('tgraph', 'id_graph', $value['id_graph']); if($graph_conf['stacked'] == 4 || $graph_conf['stacked'] == 9){ @@ -233,40 +199,28 @@ if ($get_graphs){ } else { $height = 300; } + $table .= "

".$graph[0]['name']."


"; - $table .= graphic_combined_module( - $modules, - $weights, - $value['time_lapse'], - 1000, - $height, - '', - '', - 0, - 0, - 0, - $graph_conf['stacked'], - 0, - false, - $homeurl, - 1, - false, - false, - 'white', - array(), - array(), - 1, - 1, - 1, - 1, - $labels, - false, - false, - $graph_conf['percentil'] == 1, - false, - false, - $value['fullscale'] + + $params =array( + 'period' => $value['time_lapse'], + 'width' => 1000, + 'height' => $height, + 'percentil' => $graph_conf['percentil'] == 1, + 'fullscale' => $value['fullscale'] ); + + $params_combined = array( + 'stacked' => $graph_conf['stacked'], + 'id_graph' => $value['id_graph'] + ); + + $table .= graphic_combined_module( + false, + $params, + $params_combined + ); + $contador --; } break; @@ -280,13 +234,13 @@ if ($get_graphs){ } else { $id_group = " AND id_grupo = ".$value['id_group']; } - + if($value['id_module_group'] === '0'){ $id_module_group = ""; } else { $id_module_group = " AND id_module_group = ".$value['id_module_group']; } - + if($value['id_tag'] === '0'){ $tag = ""; $id_tag = ""; @@ -294,7 +248,7 @@ if ($get_graphs){ $tag = " INNER JOIN ttag_module ON ttag_module.id_agente_modulo = tagente_modulo.id_agente_modulo "; $id_tag = " AND ttag_module.id_tag = ".$value['id_tag']; } - + if($value['module'] != ''){ $module_name = " AND nombre REGEXP '".$value['module']."'"; } @@ -303,47 +257,27 @@ if ($get_graphs){ ". $tag . "WHERE 1=1" . $id_module_group . $module_name . " AND id_agente IN (SELECT id_agente FROM tagente WHERE 1=1" .$alias.$id_group.")" . $id_tag); - + foreach ($id_agent_module as $key2 => $value2) { if ($contador > 0) { $sql_modulo2 = db_get_all_rows_sql("SELECT nombre, id_agente FROM tagente_modulo WHERE id_agente_modulo = ". $value2['id_agente_modulo']); - + $sql_alias2 = db_get_all_rows_sql("SELECT alias from tagente WHERE id_agente = ". $sql_modulo2[0]['id_agente']); - + $table .= "

AGENT " .$sql_alias2[0]['alias']." MODULE ".$sql_modulo2[0]['nombre']."


"; - - $table .= grafico_modulo_sparse( - $value2['id_agente_modulo'], - $value['time_lapse'], - 0, - 1000, - 300, - '', - '', - false, - $value['only_average'], - false, - 0, - '', - 0, - 0, - 1, - false, - ui_get_full_url(false, false, false, false), - 1, - false, - 0, - false, - false, - 1, - 'white', - null, - false, - false, - $type_graph, - $value['fullscale']); + + $params =array( + 'agent_module_id' => $value2['id_agente_modulo'], + 'period' => $value['time_lapse'], + 'avg_only' => $value['only_average'], + 'homeurl' => ui_get_full_url(false, false, false, false), + 'type_graph' => $type_graph, + 'fullscale' => $value['fullscale'] + ); + + $table .= grafico_modulo_sparse($params); $contador --; } } @@ -353,7 +287,6 @@ if ($get_graphs){ $table .= "
"; echo $table; return; - } } diff --git a/pandora_console/include/ajax/visual_console_builder.ajax.php b/pandora_console/include/ajax/visual_console_builder.ajax.php index 461c20298a..fab370763d 100755 --- a/pandora_console/include/ajax/visual_console_builder.ajax.php +++ b/pandora_console/include/ajax/visual_console_builder.ajax.php @@ -251,20 +251,41 @@ switch ($action) { continue; } } - + + $params =array( + 'period' => $period, + 'width' => $width, + 'height' => $height, + 'vconsole' => true, + 'backgroundColor'=> $background_color + ); + + $params_combined = array( + 'id_graph' => $id_custom_graph + ); + if ($id_custom_graph != 0) { - $img = custom_graphs_print( - $id_custom_graph, $height, $width, $period, - null, true, 0, true, $background_color); + $img = graphic_combined_module( + false, + $params, + $params_combined + ); } else { - $img = grafico_modulo_sparse($id_agent_module, - $period, 0, $width, $height, '', null, false, 1, - 0, 0, '', 0, 0, true, true, '', 1, false, '', - false, false, true, $background_color, - false, false, false, $config['type_module_charts']); + $params =array( + 'agent_module_id' => $id_agent_module, + 'period' => $period, + 'show_events' => false, + 'width' => $width, + 'height' => $height, + 'menu' => false, + 'backgroundColor' => $background_color, + 'vconsole' => true, + 'type_graph' => $config['type_module_charts'] + ); + $img = grafico_modulo_sparse($params); } - + //Restore db connection if (!empty($id_metaconsole)) { metaconsole_restore_db(); diff --git a/pandora_console/include/chart_generator.php b/pandora_console/include/chart_generator.php new file mode 100644 index 0000000000..fef8d1abeb --- /dev/null +++ b/pandora_console/include/chart_generator.php @@ -0,0 +1,120 @@ +"; + echo ""; + ui_print_error_message(__('There was a problem connecting with the node')); + echo ""; + echo ""; + exit; + } +} +*/ + +$user_language = get_user_language($config['id_user']); +if (file_exists ('languages/'.$user_language.'.mo')) { + $l10n = new gettext_reader (new CachedFileReader ('languages/'.$user_language.'.mo')); + $l10n->load_tables(); +} + +?> + + + + + Pandora FMS Graph (<?php echo agents_get_alias($agent_id) . ' - ' . $interface_name; ?>) + + + + + + + + + + + + + + + + + + + + + + + +'; + echo graphic_combined_module( + $module_list, + $params, + $params_combined + ); + echo ''; + } + elseif($type_graph_pdf == 'sparse'){ + echo '
'; + echo grafico_modulo_sparse($params); + echo '
'; + } +?> + + + \ No newline at end of file diff --git a/pandora_console/include/functions.php b/pandora_console/include/functions.php index 10f64bc07e..9c328336e6 100644 --- a/pandora_console/include/functions.php +++ b/pandora_console/include/functions.php @@ -2798,12 +2798,12 @@ function register_pass_change_try ($id_user, $success) { } function isJson($string) { - json_decode($string); - return (json_last_error() == JSON_ERROR_NONE); + json_decode($string); + return (json_last_error() == JSON_ERROR_NONE); } /** - * returns true or false if it is a valid ip + * returns true or false if it is a valid ip * checking ipv4 and ipv6 or resolves the name dns * @param string address * @@ -2824,6 +2824,372 @@ function validate_address($address){ return true; } +function color_graph_array(){ + global $config; + + $color_series = array(); + + $color_series[0] = array( + 'border' => '#000000', + 'color' => $config['graph_color1'], + 'alpha' => CHART_DEFAULT_ALPHA + ); + + //XXX Hablar con Sancho del tema de los slices + /* + $color_series[1] = array( + 'border' => '#000000', + 'color' => $config['graph_color2'], + 'alpha' => CHART_DEFAULT_ALPHA + ); + $color_series[2] = array( + 'border' => '#000000', + 'color' => $config['graph_color3'], + 'alpha' => CHART_DEFAULT_ALPHA + ); + */ + + $color_series[1] = array( + 'border' => '#000000', + 'color' => $config['graph_color4'], + 'alpha' => CHART_DEFAULT_ALPHA + ); + $color_series[2] = array( + 'border' => '#000000', + 'color' => $config['graph_color5'], + 'alpha' => CHART_DEFAULT_ALPHA + ); + $color_series[3] = array( + 'border' => '#000000', + 'color' => $config['graph_color6'], + 'alpha' => CHART_DEFAULT_ALPHA + ); + $color_series[4] = array( + 'border' => '#000000', + 'color' => $config['graph_color7'], + 'alpha' => CHART_DEFAULT_ALPHA + ); + $color_series[5] = array( + 'border' => '#000000', + 'color' => $config['graph_color8'], + 'alpha' => CHART_DEFAULT_ALPHA + ); + $color_series[6] = array( + 'border' => '#000000', + 'color' => $config['graph_color9'], + 'alpha' => CHART_DEFAULT_ALPHA + ); + $color_series[7] = array( + 'border' => '#000000', + 'color' => $config['graph_color10'], + 'alpha' => CHART_DEFAULT_ALPHA + ); + $color_series[8] = array( + 'border' => '#000000', + 'color' => COL_GRAPH9, + 'alpha' => CHART_DEFAULT_ALPHA + ); + $color_series[9] = array( + 'border' => '#000000', + 'color' => COL_GRAPH10, + 'alpha' => CHART_DEFAULT_ALPHA + ); + $color_series[10] = array( + 'border' => '#000000', + 'color' => COL_GRAPH11, + 'alpha' => CHART_DEFAULT_ALPHA + ); + $color_series[11] = array( + 'border' => '#000000', + 'color' => COL_GRAPH12, + 'alpha' => CHART_DEFAULT_ALPHA + ); + $color_series[12] = array( + 'border' => '#000000', + 'color' => COL_GRAPH13, + 'alpha' => CHART_DEFAULT_ALPHA + ); + + //XXX Colores fijos para eventos, alertas, desconocidos, percentil, overlapped, summatory, average, projection + $color_series['event'] = array( + 'border' => '#ff0000', + 'color' => '#FF5733', + 'alpha' => CHART_DEFAULT_ALPHA + ); + + $color_series['alert'] = array( + 'border' => '#ffff00', + 'color' => '#ffff00', + 'alpha' => CHART_DEFAULT_ALPHA + ); + + $color_series['unknown'] = array( + 'border' => '#999999', + 'color' => '#E1E1E1', + 'alpha' => CHART_DEFAULT_ALPHA + ); + + $color_series['percentil'] = array( + 'border' => '#000000', + 'color' => '#003333', + 'alpha' => CHART_DEFAULT_ALPHA + ); + + $color_series['projection'] = array( + 'border' => '#000000', + 'color' => $config['graph_color8'], + 'alpha' => CHART_DEFAULT_ALPHA + ); + + $color_series['overlapped'] = array( + 'border' => '#000000', + 'color' => $config['graph_color9'], + 'alpha' => CHART_DEFAULT_ALPHA + ); + + $color_series['summatory'] = array( + 'border' => '#000000', + 'color' => $config['graph_color7'], + 'alpha' => CHART_DEFAULT_ALPHA + ); + + $color_series['average'] = array( + 'border' => '#000000', + 'color' => $config['graph_color10'], + 'alpha' => CHART_DEFAULT_ALPHA + ); + + $color_series['no_data'] = array( + 'border' => '#000000', + 'color' => '#f2c40e', + 'alpha' => CHART_DEFAULT_ALPHA + ); + + $color_series['unit'] = array( + 'border' => null, + 'color' => '#0097BC', + 'alpha' => 10 + ); + //XXXXXXXX + /* + if($id_widget_dashboard){ + $opcion = unserialize(db_get_value_filter('options','twidget_dashboard',array('id' => $id_widget_dashboard))); + foreach ($module_list as $key => $value) { + if(!empty($opcion[$value])){ + $color[$key]['color'] = $opcion[$value]; + } + } + } + */ + + return $color_series; +} + +function series_type_graph_array($data, $show_elements_graph){ + global $config; + + if(isset($show_elements_graph['stacked'])){ + switch ($show_elements_graph['stacked']) { + case 2: + case 4: + $type_graph = 'line'; + break; + default: + $type_graph = 'area'; + break; + } + } + else{ + $type_graph = $show_elements_graph['type_graph']; + } + + $color_series = color_graph_array(); + $i = 0; + if(isset($data) && is_array($data)){ + foreach ($data as $key => $value) { + if($show_elements_graph['compare'] == 'overlapped'){ + if($key == 'sum2'){ + $str = ' (' . __('Previous') . ')'; + } + } + + if(strpos($key, 'summatory') !== false){ + $data_return['series_type'][$key] = $type_graph; + $data_return['legend'][$key] = __('Summatory series') . ' ' . $str; + $data_return['color'][$key] = $color_series['summatory']; + } + elseif(strpos($key, 'average') !== false){ + $data_return['series_type'][$key] = $type_graph; + $data_return['legend'][$key] = __('Average series') . ' ' . $str; + $data_return['color'][$key] = $color_series['average']; + } + elseif(strpos($key, 'sum') !== false || strpos($key, 'baseline') !== false){ + switch ($value['id_module_type']) { + case 21: case 2: case 6: + case 18: case 9: case 31: + $data_return['series_type'][$key] = 'boolean'; + break; + default: + $data_return['series_type'][$key] = $type_graph; + break; + } + + if (isset($show_elements_graph['labels']) && + is_array($show_elements_graph['labels']) && + (count($show_elements_graph['labels']) > 0)){ + $data_return['legend'][$key] = $show_elements_graph['labels'][$value['agent_module_id']] . ' ' ; + } + else{ + if(strpos($key, 'baseline') !== false){ + $data_return['legend'][$key] = $value['agent_alias'] . ' / ' . + $value['module_name'] . ' Baseline '; + } + else{ + $data_return['legend'][$key] = $value['agent_alias'] . ' / ' . + $value['module_name'] . ': '; + } + } + + if(strpos($key, 'baseline') === false){ + $data_return['legend'][$key] .= + __('Min:') . remove_right_zeros( + number_format( + $value['min'], + $config['graph_precision'] + ) + ) . ' ' . + __('Max:') . remove_right_zeros( + number_format( + $value['max'], + $config['graph_precision'] + ) + ) . ' ' . + _('Avg:') . remove_right_zeros( + number_format( + $value['avg'], + $config['graph_precision'] + ) + ) . ' ' . $str; + } + + if($show_elements_graph['compare'] == 'overlapped'){ + $data_return['color'][$key] = $color_series['overlapped']; + } + else{ + $data_return['color'][$key] = $color_series[$i]; + $i++; + } + } + elseif(strpos($key, 'event') !== false){ + $data_return['series_type'][$key] = 'points'; + if($show_elements_graph['show_events']){ + $data_return['legend'][$key] = __('Events') . ' ' . $str; + } + + $data_return['color'][$key] = $color_series['event']; + } + elseif(strpos($key, 'alert') !== false){ + $data_return['series_type'][$key] = 'points'; + if($show_elements_graph['show_alerts']){ + $data_return['legend'][$key] = __('Alert') . ' ' . $str; + } + + $data_return['color'][$key] = $color_series['alert']; + } + elseif(strpos($key, 'unknown') !== false){ + $data_return['series_type'][$key] = 'unknown'; + if($show_elements_graph['show_unknown']){ + $data_return['legend'][$key] = __('Unknown') . ' ' . $str; + } + $data_return['color'][$key] =$color_series['unknown']; + } + elseif(strpos($key, 'percentil') !== false){ + $data_return['series_type'][$key] = 'percentil'; + if($show_elements_graph['percentil']){ + $data_return['legend'][$key] = + __('Percentil') . ' ' . + $config['percentil'] . + 'ยบ ' . __('of module') . ' '; + if (isset($show_elements_graph['labels']) && is_array($show_elements_graph['labels'])){ + $data_return['legend'][$key] .= $show_elements_graph['labels'][$value['agent_module_id']] . ' ' ; + } + else{ + $data_return['legend'][$key] .= $value['agent_alias'] . ' / ' . + $value['module_name'] . ': ' . ' Value: '; + } + $data_return['legend'][$key] .= remove_right_zeros( + number_format( + $value['data'][0][1], + $config['graph_precision'] + ) + ) . ' ' . $str; + } + $data_return['color'][$key] =$color_series['percentil']; + } + elseif(strpos($key, 'projection') !== false){ + $data_return['series_type'][$key] = $type_graph; + $data_return['legend'][$key] = __('Projection') . ' ' . $str; + $data_return['color'][$key] = $color_series['projection']; + } + else{ + $data_return['series_type'][$key] = $type_graph; + $data_return['legend'][$key] = $key; + $data_return['color'][$key] = $color_series[$i]; + $i++; + } + } + return $data_return; + } + return false; +} + +function generator_chart_to_pdf($type_graph_pdf, $params, $params_combined = false, $module_list = false){ + global $config; + + $file_js = $config["homedir"] . "/include/web2image.js"; + $url = $config["homeurl"] . "/include/chart_generator.php"; + $img_file = "img_". uniqid() .".png"; + $img_path = $config["homedir"] . "/attachment/" . $img_file; + $img_url = $config["homeurl"] . "attachment/" . $img_file; + + $width_img = 500; + $height_img = 450; + + $params_encode_json = urlencode(json_encode($params)); + + if($params_combined){ + $params_combined = urlencode(json_encode($params_combined)); + } + + if($module_list){ + $module_list = urlencode(json_encode($module_list)); + } + + $session_id = session_id(); + + $result = exec( + "phantomjs " . $file_js . " " . + $url . " '" . + $type_graph_pdf . "' '" . + $params_encode_json . "' '" . + $params_combined . "' '" . + $module_list . "' " . + $img_path . " " . + $width_img . " " . + $height_img . " '" . + $session_id . "' " . + $params['return_img_base_64'] + ); + + if($params['return_img_base_64']){ + return $result; + } + else{ + $config["temp_images"][] = $img_path; + return ''; + } +} + /** * Get the product name. * diff --git a/pandora_console/include/functions_api.php b/pandora_console/include/functions_api.php index c054dbbe4c..13716a6051 100644 --- a/pandora_console/include/functions_api.php +++ b/pandora_console/include/functions_api.php @@ -6655,7 +6655,7 @@ function api_get_graph_module_data($id, $thrash1, $other, $thrash2) { if (defined ('METACONSOLE')) { return; } - + $period = $other['data'][0]; $width = $other['data'][1]; $height = $other['data'][2]; @@ -6667,38 +6667,40 @@ function api_get_graph_module_data($id, $thrash1, $other, $thrash2) { $avg_only = 0; $start_date = $other['data'][4]; $date = strtotime($start_date); - - + $homeurl = '../'; $ttl = 1; - + global $config; $config['flash_charts'] = 0; - - $image = grafico_modulo_sparse ($id, $period, $draw_events, - $width, $height , $label, null, - $draw_alerts, $avg_only, false, - $date, '', 0, 0,true, - false, $homeurl, $ttl); - - preg_match("/
/", - $image, $match); - - if (!empty($match[0])) { - echo "Error no data"; - } - else { - // Extract url of the image from img tag - preg_match("/src='([^']*)'/i", $image, $match); - - if (empty($match[1])) { - echo "Error getting graph"; - } - else { - header('Content-type: image/png'); - header('Location: ' . $match[1]); - } - } + + $params =array( + 'agent_module_id' => $id, + 'period' => $period, + 'show_events' => $draw_events, + 'width' => $width, + 'height' => $height, + 'show_alerts' => $draw_alerts, + 'date' => $date, + 'unit' => '', + 'baseline' => 0, + 'return_data' => 0, + 'show_title' => true, + 'only_image' => true, + 'homeurl' => $homeurl, + 'compare' => false, + 'show_unknown' => true, + 'backgroundColor' => 'white', + 'percentil' => null, + 'type_graph' => $config['type_module_charts'], + 'fullscale' => false, + 'return_img_base_64' => true + ); + + $image = grafico_modulo_sparse($params); + + header('Content-type: text/html'); + returnData('string', array('type' => 'string', 'data' => '')); } /** @@ -10039,25 +10041,24 @@ function api_set_delete_special_day($id_special_day, $thrash2, $thrash3, $thrash * */ function api_get_module_graph($id_module, $thrash2, $other, $thrash4) { - global $config; if (defined ('METACONSOLE')) { return; } - + if (is_nan($id_module) || $id_module <= 0) { returnError('error_module_graph', __('')); return; } - + $id_exist = (bool) db_get_value ('id_agente_modulo', 'tagente_modulo', 'id_agente_modulo', $id_module); - + if (!$id_exist) { // returnError('id_not_found'); return; } - + $graph_seconds = (!empty($other) && isset($other['data'][0])) ? @@ -10066,7 +10067,7 @@ function api_get_module_graph($id_module, $thrash2, $other, $thrash4) { SECONDS_1HOUR; // 1 hour by default $graph_threshold = - (!empty($other) && isset($other['data'][2])) + (!empty($other) && isset($other['data'][2]) && $other['data'][2]) ? $other['data'][2] : @@ -10076,80 +10077,38 @@ function api_get_module_graph($id_module, $thrash2, $other, $thrash4) { // returnError('error_module_graph', __('')); return; } - - $id_module_type = modules_get_agentmodule_type ($id_module); - $module_type = modules_get_moduletype_name ($id_module_type); - - $string_type = strpos($module_type,'string'); - // Get the html item - if ($string_type === false) { - $graph_html = grafico_modulo_sparse( - $id_module, $graph_seconds, false, 600, 300, '', - '', false, false, true, time(), '', 0, 0, true, true, - ui_get_full_url(false) . '/', 1, false, '', false, true, - true, 'white', null, false, false, $config['type_module_charts'], - false, false); + + $params =array( + 'agent_module_id' => $id_module, + 'period' => $graph_seconds, + 'show_events' => false, + 'width' => $width, + 'height' => $height, + 'show_alerts' => false, + 'date' => time(), + 'unit' => '', + 'baseline' => 0, + 'return_data' => 0, + 'show_title' => true, + 'only_image' => true, + 'homeurl' => ui_get_full_url(false) . '/', + 'compare' => false, + 'show_unknown' => true, + 'backgroundColor' => 'white', + 'percentil' => null, + 'type_graph' => $config['type_module_charts'], + 'fullscale' => false, + 'return_img_base_64' => true, + 'image_treshold' => $graph_threshold + ); + + $graph_html = grafico_modulo_sparse($params); + + if($other['data'][1]){ + header('Content-type: text/html'); + returnData('string', array('type' => 'string', 'data' => '')); } else { - $graph_html = grafico_modulo_string( - $id_module, $graph_seconds, false, 600, 300, '', - '', false, false, true, time(), true, ui_get_full_url(false) . '/', - '', 1, true); - } - - $graph_image_file_encoded = false; - if (preg_match("/ 'string', 'data' => '')); - } else { - returnData('string', array('type' => 'string', 'data' => $graph_image_file_encoded)); - } - // To show only the base64 code, call returnData as: - // returnData('string', array('type' => 'string', 'data' => $graph_image_file_encoded)); + returnData('string', array('type' => 'string', 'data' => $graph_html)); } } diff --git a/pandora_console/include/functions_custom_graphs.php b/pandora_console/include/functions_custom_graphs.php index c7acc7b35a..11af22a7e8 100644 --- a/pandora_console/include/functions_custom_graphs.php +++ b/pandora_console/include/functions_custom_graphs.php @@ -21,7 +21,7 @@ /** - * @global array Contents all var configs for the local instalation. + * @global array Contents all var configs for the local instalation. */ global $config; @@ -32,13 +32,13 @@ function custom_graphs_create($id_modules = array(), $name = "", $description = "", $stacked = CUSTOM_GRAPH_AREA, $width = 0, $height = 0, $events = 0 , $period = 0, $private = 0, $id_group = 0, $user = false, $fullscale = 0) { - + global $config; - + if ($user === false) { $user = $config['id_user']; } - + $id_graph = db_process_sql_insert('tgraph', array( 'id_user' => $user, @@ -54,7 +54,7 @@ function custom_graphs_create($id_modules = array(), $name = "", 'id_graph_template' => 0, 'fullscale' => $fullscale, )); - + if (empty($id_graph)) { return false; } @@ -67,22 +67,22 @@ function custom_graphs_create($id_modules = array(), $name = "", 'id_agent_module' => $id_module, 'weight' => 1 )); - + if (empty($result)) break; } - + if (empty($result)) { //Not it is a complete insert the modules. Delete all db_process_sql_delete('tgraph_source', array('id_graph' => $id_graph)); - + db_process_sql_delete('tgraph', array('id_graph' => $id_graph)); - + return false; } - + return $id_graph; } } @@ -100,30 +100,30 @@ function custom_graphs_create($id_modules = array(), $name = "", */ function custom_graphs_get_user ($id_user = 0, $only_names = false, $returnAllGroup = true, $privileges = 'RR') { global $config; - + if (!$id_user) { $id_user = $config['id_user']; } - + $groups = users_get_groups ($id_user, $privileges, $returnAllGroup); - + $all_graphs = db_get_all_rows_in_table ('tgraph', 'name'); if ($all_graphs === false) return array (); - + $graphs = array (); foreach ($all_graphs as $graph) { if (!in_array($graph['id_group'], array_keys($groups))) continue; - + if ($graph["id_user"] != $id_user && $graph['private']) continue; - + if ($graph["id_group"] > 0) if (!isset($groups[$graph["id_group"]])) { continue; } - + if ($only_names) { $graphs[$graph['id_graph']] = $graph['name']; } @@ -135,148 +135,7 @@ function custom_graphs_get_user ($id_user = 0, $only_names = false, $returnAllGr $graphs[$graph['id_graph']]['graphs_count'] = $graphsCount; } } - return $graphs; } -/** - * Print a custom graph image. - * - * @param $id_graph Graph id to print. - * @param $height Height of the returning image. - * @param $width Width of the returning image. - * @param $period Period of time to get data in seconds. - * @param $stacked Whether the graph is stacked or not. - * @param $return Whether to return an output string or echo now (optional, echo by default). - * @param $date Date to start printing the graph - * @param bool Wether to show an image instead a interactive chart or not - * @param string Background color - * @param array List of names for the items. Should have the same size as the module list. - * @param bool Show the last value of the item on the list. - * @param bool Show the max value of the item on the list. - * @param bool Show the min value of the item on the list. - * @param bool Show the average value of the item on the list. - * - * @return Mixed - */ - -function custom_graphs_print($id_graph, $height, $width, $period, - $stacked = null, $return = false, $date = 0, $only_image = false, - $background_color = 'white', $modules_param = array(), $homeurl = '', - $name_list = array(), $unit_list = array(), $show_last = true, - $show_max = true, $show_min = true, $show_avg = true, $ttl = 1, - $dashboard = false, $vconsole = false, $percentil = null, - $from_interface = false,$id_widget_dashboard=false, $fullscale = false) { - - global $config; - - if ($from_interface) { - if ($config["type_interface_charts"] == 'line') { - $graph_conf['stacked'] = CUSTOM_GRAPH_LINE; - } - else { - $graph_conf['stacked'] = CUSTOM_GRAPH_AREA; - } - } - else { - if ($id_graph == 0) { - $graph_conf['stacked'] = CUSTOM_GRAPH_LINE; - } - else { - $graph_conf = db_get_row('tgraph', 'id_graph', $id_graph); - } - } - - if ($stacked === null) { - $stacked = $graph_conf['stacked']; - } - - $sources = false; - if ($id_graph == 0) { - $modules = $modules_param; - $count_modules = count($modules); - $weights = array_fill(0, $count_modules, 1); - - if ($count_modules > 0) - $sources = true; - } - else { - $sources = db_get_all_rows_field_filter('tgraph_source', 'id_graph', - $id_graph); - - $series = db_get_all_rows_sql('SELECT summatory_series,average_series,modules_series FROM tgraph WHERE id_graph = '.$id_graph); - $summatory = $series[0]['summatory_series']; - $average = $series[0]['average_series']; - $modules_series = $series[0]['modules_series']; - - $modules = array (); - $weights = array (); - $labels = array (); - foreach ($sources as $source) { - array_push ($modules, $source['id_agent_module']); - array_push ($weights, $source['weight']); - if ($source['label'] != ''){ - $item['type'] = 'custom_graph'; - $item['id_agent'] = agents_get_module_id($source['id_agent_module']); - $item['id_agent_module'] = $source['id_agent_module']; - $labels[$source['id_agent_module']] = reporting_label_macro($item, $source['label']); - } - } - } - - - if ($sources === false) { - if ($return){ - return false; - } - else{ - ui_print_info_message ( array ( 'no_close' => true, 'message' => __('No items.') ) ); - return; - } - } - - if (empty($homeurl)) { - $homeurl = ui_get_full_url(false, false, false, false); - } - - $output = graphic_combined_module($modules, - $weights, - $period, - $width, - $height, - '', - '', - 0, - 0, - 0, - $stacked, - $date, - $only_image, - $homeurl, - $ttl, - false, - false, - $background_color, - $name_list, - array(), - $show_last, - $show_max, - $show_min, - $show_avg, - $labels, - $dashboard, - $vconsole, - $percentil, - $from_interface, - $id_widget_dashboard, - $fullscale, - $summatory, - $average, - $modules_series); - - if ($return) - return $output; - echo $output; -} - ?> diff --git a/pandora_console/include/functions_db.php b/pandora_console/include/functions_db.php index b069b4d993..95541218ae 100644 --- a/pandora_console/include/functions_db.php +++ b/pandora_console/include/functions_db.php @@ -418,7 +418,7 @@ function db_get_row ($table, $field_search, $condition, $fields = false) { */ function db_get_row_filter($table, $filter, $fields = false, $where_join = 'AND', $historydb = false) { global $config; - + switch ($config["dbtype"]) { case "mysql": return mysql_db_get_row_filter($table, $filter, $fields, $where_join, $historydb); @@ -443,10 +443,10 @@ function db_get_row_filter($table, $filter, $fields = false, $where_join = 'AND' function db_get_sql ($sql, $field = 0, $search_history_db = false) { $result = db_get_all_rows_sql ($sql, $search_history_db); - + if ($result === false) return false; - + $ax = 0; foreach ($result[0] as $f) { if ($field == $ax) @@ -467,7 +467,7 @@ function db_get_sql ($sql, $field = 0, $search_history_db = false) { */ function db_get_all_rows_sql($sql, $search_history_db = false, $cache = true, $dbconnection = false) { global $config; - + switch ($config["dbtype"]) { case "mysql": return mysql_db_get_all_rows_sql($sql, $search_history_db, $cache, $dbconnection); @@ -482,15 +482,12 @@ function db_get_all_rows_sql($sql, $search_history_db = false, $cache = true, $d } /** - * * Returns the time the module is in unknown status (by events) - * * @param int $id_agente_modulo module to check * @param int $tstart begin of search * @param int $tend end of search - * */ -function db_get_module_ranges_unknown($id_agente_modulo, $tstart = false, $tend = false, $historydb = false) { +function db_get_module_ranges_unknown($id_agente_modulo, $tstart = false, $tend = false, $historydb = false, $fix_to_range = 0) { global $config; if (!isset($id_agente_modulo)) { @@ -512,18 +509,39 @@ function db_get_module_ranges_unknown($id_agente_modulo, $tstart = false, $tend } // Retrieve going unknown events in range - $query = "SELECT utimestamp,event_type FROM tevento WHERE id_agentmodule = " . $id_agente_modulo; - $query .= " AND event_type like 'going_%' "; - $query .= " AND utimestamp >= $tstart AND utimestamp <= $tend "; - $query .= " ORDER BY utimestamp ASC"; - + $query = "SELECT * FROM tevento WHERE id_agentmodule = " . $id_agente_modulo + . " AND event_type like 'going_%' " + . " AND utimestamp >= $tstart AND utimestamp <= $tend " + . " ORDER BY utimestamp ASC"; $events = db_get_all_rows_sql($query, $historydb); - if (! is_array($events)){ + $query = "SELECT * FROM tevento WHERE id_agentmodule = " . $id_agente_modulo + . " AND event_type like 'going_%' " + . " AND utimestamp < $tstart " + . " ORDER BY utimestamp DESC LIMIT 1;"; + $previous_event = db_get_all_rows_sql($query, $historydb); + + if ($previous_event !== false) { + $last_status = $previous_event[0]["event_type"] == "going_unknown" ? 1:0; + } + else { + $last_status = 0; + } + + if ((! is_array($events)) && (! is_array($previous_event))) { return false; } - $last_status = $events[0]["event_type"] != "going_unknown" ? 1:0; + if (! is_array($events)) { + if ($previous_event[0]["event_type"] == "going_unknown") { + return array( + array( + "time_from" => (($fix_to_range == 1)?$tstart:$previous_event[0]["utimestamp"]), + ) + ); + } + } + $return = array(); $i=0; foreach ($events as $event) { @@ -558,23 +576,22 @@ function db_get_module_ranges_unknown($id_agente_modulo, $tstart = false, $tend return $return; } - /** * Uncompresses and returns the data of a given id_agent_module - * + * * @param int $id_agente_modulo id_agente_modulo * @param utimestamp $tstart Begin of the catch * @param utimestamp $tend End of the catch * @param int $interval Size of slice (default-> module_interval) - * + * * @return hash with the data uncompressed in blocks of module_interval * false in case of empty result - * + * * Note: All "unknown" data are marked as NULL * Warning: Be careful with the amount of data, check your RAM size available * We'll return a bidimensional array * Structure returned: schema: - * + * * uncompressed_data => * pool_id (int) * utimestamp (start of current slice) @@ -582,7 +599,7 @@ function db_get_module_ranges_unknown($id_agente_modulo, $tstart = false, $tend * array * datos * utimestamp - * + * */ function db_uncompress_module_data($id_agente_modulo, $tstart = false, $tend = false) { global $config; @@ -602,7 +619,7 @@ function db_uncompress_module_data($id_agente_modulo, $tstart = false, $tend = f $search_historydb = false; $table = "tagente_datos"; - + $module = modules_get_agentmodule($id_agente_modulo); if ($module === false){ // module not exists @@ -610,11 +627,11 @@ function db_uncompress_module_data($id_agente_modulo, $tstart = false, $tend = f } $module_type = $module['id_tipo_modulo']; $module_type_str = modules_get_type_name ($module_type); - + if (strstr ($module_type_str, 'string') !== false) { $table = "tagente_datos_string"; } - + $flag_async = false; if(strstr ($module_type_str, 'async_data') !== false) { $flag_async = true; @@ -634,7 +651,7 @@ function db_uncompress_module_data($id_agente_modulo, $tstart = false, $tend = f else { $query = "SELECT datos,utimestamp FROM $table "; $query .= " WHERE id_agente_modulo=$id_agente_modulo "; - $query .= " AND utimestamp=" . $first_utimestamp; + $query .= " AND utimestamp = " . $first_utimestamp; $data = db_get_all_rows_sql($query,$search_historydb); @@ -652,7 +669,6 @@ function db_uncompress_module_data($id_agente_modulo, $tstart = false, $tend = f else { $first_data["utimestamp"] = $data[0]["utimestamp"]; $first_data["datos"] = $data[0]["datos"]; - } } @@ -670,27 +686,11 @@ function db_uncompress_module_data($id_agente_modulo, $tstart = false, $tend = f } // Retrieve going unknown events in range - $unknown_events = db_get_module_ranges_unknown($id_agente_modulo, $tstart, $tend, $search_historydb); - - // Get the last event after inverval to know if graph start on unknown - $previous_unknown_events = db_get_row_filter ( - 'tevento', - array ('id_agentmodule' => $id_agente_modulo, - "utimestamp <= $tstart", - 'order' => 'utimestamp DESC' - ), - false, - 'AND', - $search_historydb + $unknown_events = db_get_module_ranges_unknown( + $id_agente_modulo, $tstart, + $tend, $search_historydb, 1 ); - //show graph if graph is inside unknown - if( $previous_unknown_events && $previous_unknown_events['event_type'] == 'going_unknown' && - $unknown_events === false){ - $last_inserted_value = $first_data["datos"]; - $unknown_events[0]['time_from'] = $tstart; - } - //if time to is missing in last event force time to outside range time if( $unknown_events && !isset($unknown_events[count($unknown_events) -1]['time_to']) ){ $unknown_events[count($unknown_events) -1]['time_to'] = $tend; @@ -699,8 +699,7 @@ function db_uncompress_module_data($id_agente_modulo, $tstart = false, $tend = f //if time to is missing in first event force time to outside range time if ($first_data["datos"] === false && !$flag_async) { $last_inserted_value = false; - }elseif(($unknown_events && !isset($unknown_events[0]['time_from']) && - $previous_unknown_events && $previous_unknown_events['event_type'] == 'going_unknown' && !$flag_async) || + }elseif(($unknown_events && !isset($unknown_events[0]['time_from']) && !$flag_async) || ($first_utimestamp < $tstart - (SECONDS_1DAY + 2*$module_interval) && !$flag_async) ){ $last_inserted_value = $first_data["datos"]; $unknown_events[0]['time_from'] = $tstart; @@ -734,7 +733,7 @@ function db_uncompress_module_data($id_agente_modulo, $tstart = false, $tend = f else{ $current_unknown = null; } - + if(is_array($raw_data)) { $current_raw_data = array_pop($raw_data); } @@ -751,15 +750,15 @@ function db_uncompress_module_data($id_agente_modulo, $tstart = false, $tend = f || (($current_timestamp - $last_timestamp) > (SECONDS_1DAY + 2 * $module_interval)) ) { $tmp_data["utimestamp"] = $current_timestamp; - + //check not init $tmp_data["datos"] = $last_value === false ? false : null; - + //async not unknown if($flag_async && $tmp_data["datos"] === null){ $tmp_data["datos"] = $last_inserted_value; } - + // debug purpose //$tmp_data["obs"] = "unknown extra"; array_push($return[$pool_id]["data"], $tmp_data); @@ -767,9 +766,9 @@ function db_uncompress_module_data($id_agente_modulo, $tstart = false, $tend = f //insert raw data while ( ($current_raw_data != null) && - ( ($current_timestamp_end > $current_raw_data['utimestamp']) && + ( ($current_timestamp_end > $current_raw_data['utimestamp']) && ($current_timestamp <= $current_raw_data['utimestamp']) ) ) { - + // Add real data detected if (count($return[$pool_id]['data']) == 0) { //insert first slice data @@ -777,6 +776,7 @@ function db_uncompress_module_data($id_agente_modulo, $tstart = false, $tend = f $tmp_data["datos"] = $last_inserted_value; // debug purpose //$tmp_data["obs"] = "virtual data (raw)"; + $tmp_data["type"] = ($current_timestamp == $tstart || ($current_timestamp == $tend)?0:1); // virtual data //Add order to avoid usort missorder in same utimestamp data cells $tmp_data["order"] = 1; @@ -785,6 +785,7 @@ function db_uncompress_module_data($id_agente_modulo, $tstart = false, $tend = f $tmp_data["utimestamp"] = $current_raw_data["utimestamp"]; $tmp_data["datos"] = $current_raw_data["datos"]; + $tmp_data["type"] = 0; // real data // debug purpose //$tmp_data["obs"] = "real data"; //Add order to avoid usort missorder in same utimestamp data cells @@ -800,16 +801,16 @@ function db_uncompress_module_data($id_agente_modulo, $tstart = false, $tend = f $current_raw_data = null; } } - + //unknown $data_slices = $return[$pool_id]["data"]; if(!$flag_async){ while ( ($current_unknown != null) && ( ( ($current_unknown['time_from'] != null) && - ($current_timestamp_end >= $current_unknown['time_from']) ) || + ($current_timestamp_end >= $current_unknown['time_from']) ) || ($current_timestamp_end >= $current_unknown['time_to']) ) ) { - if( ( $current_timestamp <= $current_unknown['time_from']) && + if( ( $current_timestamp <= $current_unknown['time_from']) && ( $current_timestamp_end >= $current_unknown['time_from'] ) ){ if (count($return[$pool_id]['data']) == 0) { //insert first slice data @@ -833,7 +834,7 @@ function db_uncompress_module_data($id_agente_modulo, $tstart = false, $tend = f array_push($return[$pool_id]["data"], $tmp_data); $current_unknown["time_from"] = null; } - elseif( ($current_timestamp <= $current_unknown['time_to']) && + elseif( ($current_timestamp <= $current_unknown['time_to']) && ($current_timestamp_end > $current_unknown['time_to'] ) ){ if (count($return[$pool_id]['data']) == 0) { @@ -862,7 +863,7 @@ function db_uncompress_module_data($id_agente_modulo, $tstart = false, $tend = f } $i--; } - + // debug purpose //$tmp_data["obs"] = "event data unknown to"; array_push($return[$pool_id]["data"], $tmp_data); @@ -883,7 +884,7 @@ function db_uncompress_module_data($id_agente_modulo, $tstart = false, $tend = f if (count($return[$pool_id]['data']) == 0) { //insert first slice data $tmp_data["utimestamp"] = $current_timestamp; - $tmp_data["datos"] = $last_inserted_value; + $tmp_data["datos"] = $last_inserted_value; // debug purpose //$tmp_data["obs"] = "virtual data (empty)"; array_push($return[$pool_id]["data"], $tmp_data); @@ -902,7 +903,7 @@ function db_uncompress_module_data($id_agente_modulo, $tstart = false, $tend = f //put the last slice data like first element of next slice $last_inserted_value = end($return[$pool_id]['data']); $last_inserted_value = $last_inserted_value['datos']; - + //increment $pool_id++; $current_timestamp = $current_timestamp_end; @@ -917,8 +918,6 @@ function db_uncompress_module_data($id_agente_modulo, $tstart = false, $tend = f array_push($return, $end_array); } - // html_debug_print($return); - return $return; } @@ -948,7 +947,7 @@ function db_uncompress_module_data($id_agente_modulo, $tstart = false, $tend = f */ function db_get_all_rows_filter($table, $filter = array(), $fields = false, $where_join = 'AND', $search_history_db = false, $returnSQL = false) { global $config; - + switch ($config["dbtype"]) { case "mysql": return mysql_db_get_all_rows_filter($table, $filter, $fields, $where_join, $search_history_db, $returnSQL); diff --git a/pandora_console/include/functions_forecast.php b/pandora_console/include/functions_forecast.php index ceafe817e2..f58e3592e7 100644 --- a/pandora_console/include/functions_forecast.php +++ b/pandora_console/include/functions_forecast.php @@ -46,12 +46,15 @@ function forecast_projection_graph($module_id, } $begin_time = time(); - - $module_data=grafico_modulo_sparse ($module_id, $period, 0, - 300, 300 , '', null, - false, 0, false, - 0, '', 0, 1, false, - true, '', 1, true); + + $params =array( + 'agent_module_id' => $module_id, + 'period' => $period, + 'return_data' => 1, + 'projection' => true + ); + + $module_data = grafico_modulo_sparse ($params); if (empty($module_data)) { return array(); @@ -62,55 +65,57 @@ function forecast_projection_graph($module_id, } // Data initialization - $sum_obs = 0; - $sum_xi = 0; - $sum_yi = 0; - $sum_xi_yi = 0; - $sum_xi2 = 0; - $sum_yi2 = 0; + $sum_obs = 0; + $sum_xi = 0; + $sum_yi = 0; + $sum_xi_yi = 0; + $sum_xi2 = 0; + $sum_yi2 = 0; $sum_diff_dates = 0; $last_timestamp = get_system_time(); $agent_interval = SECONDS_5MINUTES; - $cont = 1; - $data = array(); + $cont = 1; + $data = array(); //$table->data = array(); // Creates data for calculation if (is_array($module_data) || is_object($module_data)) { - foreach ($module_data as $utimestamp => $row) { - if ($utimestamp == '') { + foreach ($module_data['sum1']['data'] as $key => $row) { + if ($row[0] == '') { continue; } - + + $row[0] = $row[0] / 1000; + $data[0] = ''; $data[1] = $cont; - $data[2] = date($config["date_format"], $utimestamp); - $data[3] = $utimestamp; - $data[4] = $row['sum']; - $data[5] = $utimestamp * $row['sum']; - $data[6] = $utimestamp * $utimestamp; - $data[7] = $row['sum'] * $row['sum']; + $data[2] = date($config["date_format"], $row[0]); + $data[3] = $row[0]; + $data[4] = $row[1]; + $data[5] = $row[0] * $row[1]; + $data[6] = $row[0] * $row[0]; + $data[7] = $row[1] * $row[1]; if ($cont == 1) { $data[8] = 0; } else { - $data[8] = $utimestamp - $last_timestamp; + $data[8] = $row[0] - $last_timestamp; } - - $sum_obs = $sum_obs + $cont; - $sum_xi = $sum_xi + $utimestamp; - $sum_yi = $sum_yi + $row['sum']; - $sum_xi_yi = $sum_xi_yi + $data[5]; - $sum_xi2 = $sum_xi2 + $data[6]; - $sum_yi2 = $sum_yi2 + $data[7]; + + $sum_obs = $sum_obs + $cont; + $sum_xi = $sum_xi + $row[0]; + $sum_yi = $sum_yi + $row[1]; + $sum_xi_yi = $sum_xi_yi + $data[5]; + $sum_xi2 = $sum_xi2 + $data[6]; + $sum_yi2 = $sum_yi2 + $data[7]; $sum_diff_dates = $sum_diff_dates + $data[8]; - $last_timestamp = $utimestamp; + $last_timestamp = $row[0]; $cont++; } } - + $cont--; - + // Calculation over data above: // 1. Calculation of linear correlation coefficient... @@ -121,15 +126,6 @@ function forecast_projection_graph($module_id, // 3.2 Standard deviation for Y: sqrt((Sum(Yiยฒ)/Obs) - (avg Y)ยฒ) // Linear correlation coefficient: - if ($sum_xi != 0) { - $avg_x = $cont/$sum_xi; - } else { - $avg_x = 0; - } - if ($sum_yi != 0) - $avg_y = $cont/$sum_yi; - else - $avg_y = 0; /* if ($cont != 0) { @@ -229,18 +225,17 @@ function forecast_projection_graph($module_id, } } - $timestamp_f = date($time_format, $current_ts); - - //$timestamp_f = date($time_format, $current_ts); - $timestamp_f = graph_get_formatted_date($current_ts, $time_format, $time_format_2); - + $timestamp_f = $current_ts * 1000; + if ($csv) { $output_data[$idx]['date'] = $current_ts; $output_data[$idx]['data'] = ($a + ($b * $current_ts)); } else { - $output_data[$timestamp_f] = ($a + ($b * $current_ts)); + $output_data[$idx][0] = $timestamp_f; + $output_data[$idx][1] = ($a + ($b * $current_ts)); } + // Using this function for prediction_date if ($prediction_period == false) { // These statements stop the prediction when interval is greater than 2 years @@ -249,7 +244,7 @@ function forecast_projection_graph($module_id, } // Found it - if ($max_value >= $output_data[$timestamp_f] and $min_value <= $output_data[$timestamp_f]) { + if ($max_value >= $output_data[$idx][0] and $min_value <= $output_data[$idx][0]) { return $current_ts; } } @@ -259,7 +254,7 @@ function forecast_projection_graph($module_id, $current_ts = $current_ts + $agent_interval; $idx++; } - + return $output_data; } diff --git a/pandora_console/include/functions_graph.php b/pandora_console/include/functions_graph.php index 0c4a00e961..efa479df62 100644 --- a/pandora_console/include/functions_graph.php +++ b/pandora_console/include/functions_graph.php @@ -22,27 +22,27 @@ include_once($config['homedir'] . "/include/functions_users.php"); function get_graph_statistics ($chart_array) { global $config; - + /// IMPORTANT! /// /// The calculus for AVG, MIN and MAX values are in this function - /// because it must be done based on graph array data not using reporting + /// because it must be done based on graph array data not using reporting /// function to get coherent data between stats and graph visualization - + $stats = array (); - + $count = 0; - + $size = sizeof($chart_array); - + //Initialize stats array $stats = array ("avg" => 0, "min" => null, "max" => null, "last" => 0); - + foreach ($chart_array as $item) { - + //Sum all values later divide by the number of elements $stats['avg'] = $stats['avg'] + $item; - + //Get minimum if ($stats['min'] == null) { $stats['min'] = $item; @@ -50,7 +50,7 @@ function get_graph_statistics ($chart_array) { else if ($item < $stats['min']) { $stats['min'] = $item; } - + //Get maximum if ($stats['max'] == null) { $stats['max'] = $item; @@ -58,49 +58,49 @@ function get_graph_statistics ($chart_array) { else if ($item > $stats['max']) { $stats['max'] = $item; } - + $count++; - + //Get last data if ($count == $size) { $stats['last'] = $item; } } - + //End the calculus for average if ($count > 0) { - + $stats['avg'] = $stats['avg'] / $count; } - + //Format stat data to display properly $stats['last'] = remove_right_zeros(number_format($stats['last'], $config['graph_precision'])); $stats['avg'] = remove_right_zeros(number_format($stats['avg'], $config['graph_precision'])); $stats['min'] = remove_right_zeros(number_format($stats['min'], $config['graph_precision'])); $stats['max'] = remove_right_zeros(number_format($stats['max'], $config['graph_precision'])); - + return $stats; } function get_statwin_graph_statistics ($chart_array, $series_suffix = '') { - + /// IMPORTANT! /// /// The calculus for AVG, MIN and MAX values are in this function - /// because it must be done based on graph array data not using reporting + /// because it must be done based on graph array data not using reporting /// function to get coherent data between stats and graph visualization - + $stats = array (); - + $count = 0; - + $size = sizeof($chart_array); - + //Initialize stats array $stats['sum'] = array ("avg" => 0, "min" => null, "max" => null, "last" => 0); $stats['min'] = array ("avg" => 0, "min" => null, "max" => null, "last" => 0); $stats['max'] = array ("avg" => 0, "min" => null, "max" => null, "last" => 0); - + foreach ($chart_array as $item) { if ($series_suffix != '') { if (isset($item['sum' . $series_suffix])) @@ -110,13 +110,13 @@ function get_statwin_graph_statistics ($chart_array, $series_suffix = '') { if (isset($item['max' . $series_suffix])) $item['max'] = $item['max' . $series_suffix]; } - + //Get stats for normal graph if (isset($item['sum']) && $item['sum']) { - + //Sum all values later divide by the number of elements $stats['sum']['avg'] = $stats['sum']['avg'] + $item['sum']; - + //Get minimum if ($stats['sum']['min'] == null) { $stats['sum']['min'] = $item['sum']; @@ -124,7 +124,7 @@ function get_statwin_graph_statistics ($chart_array, $series_suffix = '') { else if ($item['sum'] < $stats['sum']['min']) { $stats['sum']['min'] = $item['sum']; } - + //Get maximum if ($stats['sum']['max'] == null) { $stats['sum']['max'] = $item['sum']; @@ -132,14 +132,13 @@ function get_statwin_graph_statistics ($chart_array, $series_suffix = '') { else if ($item['sum'] > $stats['sum']['max']) { $stats['sum']['max'] = $item['sum']; } - } - + //Get stats for min graph if (isset($item['min']) && $item['min']) { //Sum all values later divide by the number of elements $stats['min']['avg'] = $stats['min']['avg'] + $item['min']; - + //Get minimum if ($stats['min']['min'] == null) { $stats['min']['min'] = $item['min']; @@ -147,7 +146,7 @@ function get_statwin_graph_statistics ($chart_array, $series_suffix = '') { else if ($item['min'] < $stats['min']['min']) { $stats['min']['min'] = $item['min']; } - + //Get maximum if ($stats['min']['max'] == null) { $stats['min']['max'] = $item['min']; @@ -155,14 +154,13 @@ function get_statwin_graph_statistics ($chart_array, $series_suffix = '') { else if ($item['min'] > $stats['min']['max']) { $stats['min']['max'] = $item['min']; } - } - + //Get stats for max graph if (isset($item['max']) && $item['max']) { //Sum all values later divide by the number of elements $stats['max']['avg'] = $stats['max']['avg'] + $item['max']; - + //Get minimum if ($stats['max']['min'] == null) { $stats['max']['min'] = $item['max']; @@ -170,7 +168,7 @@ function get_statwin_graph_statistics ($chart_array, $series_suffix = '') { else if ($item['max'] < $stats['max']['min']) { $stats['max']['min'] = $item['max']; } - + //Get maximum if ($stats['max']['max'] == null) { $stats['max']['max'] = $item['max']; @@ -179,906 +177,913 @@ function get_statwin_graph_statistics ($chart_array, $series_suffix = '') { $stats['max']['max'] = $item['max']; } } - - + //Count elements $count++; - + //Get last data if ($count == $size) { if (isset($item['sum']) && $item['sum']) { $stats['sum']['last'] = $item['sum']; } - + if (isset($item['min']) && $item['min']) { $stats['min']['last'] = $item['min']; } - + if (isset($item['max']) && $item['max']) { $stats['max']['last'] = $item['max']; } } } - + //End the calculus for average if ($count > 0) { - + $stats['sum']['avg'] = $stats['sum']['avg'] / $count; $stats['min']['avg'] = $stats['min']['avg'] / $count; $stats['max']['avg'] = $stats['max']['avg'] / $count; } - + //Format stat data to display properly $stats['sum']['last'] = round($stats['sum']['last'], 2); $stats['sum']['avg'] = round($stats['sum']['avg'], 2); $stats['sum']['min'] = round($stats['sum']['min'], 2); $stats['sum']['max'] = round($stats['sum']['max'], 2); - + $stats['min']['last'] = round($stats['min']['last'], 2); $stats['min']['avg'] = round($stats['min']['avg'], 2); $stats['min']['min'] = round($stats['min']['min'], 2); $stats['min']['max'] = round($stats['min']['max'], 2); - + $stats['max']['last'] = round($stats['max']['last'], 2); $stats['max']['avg'] = round($stats['max']['avg'], 2); $stats['max']['min'] = round($stats['max']['min'], 2); $stats['max']['max'] = round($stats['max']['max'], 2); - + return $stats; } -function grafico_modulo_sparse_data_chart (&$chart, &$chart_data_extra, &$long_index, - $data, $data_i, $previous_data, $resolution, $interval, $period, $datelimit, - $projection, $avg_only = false, $uncompressed_module = false, - $show_events = false, $show_alerts = false, $show_unknown = false, $baseline = false, - $baseline_data = array(), $events = array(), $series_suffix = '', $start_unknown = false, - $percentil = null, $fullscale = false, $force_interval = false,$time_interval = 300, - $max_only = 0, $min_only = 0) { +function grafico_modulo_sparse_data_chart ( + $agent_module_id, + $date_array, + $data_module_graph, + $params, + $series_suffix + ) { + global $config; - global $chart_extra_data; - global $series_type; - global $max_value; - global $min_value; - $max_value = 0; - $min_value = null; - $flash_chart = $config['flash_charts']; - - // Event iterator - $event_i = 0; - - // Calculate chart data - $last_known = $previous_data; - - $first_events_unknown = $start_unknown; + if( $data_module_graph['id_module_type'] == 23 || + $data_module_graph['id_module_type'] == 3 || + $data_module_graph['id_module_type'] == 17 || + $data_module_graph['id_module_type'] == 10 || + $data_module_graph['id_module_type'] == 33 ){ - for ($i = 0; $i <= $resolution; $i++) { - $timestamp = $datelimit + ($interval * $i); +//XXXXXXXXXXX SLICES +/* +"SELECT count(*) as data, min(utimestamp) as utimestamp + FROM tagente_datos_string + WHERE id_agente_modulo = 227 + AND utimestamp > 1527584831 + AND utimestamp < 1527671231 + GROUP by ROUND(utimestamp / 300);" +*/ + $data = db_get_all_rows_filter ( + 'tagente_datos_string', + array ('id_agente_modulo' => (int)$agent_module_id, + "utimestamp > '". $date_array['start_date']. "'", + "utimestamp < '". $date_array['final_date'] . "'", + 'group' => "ROUND(utimestamp / 300)", + 'order' => 'utimestamp ASC'), + array ('count(*) as datos', 'min(utimestamp) as utimestamp'), + 'AND', + $data_module_graph['history_db'] + ); + } + else{ + /* + if(true){ + $data = db_get_all_rows_filter ( + 'tagente_datos', + array ('id_agente_modulo' => (int)$agent_module_id, + "utimestamp > '". $date_array['start_date']. "'", + "utimestamp < '". $date_array['final_date'] . "'", + 'group' => "ROUND(utimestamp / 86400)", + 'order' => 'utimestamp ASC'), + array ('max(datos) as datos', 'min(utimestamp) as utimestamp'), + 'AND', + $data_module_graph['history_db'] + ); + } + else{ + */ + $data = db_get_all_rows_filter ( + 'tagente_datos', + array ('id_agente_modulo' => (int)$agent_module_id, + "utimestamp > '". $date_array['start_date']. "'", + "utimestamp < '". $date_array['final_date'] . "'", + 'order' => 'utimestamp ASC'), + array ('datos', 'utimestamp'), + 'AND', + $data_module_graph['history_db'] + ); + //} + } - $total = 0; - $count = 0; - - // Read data that falls in the current interval - $interval_min = false; - $interval_max = false; + if($data === false){ + $data = array(); + } - while (isset ($data[$data_i]) && $data[$data_i]['utimestamp'] >= $timestamp - && $data[$data_i]['utimestamp'] < ($timestamp + $interval)) { - if ($interval_min === false) { - $interval_min = $data[$data_i]['datos']; - } - if ($interval_max === false) { - $interval_max = $data[$data_i]['datos']; - } - - if ($data[$data_i]['datos'] > $interval_max) { - $interval_max = $data[$data_i]['datos']; - } - else if ($data[$data_i]['datos'] < $interval_min) { - $interval_min = $data[$data_i]['datos']; - } - - $total += $data[$data_i]['datos']; - $last_known = $data[$data_i]['datos']; - $count++; - $data_i++; + // Get previous data + $previous_data = modules_get_previous_data ( + $agent_module_id, + $date_array['start_date'] + ); + + if ($previous_data !== false) { + $previous_data['utimestamp'] = $date_array['start_date']; + unset($previous_data['id_agente_modulo']); + array_unshift ($data, $previous_data); + } + + // Get next data + $nextData = modules_get_next_data ( + $agent_module_id, + $date_array['final_date'] + ); + + if ($nextData !== false) { + unset($nextData['id_agente_modulo']); + array_push ($data, $nextData); + } + else if (count ($data) > 0) { + // Propagate the last known data to the end of the interval + $nextData = array( + 'datos' => $data[count($data)-1]['datos'], + 'utimestamp' => $date_array['final_date'], + ); + array_push ($data, $nextData); + } + + // Check available data + if (count ($data) < 1) { + //return fs_error_image (); + return false; + } + + $array_data = array(); + $min_value = PHP_INT_MAX-1; + $max_value = PHP_INT_MIN+1; + $array_percentil = array(); + + foreach ($data as $k => $v) { + //convert array + if($params['flag_overlapped']){ + $array_data["sum" . $series_suffix]['data'][$k] = array( + ($v['utimestamp'] + $date_array['period'] )* 1000, + $v['datos'] + ); } - - if ($max_value < $interval_max) { - $max_value = $interval_max; - } - - if ($min_value > $interval_max || $min_value == null) { - $min_value = $interval_max; - } - - // Data in the interval - if ($count > 0) { - $total /= $count; - // If detect data, unknown period finishes - $is_unknown = false; - } - - // Read events and alerts that fall in the current interval - $event_value = 0; - $alert_value = 0; - $unknown_value = 0; - // Is the first point of a unknown interval - $check_unknown = false; - $first_unknown = false; - if($first_events_unknown){ - $is_unknown = true; + else{ + $array_data["sum" . $series_suffix]['data'][$k] = array( + $v['utimestamp'] * 1000, + $v['datos'] + ); } - $event_ids = array(); - $alert_ids = array(); - - while (isset ($events[$event_i]) && $events[$event_i]['utimestamp'] >= $timestamp - && $events[$event_i]['utimestamp'] <= ($timestamp + $interval)) { - if ($show_events == 1) { - $event_value++; - $event_ids[] = $events[$event_i]['id_evento']; - } - if ($show_alerts == 1 && substr ($events[$event_i]['event_type'], 0, 5) == 'alert') { - $alert_value++; - $alert_ids[] = $events[$event_i]['id_evento']; - } - if ($show_unknown) { - if ($events[$event_i]['event_type'] == 'going_unknown') { - if ($is_unknown == false) { - $first_unknown = true; + //min + if($min_value > $v['datos']){ + $min_value = $v['datos']; + } + + //max + if($max_value < $v['datos']){ + $max_value = $v['datos']; + } + + //avg + $sum_data += $v['datos']; + $count_data++; + + //percentil + if (!is_null($params['percentil']) && $params['percentil']) { + $array_percentil[] = $v['datos']; + } + } + + $array_data["sum" . $series_suffix]['min'] = $min_value; + $array_data["sum" . $series_suffix]['max'] = $max_value; + $array_data["sum" . $series_suffix]['avg'] = $sum_data/$count_data; + $array_data["sum" . $series_suffix]['agent_module_id']= $agent_module_id; + $array_data["sum" . $series_suffix]['id_module_type'] = $data_module_graph['id_module_type']; + $array_data["sum" . $series_suffix]['agent_name'] = $data_module_graph['agent_name']; + $array_data["sum" . $series_suffix]['module_name'] = $data_module_graph['module_name']; + $array_data["sum" . $series_suffix]['agent_alias'] = $data_module_graph['agent_alias']; + + if (!is_null($params['percentil']) && + $params['percentil'] && + !$params['flag_overlapped']) { + $percentil_result = get_percentile($params['percentil'], $array_percentil); + $array_data["percentil" . $series_suffix]['data'][0] = array( + $date_array['start_date'] * 1000, + $percentil_result + ); + $array_data["percentil" . $series_suffix]['data'][1] = array( + $date_array['final_date'] * 1000, + $percentil_result + ); + $array_data["percentil" . $series_suffix]['agent_module_id'] = $agent_module_id; + } + return $array_data; +} + +function grafico_modulo_sparse_data( + $agent_module_id, $date_array, + $data_module_graph, $params, + $series_suffix) { + + global $config; + global $array_events_alerts; + + if($params['fullscale']){ + $array_data = fullscale_data( + $agent_module_id, + $date_array, + $params['show_unknown'], + $params['percentil'], + $series_suffix, + $params['flag_overlapped'] + ); + + $array_data["sum" . $series_suffix]['agent_module_id']= $agent_module_id; + $array_data["sum" . $series_suffix]['id_module_type'] = $data_module_graph['id_module_type']; + $array_data["sum" . $series_suffix]['agent_name'] = $data_module_graph['agent_name']; + $array_data["sum" . $series_suffix]['module_name'] = $data_module_graph['module_name']; + $array_data["sum" . $series_suffix]['agent_alias'] = $data_module_graph['agent_alias']; + } + else{ + $array_data = grafico_modulo_sparse_data_chart ( + $agent_module_id, + $date_array, + $data_module_graph, + $params, + $series_suffix + ); + } + + if($array_data === false){ + return false; + } + + //XXX Esto es para un tipo especifico de report que consiste en pasarle un intervalo y hacer suma media y avg. + if($params['force_interval'] != ''){ + $period_time_interval = $date_array['period'] * 1000; + $start_period = $date_array['start_date'] * 1000; + $i = 0; + + $sum_data = 0; + $count_data = 0; + $data_last_acum = $array_data['sum1']['data'][0][1]; + + while($period_time_interval > 0) { + foreach ($array_data['sum1']['data'] as $key => $value) { + if($value[0] >= $start_period && $value[0] < $start_period + $params['time_interval'] * 1000){ + $sum_data = $value[1]; + $array_data_only[] = $value[1]; + $count_data++; + unset($array_data['sum1']['data'][$key]); + } + else{ + if($params['force_interval'] == 'max_only'){ + $acum_array_data[$i][0] = $start_period; + if(is_array($array_data_only) && count($array_data_only) > 0){ + $acum_array_data[$i][1] = max($array_data_only); + $data_last_acum = $array_data_only[count($array_data_only) - 1]; + } + else{ + $acum_array_data[$i][1] = $data_last_acum; + } } - $is_unknown = true; - $check_unknown = true; - } - else if (substr ($events[$event_i]['event_type'], 0, 5) == 'going') { - $first_events_unknown = false; - $first_unknown = false; - $is_unknown = false; + + if($params['force_interval'] == 'min_only'){ + $acum_array_data[$i][0] = $start_period; + if(is_array($array_data_only) && count($array_data_only) > 0){ + $acum_array_data[$i][1] = min($array_data_only); + $data_last_acum = $array_data_only[count($array_data_only) - 1]; + } + else{ + $acum_array_data[$i][1] = $data_last_acum; + } + } + + if($params['force_interval'] == 'avg_only'){ + $acum_array_data[$i][0] = $start_period; + if(is_array($array_data_only) && count($array_data_only) > 0){ + $acum_array_data[$i][1] = $sum_data / $count_data; + } + else{ + $acum_array_data[$i][1] = $data_last_acum; + } + } + + $start_period = $start_period + $params['time_interval'] * 1000; + $array_data_only = array(); + $sum_data = 0; + $count_data = 0; + $i++; + break; } } - $event_i++; + $period_time_interval = $period_time_interval - $params['time_interval']; } - - // In some cases, can be marked as known because a recovery event - // was found in same interval. For this cases first_unknown is - // checked too - if ($is_unknown || $first_unknown) { - $unknown_value++; - } - - if (!$flash_chart) { - // Set the title and time format - if ($period <= SECONDS_6HOURS) { - $time_format = 'H:i:s'; - } - elseif ($period < SECONDS_1DAY) { - $time_format = 'H:i'; - } - elseif ($period < SECONDS_15DAYS) { - $time_format = "M \nd H:i"; - } - elseif ($period < SECONDS_1MONTH) { - $time_format = "M \nd H\h"; - } - elseif ($period < SECONDS_6MONTHS) { - $time_format = "M \nd H\h"; - } - else { - $time_format = "Y M \nd H\h"; + $array_data['sum1']['data'] = $acum_array_data; + } + + if($params['percentil']){ + $percentil_value = $array_data['percentil' . $series_suffix]['data'][0][1]; + } + else{ + $percentil_value = 0; + } + + $events = array(); + if(isset($array_data['sum' . $series_suffix]['max'])){ + $max = $array_data['sum'. $series_suffix]['max']; + $min = $array_data['sum'. $series_suffix]['min']; + $avg = $array_data['sum'. $series_suffix]['avg']; + } + + if(!$params['flag_overlapped']){ + if($params['fullscale']){ + if( $params['show_unknown'] && + isset($array_data['unknown' . $series_suffix]) && + is_array($array_data['unknown' . $series_suffix]['data']) ){ + foreach ($array_data['unknown' . $series_suffix]['data'] as $key => $s_date) { + if ($s_date[1] == 1) { + $array_data['unknown' . $series_suffix]['data'][$key] = array($s_date[0], $max * 1.05); + } + } } } - else { - // Set the title and time format - if ($period <= SECONDS_6HOURS) { - $time_format = 'H:i:s'; - } - elseif ($period < SECONDS_1DAY) { - $time_format = 'H:i'; - } - elseif ($period < SECONDS_15DAYS) { - $time_format = "M d H:i"; - } - elseif ($period < SECONDS_1MONTH) { - $time_format = "M d H\h"; - } - elseif ($period < SECONDS_6MONTHS) { - $time_format = "M d H\h"; - } - else { - $time_format = "Y M d H\h"; + else{ + if( $params['show_unknown'] ) { + $unknown_events = db_get_module_ranges_unknown( + $agent_module_id, + $date_array['start_date'], + $date_array['final_date'], + $data_module_graph['history_db'], + 1 // fix the time ranges to start_date - final_date + ); + + if($unknown_events !== false){ + foreach ($unknown_events as $key => $s_date) { + if( isset($s_date['time_from'])) { + $array_data['unknown' . $series_suffix]['data'][] = array( + ($s_date['time_from'] - 1) * 1000, + 0 + ); + + $array_data['unknown' . $series_suffix]['data'][] = array( + $s_date['time_from'] * 1000, + $max * 1.05 + ); + } + else{ + $array_data['unknown' . $series_suffix]['data'][] = array( + $date_array['start_date'] * 1000, + $max * 1.05 + ); + } + + if( isset($s_date['time_to']) ){ + $array_data['unknown' . $series_suffix]['data'][] = array( + $s_date['time_to'] * 1000, + $max * 1.05 + ); + + $array_data['unknown' . $series_suffix]['data'][] = array( + ($s_date['time_to'] + 1) * 1000, + 0 + ); + } + else{ + $array_data['unknown' . $series_suffix]['data'][] = array( + $date_array['final_date'] * 1000, + $max * 1.05 + ); + } + } + } } } - - $timestamp_short = date($time_format, $timestamp); - $long_index[$timestamp_short] = date( - html_entity_decode($config['date_format'], ENT_QUOTES, "UTF-8"), $timestamp); - if (!$projection) { - if (!$fullscale) { - $timestamp = $timestamp_short; + + if ($params['show_events'] || + $params['show_alerts'] ) { + + $events = db_get_all_rows_filter ( + 'tevento', + array ('id_agentmodule' => $agent_module_id, + "utimestamp > " . $date_array['start_date'], + "utimestamp < " . $date_array['final_date'], + 'order' => 'utimestamp ASC' + ), + false, + 'AND', + $data_module_graph['history_db'] + ); + + $alerts_array = array(); + $events_array = array(); + + if($events && is_array($events)){ + $count_events=0; + $count_alerts=0; + foreach ($events as $k => $v) { + if (strpos($v["event_type"], "alert") !== false){ + if($params['flag_overlapped']){ + $alerts_array['data'][$count_alerts] = array( + ($v['utimestamp'] + $date_array['period'] *1000), + $max * 1.10 + ); + } + else{ + $alerts_array['data'][$count_alerts] = array( + ($v['utimestamp']*1000), + $max * 1.10 + ); + } + $count_alerts++; + } + else{ + if($params['flag_overlapped']){ + if( ( strstr($v['event_type'], 'going_up') ) || + ( strstr($v['event_type'], 'going_down') ) ){ + $events_array['data'][$count_events] = array( + (($v['utimestamp'] + 1 + $date_array['period']) * 1000), + $max * 1.15 + ); + } + else{ + $events_array['data'][$count_events] = array( + ($v['utimestamp'] + $date_array['period'] *1000), + $max * 1.15 + ); + } + } + else{ + if( ( strstr($v['event_type'], 'going_up') ) || + ( strstr($v['event_type'], 'going_down') ) ){ + $events_array['data'][$count_events] = array( + (($v['utimestamp'] + 1) *1000), + $max * 1.15 + ); + } + else{ + $events_array['data'][$count_events] = array( + ($v['utimestamp']*1000), + $max * 1.15 + ); + } + } + $count_events++; + } + } } } - - // Data - if ($show_events) { - if (!isset($chart[$timestamp]['event'.$series_suffix])) { - $chart[$timestamp]['event'.$series_suffix] = 0; - } - - $chart[$timestamp]['event'.$series_suffix] += $event_value; - $series_type['event'.$series_suffix] = 'points'; + + if($params['show_events']){ + $array_data['event' . $series_suffix] = $events_array; } - if ($show_alerts) { - if (!isset($chart[$timestamp]['alert'.$series_suffix])) { - $chart[$timestamp]['alert'.$series_suffix] = 0; - } - - $chart[$timestamp]['alert'.$series_suffix] += $alert_value; - $series_type['alert'.$series_suffix] = 'points'; + + if($params['show_alerts']){ + $array_data['alert' . $series_suffix] = $alerts_array; } - - if ($count > 0) { - - if ($avg_only) { - $chart[$timestamp]['sum'.$series_suffix] = $total; - } - else if($max_only){ - $chart[$timestamp]['max'.$series_suffix] = $interval_max; - } - else if($min_only){ - $chart[$timestamp]['min'.$series_suffix] = $interval_min; + } + + if ($params['return_data'] == 1) { + return $array_data; + } + + $array_events_alerts[$series_suffix] = $events; + + return $array_data; +} + +/* + $params =array( + 'agent_module_id' => $agent_module_id, + 'period' => $period, + 'show_events' => false, + 'width' => $width, + 'height' => $height, + 'title' => '', + 'unit_name' => null, + 'show_alerts' => false, + 'date' => 0, + 'unit' => '', + 'baseline' => 0, + 'return_data' => 0, + 'show_title' => true, + 'only_image' => false, + 'homeurl' => $config['homeurl'], + 'ttl' => 1, + 'adapt_key' => '', + 'compare' => false, + 'show_unknown' => false, + 'menu' => true, + 'backgroundColor' => 'white', + 'percentil' => null, + 'dashboard' => false, + 'vconsole' => false, + 'type_graph' => 'area', + 'fullscale' => false, + 'id_widget_dashboard' => false, + 'force_interval' => '', + 'time_interval' => 300, + 'array_data_create' => 0, + 'show_legend' => true, + 'show_overview' => true, + 'return_img_base_64' => false, + 'image_treshold' => false, + 'graph_combined' => false + ); + */ +function grafico_modulo_sparse ($params) { + global $config; + + /*XXXXXXXXXXXX Documnetar + *Set all variable + */ + + if(!isset($params) || !is_array($params)){ + return false; + } + + if(!isset($params['agent_module_id'])){ + return false; + } + else{ + $agent_module_id = $params['agent_module_id']; + } + + if(!isset($params['period'])){ + return false; + } + + if(!isset($params['show_events'])){ + $params['show_events'] = false; + } + + if(!isset($params['width'])){ + $params['width'] = '90%'; + } + + if(!isset($params['height'])){ + $params['height'] = 450; + } + + if(!isset($params['title'])){ + $params['title'] = ''; + } + + if(!isset($params['unit_name'])){ + $params['unit_name'] = null; + } + + if(!isset($params['show_alerts'])){ + $params['show_alerts'] = false; + } + + if(!isset($params['date']) || !$params['date']){ + $params['date'] = get_system_time(); + } + + if(!isset($params['unit'])){ + $params['unit'] = ''; + } + + if(!isset($params['baseline'])){ + $params['baseline'] = 0; + } + + if(!isset($params['return_data'])){ + $params['return_data'] = 0; + } + + if(!isset($params['show_title'])){ + $show_title = true; + } + + if(!isset($params['only_image'])){ + $params['only_image'] = false; + } + + if(!isset($params['homeurl'])){ + $params['homeurl'] = $config['homeurl']; + } + + if(!isset($params['ttl'])){ + $params['ttl'] = 1; + } + + if(!isset($params['adapt_key'])){ + $params['adapt_key'] = ''; + } + + if(!isset($params['compare'])){ + $params['compare'] = false; + } + + if(!isset($params['show_unknown'])){ + $params['show_unknown'] = false; + } + + if(!isset($params['menu'])){ + $params['menu'] = true; + } + + if(!isset($params['show_legend'])){ + $params['show_legend'] = true; + } + + if(!isset($params['show_overview'])){ + $params['show_overview'] = true; + } + + if(!isset($params['show_export_csv'])){ + $params['show_export_csv'] = true; + } + + if(!isset($params['backgroundColor'])){ + $params['backgroundColor'] = 'white'; + } + + if(!isset($params['percentil'])){ + $params['percentil'] = null; + } + + if(!isset($params['dashboard'])){ + $params['dashboard'] = false; + } + + if(!isset($params['vconsole']) || $params['vconsole'] == false){ + $params['vconsole'] = false; + } + else{ + $params['menu'] = false; + } + + if(!isset($params['type_graph'])){ + $params['type_graph'] = $config['type_module_charts']; + } + + if(!isset($params['fullscale'])){ + $params['fullscale'] = false; + } + + if(!isset($params['id_widget_dashboard'])){ + $params['id_widget_dashboard'] = false; + } + + if(!isset($params['force_interval'])){ + $params['force_interval'] = ''; + } + + if(!isset($params['time_interval'])){ + $params['time_interval'] = 300; + } + + if(!isset($params['array_data_create'])){ + $params['array_data_create'] = 0; + } + + if(!isset($params['return_img_base_64'])){ + $params['return_img_base_64'] = false; + } + + if(!isset($params['image_treshold'])){ + $params['image_treshold'] = false; + } + + if(!isset($params['graph_combined'])){ + $params['graph_combined'] = false; + } + + $params['font'] = $config['fontpath']; + $params['font-size'] = $config['font_size']; + + if($params['only_image']){ + return generator_chart_to_pdf('sparse', $params); + } + + global $graphic_type; + global $array_events_alerts; + + $array_data = array(); + $legend = array(); + $array_events_alerts = array(); + + + $date_array = array(); + $date_array["period"] = $params['period']; + $date_array["final_date"] = $params['date']; + $date_array["start_date"] = $params['date'] - $params['period']; + + if($agent_module_id){ + $module_data = db_get_row_sql ( + 'SELECT * FROM tagente_modulo + WHERE id_agente_modulo = ' . + $agent_module_id + ); + + $data_module_graph = array(); + $data_module_graph['history_db'] = db_search_in_history_db($date_array["start_date"]); + $data_module_graph['agent_name'] = modules_get_agentmodule_agent_name($agent_module_id); + $data_module_graph['agent_alias'] = modules_get_agentmodule_agent_alias($agent_module_id); + $data_module_graph['agent_id'] = $module_data['id_agente']; + $data_module_graph['module_name'] = $module_data['nombre']; + $data_module_graph['id_module_type'] = $module_data['id_tipo_modulo']; + $data_module_graph['module_type'] = modules_get_moduletype_name($data_module_graph['id_module_type']); + $data_module_graph['uncompressed'] = is_module_uncompressed($data_module_graph['module_type']); + $data_module_graph['w_min'] = $module_data['min_warning']; + $data_module_graph['w_max'] = $module_data['max_warning']; + $data_module_graph['w_inv'] = $module_data['warning_inverse']; + $data_module_graph['c_min'] = $module_data['min_critical']; + $data_module_graph['c_max'] = $module_data['max_critical']; + $data_module_graph['c_inv'] = $module_data['critical_inverse']; + } + else{ + $data_module_graph = false; + } + + //format of the graph + if (empty($params['unit'])) { + $params['unit'] = $module_data['unit']; + if(modules_is_unit_macro($params['unit'])){ + $params['unit'] = ""; + } + } + + if(!$params['array_data_create']){ + if ($params['compare'] !== false) { + $series_suffix = 2; + + $date_array_prev['final_date'] = $date_array['start_date']; + $date_array_prev['start_date'] = $date_array['start_date'] - $date_array['period']; + $date_array_prev['period'] = $date_array['period']; + + if ($params['compare'] === 'overlapped') { + $params['flag_overlapped'] = 1; } else{ - $chart[$timestamp]['max'.$series_suffix] = $interval_max; - $chart[$timestamp]['sum'.$series_suffix] = $total; - $chart[$timestamp]['min'.$series_suffix] = $interval_min; + $params['flag_overlapped'] = 0; } - // Compressed data - } - else { - if ($uncompressed_module || ($timestamp > time ())) { - if ($avg_only) { - $chart[$timestamp]['sum'.$series_suffix] = 0; - } - else if($max_only){ - $chart[$timestamp]['max'.$series_suffix] = 0; - } - else if($min_only){ - $chart[$timestamp]['min'.$series_suffix] = 0; - } - else{ - $chart[$timestamp]['max'.$series_suffix] = 0; - $chart[$timestamp]['sum'.$series_suffix] = 0; - $chart[$timestamp]['min'.$series_suffix] = 0; - } - } - else { - if ($avg_only) { - $chart[$timestamp]['sum'.$series_suffix] = $last_known; - } - else if ($max_only) { - $chart[$timestamp]['max'.$series_suffix] = $last_known; - } - else if ($min_only) { - $chart[$timestamp]['min'.$series_suffix] = $last_known; - } - else { - $chart[$timestamp]['max'.$series_suffix] = $last_known; - $chart[$timestamp]['sum'.$series_suffix] = $last_known; - $chart[$timestamp]['min'.$series_suffix] = $last_known; - } + + $array_data = grafico_modulo_sparse_data( + $agent_module_id, + $date_array_prev, + $data_module_graph, + $params, + $series_suffix + ); + + switch ($params['compare']) { + case 'separated': + case 'overlapped': + // Store the chart calculated + $array_data_prev = $array_data; + $legend_prev = $legend; + break; } } - if ($uncompressed_module || ($timestamp > time ())) { - if (!isset($chart[$timestamp]['no_data'.$series_suffix])) { - $chart[$timestamp]['no_data'.$series_suffix] = 0; - } - if ($chart[$timestamp]['sum'.$series_suffix] == $last_known) { - $chart[$timestamp]['no_data'.$series_suffix] = 0; - $series_type['no_data'.$series_suffix] = 'area'; - } - else { - if($uncompressed_module){ - $chart[$timestamp]['sum'.$series_suffix] = $last_known; - $series_type['sum'.$series_suffix] = 'area'; - } - else{ - $chart[$timestamp]['no_data'.$series_suffix] = $last_known; - $series_type['no_data'.$series_suffix] = 'area'; - } - } - } - - if ($show_unknown) { - if (!isset($chart[$timestamp]['unknown'.$series_suffix])) { - $chart[$timestamp]['unknown'.$series_suffix] = 0; - } - $chart[$timestamp]['unknown'.$series_suffix] = $unknown_value; + $series_suffix = 1; + $params['flag_overlapped'] = 0; - if($unknown_value == 0 && $check_unknown == true){ - $chart[$timestamp]['unknown'.$series_suffix] = 1; - $check_unknown = false; - } - - $series_type['unknown'.$series_suffix] = 'unknown'; - } - - if (!empty($event_ids)) { - $chart_extra_data[count($chart)-1]['events'] = implode(',',$event_ids); - } - if (!empty($alert_ids)) { - $chart_extra_data[count($chart)-1]['alerts'] = implode(',',$alert_ids); - } - } - - //min paint graph 2 elements - if(count($chart) == 1){ - $timestamp_short = date($time_format, $date_limit); - foreach($chart as $key => $value){ - $chart[$timestamp_short] = $value; - } - } - - if (!is_null($percentil) && $percentil) { - $avg = array_map(function($item) { return $item['sum'];}, $chart); - - $percentil_result = get_percentile($percentil, $avg); - - //Fill the data of chart - array_walk($chart, function(&$item) use ($percentil_result, $series_suffix) { - $item['percentil' . $series_suffix] = $percentil_result; }); - $series_type['percentil' . $series_suffix] = 'line'; - } -} - - -function grafico_modulo_sparse_data ($agent_module_id, $period, $show_events, - $width, $height , $title = '', $unit_name = null, - $show_alerts = false, $avg_only = 0, $date = 0, $unit = '', - $baseline = 0, $return_data = 0, $show_title = true, $projection = false, - $adapt_key = '', $compare = false, $series_suffix = '', $series_suffix_str = '', - $show_unknown = false, $percentil = null, $dashboard = false, $vconsole = false, - $type_graph='area', $fullscale = false, $flash_chart = false, $force_interval = false,$time_interval = 300, - $max_only = 0, $min_only = 0) { - - global $config; - global $chart; - global $color; - global $legend; - global $long_index; - global $series_type; - global $chart_extra_data; - global $warning_min; - global $critical_min; - global $graphic_type; - global $max_value; - global $min_value; - - $chart = array(); - $color = array(); - $legend = array(); - $long_index = array(); - $warning_min = 0; - $critical_min = 0; - $start_unknown = false; - - // Set variables - if ($date == 0) { - $date = get_system_time(); - } - - $datelimit = $date - $period; - - - $search_in_history_db = db_search_in_history_db($datelimit); - - if($force_interval){ - $resolution = $period/$time_interval; - } - else{ - $resolution = $config['graph_res'] * 50; //Number of points of the graph - } - - if($force_interval){ - $interval = $time_interval; - } - else{ - $interval = (int) ($period / $resolution); - - } - - $agent_name = modules_get_agentmodule_agent_name ($agent_module_id); - $agent_id = agents_get_agent_id ($agent_name); - $module_name = modules_get_agentmodule_name ($agent_module_id); - $id_module_type = modules_get_agentmodule_type ($agent_module_id); - $module_type = modules_get_moduletype_name ($id_module_type); - $uncompressed_module = is_module_uncompressed ($module_type); - if ($uncompressed_module) { - $avg_only = 1; - } - - $flash_chart = $config['flash_charts']; - - - // Get event data (contains alert data too) - $events = array(); - if ($show_unknown == 1 || $show_events == 1 || $show_alerts == 1) { - $events = db_get_all_rows_filter ( - 'tevento', - array ('id_agentmodule' => $agent_module_id, - "utimestamp > $datelimit", - "utimestamp < $date", - 'order' => 'utimestamp ASC'), - array ('id_evento', 'evento', 'utimestamp', 'event_type'), - 'AND', - $search_in_history_db - ); - - // Get the last event after inverval to know if graph start on unknown - $prev_event = db_get_row_filter ( - 'tevento', - array ('id_agentmodule' => $agent_module_id, - "utimestamp <= $datelimit", - 'order' => 'utimestamp DESC' - ), - false, - 'AND', - $search_in_history_db + $array_data = grafico_modulo_sparse_data( + $agent_module_id, + $date_array, + $data_module_graph, + $params, + $series_suffix ); - if (isset($prev_event['event_type']) && $prev_event['event_type'] == 'going_unknown') { - $start_unknown = true; - } - - if ($events === false) { - $events = array (); - } - } - - // Get module data - if ($fullscale) { - fullscale_data( $chart, $chart_data_extra, $long_index, $series_type, - $agent_module_id, $datelimit, $date, $events, - $show_events, $show_unknown, $show_alerts, - $series_suffix, $percentil, $flash_chart, false); - if (count($chart) > $resolution) { - $resolution = count($chart); //Number of points of the graph - $interval = (int) ($period / $resolution); - } - } - else { - $data = db_get_all_rows_filter ('tagente_datos', - array ('id_agente_modulo' => (int)$agent_module_id, - "utimestamp > $datelimit", - "utimestamp < $date", - 'order' => 'utimestamp ASC'), - array ('datos', 'utimestamp'), 'AND', $search_in_history_db); - - if ($data === false) { - $data = array (); - } - - if ($uncompressed_module) { - // Uncompressed module data - $min_necessary = 1; - } - else { - // Compressed module data - - // Get previous data - $previous_data = modules_get_previous_data ($agent_module_id, $datelimit); - if ($previous_data !== false) { - $previous_data['utimestamp'] = $datelimit; - array_unshift ($data, $previous_data); + if($params['compare']){ + if ($params['compare'] === 'overlapped') { + $array_data = array_merge($array_data, $array_data_prev); + $legend = array_merge($legend, $legend_prev); } - - // Get next data - $nextData = modules_get_next_data ($agent_module_id, $date); - if ($nextData !== false) { - array_push ($data, $nextData); - } - else if (count ($data) > 0) { - // Propagate the last known data to the end of the interval - $nextData = array_pop ($data); - array_push ($data, $nextData); - $nextData['utimestamp'] = $date; - array_push ($data, $nextData); - } - $min_necessary = 2; - } - - // Check available data - if (count ($data) < $min_necessary) { - if (!$graphic_type) { - if (!$projection) { - return fs_error_image (); - } - else { - return fs_error_image (); - } - } - graphic_error (); - } - - - // Data iterator - $data_i = 0; - - // Set initial conditions - if ($data[0]['utimestamp'] == $datelimit) { - $previous_data = $data[0]['datos']; - $data_i++; - } - else { - $previous_data = 0; - } - } - // Get baseline data - $baseline_data = array(); - if ($baseline) { - $baseline_data = array (); - if ($baseline == 1) { - $baseline_data = enterprise_hook( - 'reporting_enterprise_get_baseline', - array ($agent_module_id, $period, $width, $height , $title, $unit_name, $date)); - if ($baseline_data === ENTERPRISE_NOT_HOOK) { - $baseline_data = array (); - } - } - } - - if (empty($unit)) { - $unit = modules_get_unit($agent_module_id); - if(modules_is_unit_macro($unit)){ - $unit = ""; - } - } - - // Get module warning_min and critical_min - $warning_min = db_get_value('min_warning','tagente_modulo','id_agente_modulo',$agent_module_id); - $critical_min = db_get_value('min_critical','tagente_modulo','id_agente_modulo',$agent_module_id); - - // Calculate chart data - if($fullscale){ - $avg_only = 1; - - //Percentil - if (!is_null($percentil) && $percentil) { - $avg = array_map(function($item) { return $item['sum'];}, $chart); - - $percentil_result = get_percentile($percentil, $avg); - - //Fill the data of chart - array_walk($chart, function(&$item) use ($percentil_result, $series_suffix) { - $item['percentil' . $series_suffix] = $percentil_result; }); - $series_type['percentil' . $series_suffix] = 'line'; } } else{ - grafico_modulo_sparse_data_chart ($chart, $chart_data_extra, $long_index, - $data, $data_i, $previous_data, $resolution, $interval, $period, $datelimit, - $projection, $avg_only, $uncompressed_module, - $show_events, $show_alerts, $show_unknown, $baseline, - $baseline_data, $events, $series_suffix, $start_unknown, - $percentil, $fullscale, $force_interval, $time_interval, - $max_only, $min_only); + $array_data = $params['array_data_create']; } - // Return chart data and don't draw - if ($return_data == 1) { - return $chart; - } - - $graph_stats = get_statwin_graph_statistics($chart, $series_suffix); - // Fix event and alert scale - if ($max_value > 0) { - $event_max = 2 + (float)$max_value * 1.05; - } - else { - $event_max = abs(($max_value+$min_value)/2); - if ($event_max < 5) { - $event_max = 5; - } + if($params['return_data']){ + return $array_data; } - foreach ($chart as $timestamp => $chart_data) { - if($chart_data['max'] > $event_max){ - $event_max = $chart_data['max']; - } - if ($show_events && $chart_data['event' . $series_suffix] > 0) { - $chart[$timestamp]['event' . $series_suffix] = $event_max * 1.2; - } - if ($show_alerts && $chart_data['alert' . $series_suffix] > 0) { - $chart[$timestamp]['alert' . $series_suffix] = $event_max * 1.10; - } - if ($show_unknown && $chart_data['unknown' . $series_suffix] > 0) { - $chart[$timestamp]['unknown' . $series_suffix] = $event_max * 1.05; - } - } - - // Only show caption if graph is not small - if ($width > MIN_WIDTH_CAPTION && $height > MIN_HEIGHT) - //Flash chart - $caption = - __('Max. Value') . $series_suffix_str . ': ' . $graph_stats['sum']['max'] . ' ' . - __('Avg. Value') . $series_suffix_str . ': ' . $graph_stats['sum']['avg'] . ' ' . - __('Min. Value') . $series_suffix_str . ': ' . $graph_stats['sum']['min'] . ' ' . - __('Units. Value') . $series_suffix_str . ': ' . $unit; - else - $caption = array(); - - /////// - // Color commented not to restrict serie colors - if ($show_events) { - $color['event' . $series_suffix] = - array('border' => '#ff0000', 'color' => '#ff0000', - 'alpha' => CHART_DEFAULT_ALPHA); - } - if ($show_alerts) { - $color['alert' . $series_suffix] = - array('border' => '#ff7f00', 'color' => '#ff7f00', - 'alpha' => CHART_DEFAULT_ALPHA); - } - $color['no_data'.$series_suffix] = array( - 'border' => '#000000', 'color' => '#f2c40e', - 'alpha' => CHART_DEFAULT_ALPHA); - if ($show_unknown) { - $color['unknown' . $series_suffix] = - array('border' => '#999999', 'color' => '#999999', - 'alpha' => CHART_DEFAULT_ALPHA); - } - $color['max'.$series_suffix] = array( - 'border' => '#000000', 'color' => $config['graph_color3'], - 'alpha' => CHART_DEFAULT_ALPHA); - $color['sum'.$series_suffix] = array( - 'border' => '#000000', 'color' => $config['graph_color2'], - 'alpha' => CHART_DEFAULT_ALPHA); - $color['min'.$series_suffix] = array( - 'border' => '#000000', 'color' => $config['graph_color1'], - 'alpha' => CHART_DEFAULT_ALPHA); + $series_type_array = series_type_graph_array( + $array_data, + $params + ); - $color['unit'.$series_suffix] = array('border' => null, 'color' => '#0097BC', 'alpha' => 10); - if(modules_is_unit_macro($unit)){ - $unit = ""; - } - if ($show_events) { - $legend['event'.$series_suffix_str] = __('Events').$series_suffix_str; - $chart_extra_data['legend_events'] = $legend['event'.$series_suffix_str]; - } - if ($show_alerts) { - $legend['alert'.$series_suffix] = __('Alerts').$series_suffix_str; - $chart_extra_data['legend_alerts'] = $legend['alert'.$series_suffix_str]; - } - - if ($vconsole) { - $legend['sum'.$series_suffix] = - __('Last') . ': ' . remove_right_zeros(number_format($graph_stats['sum']['last'], $config['graph_precision'])) . ($unit ? ' ' . $unit : '') . ' ; ' - . __('Avg') . ': ' . remove_right_zeros(number_format($graph_stats['sum']['avg'], $config['graph_precision'])) . ($unit ? ' ' . $unit : ''); - } - else if ($dashboard && !$avg_only) { - $legend['max'.$series_suffix] = __('Max').$series_suffix_str.': '.__('Avg').': '.remove_right_zeros(number_format($graph_stats['max']['avg'], $config['graph_precision'])).' '.$unit.' ; '.__('Max').': '.remove_right_zeros(number_format($graph_stats['max']['max'], $config['graph_precision'])).' '.$unit.' ; '.__('Min').': '.remove_right_zeros(number_format($graph_stats['max']['min'], $config['graph_precision'])).' '.$unit; - $legend['sum'.$series_suffix] = __('Avg').$series_suffix_str.': '.__('Avg').': '.remove_right_zeros(number_format($graph_stats['sum']['avg'], $config['graph_precision'])).' '.$unit.' ; '.__('Max').': '.remove_right_zeros(number_format($graph_stats['sum']['max'], $config['graph_precision'])).' '.$unit.' ; '.__('Min').': '.remove_right_zeros(number_format($graph_stats['sum']['min'], $config['graph_precision'])).' '.$unit; - $legend['min'.$series_suffix] = __('Min').$series_suffix_str.': '.__('Avg').': '.remove_right_zeros(number_format($graph_stats['min']['avg'], $config['graph_precision'])).' '.$unit.' ; '.__('Max').': '.remove_right_zeros(number_format($graph_stats['min']['max'], $config['graph_precision'])).' '.$unit.' ; '.__('Min').': '.remove_right_zeros(number_format($graph_stats['min']['min'], $config['graph_precision'])).' '.$unit; - } - else if ($dashboard) { - $legend['sum'.$series_suffix] = - __('Last') . ': ' . remove_right_zeros(number_format($graph_stats['sum']['last'], $config['graph_precision'])) . ($unit ? ' ' . $unit : '') . ' ; ' - . __('Avg') . ': ' . remove_right_zeros(number_format($graph_stats['sum']['avg'], $config['graph_precision'])) . ($unit ? ' ' . $unit : ''); - } - else if (!$avg_only && !$fullscale) { - $legend['max'.$series_suffix] = __('Max').$series_suffix_str.': '.__('Avg').': '.remove_right_zeros(number_format($graph_stats['max']['avg'], $config['graph_precision'])).' '.$unit.' ; '.__('Max').': '.remove_right_zeros(number_format($graph_stats['max']['max'], $config['graph_precision'])).' '.$unit.' ; '.__('Min').': '.remove_right_zeros(number_format($graph_stats['max']['min'], $config['graph_precision'])).' '.$unit; - $legend['sum'.$series_suffix] = __('Avg').$series_suffix_str.': '.__('Avg').': '.remove_right_zeros(number_format($graph_stats['sum']['avg'], $config['graph_precision'])).' '.$unit.' ; '.__('Max').': '.remove_right_zeros(number_format($graph_stats['sum']['max'], $config['graph_precision'])).' '.$unit.' ; '.__('Min').': '.remove_right_zeros(number_format($graph_stats['sum']['min'], $config['graph_precision'])).' '.$unit; - $legend['min'.$series_suffix] = __('Min').$series_suffix_str.': '.__('Avg').': '.remove_right_zeros(number_format($graph_stats['min']['avg'], $config['graph_precision'])).' '.$unit.' ; '.__('Max').': '.remove_right_zeros(number_format($graph_stats['min']['max'], $config['graph_precision'])).' '.$unit.' ; '.__('Min').': '.remove_right_zeros(number_format($graph_stats['min']['min'], $config['graph_precision'])).' '.$unit; - } - else if ($fullscale){ - $legend['sum'.$series_suffix] = __('Data').$series_suffix_str.': '; - } - else { - $legend['sum'.$series_suffix] = __('Avg').$series_suffix_str.': '.__('Avg').': '.remove_right_zeros(number_format($graph_stats['sum']['avg'], $config['graph_precision'])).' '.$unit.' ; '.__('Max').': '.remove_right_zeros(number_format($graph_stats['sum']['max'], $config['graph_precision'])).' '.$unit.' ; '.__('Min').': '.remove_right_zeros(number_format($graph_stats['sum']['min'], $config['graph_precision'])).' '.$unit; - } + $series_type = $series_type_array['series_type']; + $legend = $series_type_array['legend']; + $color = $series_type_array['color']; - if ($show_unknown) { - $legend['unknown'.$series_suffix] = __('Unknown').$series_suffix_str; - $chart_extra_data['legend_unknown'] = $legend['unknown'.$series_suffix_str]; - } - - if (!is_null($percentil) && $percentil) { - $first_data = reset($chart); - $percentil_value = format_for_graph($first_data['percentil'], 2); - - $legend['percentil'.$series_suffix] = __('Percentile %dยบ', $percentil) .$series_suffix_str . " (" . $percentil_value . " " . $unit . ") "; - $chart_extra_data['legend_percentil'] = $legend['percentil'.$series_suffix_str]; - } - - if($force_interval){ - $legend = array(); - if($avg_only){ - $legend['sum'.$series_suffix] = __('Avg'); - } - elseif ($max_only) { - $legend['min'.$series_suffix] = __('Max'); - } - elseif ($min_only) { - $legend['max'.$series_suffix] = __('Min'); - } - } -} - -function grafico_modulo_sparse ($agent_module_id, $period, $show_events, - $width, $height , $title = '', $unit_name = null, - $show_alerts = false, $avg_only = 0, $pure = false, $date = 0, - $unit = '', $baseline = 0, $return_data = 0, $show_title = true, - $only_image = false, $homeurl = '', $ttl = 1, $projection = false, - $adapt_key = '', $compare = false, $show_unknown = false, - $menu = true, $backgroundColor = 'white', $percentil = null, - $dashboard = false, $vconsole = false, $type_graph = 'area', $fullscale = false, - $id_widget_dashboard = false,$force_interval = 0,$time_interval = 300, - $max_only = 0, $min_only = 0) { - - - global $config; - global $graphic_type; - - $flash_chart = $config['flash_charts']; - - enterprise_include_once("include/functions_reporting.php"); - - global $chart; - global $color; - global $color_prev; - global $legend; - global $long_index; - global $series_type; - global $chart_extra_data; - global $warning_min; - global $critical_min; - - $series_suffix_str = ''; - if ($compare !== false) { - $series_suffix = '2'; - $series_suffix_str = ' (' . __('Previous') . ')'; - // Build the data of the previous period - - grafico_modulo_sparse_data ($agent_module_id, $period, - $show_events, $width, $height, $title, $unit_name, - $show_alerts, $avg_only, $date-$period, $unit, $baseline, - $return_data, $show_title, $projection, $adapt_key, - $compare, $series_suffix, $series_suffix_str, - $show_unknown, $percentil, $dashboard, $vconsole,$type_graph, - $fullscale, $flash_chart,$force_interval,$time_interval,$max_only,$min_only); - - switch ($compare) { - case 'separated': - // Store the chart calculated - $chart_prev = $chart; - $legend_prev = $legend; - $long_index_prev = $long_index; - $series_type_prev = $series_type; - $color_prev = $color; - break; - case 'overlapped': - // Store the chart calculated deleting index, - // because will be over the current period - $chart_prev = array_values($chart); - $legend_prev = $legend; - $series_type_prev = $series_type; - $color_prev = $color; - foreach($color_prev as $k => $col) { - $color_prev[$k]['color'] = '#' . - get_complementary_rgb($color_prev[$k]['color']); - } - break; - } - } - - // Build the data of the current period - $data_returned = grafico_modulo_sparse_data ($agent_module_id, - $period, $show_events, - $width, $height , $title, $unit_name, - $show_alerts, $avg_only, - $date, $unit, $baseline, $return_data, $show_title, - $projection, $adapt_key, $compare, '', '', $show_unknown, - $percentil, $dashboard, $vconsole, $type_graph, $fullscale,$flash_chart, - $force_interval,$time_interval,$max_only,$min_only); - - if ($return_data) { - return $data_returned; - } - if ($compare === 'overlapped') { - $i = 0; - foreach ($chart as $k=>$v) { - if (!isset($chart_prev[$i])) { - continue; - } - $chart[$k] = array_merge($v,$chart_prev[$i]); - $i++; - } - - $legend = array_merge($legend, $legend_prev); - $color = array_merge($color, $color_prev); - } - - if ($only_image) { - $flash_chart = false; - } if($config["fixed_graph"] == false){ - $water_mark = array('file' => - $config['homedir'] . "/images/logo_vertical_water.png", - 'url' => ui_get_full_url("images/logo_vertical_water.png", false, false, false)); + $water_mark = array( + 'file' => $config['homedir'] . "/images/logo_vertical_water.png", + 'url' => ui_get_full_url( + "/images/logo_vertical_water.png", + false, + false, + false + ) + ); } - - if ($type_graph === 'area') { - if ($compare === 'separated') { - return - area_graph($flash_chart, $chart, $width, $height/2, $color, - $legend, $long_index, - ui_get_full_url("images/image_problem_area_small.png", false, false, false), - $title, $unit, $homeurl, $water_mark, $config['fontpath'], - $config['font_size'], $unit, $ttl, $series_type, - $chart_extra_data, $warning_min, $critical_min, - $adapt_key, false, $series_suffix_str, $menu, - $backgroundColor). - '
'. - area_graph($flash_chart, $chart_prev, $width, $height/2, - $color_prev, $legend_prev, $long_index_prev, - ui_get_full_url("images/image_problem_area_small.png", false, false, false), - $title, $unit, $homeurl, $water_mark, $config['fontpath'], - $config['font_size'], $unit, $ttl, $series_type_prev, - $chart_extra_data, $warning_min, $critical_min, - $adapt_key, false, $series_suffix_str, $menu, - $backgroundColor); + + $data_module_graph['series_suffix'] = $series_suffix; + + // Check available data + if ($params['compare'] === 'separated') { + if (!empty($array_data)) { + $return = area_graph( + $agent_module_id, + $array_data, + $legend, + $series_type, + $color, + $date_array, + $data_module_graph, + $params, + $water_mark, + $array_events_alerts + ); } - else { - // Color commented not to restrict serie colors - if($id_widget_dashboard){ - $opcion = unserialize(db_get_value_filter('options','twidget_dashboard',array('id' => $id_widget_dashboard))); - $color['min']['color'] = $opcion['min']; - $color['sum']['color'] = $opcion['avg']; - $color['max']['color'] = $opcion['max']; - } - - return - area_graph($flash_chart, $chart, $width, $height, $color, - $legend, $long_index, - ui_get_full_url("images/image_problem_area_small.png", false, false, false), - $title, $unit, $homeurl, $water_mark, $config['fontpath'], - $config['font_size'], $unit, $ttl, $series_type, - $chart_extra_data, $warning_min, $critical_min, - $adapt_key, false, $series_suffix_str, $menu, - $backgroundColor, $dashboard, $vconsole, $agent_module_id); + else{ + $return = graph_nodata_image($params['width'], $params['height']); + } + $return .= '
'; + if (!empty($array_data_prev)) { + + $series_type_array = series_type_graph_array( + $array_data_prev, + $params + ); + + $series_type = $series_type_array['series_type']; + $legend = $series_type_array['legend']; + $color = $series_type_array['color']; + + $return .= area_graph( + $agent_module_id, + $array_data_prev, + $legend, + $series_type, + $color, + $date_array, + $data_module_graph, + $params, + $water_mark, + $array_events_alerts + ); + } + else{ + $return .= graph_nodata_image($params['width'], $params['height']); } } - elseif ($type_graph === 'line') { - if ($compare === 'separated') { - return - line_graph($flash_chart, $chart, $width, $height/2, $color, - $legend, $long_index, - ui_get_full_url("images/image_problem_area_small.png", false, false, false), - $title, $unit, $water_mark, $config['fontpath'], - $config['font_size'], $unit, $ttl, $homeurl, $backgroundColor). - '
'. - line_graph($flash_chart, $chart_prev, $width, $height/2, $color, - $legend, $long_index, - ui_get_full_url("images/image_problem_area_small.png", false, false, false), - $title, $unit, $water_mark, $config['fontpath'], - $config['font_size'], $unit, $ttl, $homeurl, $backgroundColor); + else{ + if (!empty($array_data)) { + $return = area_graph( + $agent_module_id, + $array_data, + $legend, + $series_type, + $color, + $date_array, + $data_module_graph, + $params, + $water_mark, + $array_events_alerts + ); } - else { - // Color commented not to restrict serie colors - return - line_graph($flash_chart, $chart, $width, $height, $color, - $legend, $long_index, - ui_get_full_url("images/image_problem_area_small.png", false, false, false), - $title, $unit, $water_mark, $config['fontpath'], - $config['font_size'], $unit, $ttl, $homeurl, $backgroundColor); + else{ + $return = graph_nodata_image( + $params['width'], + $params['height'] + ); } } + +return $return; } function graph_get_formatted_date($timestamp, $format1, $format2) { global $config; - + if ($config['flash_charts']) { $date = date("$format1 $format2", $timestamp); } @@ -1088,7 +1093,7 @@ function graph_get_formatted_date($timestamp, $format1, $format2) { $date .= "\n".date($format2, $timestamp); } } - + return $date; } @@ -1106,7 +1111,7 @@ function graph_get_formatted_date($timestamp, $format1, $format2) { * @param int Show alerts in graph (set to 1) * @param int Pure mode (without titles) (set to 1) * @param int Date to start of getting info. - * @param mixed If is a projection graph this parameter will be module data with prediction data (the projection) + * @param mixed If is a projection graph this parameter will be module data with prediction data (the projection) * or false in other case. * @param array List of names for the items. Should have the same size as the module list. * @param array List of units for the items. Should have the same size as the module list. @@ -1114,1562 +1119,1244 @@ function graph_get_formatted_date($timestamp, $format1, $format2) { * @param bool Show the max value of the item on the list. * @param bool Show the min value of the item on the list. * @param bool Show the average value of the item on the list. - * - * @return Mixed + * + * @return Mixed */ + + +/* + $params =array( + 'period' => $period, + 'show_events' => false, + 'width' => $width, + 'height' => $height, + 'title' => '', + 'unit_name' => null, + 'show_alerts' => false, + 'date' => 0, + 'unit' => '', + 'only_image' => false, + 'homeurl' => '', + 'ttl' => 1, + 'percentil' => null, + 'dashboard' => false, + 'vconsole' => false, + 'fullscale' => false, + 'id_widget_dashboard' => false, + ); + + $params_combined = array( + 'weight_list' => array(), + 'stacked' => 0, + 'projection' => false, + 'labels' => array(), + 'from_interface' => false, + 'summatory' => 0, + 'average' => 0, + 'modules_series' => 0, + 'id_graph' => 0, + 'return' => 1 + ); + */ + function graphic_combined_module ( $module_list, - $weight_list, - $period, - $width, - $height, - $title, - $unit_name, - $show_events = 0, - $show_alerts = 0, - $pure = 0, - $stacked = 0, - $date = 0, - $only_image = false, - $homeurl = '', - $ttl = 1, - $projection = false, - $prediction_period = false, - $background_color = 'white', - $name_list = array(), - $unit_list = array(), - $show_last = true, - $show_max = true, - $show_min = true, - $show_avg = true, - $labels = array(), - $dashboard = false, - $vconsole = false, - $percentil = null, - $from_interface = false, - $id_widget_dashboard=false, - $fullscale = false, - $summatory = 0, - $average = 0, - $modules_series = 0 + $params, + $params_combined ) { - + + if(!isset($params_combined['from_interface'])){ + $params_combined['from_interface'] = false; + } + + if(!isset($params_combined['stacked'])){ + if ($params_combined['from_interface']) { + if ($config["type_interface_charts"] == 'line') { + $params_combined['stacked'] = CUSTOM_GRAPH_LINE; + } + else { + $params_combined['stacked'] = CUSTOM_GRAPH_AREA; + } + } + else { + if ($id_graph == 0) { + $params_combined['stacked'] = CUSTOM_GRAPH_AREA; + } + else { + $params_combined['stacked'] = db_get_row('tgraph', 'id_graph', $id_graph); + } + } + } + + $params['stacked'] = $params_combined['stacked']; + + if(!isset($params_combined['projection'])){ + $params_combined['projection'] = false; + } + else{ + $params['stacked'] = 'area'; + } + + if(!isset($params_combined['labels'])){ + $params_combined['labels'] = array(); + } + + if(!isset($params_combined['summatory'])){ + $params_combined['summatory'] = 0; + } + + if(!isset($params_combined['average'])){ + $params_combined['average'] = 0; + } + + if(!isset($params_combined['modules_series'])){ + $params_combined['modules_series'] = 0; + } + + if(!isset($params_combined['return'])){ + $params_combined['return'] = 1; + } + + if(!isset($params_combined['id_graph'])){ + $params_combined['id_graph'] = 0; + } + + if(!isset($params['percentil'])){ + $params_combined['percentil'] = null; + } + else{ + $params_combined['percentil'] = $params['percentil']; + } + + + if(!isset($params['period'])){ + return false; + } + + if(!isset($params['width'])){ + $params['width'] = '90%'; + } + + if(!isset($params['height'])){ + $params['height'] = 450; + } + + if(!isset($params['title'])){ + $params['title'] = ''; + } + + if(!isset($params['unit_name'])){ + $params['unit_name'] = null; + } + + if(!isset($params['show_alerts'])){ + $params['show_alerts'] = false; + } + + if(!isset($params['date']) || !$params['date']){ + $params['date'] = get_system_time(); + } + + if(!isset($params['only_image'])){ + $params['only_image'] = false; + } + + if(!isset($params['ttl'])){ + $params['ttl'] = 1; + } + + if(!isset($params['backgroundColor'])){ + $params['backgroundColor'] = 'white'; + } + + if(!isset($params['dashboard'])){ + $params['dashboard'] = false; + } + + if(!isset($params['menu']) || $params['only_image']){ + $params['menu'] = true; + } + else{ + $params['menu'] = false; + } + + if(!isset($params['vconsole']) || $params['vconsole'] == false){ + $params['vconsole'] = false; + } + else{ + $params['menu'] = false; + } + + if(!isset($params['type_graph'])){ + $params['type_graph'] = $config['type_module_charts']; + } + + if(!isset($params['percentil'])){ + $params['percentil'] = null; + } + + if(!isset($params['fullscale'])){ + $params['fullscale'] = false; + } + + if(!isset($params['id_widget_dashboard'])){ + $params['id_widget_dashboard'] = false; + } + + if(!isset($params['homeurl'])){ + $params['homeurl'] = ui_get_full_url(false, false, false, false); + } + + if(!isset($params['show_legend'])){ + $params['show_legend'] = true; + } + + if(!isset($params['show_overview'])){ + $params['show_overview'] = true; + } + + if(!isset($params['show_export_csv'])){ + $params['show_export_csv'] = true; + } + + if(!isset($params['return_img_base_64'])){ + $params['return_img_base_64'] = false; + } + + if(!isset($params['image_treshold'])){ + $params['image_treshold'] = false; + } + + if(!isset($params['show_unknown'])){ + $params['show_unknown'] = false; + } + + $params['graph_combined'] = true; + + if($params['only_image']){ + return generator_chart_to_pdf('combined', $params, $params_combined, $module_list); + } + global $config; global $graphic_type; - if(!$fullscale || $stacked >= 4){ - - $fullscale = 0; - $time_format_2 = ''; - $temp_range = $period; - $unit_list_aux = array(); - - if ($projection != false) { - if ($period < $prediction_period) - $temp_range = $prediction_period; - } - - // Set the title and time format - if ($temp_range <= SECONDS_1DAY) { - $time_format = 'Y M d H:i:s'; - } - elseif ($temp_range < SECONDS_15DAYS) { - $time_format = 'Y M d'; - $time_format_2 = 'H:i'; - if ($projection != false) { - $time_format_2 = 'H\h'; - } - } - elseif ($temp_range <= SECONDS_1MONTH) { - $time_format = 'Y M d'; - $time_format_2 = 'H\h'; - } - elseif ($temp_range <= SECONDS_1MONTH) { - $time_format = 'Y M d'; - $time_format_2 = 'H\h'; - } - elseif ($period < SECONDS_6MONTHS) { - $time_format = 'Y M d'; - $time_format_2 = 'H\h'; - } - else { - $time_format = "Y M d"; - $time_format_2 = 'H\h'; - } - - // Set variables - if ($date == 0) - $date = get_system_time(); - $datelimit = $date - $period; - - $resolution = $config['graph_res'] * 50; //Number of points of the graph - if($resolution > $period){ - $resolution = $period; - } - $interval = (int) ($period / $resolution); - - // If projection graph, fill with zero previous data to projection interval - if ($projection != false) { - $j = $datelimit; - $in_range = true; - while ($in_range) { - $timestamp_f = graph_get_formatted_date($j, $time_format, $time_format_2); - - $before_projection[$timestamp_f] = 0; - - if ($j > $date) { - $in_range = false; - } - $j = $j + $interval; - } - } - - // Added support for projection graphs (normal_module + 1(prediction data)) - if ($projection !== false) { - $module_number = count ($module_list) + 1; - } - else { - $module_number = count ($module_list); - } - - $names_number = count($name_list); - $units_number = count($unit_list); - - // interval - This is the number of "rows" we are divided the time to fill data. - // more interval, more resolution, and slower. - // periodo - Gap of time, in seconds. This is now to (now-periodo) secs - - // Init weights - for ($i = 0; $i < $module_number; $i++) { - if (! isset ($weight_list[$i])) { - $weight_list[$i] = 1; - } - else if ($weight_list[$i] == 0) { - $weight_list[$i] = 1; - } + $sources = false; + if ($params_combined['id_graph'] == 0) { + $count_modules = count($module_list); + + if(!$params_combined['weight_list']){ + $weights = array_fill(0, $count_modules, 1); } - $aux_array = array(); - // Set data containers - for ($i = 0; $i < $resolution; $i++) { - $timestamp = $datelimit + ($interval * $i);/* - $timestamp_short = date($time_format, $timestamp); - $long_index[$timestamp_short] = date( - html_entity_decode($config['date_format'], ENT_QUOTES, "UTF-8"), $timestamp); - $timestamp = $timestamp_short;*/ - - $graph[$timestamp]['count'] = 0; - $graph[$timestamp]['timestamp_bottom'] = $timestamp; - $graph[$timestamp]['timestamp_top'] = $timestamp + $interval; - $graph[$timestamp]['min'] = 0; - $graph[$timestamp]['max'] = 0; - $graph[$timestamp]['event'] = 0; - $graph[$timestamp]['alert'] = 0; - } - $long_index = array(); - - $graph_values = array(); - $module_name_list = array(); - $collector = 0; - $user = users_get_user_by_id($config['id_user']); - $user_flash_charts = $user['flash_chart']; - - if ($user_flash_charts == 1) - $flash_charts = true; - elseif($user_flash_charts == -1) - $flash_charts = $config['flash_charts']; - elseif($user_flash_charts == 0) - $flash_charts = false; - - if ($only_image) { - $flash_charts = false; - } - - // Calculate data for each module - for ($i = 0; $i < $module_number; $i++) { - $automatic_custom_graph_meta = false; - if ($config['metaconsole']) { - // Automatic custom graph from the report template in metaconsole - if (is_array($module_list[$i])) { - $server = metaconsole_get_connection_by_id ($module_list[$i]['server']); - metaconsole_connect($server); - $automatic_custom_graph_meta = true; - } - } - - $search_in_history_db = db_search_in_history_db($datelimit); - - // If its a projection graph, - // first module will be data and second will be the projection - - if ($projection != false && $i != 0) { - if ($automatic_custom_graph_meta) - $agent_module_id = $module_list[0]['module']; - else - $agent_module_id = $module_list[0]; - $id_module_type = modules_get_agentmodule_type ($agent_module_id); - $module_type = modules_get_moduletype_name ($id_module_type); - $uncompressed_module = is_module_uncompressed ($module_type); - } - else { - if ($automatic_custom_graph_meta) - $agent_module_id = $module_list[$i]['module']; - else - $agent_module_id = $module_list[$i]; - - $id_module_type = modules_get_agentmodule_type ($agent_module_id); - $module_type = modules_get_moduletype_name ($id_module_type); - $uncompressed_module = is_module_uncompressed ($module_type); - } - - if ($uncompressed_module) { - $avg_only = 1; - } - - // Get event data (contains alert data too) - if ($show_events == 1 || $show_alerts == 1) { - $events = db_get_all_rows_filter ('tevento', - array ('id_agentmodule' => $agent_module_id, - "utimestamp > $datelimit", - "utimestamp < $date", - 'order' => 'utimestamp ASC'), - array ('evento', 'utimestamp', 'event_type')); - if ($events === false) { - $events = array (); - } - } - - // Get module data - $data = db_get_all_rows_filter ('tagente_datos', - array ('id_agente_modulo' => $agent_module_id, - "utimestamp > $datelimit", - "utimestamp < $date", - 'order' => 'utimestamp ASC'), - array ('datos', 'utimestamp'), 'AND', $search_in_history_db); - - if ($data === false) { - $data = array (); - } - - // Uncompressed module data - if ($uncompressed_module) { - $min_necessary = 1; - - // Compressed module data - } - else { - // Get previous data - $previous_data = modules_get_previous_data ($agent_module_id, $datelimit); - if ($previous_data !== false) { - $previous_data['utimestamp'] = $datelimit; - array_unshift ($data, $previous_data); - } - - // Get next data - $nextData = modules_get_next_data ($agent_module_id, $date); - if ($nextData !== false) { - array_push ($data, $nextData); - } - else if (count ($data) > 0) { - // Propagate the last known data to the end of the interval - $nextData = array_pop ($data); - array_push ($data, $nextData); - $nextData['utimestamp'] = $date; - array_push ($data, $nextData); - } - - $min_necessary = 2; - } - - // Set initial conditions - $graph_values[$i] = array(); - - // Check available data - if (count ($data) < $min_necessary) { - continue; - } - - // if(empty($aux_array)){ - // foreach ($data as $key => $value) { - // $aux_array[$value['utimestamp']] = $value['datos']; - // } - // } else { - // foreach ($data as $key => $value) { - // if(array_key_exists($value['utimestamp'],$aux_array)){ - // $aux_array[$value['utimestamp']] = $aux_array[$value['utimestamp']] + $value['datos']; - // } else { - // $aux_array[$value['utimestamp']] = $value['datos']; - // } - // } - // } - - // html_debug($aux_array); - if (!empty($name_list) && $names_number == $module_number && isset($name_list[$i])) { - if ($labels[$agent_module_id] != '') - $module_name_list[$i] = $labels[$agent_module_id]; - else { - $agent_name = io_safe_output( - modules_get_agentmodule_agent_name ($agent_module_id)); - $alias = db_get_value ("alias","tagente","nombre",$agent_name); - $module_name = io_safe_output( - modules_get_agentmodule_name ($agent_module_id)); - - if ($flash_charts) - $module_name_list[$i] = '' . $alias . " / " . $module_name. ''; - else - $module_name_list[$i] = $alias . " / " . $module_name; - } - } - else { - //Get and process agent name - $agent_name = io_safe_output( - modules_get_agentmodule_agent_name ($agent_module_id)); - $alias = db_get_value ("alias","tagente","nombre",$agent_name); - $agent_name = ui_print_truncate_text($agent_name, 'agent_small', false, true, false, '...', false); - - $agent_id = agents_get_agent_id ($agent_name); - - if(empty($unit_list)){ - $unit_aux = modules_get_unit($agent_module_id); - array_push($unit_list_aux,$unit_aux); - } - //Get and process module name - $module_name = io_safe_output( - modules_get_agentmodule_name ($agent_module_id)); - $module_name = sprintf(__("%s"), $module_name); - $module_name = ui_print_truncate_text($module_name, 'module_small', false, true, false, '...', false); - - if ($flash_charts) { - if ($labels[$agent_module_id] != '') - $module_name_list[$i] = '' . - $labels[$agent_module_id] . ''; - else - $module_name_list[$i] = '' . - $alias . ' / ' . $module_name . ''; - } - else { - if ($labels[$agent_module_id] != '') - $module_name_list[$i] = $labels[$agent_module_id]; - else - $module_name_list[$i] = $alias . ' / ' . $module_name; - } - } - - // Data iterator - $j = 0; - - // Event iterator - $k = 0; - - // Set initial conditions - - //$graph_values[$i] = array(); - $temp_graph_values = array(); - - if ($data[0]['utimestamp'] == $datelimit) { - $previous_data = $data[0]['datos']; - $j++; - } - else { - $previous_data = 0; - } - - $max = 0; - $min = null; - $avg = 0; - $countAvg = 0; - - // Calculate chart data - $last_known = $previous_data; - for ($l = 0; $l <= $resolution; $l++) { - $countAvg ++; - - $timestamp = $datelimit + ($interval * $l); - $timestamp_short = graph_get_formatted_date($timestamp, $time_format, $time_format_2); - - $long_index[$timestamp_short] = date( - html_entity_decode($config['date_format'], ENT_QUOTES, "UTF-8"), $timestamp); - //$timestamp = $timestamp_short; - - $total = 0; - $count = 0; - - // Read data that falls in the current interval - $interval_min = $last_known; - $interval_max = $last_known; - - while (isset ($data[$j]) && $data[$j]['utimestamp'] >= $timestamp && $data[$j]['utimestamp'] < ($timestamp + $interval)) { - if ($data[$j]['datos'] > $interval_max) { - $interval_max = $data[$j]['datos']; - } - else if ($data[$j]['datos'] < $interval_max) { - $interval_min = $data[$j]['datos']; - } - $total += $data[$j]['datos']; - $last_known = $data[$j]['datos']; - $count++; - $j++; - } - - // Average - if ($count > 0) { - $total /= $count; - } - - // Read events and alerts that fall in the current interval - $event_value = 0; - $alert_value = 0; - while (isset ($events[$k]) && $events[$k]['utimestamp'] >= $timestamp && $events[$k]['utimestamp'] <= ($timestamp + $interval)) { - if ($show_events == 1) { - $event_value++; - } - if ($show_alerts == 1 && substr ($events[$k]['event_type'], 0, 5) == 'alert') { - $alert_value++; - } - $k++; - } - - // Data - if ($count > 0) { - //$graph_values[$i][$timestamp] = $total * $weight_list[$i]; - $temp_graph_values[$timestamp_short] = $total * $weight_list[$i]; - } - else { - // Compressed data - if ($uncompressed_module || ($timestamp > time ())) { - $temp_graph_values[$timestamp_short] = 0; - } - else { - $temp_graph_values[$timestamp_short] = $last_known * $weight_list[$i]; - } - } - - //Extract max, min, avg - if ($max < $temp_graph_values[$timestamp_short]) { - $max = $temp_graph_values[$timestamp_short]; - } - - if (isset($min)) { - if ($min > $temp_graph_values[$timestamp_short]) { - $min = $temp_graph_values[$timestamp_short]; - } - } - else { - $min = $temp_graph_values[$timestamp_short]; - } - $avg += $temp_graph_values[$timestamp_short]; - - // Added to support projection graphs - if ($projection != false and $i != 0) { - $projection_data = array(); - $projection_data = array_merge($before_projection, $projection); - $graph_values[$i] = $projection_data; - } - else { - $graph_values[$i] = $temp_graph_values; - } - } - - //Add the max, min and avg in the legend - $avg = round($avg / $countAvg, 1); - - $graph_stats = get_graph_statistics($graph_values[$i]); - - if (!isset($config["short_module_graph_data"])) - $config["short_module_graph_data"] = true; - - if ($config["short_module_graph_data"]) { - $min = $graph_stats['min']; - $max = $graph_stats['max']; - $avg = $graph_stats['avg']; - $last = $graph_stats['last']; - - if ($min > 1000000) - $min = sprintf("%sM", remove_right_zeros(number_format($min / 1000000, remove_right_zeros))); - else if ($min > 1000) - $min = sprintf("%sK", remove_right_zeros(number_format($min / 1000, $config['graph_precision']))); - - if ($max > 1000000) - $max = sprintf("%sM", remove_right_zeros(number_format($max / 1000000, $config['graph_precision']))); - else if ($max > 1000) - $max = sprintf("%sK", remove_right_zeros(number_format($max / 1000, $config['graph_precision']))); - - if ($avg > 1000000) - $avg = sprintf("%sM", remove_right_zeros(number_format($avg / 1000000, $config['graph_precision']))); - else if ($avg > 1000) - $avg = sprintf("%sK", remove_right_zeros(number_format($avg / 1000, $config['graph_precision']))); - - if ($last > 1000000) - $last = sprintf("%sM", remove_right_zeros(number_format($last / 1000000, $config['graph_precision']))); - else if ($last > 1000) - $last = sprintf("%sK", remove_right_zeros(number_format($last / 1000, $config['graph_precision']))); - } - else { - $min = remove_right_zeros(number_format($graph_stats['min'], $config['graph_precision'])); - $max = remove_right_zeros(number_format($graph_stats['max'], $config['graph_precision'])); - $avg = remove_right_zeros(number_format($graph_stats['avg'], $config['graph_precision'])); - $last = remove_right_zeros(number_format($graph_stats['last'], $config['graph_precision'])); - } - - - if (!empty($unit_list) && $units_number == $module_number && isset($unit_list[$i])) { - $unit = $unit_list[$i]; - }else{ - $unit = $unit_list_aux[$i]; - } - - if ($projection == false or ($projection != false and $i == 0)) { - $module_name_list[$i] .= ": "; - if ($show_max) - $module_name_list[$i] .= __("Max") . ": $max $unit; "; - if ($show_min) - $module_name_list[$i] .= __("Min") . ": $min $unit; "; - if ($show_avg) - $module_name_list[$i] .= __("Avg") . ": $avg $unit"; - } - - if ($weight_list[$i] != 1) { - //$module_name_list[$i] .= " (x". format_numeric ($weight_list[$i], 1).")"; - $module_name_list[$i] .= " (x". format_numeric ($weight_list[$i], 1).")"; - } - - //$graph_values[$module_name_list[$i]] = $graph_values[$i]; - //unset($graph_values[$i]); - - //$graph_values[$i] = $graph_values[$i]; - - if ($config['metaconsole']) { - // Automatic custom graph from the report template in metaconsole - if (is_array($module_list[0])) { - metaconsole_restore_db(); - } - } - } - - $temp = array(); - - if ($flash_charts === false && $stacked == CUSTOM_GRAPH_GAUGE) - $stacked = CUSTOM_GRAPH_BULLET_CHART; - switch ($stacked) { - case CUSTOM_GRAPH_BULLET_CHART_THRESHOLD: - case CUSTOM_GRAPH_BULLET_CHART: - $datelimit = $date - $period; - if($stacked == CUSTOM_GRAPH_BULLET_CHART_THRESHOLD){ - $acumulador = 0; - foreach ($module_list as $module_item) { - $module = $module_item; - $query_last_value = sprintf(' - SELECT datos - FROM tagente_datos - WHERE id_agente_modulo = %d - AND utimestamp < %d - ORDER BY utimestamp DESC', - $module, $date); - $temp_data = db_get_value_sql($query_last_value); - if ($acumulador < $temp_data){ - $acumulador = $temp_data; - } - } - } - foreach ($module_list as $module_item) { - $automatic_custom_graph_meta = false; - if ($config['metaconsole']) { - // Automatic custom graph from the report template in metaconsole - if (is_array($module_list[$i])) { - $server = metaconsole_get_connection_by_id ($module_item['server']); - metaconsole_connect($server); - $automatic_custom_graph_meta = true; - } - } - - if ($automatic_custom_graph_meta) - $module = $module_item['module']; - else - $module = $module_item; - - $search_in_history_db = db_search_in_history_db($datelimit); - - $temp[$module] = modules_get_agentmodule($module); - $query_last_value = sprintf(' - SELECT datos - FROM tagente_datos - WHERE id_agente_modulo = %d - AND utimestamp < %d - ORDER BY utimestamp DESC', - $module, $date); - $temp_data = db_get_value_sql($query_last_value); - - if ($temp_data) { - if (is_numeric($temp_data)) - $value = $temp_data; - else - $value = count($value); - } - else { - if ($flash_charts === false) - $value = 0; - else - $value = false; - } - - if ( !empty($labels) && isset($labels[$module]) ){ - $label = io_safe_input($labels[$module]); - }else{ - $alias = db_get_value ("alias","tagente","id_agente",$temp[$module]['id_agente']); - $label = $alias . ': ' . $temp[$module]['nombre']; - } - - - $temp[$module]['label'] = $label; - $temp[$module]['value'] = $value; - $temp_max = reporting_get_agentmodule_data_max($module,$period,$date); - if ($temp_max < 0) - $temp_max = 0; - if (isset($acumulador)){ - $temp[$module]['max'] = $acumulador; - }else{ - $temp[$module]['max'] = ($temp_max === false) ? 0 : $temp_max; - } - - $temp_min = reporting_get_agentmodule_data_min($module,$period,$date); - if ($temp_min < 0) - $temp_min = 0; - $temp[$module]['min'] = ($temp_min === false) ? 0 : $temp_min; - - if ($config['metaconsole']) { - // Automatic custom graph from the report template in metaconsole - if (is_array($module_list[0])) { - metaconsole_restore_db(); - } - } - - } - - break; - case CUSTOM_GRAPH_HBARS: - case CUSTOM_GRAPH_VBARS: - $datelimit = $date - $period; - - $label = ''; - foreach ($module_list as $module_item) { - $automatic_custom_graph_meta = false; - if ($config['metaconsole']) { - // Automatic custom graph from the report template in metaconsole - if (is_array($module_list[$i])) { - $server = metaconsole_get_connection_by_id ($module_item['server']); - metaconsole_connect($server); - $automatic_custom_graph_meta = true; - } - } - - if ($automatic_custom_graph_meta) - $module = $module_item['module']; - else - $module = $module_item; - - $module_data = modules_get_agentmodule($module); - $query_last_value = sprintf(' - SELECT datos - FROM tagente_datos - WHERE id_agente_modulo = %d - AND utimestamp < %d - ORDER BY utimestamp DESC', - $module, $date); - $temp_data = db_get_value_sql($query_last_value); - - $agent_name = io_safe_output( - modules_get_agentmodule_agent_name ($module)); - - if (!empty($labels) && isset($labels[$module]) ){ - $label = $labels[$module]; - }else { - $alias = db_get_value ("alias","tagente","id_agente",$module_data['id_agente']); - $label = $alias . " - " .$module_data['nombre']; - } - - $temp[$label]['g'] = round($temp_data,4); - - - if ($config['metaconsole']) { - // Automatic custom graph from the report template in metaconsole - if (is_array($module_list[0])) { - metaconsole_restore_db(); - } - } - - } - break; - case CUSTOM_GRAPH_PIE: - $datelimit = $date - $period; - $total_modules = 0; - foreach ($module_list as $module_item) { - $automatic_custom_graph_meta = false; - if ($config['metaconsole']) { - // Automatic custom graph from the report template in metaconsole - if (is_array($module_list[$i])) { - $server = metaconsole_get_connection_by_id ($module_item['server']); - metaconsole_connect($server); - $automatic_custom_graph_meta = true; - } - } - - if ($automatic_custom_graph_meta) - $module = $module_item['module']; - else - $module = $module_item; - - $data_module = modules_get_agentmodule($module); - $query_last_value = sprintf(' - SELECT datos - FROM tagente_datos - WHERE id_agente_modulo = %d - AND utimestamp > %d - AND utimestamp < %d - ORDER BY utimestamp DESC', - $module, $datelimit, $date); - $temp_data = db_get_value_sql($query_last_value); - - if ( $temp_data ){ - if (is_numeric($temp_data)) - $value = $temp_data; - else - $value = count($value); - } - else { - $value = false; - } - $total_modules += $value; - - if ( !empty($labels) && isset($labels[$module]) ){ - $label = io_safe_output($labels[$module]); - }else { - $alias = db_get_value ("alias","tagente","id_agente",$data_module['id_agente']); - $label = io_safe_output($alias . ": " . $data_module['nombre']); - } - - $temp[$label] = array('value'=>$value, - 'unit'=>$data_module['unit']); - if ($config['metaconsole']) { - // Automatic custom graph from the report template in metaconsole - if (is_array($module_list[0])) { - metaconsole_restore_db(); - } - } - } - $temp['total_modules'] = $total_modules; - - break; - case CUSTOM_GRAPH_GAUGE: - $datelimit = $date - $period; - $i = 0; - foreach ($module_list as $module_item) { - $automatic_custom_graph_meta = false; - if ($config['metaconsole']) { - // Automatic custom graph from the report template in metaconsole - if (is_array($module_list[$i])) { - $server = metaconsole_get_connection_by_id ($module_item['server']); - metaconsole_connect($server); - $automatic_custom_graph_meta = true; - } - } - - if ($automatic_custom_graph_meta) - $module = $module_item['module']; - else - $module = $module_item; - - $temp[$module] = modules_get_agentmodule($module); - $query_last_value = sprintf(' - SELECT datos - FROM tagente_datos - WHERE id_agente_modulo = %d - AND utimestamp < %d - ORDER BY utimestamp DESC', - $module, $date); - $temp_data = db_get_value_sql($query_last_value); - if ( $temp_data ) { - if (is_numeric($temp_data)) - $value = $temp_data; - else - $value = count($value); - } - else { - $value = false; - } - $temp[$module]['label'] = ($labels[$module] != '') ? $labels[$module] : $temp[$module]['nombre']; - - $temp[$module]['value'] = $value; - $temp[$module]['label'] = ui_print_truncate_text($temp[$module]['label'],"module_small",false,true,false,".."); - - if ($temp[$module]['unit'] == '%') { - $temp[$module]['min'] = 0; - $temp[$module]['max'] = 100; - } - else { - $min = $temp[$module]['min']; - if ($temp[$module]['max'] == 0) - $max = reporting_get_agentmodule_data_max($module,$period,$date); - else - $max = $temp[$module]['max']; - $temp[$module]['min'] = ($min == 0 ) ? 0 : $min; - $temp[$module]['max'] = ($max == 0 ) ? 100 : $max; - } - $temp[$module]['gauge'] = uniqid('gauge_'); - - if ($config['metaconsole']) { - // Automatic custom graph from the report template in metaconsole - if (is_array($module_list[0])) { - metaconsole_restore_db(); - } - } - $i++; - } - break; - case CUSTOM_GRAPH_THERMOMETER: - $datelimit = $date - $period; - $i = 0; - foreach ($module_list as $module_item) { - $automatic_custom_graph_meta = false; - if ($config['metaconsole']) { - // Automatic custom graph from the report template in metaconsole - if (is_array($module_list[$i])) { - $server = metaconsole_get_connection_by_id ($module_item['server']); - metaconsole_connect($server); - $automatic_custom_graph_meta = true; - } - } - - if ($automatic_custom_graph_meta) - $module = $module_item['module']; - else - $module = $module_item; - - $temp[$module] = modules_get_agentmodule($module); - $query_last_value = sprintf(' - SELECT datos - FROM tagente_datos - WHERE id_agente_modulo = %d - AND utimestamp < %d - ORDER BY utimestamp DESC', - $module, $date); - $temp_data = db_get_value_sql($query_last_value); - if ( $temp_data ) { - if (is_numeric($temp_data)) - $value = $temp_data; - else - $value = count($value); - } - else { - $value = false; - } - $temp[$module]['label'] = ($labels[$module] != '') ? $labels[$module] : $temp[$module]['nombre']; - - $temp[$module]['value'] = $value; - $temp[$module]['label'] = ui_print_truncate_text($temp[$module]['label'],"module_small",false,true,false,".."); - - if ($temp[$module]['unit'] == '%') { - $temp[$module]['min'] = 0; - $temp[$module]['max'] = 100; - } - else { - $min = $temp[$module]['min']; - if ($temp[$module]['max'] == 0) - $max = reporting_get_agentmodule_data_max($module,$period,$date); - else - $max = $temp[$module]['max']; - $temp[$module]['min'] = ($min == 0 ) ? 0 : $min; - $temp[$module]['max'] = ($max == 0 ) ? 100 : $max; - } - $temp[$module]['gauge'] = uniqid('gauge_'); - - if ($config['metaconsole']) { - // Automatic custom graph from the report template in metaconsole - if (is_array($module_list[0])) { - metaconsole_restore_db(); - } - } - $i++; - } - break; - default: - if (!is_null($percentil) && $percentil) { - foreach ($graph_values as $graph_group => $point) { - foreach ($point as $timestamp_point => $point_value) { - $temp[$timestamp_point][$graph_group] = $point_value; - } - - $percentile_value = get_percentile($config['percentil'], $point); - $percentil_result[$graph_group] = array_fill ( 0, count($point), $percentile_value); - $series_type[$graph_group] = 'line'; - $agent_name = io_safe_output( - modules_get_agentmodule_agent_alias ($module_list[$graph_group])); - $module_name = io_safe_output( - modules_get_agentmodule_name ($module_list[$graph_group])); - $module_name_list['percentil'.$graph_group] = __('Percentile %dยบ', $config['percentil']) . __(' of module ') . $agent_name .' / ' . $module_name . ' (' . $percentile_value . ' ' . $unit . ') '; - } - } - else { - foreach ($graph_values as $graph_group => $point) { - foreach ($point as $timestamp_point => $point_value) { - $temp[$timestamp_point][$graph_group] = $point_value; - } - } - } - - //check min array two elements - if(count($temp) == 1){ - $timestamp_short = graph_get_formatted_date($date, $time_format, $time_format_2); - foreach($temp as $key => $value){ - foreach($value as $k => $v){ - $temp[$timestamp_short][$k] = $v; - } - } - } - break; + if ($count_modules > 0){ + $sources = true; } } - else{ - $flash_charts = true; - if($ttl>1 || !$config['flash_charts']){ - $flash_charts = false; - } + else { + $sources = db_get_all_rows_field_filter( + 'tgraph_source', + 'id_graph', + $params_combined['id_graph'] + ); - $temp = fullscale_data_combined($module_list, $period, $date, $flash_charts, $percentil); + $series = db_get_all_rows_sql( + 'SELECT summatory_series,average_series,modules_series + FROM tgraph + WHERE id_graph = '. + $params_combined['id_graph'] + ); - if (!is_null($percentil) && $percentil) { - if(isset($temp['percentil'])){ - $percentil_result = array_pop($temp); - } - } + $summatory = $series[0]['summatory_series']; + $average = $series[0]['average_series']; + $modules_series = $series[0]['modules_series']; - $resolution = count($temp); //Number of points of the graph - $interval = (int) ($period / $resolution); - $module_name_list = array(); - - if($ttl>1 || !$config['flash_charts']){ - $temp2 = array(); - foreach ($temp as $key => $value) { - $real_date = date("Y/M/d", $key); - $real_date .= "\n"; - $real_date .= date(" H:i:s", $key); - $temp2[$real_date] = $value; - } - $temp = $temp2; - } + $weights = array (); + $labels = array (); + $modules = array (); - foreach ($module_list as $key => $value) { - if (is_metaconsole() && is_array($value)) { - $server = metaconsole_get_connection_by_id ($value['server']); - metaconsole_connect($server); - $value = $value['module']; - } - if ($labels[$value] != ''){ - $module_name_list[$key] = $labels[$value]; - } - else { - $agent_name = io_safe_output( modules_get_agentmodule_agent_name ($value) ); - $alias = db_get_value ("alias","tagente","nombre",$agent_name); - $module_name = io_safe_output( modules_get_agentmodule_name ($value) ); - - if ($flash_charts){ - $module_name_list[$key] = '' . $alias . " / " . $module_name. ''; + if(isset($sources) && is_array($sources)){ + foreach ($sources as $source) { + array_push ($modules, $source['id_agent_module']); + array_push ($weights, $source['weight']); + if ($source['label'] != ''){ + $item['type'] = 'custom_graph'; + $item['id_agent'] = agents_get_module_id($source['id_agent_module']); + $item['id_agent_module'] = $source['id_agent_module']; + $labels[$source['id_agent_module']] = reporting_label_macro($item, $source['label']); } - else{ - $module_name_list[$key] = $alias . " / " . $module_name; - } - } - if (is_metaconsole() && is_array($value)) { - metaconsole_restore_db(); - } - } - - if (!is_null($percentil) && $percentil) { - foreach ($module_list as $key => $value) { - if (is_metaconsole() && is_array($value)) { - $server = metaconsole_get_connection_by_id ($value['server']); - metaconsole_connect($server); - $value = $value['module']; - } - - $agent_name = io_safe_output( modules_get_agentmodule_agent_name ($value) ); - $alias = db_get_value ("alias","tagente","nombre",$agent_name); - $module_name = io_safe_output( modules_get_agentmodule_name ($value) ); - - if (is_metaconsole() && is_array($value)) { - metaconsole_restore_db(); - } - - $module_name_list['percentil'.$key] = __('Percentile %dยบ', $config['percentil']) . __(' of module ') . $agent_name .' / ' . $module_name . ' (' . $percentil_result[$key][0] . ' ' . $unit . ') '; - $series_type[$key] = 'line'; } } } - $graph_values = $temp; - + if(isset($summatory)){ + $params_combined['summatory'] = $summatory; + } + + if(isset($average)){ + $params_combined['average'] = $average; + } + + if(isset($modules_series)){ + $params_combined['modules_series'] = $modules_series; + } + + if(isset($labels)){ + $params_combined['labels'] = $labels; + } + + if(isset($weights)){ + $params_combined['weight_list'] = $weights; + } + + if(!$module_list){ + $module_list = $modules; + } + + if ($sources === false) { + if ($params_combined['return']){ + return false; + } + else{ + ui_print_info_message ( + array ( + 'no_close' => true, + 'message' => __('No items.') + ) + ); + return; + } + } + + $width = $params['width']; + $height = $params['height']; + $homeurl = $params['homeurl']; + $ttl = $params['ttl']; + $background_color = $params['backgroundColor']; + $datelimit = $date_array["start_date"]; + $fixed_font_size = $config['font_size']; + $flash_charts = false; + if($config["fixed_graph"] == false){ $water_mark = array( 'file' => $config['homedir'] . "/images/logo_vertical_water.png", - 'url' => ui_get_full_url("images/logo_vertical_water.png", false, false, false)); + 'url' => ui_get_full_url( + "/images/logo_vertical_water.png", + false, + false, + false + ) + ); } - - //Work around for fixed the agents name with huge size chars. - $fixed_font_size = $config['font_size']; - - //Set graph color - - $color = array(); - - $color[0] = array('border' => '#000000', - 'color' => $config['graph_color1'], - 'alpha' => CHART_DEFAULT_ALPHA); - $color[1] = array('border' => '#000000', - 'color' => $config['graph_color2'], - 'alpha' => CHART_DEFAULT_ALPHA); - $color[2] = array('border' => '#000000', - 'color' => $config['graph_color3'], - 'alpha' => CHART_DEFAULT_ALPHA); - $color[3] = array('border' => '#000000', - 'color' => $config['graph_color4'], - 'alpha' => CHART_DEFAULT_ALPHA); - $color[4] = array('border' => '#000000', - 'color' => $config['graph_color5'], - 'alpha' => CHART_DEFAULT_ALPHA); - $color[5] = array('border' => '#000000', - 'color' => $config['graph_color6'], - 'alpha' => CHART_DEFAULT_ALPHA); - $color[6] = array('border' => '#000000', - 'color' => $config['graph_color7'], - 'alpha' => CHART_DEFAULT_ALPHA); - $color[7] = array('border' => '#000000', - 'color' => $config['graph_color8'], - 'alpha' => CHART_DEFAULT_ALPHA); - $color[8] = array('border' => '#000000', - 'color' => $config['graph_color9'], - 'alpha' => CHART_DEFAULT_ALPHA); - $color[9] = array('border' => '#000000', - 'color' => $config['graph_color10'], - 'alpha' => CHART_DEFAULT_ALPHA); - $color[11] = array('border' => '#000000', - 'color' => COL_GRAPH9, - 'alpha' => CHART_DEFAULT_ALPHA); - $color[12] = array('border' => '#000000', - 'color' => COL_GRAPH10, - 'alpha' => CHART_DEFAULT_ALPHA); - $color[13] = array('border' => '#000000', - 'color' => COL_GRAPH11, - 'alpha' => CHART_DEFAULT_ALPHA); - $color[14] = array('border' => '#000000', - 'color' => COL_GRAPH12, - 'alpha' => CHART_DEFAULT_ALPHA); - $color[15] = array('border' => '#000000', - 'color' => COL_GRAPH13, - 'alpha' => CHART_DEFAULT_ALPHA); - - if($id_widget_dashboard){ - $opcion = unserialize(db_get_value_filter('options','twidget_dashboard',array('id' => $id_widget_dashboard))); - foreach ($module_list as $key => $value) { - if(!empty($opcion[$value])){ - $color[$key]['color'] = $opcion[$value]; - } - } - } - - $threshold_data = array(); - if ($from_interface) { - $yellow_threshold = 0; - $red_threshold = 0; + //XXX arreglar estas + $long_index = ''; - $yellow_up = 0; - $red_up = 0; - - $yellow_inverse = 0; - $red_inverse = 0; - - $compare_warning = false; - $compare_critical = false; - - $do_it_warning_min = true; - $do_it_critical_min = true; - - $do_it_warning_max = true; - $do_it_critical_max = true; - - $do_it_warning_inverse = true; - $do_it_critical_inverse = true; - foreach ($module_list as $index => $id_module) { - // Get module warning_min and critical_min - $warning_min = db_get_value('min_warning','tagente_modulo','id_agente_modulo',$id_module); - $critical_min = db_get_value('min_critical','tagente_modulo','id_agente_modulo',$id_module); - - if ($index == 0) { - $compare_warning = $warning_min; - } - else { - if ($compare_warning != $warning_min) { - $do_it_warning_min = false; - } - } - - if ($index == 0) { - $compare_critical = $critical_min; - } - else { - if ($compare_critical != $critical_min) { - $do_it_critical_min = false; - } - } - } - - if ($do_it_warning_min || $do_it_critical_min) { - foreach ($module_list as $index => $id_module) { - $warning_max = db_get_value('max_warning','tagente_modulo','id_agente_modulo',$id_module); - $critical_max = db_get_value('max_critical','tagente_modulo','id_agente_modulo',$id_module); - - if ($index == 0) { - $yellow_up = $warning_max; - } - else { - if ($yellow_up != $warning_max) { - $do_it_warning_max = false; - } - } - - if ($index == 0) { - $red_up = $critical_max; - } - else { - if ($red_up != $critical_max) { - $do_it_critical_max = false; - } - } - } - } - - if ($do_it_warning_min || $do_it_critical_min) { - foreach ($module_list as $index => $id_module) { - $warning_inverse = db_get_value('warning_inverse','tagente_modulo','id_agente_modulo',$id_module); - $critical_inverse = db_get_value('critical_inverse','tagente_modulo','id_agente_modulo',$id_module); - - if ($index == 0) { - $yellow_inverse = $warning_inverse; - } - else { - if ($yellow_inverse != $warning_inverse) { - $do_it_warning_inverse = false; - } - } - - if ($index == 0) { - $red_inverse = $critical_inverse; - } - else { - if ($red_inverse != $critical_inverse) { - $do_it_critical_inverse = false; - } - } - } - } - - if ($do_it_warning_min && $do_it_warning_max && $do_it_warning_inverse) { - $yellow_threshold = $compare_warning; - $threshold_data['yellow_up'] = $yellow_up; - $threshold_data['yellow_inverse'] = (bool)$yellow_inverse; - } - - if ($do_it_critical_min && $do_it_critical_max && $do_it_critical_inverse) { - $red_threshold = $compare_critical; - $threshold_data['red_up'] = $red_up; - $threshold_data['red_inverse'] = (bool)$red_inverse; - } - } - - //summatory and average series - if($stacked == CUSTOM_GRAPH_AREA || $stacked == CUSTOM_GRAPH_LINE) { - //Fix pdf label - $static_pdf = strpos($module_name_list[0], ' $value) { - $cont = count($value); - $summ = array_sum($value); - array_push($value,$summ); - array_push($value,$summ/$cont); - $graph_values[$key] = $value; - if(!$modules_series) { - array_splice($graph_values[$key],0,count($graph_values[$key])-2); - } - } - - if(!$modules_series) { - if(empty($percentil)) { - array_splice($module_name_list,0,count($module_name_list)); - } else { - array_splice($module_name_list,0,count($module_name_list)-(count($module_name_list)/2)); - } - if($static_pdf === 0) { - array_unshift($module_name_list,'' . __('summatory'). ''); - array_unshift($module_name_list,'' . __('average'). ''); - } else { - array_unshift($module_name_list, __('summatory')); - array_unshift($module_name_list, __('average')); - } - - } else { - if(empty($percentil)) { - if($static_pdf === 0) { - array_push($module_name_list,'' . __('summatory'). ''); - array_push($module_name_list,'' . __('average'). ''); - } else { - array_push($module_name_list, __('summatory')); - array_push($module_name_list, __('average')); - } - - } else { - if($static_pdf === 0) { - array_splice($module_name_list,(count($module_name_list)/2),0,'' . __('average'). ''); - array_splice($module_name_list,(count($module_name_list)/2),0,'' . __('summatory'). ''); - } else { - array_splice($module_name_list,(count($module_name_list)/2),0, __('average')); - array_splice($module_name_list,(count($module_name_list)/2),0,__('summatory')); - } - - } - - } - - } elseif($summatory) { - foreach ($graph_values as $key => $value) { - array_push($value,array_sum($value)); - $graph_values[$key] = $value; - if(!$modules_series){ - array_splice($graph_values[$key],0,count($graph_values[$key])-1); - } - } - - if(!$modules_series) { - if(empty($percentil)) { - array_splice($module_name_list,0,count($module_name_list)); - } else { - array_splice($module_name_list,0,count($module_name_list)-(count($module_name_list)/2)); - } - if($static_pdf === 0) { - array_unshift($module_name_list,'' . __('summatory'). ''); - } else { - array_unshift($module_name_list, __('summatory')); - } - - } else { - if(empty($percentil)) { - if($static_pdf === 0) { - array_push($module_name_list,'' . __('summatory'). ''); - } else { - array_push($module_name_list,__('summatory')); - } - - } else { - if($static_pdf === 0) { - array_splice($module_name_list,(count($module_name_list)/2),0,'' . __('summatory'). ''); - } else { - array_splice($module_name_list,(count($module_name_list)/2),0,__('summatory')); - } - - } - } - - } elseif($average) { - foreach ($graph_values as $key => $value) { - $summ = array_sum($value) / count($value); - array_push($value,$summ); - $graph_values[$key] = $value; - if(!$modules_series){ - array_splice($graph_values[$key],0,count($graph_values[$key])-1); - } - } - - if(!$modules_series) { - if(empty($percentil)) { - array_splice($module_name_list,0,count($module_name_list)); - } else { - array_splice($module_name_list,0,count($module_name_list)-(count($module_name_list)/2)); - } - if($static_pdf === 0) { - array_unshift($module_name_list,'' . __('average'). ''); - } else { - array_unshift($module_name_list,__('average')); - } - - } else { - if(empty($percentil)) { - if($static_pdf === 0) { - array_push($module_name_list,'' . __('average'). ''); - } else { - array_push($module_name_list,__('average')); - } - - } else { - if($static_pdf === 0) { - array_splice($module_name_list,(count($module_name_list)/2),0,'' . __('average'). ''); - } else { - array_splice($module_name_list,(count($module_name_list)/2),0,__('average')); - } - - } - } - } - } - - switch ($stacked) { - case CUSTOM_GRAPH_AREA: - return area_graph($flash_charts, $graph_values, $width, - $height, $color, $module_name_list, $long_index, - ui_get_full_url("images/image_problem_area_small.png", false, false, false), - $title, "", $homeurl, $water_mark, $config['fontpath'], - $fixed_font_size, $unit, $ttl, array(), array(), $yellow_threshold, $red_threshold, '', - false, '', true, $background_color,$dashboard, $vconsole, 0, $percentil_result, $threshold_data); - break; + switch ($params_combined['stacked']) { default: - case CUSTOM_GRAPH_STACKED_AREA: - return stacked_area_graph($flash_charts, $graph_values, - $width, $height, $color, $module_name_list, $long_index, - ui_get_full_url("images/image_problem_area_small.png", false, false, false), - $title, "", $water_mark, $config['fontpath'], $fixed_font_size, - "", $ttl, $homeurl, $background_color,$dashboard, $vconsole); - break; - case CUSTOM_GRAPH_LINE: - return line_graph($flash_charts, $graph_values, $width, - $height, $color, $module_name_list, $long_index, - ui_get_full_url("images/image_problem_area_small.png", false, false, false), - $title, "", $water_mark, $config['fontpath'], $fixed_font_size, - $unit, $ttl, $homeurl, $background_color, $dashboard, - $vconsole, $series_type, $percentil_result, $yellow_threshold, $red_threshold, $threshold_data); - break; case CUSTOM_GRAPH_STACKED_LINE: - return stacked_line_graph($flash_charts, $graph_values, - $width, $height, $color, $module_name_list, $long_index, - ui_get_full_url("images/image_problem_area_small.png", false, false, false), - "", "", $water_mark, $config['fontpath'], $fixed_font_size, - "", $ttl, $homeurl, $background_color, $dashboard, $vconsole); + case CUSTOM_GRAPH_STACKED_AREA: + case CUSTOM_GRAPH_AREA: + case CUSTOM_GRAPH_LINE: + $date_array = array(); + $date_array["period"] = $params['period']; + $date_array["final_date"] = $params['date']; + $date_array["start_date"] = $params['date'] - $params['period']; + + $i=0; + $array_data = array(); + foreach ($module_list as $key => $agent_module_id) { + $module_data = db_get_row_sql ( + 'SELECT * FROM tagente_modulo + WHERE id_agente_modulo = ' . + $agent_module_id + ); + + $data_module_graph = array(); + $data_module_graph['history_db'] = db_search_in_history_db($date_array["start_date"]); + $data_module_graph['agent_name'] = modules_get_agentmodule_agent_name($agent_module_id); + $data_module_graph['agent_alias'] = modules_get_agentmodule_agent_alias($agent_module_id); + $data_module_graph['agent_id'] = $module_data['id_agente']; + $data_module_graph['module_name'] = $module_data['nombre']; + $data_module_graph['id_module_type'] = $module_data['id_tipo_modulo']; + $data_module_graph['module_type'] = modules_get_moduletype_name($data_module_graph['id_module_type']); + $data_module_graph['uncompressed'] = is_module_uncompressed($data_module_graph['module_type']); + $data_module_graph['w_min'] = $module_data['min_warning']; + $data_module_graph['w_max'] = $module_data['max_warning']; + $data_module_graph['w_inv'] = $module_data['warning_inverse']; + $data_module_graph['c_min'] = $module_data['min_critical']; + $data_module_graph['c_max'] = $module_data['max_critical']; + $data_module_graph['c_inv'] = $module_data['critical_inverse']; + $data_module_graph['module_id'] = $agent_module_id; + + //stract data + $array_data_module = grafico_modulo_sparse_data( + $agent_module_id, + $date_array, + $data_module_graph, + $params, + $i + ); + + $series_suffix = $i; + + //convert to array graph and weight + foreach ($array_data_module as $key => $value) { + $array_data[$key] = $value; + if($params_combined['weight_list'][$i] > 1){ + foreach ($value['data'] as $k => $v) { + $array_data[$key]['data'][$k][1] = $v[1] * $params_combined['weight_list'][$i]; + } + } + } + + $max = $array_data['sum' . $i]['max']; + $min = $array_data['sum' . $i]['min']; + $avg = $array_data['sum' . $i]['avg']; + + $percentil_value = $array_data['percentil' . $i]['data'][0][1]; + + if($config["fixed_graph"] == false){ + $water_mark = array( + 'file' => $config['homedir'] . "/images/logo_vertical_water.png", + 'url' => ui_get_full_url("images/logo_vertical_water.png", false, false, false)); + } + + //Work around for fixed the agents name with huge size chars. + $fixed_font_size = $config['font_size']; + + //$array_events_alerts[$series_suffix] = $events; + $i++; + } + + if($params_combined['projection'] && is_array($params_combined['projection'])){ + $array_data['projection']['data']= $params_combined['projection']; + } + + //summatory and average series + if($params_combined['stacked'] == CUSTOM_GRAPH_AREA || $params_combined['stacked'] == CUSTOM_GRAPH_LINE) { + if($params_combined['summatory'] || $params_combined['average']) { + $array_data = combined_graph_summatory_average ( + $array_data, + $params_combined['average'], + $params_combined['summatory'], + $params_combined['modules_series'] + ); + } + } + + $series_type_array = series_type_graph_array( + $array_data, + $params_combined + ); + + $series_type = $series_type_array['series_type']; + $legend = $series_type_array['legend']; + $color = $series_type_array['color']; + + $threshold_data = array(); + if ($params_combined['from_interface']) { + $yellow_threshold = 0; + $red_threshold = 0; + + $yellow_up = 0; + $red_up = 0; + + $yellow_inverse = 0; + $red_inverse = 0; + + $compare_warning = false; + $compare_critical = false; + + $do_it_warning_min = true; + $do_it_critical_min = true; + + $do_it_warning_max = true; + $do_it_critical_max = true; + + $do_it_warning_inverse = true; + $do_it_critical_inverse = true; + + foreach ($module_list as $index => $id_module) { + // Get module warning_min and critical_min + $warning_min = db_get_value('min_warning','tagente_modulo','id_agente_modulo',$id_module); + $critical_min = db_get_value('min_critical','tagente_modulo','id_agente_modulo',$id_module); + + if ($index == 0) { + $compare_warning = $warning_min; + } + else { + if ($compare_warning != $warning_min) { + $do_it_warning_min = false; + } + } + + if ($index == 0) { + $compare_critical = $critical_min; + } + else { + if ($compare_critical != $critical_min) { + $do_it_critical_min = false; + } + } + } + + if ($do_it_warning_min || $do_it_critical_min) { + foreach ($module_list as $index => $id_module) { + $warning_max = db_get_value('max_warning','tagente_modulo','id_agente_modulo',$id_module); + $critical_max = db_get_value('max_critical','tagente_modulo','id_agente_modulo',$id_module); + + if ($index == 0) { + $yellow_up = $warning_max; + } + else { + if ($yellow_up != $warning_max) { + $do_it_warning_max = false; + } + } + + if ($index == 0) { + $red_up = $critical_max; + } + else { + if ($red_up != $critical_max) { + $do_it_critical_max = false; + } + } + } + } + + if ($do_it_warning_min || $do_it_critical_min) { + foreach ($module_list as $index => $id_module) { + $warning_inverse = db_get_value('warning_inverse','tagente_modulo','id_agente_modulo',$id_module); + $critical_inverse = db_get_value('critical_inverse','tagente_modulo','id_agente_modulo',$id_module); + + if ($index == 0) { + $yellow_inverse = $warning_inverse; + } + else { + if ($yellow_inverse != $warning_inverse) { + $do_it_warning_inverse = false; + } + } + + if ($index == 0) { + $red_inverse = $critical_inverse; + } + else { + if ($red_inverse != $critical_inverse) { + $do_it_critical_inverse = false; + } + } + } + } + + if ($do_it_warning_min && $do_it_warning_max && $do_it_warning_inverse) { + $yellow_threshold = $compare_warning; + $threshold_data['yellow_up'] = $yellow_up; + $threshold_data['yellow_inverse'] = (bool)$yellow_inverse; + } + + if ($do_it_critical_min && $do_it_critical_max && $do_it_critical_inverse) { + $red_threshold = $compare_critical; + $threshold_data['red_up'] = $red_up; + $threshold_data['red_inverse'] = (bool)$red_inverse; + } + + $params['threshold_data'] = $threshold_data; + } + + $output = area_graph( + $agent_module_id, + $array_data, + $legend, + $series_type, + $color, + $date_array, + $data_module_graph, + $params, + $water_mark, + $array_events_alerts + ); + break; case CUSTOM_GRAPH_BULLET_CHART_THRESHOLD: case CUSTOM_GRAPH_BULLET_CHART: - return stacked_bullet_chart($flash_charts, $graph_values, - $width, $height, $color, $module_name_list, $long_index, + if($params_combined['stacked'] == CUSTOM_GRAPH_BULLET_CHART_THRESHOLD){ + $acumulador = 0; + foreach ($module_list as $module_item) { + $module = $module_item; + $query_last_value = sprintf(' + SELECT datos + FROM tagente_datos + WHERE id_agente_modulo = %d + AND utimestamp < %d + ORDER BY utimestamp DESC', + $module, $params['date']); + $temp_data = db_get_value_sql($query_last_value); + if ($acumulador < $temp_data){ + $acumulador = $temp_data; + } + } + } + + foreach ($module_list as $module_item) { + $automatic_custom_graph_meta = false; + if ($config['metaconsole']) { + // Automatic custom graph from the report template in metaconsole + if (is_array($module_list[$i])) { + $server = metaconsole_get_connection_by_id ($module_item['server']); + metaconsole_connect($server); + $automatic_custom_graph_meta = true; + } + } + + if ($automatic_custom_graph_meta) + $module = $module_item['module']; + else + $module = $module_item; + + $search_in_history_db = db_search_in_history_db($datelimit); + + $temp[$module] = modules_get_agentmodule($module); + $query_last_value = sprintf(' + SELECT datos + FROM tagente_datos + WHERE id_agente_modulo = %d + AND utimestamp < %d + ORDER BY utimestamp DESC', + $module, $params['date']); + $temp_data = db_get_value_sql($query_last_value); + + if ($temp_data) { + if (is_numeric($temp_data)) + $value = $temp_data; + else + $value = count($value); + } + else { + $value = false; + } + + if ( !empty($params_combined['labels']) && isset($params_combined['labels'][$module]) ){ + $label = io_safe_input($params_combined['labels'][$module]); + }else{ + $alias = db_get_value ("alias","tagente","id_agente",$temp[$module]['id_agente']); + $label = $alias . ': ' . $temp[$module]['nombre']; + } + + $temp[$module]['label'] = $label; + $temp[$module]['value'] = $value; + $temp_max = reporting_get_agentmodule_data_max($module, $params['period'], $params['date']); + if ($temp_max < 0) + $temp_max = 0; + if (isset($acumulador)){ + $temp[$module]['max'] = $acumulador; + }else{ + $temp[$module]['max'] = ($temp_max === false) ? 0 : $temp_max; + } + + $temp_min = reporting_get_agentmodule_data_min($module, $params['period'], $params['date']); + if ($temp_min < 0) + $temp_min = 0; + $temp[$module]['min'] = ($temp_min === false) ? 0 : $temp_min; + + if ($config['metaconsole']) { + // Automatic custom graph from the report template in metaconsole + if (is_array($module_list[0])) { + metaconsole_restore_db(); + } + } + } + + $graph_values = $temp; + + $width = 1024; + $height = 50; + + $color = color_graph_array(); + + $output = stacked_bullet_chart( + $graph_values, + $width, + $height, + $color, + $module_name_list, + $long_index, ui_get_full_url("images/image_problem_area_small.png", false, false, false), - "", "", $water_mark, $config['fontpath'], ($config['font_size']+1), - "", $ttl, $homeurl, $background_color); + "", + "", + $water_mark, + $config['fontpath'], + ($config['font_size']+1), + "", + $ttl, + $homeurl, + $background_color + ); + break; + case CUSTOM_GRAPH_GAUGE: - return stacked_gauge($flash_charts, $graph_values, - $width, $height, $color, $module_name_list, $long_index, + $i = 0; + foreach ($module_list as $module_item) { + $automatic_custom_graph_meta = false; + if ($config['metaconsole']) { + // Automatic custom graph from the report template in metaconsole + if (is_array($module_list[$i])) { + $server = metaconsole_get_connection_by_id ($module_item['server']); + metaconsole_connect($server); + $automatic_custom_graph_meta = true; + } + } + + if ($automatic_custom_graph_meta) + $module = $module_item['module']; + else + $module = $module_item; + + $temp[$module] = modules_get_agentmodule($module); + $query_last_value = sprintf(' + SELECT datos + FROM tagente_datos + WHERE id_agente_modulo = %d + AND utimestamp < %d + ORDER BY utimestamp DESC', + $module, $params['date']); + $temp_data = db_get_value_sql($query_last_value); + if ( $temp_data ) { + if (is_numeric($temp_data)) + $value = $temp_data; + else + $value = count($value); + } + else { + $value = false; + } + $temp[$module]['label'] = ($params_combined['labels'][$module] != '') ? $params_combined['labels'][$module] : $temp[$module]['nombre']; + + $temp[$module]['value'] = $value; + $temp[$module]['label'] = ui_print_truncate_text($temp[$module]['label'],"module_small",false,true,false,".."); + + if ($temp[$module]['unit'] == '%') { + $temp[$module]['min'] = 0; + $temp[$module]['max'] = 100; + } + else { + $min = $temp[$module]['min']; + if ($temp[$module]['max'] == 0) + $max = reporting_get_agentmodule_data_max($module, $params['period'], $params['date']); + else + $max = $temp[$module]['max']; + $temp[$module]['min'] = ($min == 0 ) ? 0 : $min; + $temp[$module]['max'] = ($max == 0 ) ? 100 : $max; + } + $temp[$module]['gauge'] = uniqid('gauge_'); + + if ($config['metaconsole']) { + // Automatic custom graph from the report template in metaconsole + if (is_array($module_list[0])) { + metaconsole_restore_db(); + } + } + $i++; + } + + $graph_values = $temp; + + $color = color_graph_array(); + + $width = 200; + $height = 200; + + $output = stacked_gauge( + $graph_values, + $width, + $height, + $color, + $module_name_list, ui_get_full_url("images/image_problem_area_small.png", false, false, false), - "", "", $water_mark, $config['fontpath'], $fixed_font_size, - "", $ttl, $homeurl, $background_color); + $config['fontpath'], + $fixed_font_size, + "", + $homeurl + ); + + break; + case CUSTOM_GRAPH_HBARS: + case CUSTOM_GRAPH_VBARS: + $label = ''; + foreach ($module_list as $module_item) { + $automatic_custom_graph_meta = false; + if ($config['metaconsole']) { + // Automatic custom graph from the report template in metaconsole + if (is_array($module_list[$i])) { + $server = metaconsole_get_connection_by_id ($module_item['server']); + metaconsole_connect($server); + $automatic_custom_graph_meta = true; + } + } + + if ($automatic_custom_graph_meta) + $module = $module_item['module']; + else + $module = $module_item; + + $module_data = modules_get_agentmodule($module); + $query_last_value = sprintf(' + SELECT datos + FROM tagente_datos + WHERE id_agente_modulo = %d + AND utimestamp < %d + ORDER BY utimestamp DESC', + $module, $params['date']); + $temp_data = db_get_value_sql($query_last_value); + + $agent_name = io_safe_output( + modules_get_agentmodule_agent_name ($module)); + + if (!empty($params_combined['labels']) && isset($params_combined['labels'][$module]) ){ + $label = $params_combined['labels'][$module]; + }else { + $alias = db_get_value ("alias","tagente","id_agente",$module_data['id_agente']); + $label = $alias . " - " .$module_data['nombre']; + } + + $temp[$label]['g'] = round($temp_data,4); + + if ($config['metaconsole']) { + // Automatic custom graph from the report template in metaconsole + if (is_array($module_list[0])) { + metaconsole_restore_db(); + } + } + } + + $color = color_graph_array(); + + $graph_values = $temp; + + $width = 1024; + $height = 500; + $flash_charts = true; + + if($params_combined['stacked'] == CUSTOM_GRAPH_HBARS){ + $output = hbar_graph( + true, + $graph_values, + $width, + $height, + $color, + $module_name_list, + $long_index, + ui_get_full_url("images/image_problem_area_small.png", false, false, false), + "", + "", + $water_mark, + $config['fontpath'], + $fixed_font_size, + "", + $ttl, + $homeurl, + $background_color, + 'black' + ); + } + + if($params_combined['stacked'] == CUSTOM_GRAPH_VBARS){ + $output = vbar_graph( + true, + $graph_values, + $width, + $height, + $color, + $module_name_list, + $long_index, + ui_get_full_url("images/image_problem_area_small.png", false, false, false), + "", + "", + $water_mark, + $config['fontpath'], + $fixed_font_size, + "", + $ttl, + $homeurl, + $background_color, + true, + false, + "black" + ); + } + break; case CUSTOM_GRAPH_THERMOMETER: - return stacked_thermometers($flash_charts, $graph_values, - $width, $height, $color, $module_name_list, $long_index, + $datelimit = $params['date'] - $params['period']; + $i = 0; + foreach ($module_list as $module_item) { + $automatic_custom_graph_meta = false; + if ($config['metaconsole']) { + // Automatic custom graph from the report template in metaconsole + if (is_array($module_list[$i])) { + $server = metaconsole_get_connection_by_id ($module_item['server']); + metaconsole_connect($server); + $automatic_custom_graph_meta = true; + } + } + + if ($automatic_custom_graph_meta) + $module = $module_item['module']; + else + $module = $module_item; + + $temp[$module] = modules_get_agentmodule($module); + $query_last_value = sprintf(' + SELECT datos + FROM tagente_datos + WHERE id_agente_modulo = %d + AND utimestamp < %d + ORDER BY utimestamp DESC', + $module, $params['date']); + $temp_data = db_get_value_sql($query_last_value); + if ( $temp_data ) { + if (is_numeric($temp_data)) + $value = $temp_data; + else + $value = count($value); + } + else { + $value = false; + } + $temp[$module]['label'] = ($labels[$module] != '') ? $labels[$module] : $temp[$module]['nombre']; + $temp[$module]['value'] = $value; + $temp[$module]['label'] = ui_print_truncate_text($temp[$module]['label'],"module_small",false,true,false,".."); + + if ($temp[$module]['unit'] == '%') { + $temp[$module]['min'] = 0; + $temp[$module]['max'] = 100; + } + else { + $min = $temp[$module]['min']; + if ($temp[$module]['max'] == 0) + $max = reporting_get_agentmodule_data_max($module,$params['period'],$params['date']); + else + $max = $temp[$module]['max']; + $temp[$module]['min'] = ($min == 0 ) ? 0 : $min; + $temp[$module]['max'] = ($max == 0 ) ? 100 : $max; + } + $temp[$module]['gauge'] = uniqid('gauge_'); + + if ($config['metaconsole']) { + // Automatic custom graph from the report template in metaconsole + if (is_array($module_list[0])) { + metaconsole_restore_db(); + } + } + $i++; + + $color = color_graph_array(); + + $graph_values = $temp; + + return stacked_thermometers( + $flash_charts, + $graph_values, + $width, + $height, + $color, + $module_name_list, + $long_index, ui_get_full_url("images/image_problem_area_small.png", false, false, false), - "", "", $water_mark, $config['fontpath'], $fixed_font_size, - "", $ttl, $homeurl, $background_color); - break; - case CUSTOM_GRAPH_HBARS: - return hbar_graph($flash_charts, $graph_values, - $width, $height, $color, $module_name_list, $long_index, - ui_get_full_url("images/image_problem_area_small.png", false, false, false), - "", "", $water_mark, $config['fontpath'], $fixed_font_size, - "", $ttl, $homeurl, $background_color, 'black'); - break; - case CUSTOM_GRAPH_VBARS: - return vbar_graph($flash_charts, $graph_values, - $width, $height, $color, $module_name_list, $long_index, - ui_get_full_url("images/image_problem_area_small.png", false, false, false), - "", "", $water_mark, $config['fontpath'], $fixed_font_size, - "", $ttl, $homeurl, $background_color, true, false, "black"); + "", + "", + $water_mark, + $config['fontpath'], + $fixed_font_size, + "", + $ttl, + $homeurl, + $background_color + ); + } break; case CUSTOM_GRAPH_PIE: - return ring_graph($flash_charts, $graph_values, $width, $height, - $others_str, $homeurl, $water_mark, $config['fontpath'], - ($config['font_size']+1), $ttl, false, $color, false,$background_color); + $total_modules = 0; + foreach ($module_list as $module_item) { + $automatic_custom_graph_meta = false; + if ($config['metaconsole']) { + // Automatic custom graph from the report template in metaconsole + if (is_array($module_list[$i])) { + $server = metaconsole_get_connection_by_id ($module_item['server']); + metaconsole_connect($server); + $automatic_custom_graph_meta = true; + } + } + + if ($automatic_custom_graph_meta) + $module = $module_item['module']; + else + $module = $module_item; + + $data_module = modules_get_agentmodule($module); + $query_last_value = sprintf(' + SELECT datos + FROM tagente_datos + WHERE id_agente_modulo = %d + AND utimestamp > %d + AND utimestamp < %d + ORDER BY utimestamp DESC', + $module, $datelimit, $params['date']); + $temp_data = db_get_value_sql($query_last_value); + + if ( $temp_data ){ + if (is_numeric($temp_data)) + $value = $temp_data; + else + $value = count($value); + } + else { + $value = false; + } + $total_modules += $value; + + if ( !empty($params_combined['labels']) && isset($params_combined['labels'][$module]) ){ + $label = io_safe_output($params_combined['labels'][$module]); + }else { + $alias = db_get_value ("alias","tagente","id_agente",$data_module['id_agente']); + $label = io_safe_output($alias . ": " . $data_module['nombre']); + } + + $temp[$label] = array('value'=>$value, + 'unit'=>$data_module['unit']); + if ($config['metaconsole']) { + // Automatic custom graph from the report template in metaconsole + if (is_array($module_list[0])) { + metaconsole_restore_db(); + } + } + } + + $temp['total_modules'] = $total_modules; + + $graph_values = $temp; + + $width = 1024; + $height = 500; + + $color = color_graph_array(); + + $output = ring_graph( + true, + $graph_values, + $width, + $height, + $others_str, + $homeurl, + $water_mark, + $config['fontpath'], + ($config['font_size']+1), + $ttl, + false, + $color, + false, + $background_color + ); + break; } -} -function fullscale_data_combined($module_list, $period, $date, $flash_charts, $percentil){ - global $config; - // Set variables - if ($date == 0){ - $date = get_system_time(); + if ($params_combined['return']){ + return $output; } - $datelimit = $date - $period; - $count_data_all = 0; + echo $output; +} - foreach ($module_list as $key_module => $value_module) { - if (!is_null($percentil) && $percentil) { - $array_percentil = array(); - } - - if (is_metaconsole() && is_array($value_module)) { - $server = metaconsole_get_connection_by_id ($value_module['server']); - metaconsole_connect($server); - $previous_data = modules_get_previous_data ($value_module['module'], $datelimit); - $data_uncompress = db_uncompress_module_data($value_module['module'], $datelimit, $date); - metaconsole_restore_db(); - } - else{ - $previous_data = modules_get_previous_data ($value_module, $datelimit); - $data_uncompress = db_uncompress_module_data($value_module, $datelimit, $date); - } - - foreach ($data_uncompress as $key_data => $value_data) { - foreach ($value_data['data'] as $k => $v) { - $real_date = $v['utimestamp']; - if(!isset($v['datos'])){ - $v['datos'] = $previous_data; +function combined_graph_summatory_average ($array_data, $average = false, $summatory = false, $modules_series = false, $baseline = false){ + if(isset($array_data) && is_array($array_data)){ + foreach ($array_data as $key => $value) { + if(strpos($key, 'sum') !== false){ + $data_array_reverse[$key] = array_reverse($value['data']); + if(!$modules_series) { + unset($array_data[$key]); } - else{ - $previous_data = $v['datos']; - } - - if (!is_null($percentil) && $percentil) { - $array_percentil[] = $v['datos']; - } - - $data_all[$real_date][$key_module] = $v['datos']; } } - if (!is_null($percentil) && $percentil) { - $percentil_value = get_percentile($config['percentil'], $array_percentil); - $percentil_result[$key_module] = array_fill (0, count($data_all), $percentil_value); - if(count($data_all) > $count_data_all){ - $count_data_all = count($data_all); + if(isset($data_array_reverse) && is_array($data_array_reverse)){ + $array_sum_reverse = array(); + $array_avg_reverse = array(); + $data_array_prev = false; + $data_array_pop = array(); + $count = 0; + + while(count($data_array_reverse['sum0']) > 0){ + foreach ($data_array_reverse as $key_reverse => $value_reverse) { + if(is_array($value_reverse) && count($value_reverse) > 0){ + $data_array_pop[$key_reverse] = array_pop($data_array_reverse[$key_reverse]); + } } - } - } - if (!is_null($percentil) && $percentil) { - foreach ($percentil_result as $k => $v){ - if(count($v) < $count_data_all){ - $percentil_result[$k] = array_fill (0, $count_data_all, $v[0]); + if(isset($data_array_pop) && is_array($data_array_pop)){ + $acum_data = 0; + $acum_array = array(); + $sum_data = 0; + $count_pop = 0; + foreach ($data_array_pop as $key_pop => $value_pop) { + if( $value_pop[0] > $acum_data ){ + if($acum_data != 0){ + $sum_data = $sum_data + $data_array_prev[$key_pop][1]; + $data_array_reverse[$key_pop][] = $value_pop; + $data_array_prev[$acum_key] = $acum_array; + } + else{ + if($data_array_prev[$key_pop] == false){ + $data_array_prev[$key_pop] = $value_pop; + } + $acum_key = $key_pop; + $acum_data = $value_pop[0]; + $acum_array = $value_pop; + $sum_data = $value_pop[1]; + } + } + elseif($value_pop[0] < $acum_data){ + $sum_data = $sum_data + $data_array_prev[$key_pop][1]; + $data_array_reverse[$acum_key][] = $acum_array; + $data_array_prev[$key_pop] = $value_pop; + $acum_key = $key_pop; + $acum_data = $value_pop[0]; + $acum_array = $value_pop; + } + elseif($value_pop[0] == $acum_data){ + $data_array_prev[$key_pop] = $value_pop; + $sum_data += $value_pop[1]; + } + $count_pop++; + } + if($summatory){ + $array_sum_reverse[$count][0] = $acum_data; + $array_sum_reverse[$count][1] = $sum_data; + } + if($average){ + $array_avg_reverse[$count][0] = $acum_data; + $array_avg_reverse[$count][1] = $sum_data / $count_pop; + } + } + $count++; } - } - } - $data_prev = array(); - $data_all_rev = array(); - ksort($data_all); + if($summatory && isset($array_sum_reverse) && is_array($array_sum_reverse) && count($array_sum_reverse) > 0){ + $array_data['summatory']['data'] = $array_sum_reverse; + $array_data['summatory']['color'] = 'purple'; + } - foreach ($data_all as $key => $value) { - if($flash_charts) { - $real_date = date("Y M d H:i:s", $key); - } - else{ - $real_date = $key; - } - - foreach ($module_list as $key_module => $value_module) { - if(!isset($value[$key_module])){ - $data_all[$key][$key_module] = $data_prev[$key_module]; + if($average && isset($array_avg_reverse) && is_array($array_avg_reverse) && count($array_avg_reverse) > 0){ + if($baseline){ + $array_data['baseline']['data'] = $array_avg_reverse; + $array_data['baseline']['color'] = 'green'; } else{ - $data_prev[$key_module] = $value[$key_module]; + $array_data['average']['data'] = $array_avg_reverse; + $array_data['average']['color'] = 'orange'; } + } + } - $data_all_rev[$real_date] = $data_all[$key]; + return $array_data; } - - if (!is_null($percentil) && $percentil) { - $data_all_rev['percentil'] = $percentil_result; + else{ + return false; } - - return $data_all_rev; } + + + + + + + + + + + + + + + + + + /** * Print a graph with access data of agents - * + * * @param integer id_agent Agent ID * @param integer width pie graph width * @param integer height pie graph height * @param integer period time period * @param bool return or echo the result flag */ -function graphic_agentaccess ($id_agent, $width, $height, $period = 0, $return = false) { +function graphic_agentaccess ($id_agent, $width, $height, $period = 0, $return = false, $tree = false) { global $config; global $graphic_type; - - - $data = array (); - - $resolution = $config["graph_res"] * ($period * 2 / $width); // Number of "slices" we want in graph - - $interval = (int) ($period / $resolution); - $date = get_system_time (); - $datelimit = $date - $period; - $periodtime = floor ($period / $interval); - $time = array (); - $data = array (); - - $empty_data = true; - for ($i = 0; $i < $interval; $i++) { - $bottom = $datelimit + ($periodtime * $i); - if (! $graphic_type) { - $name = date('G:i', $bottom); + + $date = get_system_time(); + $datelimit = $date - $period; + $data_array = array (); + + $data = db_get_all_rows_sql( + "SELECT count(*) as data, min(utimestamp) as utimestamp + FROM tagent_access + WHERE id_agent = $id_agent + AND utimestamp > $datelimit + AND utimestamp < $date + GROUP by ROUND(utimestamp / 300)" + ); + + if(isset($data) && is_array($data)){ + foreach ($data as $key => $value) { + $data_array['Agent access']['data'][$key][0] = $value['utimestamp'] * 1000; + $data_array['Agent access']['data'][$key][1] = $value['data']; } - else { - $name = $bottom; + $data_array['Agent access']['color'] = 'green'; + } + else{ + if ($return) { + return graph_nodata_image($width, $height); } - - $top = $datelimit + ($periodtime * ($i + 1)); - switch ($config["dbtype"]) { - case "mysql": - case "postgresql": - $data[$name]['data'] = (int) db_get_value_filter ('COUNT(*)', - 'tagent_access', - array ('id_agent' => $id_agent, - 'utimestamp > '.$bottom, - 'utimestamp < '.$top)); - break; - case "oracle": - $data[$name]['data'] = (int) db_get_value_filter ('count(*)', - 'tagent_access', - array ('id_agent' => $id_agent, - 'utimestamp > '.$bottom, - 'utimestamp < '.$top)); - break; - } - - if ($data[$name]['data'] != 0) { - $empty_data = false; + else{ + echo graph_nodata_image($width, $height); } } - + if($config["fixed_graph"] == false){ $water_mark = array('file' => $config['homedir'] . "/images/logo_vertical_water.png", 'url' => ui_get_full_url("images/logo_vertical_water.png", false, false, false)); } - if ($empty_data) { - $out = graph_nodata_image($width, $height); - } - else { - $out = area_graph($config['flash_charts'], $data, $width, $height, null, null, null, - ui_get_full_url("images/image_problem_area_small.png", false, false, false), - "", "", ui_get_full_url(false, false, false, false), $water_mark, - $config['fontpath'], $config['font_size'], "", 1, array(), array(), 0, 0, '', false, '', false); - } - + $params =array( + 'agent_module_id' => false, + 'period' => $period, + 'width' => $width, + 'height' => $height, + 'unit' => $unit, + 'only_image' => $only_image, + 'homeurl' => $homeurl, + 'menu' => true, + 'backgroundColor' => 'white', + 'type_graph' => 'area', + 'font' => $config['fontpath'], + 'font-size' => $config['font_size'], + 'array_data_create' => $data_array, + 'show_overview' => false, + 'show_export_csv' => false, + 'vconsole' => $tree + ); + if ($return) { - return $out; + return grafico_modulo_sparse($params); } else { - echo $out; + echo grafico_modulo_sparse($params); } } @@ -3467,7 +3154,6 @@ function graphic_incident_group () { /** * Print a graph with access data of agents - * * @param integer id_agent Agent ID * @param integer width pie graph width * @param integer height pie graph height @@ -4243,9 +3929,9 @@ function graph_graphic_agentevents ($id_agent, $width, $height, $period = 0, $ho function graph_graphic_moduleevents ($id_agent, $id_module, $width, $height, $period = 0, $homeurl, $return = false) { global $config; global $graphic_type; - + $data = array (); - + $resolution = $config['graph_res'] * ($period * 2 / $width); // Number of "slices" we want in graph $interval = (int) ($period / $resolution); $date = get_system_time (); @@ -4255,7 +3941,7 @@ function graph_graphic_moduleevents ($id_agent, $id_module, $width, $height, $pe $data = array (); $legend = array(); $full_legend = array(); - + $cont = 0; for ($i = 0; $i < $interval; $i++) { $bottom = $datelimit + ($periodtime * $i); @@ -4270,13 +3956,13 @@ function graph_graphic_moduleevents ($id_agent, $id_module, $width, $height, $pe else { $name = $bottom; } - + // Show less values in legend if ($cont == 0 or $cont % 2) $legend[$cont] = $name; - + $full_legend[$cont] = $name; - + $top = $datelimit + ($periodtime * ($i + 1)); $event_filter = array ('id_agente' => $id_agent, @@ -4307,16 +3993,16 @@ function graph_graphic_moduleevents ($id_agent, $id_module, $width, $height, $pe } $cont++; } - + $colors = array(1 => COL_NORMAL, 2 => COL_WARNING, 3 => COL_CRITICAL, 4 => COL_UNKNOWN); - + // Draw slicebar graph if ($config['flash_charts']) { $out = flot_slicesbar_graph($data, $period, $width, $height, $full_legend, $colors, $config['fontpath'], $config['round_corner'], $homeurl, '', '', false, $id_agent); } else { $out = slicesbar_graph($data, $period, $width, $height, $colors, $config['fontpath'], $config['round_corner'], $homeurl); - + // Draw legend $out .= "
"; $out .= " "; @@ -4325,7 +4011,7 @@ function graph_graphic_moduleevents ($id_agent, $id_module, $width, $height, $pe $out .= " "; } } - + if ($return) { return $out; } @@ -4337,933 +4023,261 @@ function graph_graphic_moduleevents ($id_agent, $id_module, $width, $height, $pe // Prints an error image function fs_error_image ($width = 300, $height = 110) { global $config; - return graph_nodata_image($width, $height, 'area'); } -function grafico_modulo_boolean_data ($agent_module_id, $period, $show_events, - $unit_name, $show_alerts, $avg_only = 0, - $date = 0, $series_suffix = '', $series_suffix_str = '', $show_unknown = false, - $fullscale = false, $flash_chart = true) { +function fullscale_data ( + $agent_module_id, $date_array, + $show_unknown = 0, $show_percentil = 0, + $series_suffix, + $compare = false){ global $config; - global $chart; - global $color; - global $legend; - global $long_index; - global $series_type; - global $chart_extra_data; - - $chart = array(); - $color = array(); - $legend = array(); - $long_index = array(); - $start_unknown = false; - - // Set variables - if ($date == 0) $date = get_system_time(); - $datelimit = $date - $period; - $search_in_history_db = db_search_in_history_db($datelimit); - $resolution = $config['graph_res'] * 50; //Number of points of the graph - $interval = (float) ($period / $resolution); - $agent_name = modules_get_agentmodule_agent_name ($agent_module_id); - $agent_id = agents_get_agent_id ($agent_name); - $module_name = modules_get_agentmodule_name ($agent_module_id); - $id_module_type = modules_get_agentmodule_type ($agent_module_id); - $module_type = modules_get_moduletype_name ($id_module_type); - $uncompressed_module = is_module_uncompressed ($module_type); - if ($uncompressed_module) { - $avg_only = 1; - } - - // Get event data (contains alert data too) - if ($show_unknown == 1 || $show_events == 1 || $show_alerts == 1) { - $events = db_get_all_rows_filter( - 'tevento', - array ('id_agentmodule' => $agent_module_id, - "utimestamp > $datelimit", - "utimestamp < $date", - 'order' => 'utimestamp ASC' - ), - array ('evento', 'utimestamp', 'event_type', 'id_evento'), - 'AND', - $search_in_history_db - ); - - // Get the last event after inverval to know if graph start on unknown - $prev_event = db_get_row_filter ( - 'tevento', - array ('id_agentmodule' => $agent_module_id, - "utimestamp <= $datelimit", - 'order' => 'utimestamp DESC' - ), - false, - 'AND', - $search_in_history_db + $data_uncompress = + db_uncompress_module_data( + $agent_module_id, + $date_array['start_date'], + $date_array['final_date'] ); - if (isset($prev_event['event_type']) && $prev_event['event_type'] == 'going_unknown') { - $start_unknown = true; - } - - if ($events === false) { - $events = array (); - } - } - - if ($fullscale) { - fullscale_data( $chart, $chart_data_extra, $long_index, $series_type, - $agent_module_id, $datelimit, $date, $events, - $show_events, $show_unknown, $show_alerts, - $series_suffix, $percentil, $flash_chart,true); - if (count($chart) > $resolution) { - $resolution = count($chart); //Number of points of the graph - $interval = (int) ($period / $resolution); - } - $max_value=1; - } - else { - // Get module data - $data = db_get_all_rows_filter ('tagente_datos', - array ('id_agente_modulo' => $agent_module_id, - "utimestamp > $datelimit", - "utimestamp < $date", - 'order' => 'utimestamp ASC'), - array ('datos', 'utimestamp'), 'AND', $search_in_history_db); - - - if ($data === false) { - $data = array (); - } - - // Uncompressed module data - if ($uncompressed_module) { - $min_necessary = 1; - } - else { - // Get previous data - $previous_data = modules_get_previous_data ($agent_module_id, $datelimit); - if ($previous_data !== false) { - $previous_data['utimestamp'] = $datelimit; - array_unshift ($data, $previous_data); - } - - // Get next data - $nextData = modules_get_next_data ($agent_module_id, $date); - if ($nextData !== false) { - array_push ($data, $nextData); - } - else if (count ($data) > 0) { - // Propagate the last known data to the end of the interval - $nextData = array_pop ($data); - array_push ($data, $nextData); - $nextData['utimestamp'] = $date; - array_push ($data, $nextData); - } - - $min_necessary = 2; - } - - // Check available data - if (count ($data) < $min_necessary) { - if (!$graphic_type) { - return fs_error_image (); - } - graphic_error (); - } - - // Data iterator - $j = 0; - - // Event iterator - $k = 0; - - // Set initial conditions - if ($data[0]['utimestamp'] == $datelimit) { - $previous_data = $data[0]['datos']; - $j++; - } - else { - $previous_data = 0; - } - - $max_value = 0; - // Calculate chart data - $last_known = $previous_data; - $first_events_unknown = $start_unknown; - - for ($i = 0; $i <= $resolution; $i++) { - $timestamp = $datelimit + ($interval * $i); - - - $zero = 0; - $total = 0; - $count = 0; - - // Read data that falls in the current interval - while (isset ($data[$j]) && - $data[$j]['utimestamp'] >= $timestamp && - $data[$j]['utimestamp'] <= ($timestamp + $interval)) { - if ($data[$j]['datos'] == 0) { - $zero = 1; - } - else { - $total += $data[$j]['datos']; - $count++; - } - - $last_known = $data[$j]['datos']; - - if ($show_unknown && $data[$j]['unknown']){ - $is_unknown = true; - } - $j++; - } - - // Average - if ($count > 0) { - $total /= $count; - } - - // Read events and alerts that fall in the current interval - $event_value = 0; - $alert_value = 0; - $unknown_value = 0; - // Is the first point of a unknown interval - $check_unknown = false; - $first_unknown = false; - if($first_events_unknown){ - $is_unknown = true; - } - - $event_ids = array(); - $alert_ids = array(); - while (isset ($events[$k]) && - $events[$k]['utimestamp'] >= $timestamp && - $events[$k]['utimestamp'] < ($timestamp + $interval)) { - if ($show_events == 1) { - $event_value++; - $event_ids[] = $events[$k]['id_evento']; - } - if ($show_alerts == 1 && substr ($events[$k]['event_type'], 0, 5) == 'alert') { - $alert_value++; - $alert_ids[] = $events[$k]['id_evento']; - } - if ($show_unknown) { - if ($events[$k]['event_type'] == 'going_unknown') { - if ($is_unknown == false) { - $first_unknown = true; - } - $is_unknown = true; - $check_unknown = true; - } - else if (substr ($events[$k]['event_type'], 0, 5) == 'going') { - $first_events_unknown = false; - $first_unknown = false; - $is_unknown = false; - } - } - $k++; - } - - // In some cases, can be marked as known because a recovery event - // was found in same interval. For this cases first_unknown is - // checked too - if ($is_unknown || $first_unknown) { - $unknown_value++; - } - - // Set the title and time format - if ($period <= SECONDS_6HOURS) { - $time_format = 'H:i:s'; - } - elseif ($period < SECONDS_1DAY) { - $time_format = 'H:i'; - } - elseif ($period < SECONDS_15DAYS) { - $time_format = 'M d H:i'; - } - elseif ($period < SECONDS_1MONTH) { - $time_format = 'M d H\h'; - } - else { - $time_format = 'M d H\h'; - } - - $timestamp_short = date($time_format, $timestamp); - $long_index[$timestamp_short] = date( - html_entity_decode($config['date_format'], ENT_QUOTES, "UTF-8"), $timestamp); - if (!$fullscale) { - $timestamp = $timestamp_short; - } - ///////////////////////////////////////////////////////////////// - - if ($total > $max_value) { - $max_value = $total; - } - // Data - if ($show_events) { - if (!isset($chart[$timestamp]['event'.$series_suffix])) { - $chart[$timestamp]['event'.$series_suffix] = 0; - } - - $chart[$timestamp]['event'.$series_suffix] += $event_value; - $series_type['event'.$series_suffix] = 'points'; - } - if ($show_alerts) { - if (!isset($chart[$timestamp]['alert'.$series_suffix])) { - $chart[$timestamp]['alert'.$series_suffix] = 0; - } - - $chart[$timestamp]['alert'.$series_suffix] += $alert_value; - $series_type['alert'.$series_suffix] = 'points'; - } - - // Data and zeroes (draw a step) - if ($zero == 1 && $count > 0) { - $chart[$timestamp]['sum'.$series_suffix] = 0; - } - else if ($zero == 1) { // Just zeros - $chart[$timestamp]['sum'.$series_suffix] = 0; - } - else if ($count > 0) { // No zeros - $chart[$timestamp]['sum'.$series_suffix] = $total; - } - else { // Compressed data - if ($uncompressed_module || ($timestamp > time ()) || $is_unknown) { - $chart[$timestamp]['sum'.$series_suffix] = 0; - } - else { - $chart[$timestamp]['sum'.$series_suffix] = $last_known; - } - } - - $series_type['sum' . $series_suffix] = 'boolean'; - - if ($show_unknown) { - if (!isset($chart[$timestamp]['unknown'.$series_suffix])) { - $chart[$timestamp]['unknown'.$series_suffix] = 0; - } - $chart[$timestamp]['unknown'.$series_suffix] = $unknown_value; - - if($unknown_value == 0 && $check_unknown == true){ - $chart[$timestamp]['unknown'.$series_suffix] = 1; - $check_unknown = false; - } - - $series_type['unknown'.$series_suffix] = 'unknown'; - } - - if (!empty($event_ids)) { - $chart_extra_data[count($chart)-1]['events'] = implode(',',$event_ids); - } - if (!empty($alert_ids)) { - $chart_extra_data[count($chart)-1]['alerts'] = implode(',',$alert_ids); - } - } - } - - if (empty($unit_name)) { - $unit = modules_get_unit($agent_module_id); - } - else - $unit = $unit_name; - - // Get min, max and avg (less efficient but centralized for all modules and reports) - $graph_stats = get_statwin_graph_statistics($chart, $series_suffix); - - // Fix event and alert scale - $max_value = 1; - foreach ($chart as $timestamp => $chart_data) { - if ($show_events) { - if ($chart_data['event'.$series_suffix] > 0) { - $chart[$timestamp]['event'.$series_suffix] = $max_value * 1.2; - } - } - if ($show_alerts) { - if ($chart_data['alert'.$series_suffix] > 0) { - $chart[$timestamp]['alert'.$series_suffix] = $max_value * 1.10; - } - } - if ($show_unknown) { - if ($chart_data['unknown'.$series_suffix] > 0) { - $chart[$timestamp]['unknown'.$series_suffix] = $max_value * 1.05; - } - } - } - /////////////////////////////////////////////////// - if(!$fullscale){ - // Set the title and time format - if ($period <= SECONDS_6HOURS) { - $time_format = 'H:i:s'; - } - elseif ($period < SECONDS_1DAY) { - $time_format = 'H:i'; - } - elseif ($period < SECONDS_15DAYS) { - $time_format = 'M d H:i'; - } - elseif ($period < SECONDS_1MONTH) { - $time_format = 'M d H\h'; - } - elseif ($period < SECONDS_6MONTHS) { - $time_format = "M d H\h"; - } - else { - $time_format = 'M d H\h'; - } - } - // Flash chart - $caption = __('Max. Value').$series_suffix_str . ': ' . $graph_stats['sum']['max'] . ' ' . __('Avg. Value').$series_suffix_str . - ': ' . $graph_stats['sum']['avg'] . ' ' . __('Min. Value').$series_suffix_str . ': ' . $graph_stats['sum']['min'] . ' ' . __('Units').$series_suffix_str . ': ' . $unit; - - ///////////////////////////////////////////////////////////////////////////////////////// - if ($show_events) { - $legend['event'.$series_suffix] = __('Events').$series_suffix_str; - $chart_extra_data['legend_events'] = $legend['event'.$series_suffix]; - } - if ($show_alerts) { - $legend['alert'.$series_suffix] = __('Alerts').$series_suffix_str; - $chart_extra_data['legend_alerts'] = $legend['alert'.$series_suffix]; - } - - if(!$fullscale){ - $legend['sum'.$series_suffix] = __('Avg').$series_suffix_str.': '.__('Last').': '.remove_right_zeros(number_format($graph_stats['sum']['last'], $config['graph_precision'])).' '.$unit.' ; '.__('Avg').': '.remove_right_zeros(number_format($graph_stats['sum']['avg'], $config['graph_precision'])).' '.$unit.' ; '.__('Max').': '.remove_right_zeros(number_format($graph_stats['sum']['max'], $config['graph_precision'])).' '.$unit.' ; '.__('Min').': '.remove_right_zeros(number_format($graph_stats['sum']['min'], $config['graph_precision'])).' '.$unit; - } - else{ - $legend['sum'.$series_suffix] = __('Data'); - } - - if ($show_unknown) { - $legend['unknown'.$series_suffix] = __('Unknown').$series_suffix_str; - $chart_extra_data['legend_unknown'] = $legend['unknown'.$series_suffix]; - } - //$legend['baseline'.$series_suffix] = __('Baseline').$series_suffix_str; - ///////////////////////////////////////////////////////////////////////////////////////// - if ($show_events) { - $color['event'.$series_suffix] = - array('border' => '#ff0000', 'color' => '#ff0000', - 'alpha' => CHART_DEFAULT_ALPHA); - } - if ($show_alerts) { - $color['alert'.$series_suffix] = - array('border' => '#ff7f00', 'color' => '#ff7f00', - 'alpha' => CHART_DEFAULT_ALPHA); - } - $color['max'.$series_suffix] = - array('border' => '#000000', 'color' => $config['graph_color3'], - 'alpha' => CHART_DEFAULT_ALPHA); - $color['sum'.$series_suffix] = - array('border' => '#000000', 'color' => $config['graph_color2'], - 'alpha' => CHART_DEFAULT_ALPHA); - $color['min'.$series_suffix] = - array('border' => '#000000', 'color' => $config['graph_color1'], - 'alpha' => CHART_DEFAULT_ALPHA); - if ($show_unknown) { - $color['unknown'.$series_suffix] = - array('border' => '#999999', 'color' => '#999999', - 'alpha' => CHART_DEFAULT_ALPHA); - } -} - -function fullscale_data ( &$chart_data, &$chart_extra_data, &$long_index, - $series_type, $agent_module_id, $datelimit, $date, - $events = false, $show_events = false, - $show_unknown = false, $show_alerts = false, - $series_suffix = '', $percentil = false, - $flash_chart = true, $boolean_graph = false){ - - global $config; - global $max_value; - global $min_value; - global $series_type; - global $chart_extra_data; - - $first_data = 0; - - $data_uncompress = db_uncompress_module_data($agent_module_id, $datelimit, $date); - - $chart_data = array(); - + $data = array(); + $previous_data = 0; $min_value = PHP_INT_MAX-1; $max_value = PHP_INT_MIN+1; - $previous_data = $first_data; - $previous_unknown = 0; - - $i=0; - $current_event = $events[0]; - $prueba = array(); + $flag_unknown = 0; + $array_percentil = array(); foreach ($data_uncompress as $k) { foreach ($k["data"] as $v) { - $real_date = date("Y M d H:i:s", $v['utimestamp']); - - if(!$flash_chart){ - $real_date = date("Y/M/d", $v['utimestamp']); - $real_date .= "\n"; - $real_date .= date(" H:i:s", $v['utimestamp']); + if (isset($v["type"]) && $v["type"] == 1) { # skip unnecesary virtual data + continue; + } + if($compare){ // * 1000 need js utimestam mlsecond + $real_date = ($v['utimestamp'] + $date_array['period']) * 1000; + } + else{ + $real_date = $v['utimestamp'] * 1000; } - - $event_ids = array(); - $alert_ids = array(); - while (isset($current_event) && ($v['utimestamp'] >= $current_event["utimestamp"]) ) { - $event_date = date("Y M d H:i:s", $current_event['utimestamp']); - if(!$flash_chart){ - $event_date = date("Y/M/d", $current_event['utimestamp']); - $event_date .= "\n"; - $event_date .= date(" H:i:s", $current_event['utimestamp']); - } - - if ($show_events && (strpos($current_event["event_type"], "going") !== false)) { - $event_ids[$event_date][] = $current_event["id_evento"]; - - $chart_data[$event_date]["event" . $series_suffix] = 1; - $chart_data[$event_date]["alert" . $series_suffix] = NULL; - $chart_extra_data[count($chart_data)-1]['events'] = implode (',', $event_ids[$event_date]); - } - elseif ($show_alerts && (strpos($current_event["event_type"], "alert") !== false)) { - $alert_ids[$event_date][] = $current_event["id_evento"]; - - $chart_data[$event_date]["event" . $series_suffix] = NULL; - $chart_data[$event_date]["alert" . $series_suffix] = 1; - $chart_extra_data[count($chart_data)-1]['alerts'] = implode (',', $alert_ids[$event_date]); - } - else{ - $chart_data[$event_date]["event" . $series_suffix] = NULL; - $chart_data[$event_date]["alert" . $series_suffix] = NULL; - } - - $chart_data[$event_date]["sum" . $series_suffix] = $previous_data; - if($show_unknown) { - $chart_data[$event_date]["unknown" . $series_suffix] = $previous_unknown; - } - $current_event = $events[$i++]; - } if ($v["datos"] === NULL) { // Unknown - if (!isset($chart_data[$real_date]["event" . $series_suffix])) { - if($show_events) { - $chart_data[$real_date]["event" . $series_suffix] = NULL; + if($show_unknown){ + if(!$compare){ + if($flag_unknown){ + $data["unknown" . $series_suffix]['data'][] = array($real_date , 1); + } + else{ + $data["unknown" . $series_suffix]['data'][] = array( ($real_date - 1) , 0); + $data["unknown" . $series_suffix]['data'][] = array($real_date , 1); + $flag_unknown = 1; + } } - if($show_alerts) { - $chart_data[$real_date]["alert" . $series_suffix] = NULL; - } - } - - $chart_data[$real_date]["sum" . $series_suffix] = $previous_data; - if($show_unknown) { - $chart_data[$real_date]["unknown" . $series_suffix] = "1"; - } - $previous_unknown = "1"; - } - elseif($v["datos"] === false) { - // Not Init - $previous_data = $v["datos"]; - if (!isset($chart_data[$real_date]["event" . $series_suffix])) { - if ($show_events) { - $chart_data[$real_date]["event" . $series_suffix] = NULL; - } - if ($show_alerts) { - $chart_data[$real_date]["alert" . $series_suffix] = NULL; - } - } - - $chart_data[$real_date]["sum" . $series_suffix] = $v["datos"]; - - if($v['datos'] >= $max_value){ - $max_value = $v['datos']; } - if($v['datos'] <= $min_value){ - $min_value = $v['datos']; - } - - if($show_unknown) { - $chart_data[$real_date]["unknown" . $series_suffix] = NULL; - $previous_unknown = NULL; - } + $data["sum" . $series_suffix]['data'][] = array($real_date , $previous_data); } else { + //normal $previous_data = $v["datos"]; - if (!isset($chart_data[$real_date]["event" . $series_suffix])) { - if ($show_events) { - $chart_data[$real_date]["event" . $series_suffix] = NULL; - } - if ($show_alerts) { - $chart_data[$real_date]["alert" . $series_suffix] = NULL; + $data["sum" . $series_suffix]['data'][] = array($real_date , $v["datos"]); + if($show_unknown){ + if(!$compare){ + if($flag_unknown){ + $data["unknown" . $series_suffix]['data'][] = array($real_date , 0); + $flag_unknown = 0; + } } } - - $chart_data[$real_date]["sum" . $series_suffix] = $v["datos"]; + } + if(isset($v["datos"]) && $v["datos"]){ + //max if($v['datos'] >= $max_value){ $max_value = $v['datos']; } - + //min if($v['datos'] <= $min_value){ $min_value = $v['datos']; } + //avg sum + $sum_data += $v["datos"]; + } + //avg count + $count_data++; - if($show_unknown) { - $chart_data[$real_date]["unknown" . $series_suffix] = NULL; - $previous_unknown = NULL; - } - } + if($show_percentil && !$compare){ + $array_percentil[] = $v["datos"]; + } + + $last_data = $v["datos"]; } } - $series_type['event'.$series_suffix] = 'points'; - $series_type['alert'.$series_suffix] = 'points'; - $series_type['unknown'.$series_suffix] = 'unknown'; - if($boolean_graph){ - $series_type['sum'.$series_suffix] = 'boolean'; + + if($show_percentil && !$compare){ + $percentil_result = get_percentile($show_percentil, $array_percentil); + if($compare){ + $data["percentil" . $series_suffix]['data'][] = array( + ($date_array['start_date'] + $date_array['period']) * 1000, + $percentil_result + ); + $data["percentil" . $series_suffix]['data'][] = array( + ($date_array['final_date'] + $date_array['period']) * 1000, + $percentil_result + ); + } + else{ + $data["percentil" . $series_suffix]['data'][] = array( + $date_array['start_date'] * 1000, + $percentil_result + ); + $data["percentil" . $series_suffix]['data'][] = array( + $date_array['final_date'] * 1000, + $percentil_result + ); + } + } + // Add missed last data + if($compare){ + $data["sum" . $series_suffix]['data'][] = array( + ($date_array['final_date'] + $date_array['period']) * 1000, + $last_data + ); } else{ - $series_type['sum'.$series_suffix] = 'area'; + $data["sum" . $series_suffix]['data'][] = array( + $date_array['final_date'] * 1000, + $last_data + ); } + + $data["sum" . $series_suffix]['min'] = $min_value; + $data["sum" . $series_suffix]['max'] = $max_value; + $data["sum" . $series_suffix]['avg'] = $sum_data/$count_data; + + return $data; } -function grafico_modulo_boolean ($agent_module_id, $period, $show_events, - $width, $height , $title='', $unit_name, $show_alerts, $avg_only = 0, $pure=0, - $date = 0, $only_image = false, $homeurl = '', $adapt_key = '', $compare = false, - $show_unknown = false, $menu = true, $fullscale = false) { - - global $config; - global $graphic_type; - - $flash_chart = $config['flash_charts']; - - global $chart; - global $color; - global $color_prev; - global $legend; - global $long_index; - global $series_type; - global $chart_extra_data; - - if (empty($unit_name)) { - $unit = modules_get_unit($agent_module_id); - } - else - $unit = $unit_name; - - $series_suffix_str = ''; - if ($compare !== false) { - $series_suffix = '2'; - $series_suffix_str = ' (' . __('Previous') . ')'; - // Build the data of the previous period - grafico_modulo_boolean_data ($agent_module_id, $period, $show_events, - $unit_name, $show_alerts, $avg_only, $date-$period, $series_suffix, - $series_suffix_str, $show_unknown, $fullscale, $flash_chart); - switch ($compare) { - case 'separated': - // Store the chart calculated - $chart_prev = $chart; - $legend_prev = $legend; - $long_index_prev = $long_index; - $series_type_prev = $series_type; - $chart_extra_data_prev = $chart_extra_data; - $chart_extra_data = array(); - $color_prev = $color; - break; - case 'overlapped': - // Store the chart calculated deleting index, because will be over the current period - $chart_prev = array_values($chart); - $legend_prev = $legend; - $series_type_prev = $series_type; - $color_prev = $color; - foreach ($color_prev as $k => $col) { - $color_prev[$k]['color'] = '#' . get_complementary_rgb($color_prev[$k]['color']); - } - break; - } - } - - grafico_modulo_boolean_data ($agent_module_id, $period, $show_events, - $unit_name, $show_alerts, $avg_only, $date, '', '', $show_unknown, $fullscale, $flash_chart); - - - if ($compare === 'overlapped') { - $i = 0; - foreach($chart as $k => $v) { - $chart[$k] = array_merge($v, $chart_prev[$i]); - $i++; - } - - $legend = array_merge($legend, $legend_prev); - $color = array_merge($color, $color_prev); - } - - if ($only_image) { - $flash_chart = false; - } - - $water_mark = array( - 'file' => $config['homedir'] . "/images/logo_vertical_water.png", - 'url' => ui_get_full_url("/images/logo_vertical_water.png", - false, false, false)); - $type_graph = $config['type_module_charts']; - - if ($type_graph === 'area') { - if ($compare === 'separated') { - return area_graph($flash_chart, $chart, $width, $height/2, $color, $legend, - $long_index, ui_get_full_url("images/image_problem_area_small.png", false, false, false), - "", $unit, $homeurl, $water_mark, - $config['fontpath'], $config['font_size'], $unit, 1, $series_type, - $chart_extra_data, 0, 0, $adapt_key, false, $series_suffix_str, $menu). - '
'. - area_graph($flash_chart, $chart_prev, $width, $height/2, $color_prev, $legend_prev, - $long_index_prev, ui_get_full_url("images/image_problem_area_small.png", false, false, false), - "", $unit, $homeurl, $water_mark, - $config['fontpath'], $config['font_size'], $unit, 1, $series_type_prev, - $chart_extra_data_prev, 0, 0, $adapt_key, false, $series_suffix_str, $menu); - } - else { - return area_graph($flash_chart, $chart, $width, $height, $color, $legend, - $long_index, ui_get_full_url("images/image_problem_area_small.png", false, false, false), - $title, $unit, $homeurl, $water_mark, - $config['fontpath'], $config['font_size'], $unit, 1, $series_type, - $chart_extra_data, 0, 0, $adapt_key, false, $series_suffix_str, $menu); - } - } - elseif ($type_graph === 'line') { - if ($compare === 'separated') { - return - line_graph($flash_chart, $chart, $width, $height/2, $color, - $legend, $long_index, - ui_get_full_url("images/image_problem_area_small.png", false, false, false), - "", $unit, $water_mark, $config['fontpath'], - $config['font_size'], $unit, $ttl, $homeurl, $backgroundColor). - '
'. - line_graph($flash_chart, $chart_prev, $width, $height/2, $color, - $legend, $long_index, - ui_get_full_url("images/image_problem_area_small.png", false, false, false), - "", $unit, $water_mark, $config['fontpath'], - $config['font_size'], $unit, $ttl, $homeurl, $backgroundColor); - } - else { - // Color commented not to restrict serie colors - return - line_graph($flash_chart, $chart, $width, $height, $color, - $legend, $long_index, - ui_get_full_url("images/image_problem_area_small.png", false, false, false), - $title, $unit, $water_mark, $config['fontpath'], - $config['font_size'], $unit, $ttl, $homeurl, $backgroundColor); - } - } -} - - /** * Print an area graph with netflow aggregated */ - function graph_netflow_aggregate_area ($data, $period, $width, $height, $unit = '', $ttl = 1, $only_image = false) { global $config; global $graphic_type; - + if (empty ($data)) { echo fs_error_image (); return; } - - - if ($period <= SECONDS_6HOURS) { - $chart_time_format = 'H:i:s'; - } - elseif ($period < SECONDS_1DAY) { - $chart_time_format = 'H:i'; - } - elseif ($period < SECONDS_15DAYS) { - $chart_time_format = 'M d H:i'; - } - elseif ($period < SECONDS_1MONTH) { - $chart_time_format = 'M d H\h'; - } - elseif ($period < SECONDS_6MONTHS) { - $chart_time_format = "M d H\h"; - } - else { - $chart_time_format = "Y M d H\h"; - } - + // Calculate source indexes - $i = 0; - $sources = array (); - foreach ($data['sources'] as $source => $value) { - $source_indexes[$source] = $i; - $sources[$i] = $source; - $i++; - } - - // Add sources to chart - $chart = array (); - foreach ($data['data'] as $timestamp => $data) { - $chart_date = date ($chart_time_format, $timestamp); - $chart[$chart_date] = array (); - foreach ($source_indexes as $source => $index) { - $chart[$chart_date][$index] = 0; - } - foreach ($data as $source => $value) { - $chart[$chart_date][$source_indexes[$source]] = $value; + foreach ($data['sources'] as $key => $value) { + $i = 0; + foreach($data['data'] as $k => $v){ + $chart['netflow_' . $key]['data'][$i][0] = $k * 1000; + $chart['netflow_' . $key]['data'][$i][1] = $v[$key]; + $i++; } } - - - $flash_chart = $config['flash_charts']; - if ($only_image) { - $flash_chart = false; - } - + if ($config['homeurl'] != '') { $homeurl = $config['homeurl']; } else { $homeurl = ''; } - + if($config["fixed_graph"] == false){ $water_mark = array('file' => $config['homedir'] . "/images/logo_vertical_water.png", 'url' => ui_get_full_url("images/logo_vertical_water.png", false, false, false)); + + $water_mark = $config['homedir'] . "/images/logo_vertical_water.png"; } - - $color = array(); - $color[0] = array('border' => '#000000', - 'color' => $config['graph_color1'], - 'alpha' => CHART_DEFAULT_ALPHA); - $color[1] = array('border' => '#000000', - 'color' => $config['graph_color2'], - 'alpha' => CHART_DEFAULT_ALPHA); - $color[2] = array('border' => '#000000', - 'color' => $config['graph_color3'], - 'alpha' => CHART_DEFAULT_ALPHA); - $color[3] = array('border' => '#000000', - 'color' => $config['graph_color4'], - 'alpha' => CHART_DEFAULT_ALPHA); - $color[4] = array('border' => '#000000', - 'color' => $config['graph_color5'], - 'alpha' => CHART_DEFAULT_ALPHA); - $color[5] = array('border' => '#000000', - 'color' => $config['graph_color6'], - 'alpha' => CHART_DEFAULT_ALPHA); - $color[6] = array('border' => '#000000', - 'color' => $config['graph_color7'], - 'alpha' => CHART_DEFAULT_ALPHA); - $color[7] = array('border' => '#000000', - 'color' => $config['graph_color8'], - 'alpha' => CHART_DEFAULT_ALPHA); - $color[8] = array('border' => '#000000', - 'color' => $config['graph_color9'], - 'alpha' => CHART_DEFAULT_ALPHA); - $color[9] = array('border' => '#000000', - 'color' => $config['graph_color10'], - 'alpha' => CHART_DEFAULT_ALPHA); - $color[11] = array('border' => '#000000', - 'color' => COL_GRAPH9, - 'alpha' => CHART_DEFAULT_ALPHA); - $color[12] = array('border' => '#000000', - 'color' => COL_GRAPH10, - 'alpha' => CHART_DEFAULT_ALPHA); - $color[13] = array('border' => '#000000', - 'color' => COL_GRAPH11, - 'alpha' => CHART_DEFAULT_ALPHA); - $color[14] = array('border' => '#000000', - 'color' => COL_GRAPH12, - 'alpha' => CHART_DEFAULT_ALPHA); - $color[15] = array('border' => '#000000', - 'color' => COL_GRAPH13, - 'alpha' => CHART_DEFAULT_ALPHA); - - - return area_graph($flash_chart, $chart, $width, $height, $color, - $sources, array (), ui_get_full_url("images/image_problem_area_small.png", false, false, false), - "", $unit, $homeurl, - $config['homedir'] . "/images/logo_vertical_water.png", - $config['fontpath'], $config['font_size'], $unit, $ttl); + + if($ttl >= 2){ + $only_image = true; + } + else{ + $only_image = false; + } + + $params =array( + 'agent_module_id' => false, + 'period' => $period, + 'width' =>'90%', + 'height' => 450, + 'unit' => $unit, + 'only_image' => $only_image, + 'homeurl' => $homeurl, + 'menu' => true, + 'backgroundColor' => 'white', + 'type_graph' => 'area', + 'font' => $config['fontpath'], + 'font-size' => $config['font_size'], + 'array_data_create' => $chart + ); + + return grafico_modulo_sparse($params); } - - /** * Print an area graph with netflow total */ function graph_netflow_total_area ($data, $period, $width, $height, $unit = '', $ttl = 1, $only_image = false) { global $config; global $graphic_type; - + if (empty ($data)) { echo fs_error_image (); return; } - - if ($period <= SECONDS_6HOURS) { - $chart_time_format = 'H:i:s'; - } - elseif ($period < SECONDS_1DAY) { - $chart_time_format = 'H:i'; - } - elseif ($period < SECONDS_15DAYS) { - $chart_time_format = 'M d H:i'; - } - elseif ($period < SECONDS_1MONTH) { - $chart_time_format = 'M d H\h'; - } - elseif ($period < SECONDS_6MONTHS) { - $chart_time_format = "M d H\h"; - } - else { - $chart_time_format = "Y M d H\h"; + + // Calculate source indexes + $i=0; + foreach ($data as $key => $value) { + $chart['netflow']['data'][$i][0] = $key * 1000; + $chart['netflow']['data'][$i][1] = $value['data']; + $i++; } - // Calculate min, max and avg values - $avg = 0; - foreach ($data as $timestamp => $value) { - $max = $value['data']; - $min = $value['data']; - break; - } - - // Populate chart - $count = 0; - $chart = array (); - foreach ($data as $timestamp => $value) { - $chart[date ($chart_time_format, $timestamp)] = $value; - if ($value['data'] > $max) { - $max = $value['data']; - } - if ($value['data'] < $min) { - $min = $value['data']; - } - $avg += $value['data']; - $count++; - } - if ($count > 0) { - $avg /= $count; - } - - $flash_chart = $config['flash_charts']; - if ($only_image) { - $flash_chart = false; - } - if ($config['homeurl'] != '') { $homeurl = $config['homeurl']; } else { $homeurl = ''; } - + if($config["fixed_graph"] == false){ $water_mark = array('file' => $config['homedir'] . "/images/logo_vertical_water.png", 'url' => ui_get_full_url("images/logo_vertical_water.png", false, false, false)); + + $water_mark = $config['homedir'] . "/images/logo_vertical_water.png"; } - - $legend = array (__('Max.') . ' ' . format_numeric($max) . ' ' . __('Min.') . ' ' . format_numeric($min) . ' ' . __('Avg.') . ' ' . format_numeric ($avg)); - return area_graph($flash_chart, $chart, $width, $height, array (), $legend, - array (), ui_get_full_url("images/image_problem_area_small.png", false, false, false), - "", "", $homeurl, $water_mark, - $config['fontpath'], $config['font_size'], $unit, $ttl); + + if($ttl >= 2){ + $only_image = true; + } + else{ + $only_image = false; + } + + $params =array( + 'agent_module_id' => false, + 'period' => $period, + 'width' =>'90%', + 'height' => 450, + 'unit' => $unit, + 'only_image' => $only_image, + 'homeurl' => $homeurl, + 'menu' => true, + 'backgroundColor' => 'white', + 'type_graph' => 'area', + 'font' => $config['fontpath'], + 'font-size' => $config['font_size'], + 'array_data_create' => $chart + ); + + return grafico_modulo_sparse($params); } /** @@ -5272,11 +4286,16 @@ function graph_netflow_total_area ($data, $period, $width, $height, $unit = '', function graph_netflow_aggregate_pie ($data, $aggregate, $ttl = 1, $only_image = false) { global $config; global $graphic_type; - + if (empty ($data)) { return fs_error_image (); } - + + $date_array = array(); + $date_array["period"] = 300; + $date_array["final_date"] = time(); + $date_array["start_date"] = time() - 300; + $i = 0; $values = array(); $agg = ''; @@ -5290,18 +4309,18 @@ function graph_netflow_aggregate_pie ($data, $aggregate, $ttl = 1, $only_image = } $i++; } - + $flash_chart = $config['flash_charts']; if ($only_image) { $flash_chart = false; } - + if($config["fixed_graph"] == false){ $water_mark = array('file' => $config['homedir'] . "/images/logo_vertical_water.png", 'url' => ui_get_full_url("images/logo_vertical_water.png", false, false, false)); } - + return pie3d_graph($flash_chart, $values, 370, 200, __('Other'), $config['homeurl'], $water_mark, $config['fontpath'], $config['font_size'], $ttl); @@ -5337,312 +4356,6 @@ function graph_netflow_host_traffic ($data, $unit, $width = 700, $height = 700) return d3_tree_map_graph ($data, $width, $height, true); } -/** - * Draw a graph of Module string data of agent - * - * @param integer id_agent_modulo Agent Module ID - * @param integer show_event show event (1 or 0) - * @param integer height graph height - * @param integer width graph width - * @param string title graph title - * @param string unit_name String of unit name - * @param integer show alerts (1 or 0) - * @param integer avg_only calcules avg only (1 or 0) - * @param integer pure Fullscreen (1 or 0) - * @param integer date date - */ -function grafico_modulo_string ($agent_module_id, $period, $show_events, - $width, $height, $title, $unit_name, $show_alerts, $avg_only = 0, $pure = 0, - $date = 0, $only_image = false, $homeurl = '', $adapt_key = '', $ttl = 1, $menu = true) { - global $config; - global $graphic_type; - global $max_value; - - - // Set variables - if ($date == 0) - $date = get_system_time(); - $datelimit = $date - $period; - $search_in_history_db = db_search_in_history_db($datelimit); - $resolution = $config['graph_res'] * 50; //Number of points of the graph - $interval = (int) ($period / $resolution); - $agent_name = modules_get_agentmodule_agent_name ($agent_module_id); - $agent_id = agents_get_agent_id ($agent_name); - $module_name = modules_get_agentmodule_name ($agent_module_id); - $id_module_type = modules_get_agentmodule_type ($agent_module_id); - $module_type = modules_get_moduletype_name ($id_module_type); - $uncompressed_module = is_module_uncompressed ($module_type); - if ($uncompressed_module) { - $avg_only = 1; - } - $search_in_history_db = db_search_in_history_db($datelimit); - - // Get event data (contains alert data too) - if ($show_events == 1 || $show_alerts == 1) { - $events = db_get_all_rows_filter ('tevento', - array ('id_agentmodule' => $agent_module_id, - "utimestamp > $datelimit", - "utimestamp < $date", - 'order' => 'utimestamp ASC'), - array ('evento', 'utimestamp', 'event_type')); - if ($events === false) { - $events = array (); - } - } - - // Get module data - $data = db_get_all_rows_filter ('tagente_datos_string', - array ('id_agente_modulo' => $agent_module_id, - "utimestamp > $datelimit", - "utimestamp < $date", - 'order' => 'utimestamp ASC'), - array ('datos', 'utimestamp'), 'AND', $search_in_history_db); - if ($data === false) { - $data = array (); - } - - // Uncompressed module data - if ($uncompressed_module) { - $min_necessary = 1; - } - else { - // Compressed module data - - // Get previous data - $previous_data = modules_get_previous_data ($agent_module_id, $datelimit, 1); - if ($previous_data !== false) { - $previous_data['utimestamp'] = $datelimit; - array_unshift ($data, $previous_data); - } - - // Get next data - $nextData = modules_get_next_data ($agent_module_id, $date, 1); - if ($nextData !== false) { - array_push ($data, $nextData); - } - else if (count ($data) > 0) { - // Propagate the last known data to the end of the interval - $nextData = array_pop ($data); - array_push ($data, $nextData); - $nextData['utimestamp'] = $date; - array_push ($data, $nextData); - } - - $min_necessary = 2; - } - - // Check available data - if (count ($data) < $min_necessary) { - if (!$graphic_type) { - return fs_error_image ($width, $height); - } - graphic_error (); - } - - // Data iterator - $j = 0; - - // Event iterator - $k = 0; - - // Set initial conditions - $chart = array(); - if ($data[0]['utimestamp'] == $datelimit) { - $previous_data = 1; - $j++; - } - else { - $previous_data = 0; - } - - // Calculate chart data - $last_known = $previous_data; - for ($i = 0; $i < $resolution; $i++) { - $timestamp = $datelimit + ($interval * $i); - - $count = 0; - $total = 0; - // Read data that falls in the current interval - while (isset($data[$j]) && - isset ($data[$j]) !== null && - $data[$j]['utimestamp'] >= $timestamp && - $data[$j]['utimestamp'] <= ($timestamp + $interval)) { - - // --------------------------------------------------------- - // FIX TICKET #1749 - $last_known = $count; - // --------------------------------------------------------- - $count++; - $j++; - } - - if ($max_value < $count) { - $max_value = $count; - } - - // Read events and alerts that fall in the current interval - $event_value = 0; - $alert_value = 0; - while (isset ($events[$k]) && $events[$k]['utimestamp'] >= $timestamp && $events[$k]['utimestamp'] <= ($timestamp + $interval)) { - if ($show_events == 1) { - $event_value++; - } - if ($show_alerts == 1 && substr ($events[$k]['event_type'], 0, 5) == 'alert') { - $alert_value++; - } - $k++; - } - - ///////////////////////////////////////////////////////////////// - // Set the title and time format - if ($period <= SECONDS_6HOURS) { - $time_format = 'H:i:s'; - } - elseif ($period < SECONDS_1DAY) { - $time_format = 'H:i'; - } - elseif ($period < SECONDS_15DAYS) { - $time_format = 'M d H:i'; - } - elseif ($period < SECONDS_1MONTH) { - $time_format = 'M d H\h'; - } - elseif ($period < SECONDS_6MONTHS) { - $time_format = "M d H\h"; - } - else { - $time_format = "Y M d H\h"; - } - - $timestamp_short = date($time_format, $timestamp); - $long_index[$timestamp_short] = date( - html_entity_decode($config['date_format'], ENT_QUOTES, "UTF-8"), $timestamp); - $timestamp = $timestamp_short; - ///////////////////////////////////////////////////////////////// - - // Data in the interval - //The order in chart array is very important!!!! - if ($show_events) { - $chart[$timestamp]['event'] = $event_value; - } - - if ($show_alerts) { - $chart[$timestamp]['alert'] = $alert_value; - } - - if (!$avg_only) { - $chart[$timestamp]['max'] = 0; - } - - if ($count > 0) { - $chart[$timestamp]['sum'] = $count; - } - else { - // Compressed data - $chart[$timestamp]['sum'] = $last_known; - } - - if (!$avg_only) { - $chart[$timestamp]['min'] = 0; - } - } - - $graph_stats = get_statwin_graph_statistics($chart); - - // Fix event and alert scale - $event_max = 2 + (float)$max_value * 1.05; - foreach ($chart as $timestamp => $chart_data) { - if (!empty($chart_data['event']) && $chart_data['event'] > 0) { - $chart[$timestamp]['event'] = $event_max; - } - if (!empty($chart_data['alert']) && $chart_data['alert'] > 0) { - $chart[$timestamp]['alert'] = $event_max; - } - } - - if (empty($unit_name)) { - $unit = modules_get_unit($agent_module_id); - } - else - $unit = $unit_name; - - ///////////////////////////////////////////////////////////////////////////////////////// - $color = array(); - - if ($show_events) { - $color['event'] = array('border' => '#ff0000', - 'color' => '#ff0000', 'alpha' => CHART_DEFAULT_ALPHA); - } - if ($show_alerts) { - $color['alert'] = array('border' => '#ff7f00', - 'color' => '#ff7f00', 'alpha' => CHART_DEFAULT_ALPHA); - } - - if (!$avg_only) { - $color['max'] = array('border' => '#000000', - 'color' => $config['graph_color3'], - 'alpha' => CHART_DEFAULT_ALPHA); - } - $color['sum'] = array('border' => '#000000', - 'color' => $config['graph_color2'], - 'alpha' => CHART_DEFAULT_ALPHA); - - if (!$avg_only) { - $color['min'] = array('border' => '#000000', - 'color' => $config['graph_color1'], - 'alpha' => CHART_DEFAULT_ALPHA); - } - - //$color['baseline'] = array('border' => null, 'color' => '#0097BD', 'alpha' => 10); - ///////////////////////////////////////////////////////////////////////////////////////// - - $flash_chart = $config['flash_charts']; - if ($only_image) { - $flash_chart = false; - } - - $legend = array(); - - if ($show_events) { - $legend['event'] = __('Events'); - } - - if ($show_alerts) { - $legend['alert'] = __('Alerts'); - } - - if (!$avg_only) { - $legend['max'] = __('Max').': '.__('Last').': '.remove_right_zeros(number_format($graph_stats['max']['last'], $config['graph_precision'])).' '.$unit.' ; '.__('Avg').': '.remove_right_zeros(number_format($graph_stats['max']['avg'], $config['graph_precision'])).' '.$unit.' ; '.__('Max').': '.remove_right_zeros(number_format($graph_stats['max']['max'], $config['graph_precision'])).' '.$unit.' ; '.__('Min').': '.remove_right_zeros(number_format($graph_stats['max']['min'], $config['graph_precision'])).' '.$unit; - } - - $legend['sum'] = __('Avg').': '.__('Last').': '.remove_right_zeros(number_format($graph_stats['sum']['last'], $config['graph_precision'])).' '.$unit.' ; '.__('Avg').': '.remove_right_zeros(number_format($graph_stats['sum']['avg'], $config['graph_precision'])).' '.$unit.' ; '.__('Max').': '.remove_right_zeros(number_format($graph_stats['sum']['max'], $config['graph_precision'])).' '.$unit.' ; '.__('Min').': '.remove_right_zeros(number_format($graph_stats['sum']['min'], $config['graph_precision'])).' '.$unit; - - if (!$avg_only) { - $legend['min'] = __('Min').': '.__('Last').': '.remove_right_zeros(number_format($graph_stats['min']['last'], $config['graph_precision'])).' '.$unit.' ; '.__('Avg').': '.remove_right_zeros(number_format($graph_stats['min']['avg'], $config['graph_precision'])).' '.$unit.' ; '.__('Max').': '.remove_right_zeros(number_format($graph_stats['min']['max'], $config['graph_precision'])).' '.$unit.' ; '.__('Min').': '.remove_right_zeros(number_format($graph_stats['min']['min'], $config['graph_precision'])).' '.$unit; - } - - if($config["fixed_graph"] == false){ - $water_mark = array('file' => - $config['homedir'] . "/images/logo_vertical_water.png", - 'url' => ui_get_full_url("images/logo_vertical_water.png", false, false, false)); - } - - if ($type_graph === 'area') { - return area_graph($flash_chart, $chart, $width, $height, $color, - $legend, array(), '', $title, $unit, $homeurl, - $water_mark, $config['fontpath'], $config['font_size'], $unit, - 1, array(), array(), 0, 0, $adapt_key, true, '', $menu); - } - else { - return - line_graph($flash_chart, $chart, $width, $height, $color, - $legend, $long_index, - ui_get_full_url("images/image_problem_area_small.png", false, false, false), - $title, $unit, $water_mark, $config['fontpath'], - $config['font_size'], $unit, $ttl, $homeurl, $backgroundColor); - } -} - /** * Print a graph with event data of module * @@ -5658,11 +4371,12 @@ function grafico_modulo_string ($agent_module_id, $period, $show_events, function graphic_module_events ($id_module, $width, $height, $period = 0, $homeurl = '', $zoom = 0, $adapt_key = '', $date = false, $stat_win = false) { global $config; global $graphic_type; - + $data = array (); - +$width = 90; +$height = 100; $resolution = $config['graph_res'] * ($period * 2 / $width); // Number of "slices" we want in graph - + $interval = (int) ($period / $resolution); if ($date === false) { $date = get_system_time (); @@ -5671,7 +4385,7 @@ function graphic_module_events ($id_module, $width, $height, $period = 0, $homeu $periodtime = floor ($period / $interval); $time = array (); $data = array (); - + // Set the title and time format if ($period <= SECONDS_6HOURS) { $time_format = 'H:i:s'; @@ -5691,7 +4405,7 @@ function graphic_module_events ($id_module, $width, $height, $period = 0, $homeu else { $time_format = "Y M d H\h"; } - + $legend = array(); $cont = 0; for ($i = 0; $i < $interval; $i++) { @@ -5703,10 +4417,10 @@ function graphic_module_events ($id_module, $width, $height, $period = 0, $homeu else { $name = $bottom; } - + $top = $datelimit + ($periodtime * ($i + 1)); - - $events = db_get_all_rows_filter ('tevento', + + $events = db_get_all_rows_filter ('tevento', array ('id_agentmodule' => $id_module, 'utimestamp > '.$bottom, 'utimestamp < '.$top), @@ -5718,7 +4432,6 @@ function graphic_module_events ($id_module, $width, $height, $period = 0, $homeu if (empty($event['utimestamp'])) { continue; } - switch($event['event_type']) { case 'going_down_normal': case 'going_up_normal': @@ -5742,9 +4455,9 @@ function graphic_module_events ($id_module, $width, $height, $period = 0, $homeu } } } - + $data[$cont]['utimestamp'] = $periodtime; - + if (!empty($events)) { switch ($status) { case 'warning': @@ -5765,11 +4478,11 @@ function graphic_module_events ($id_module, $width, $height, $period = 0, $homeu $data[$cont]['data'] = 1; } $current_timestamp = $bottom; - - $legend[] = date($time_format, $current_timestamp); + + $legend[] = date($time_format, $current_timestamp); $cont++; } - + $pixels_between_xdata = 25; $max_xdata_display = round($width / $pixels_between_xdata); $ndata = count($data); @@ -5779,374 +4492,20 @@ function graphic_module_events ($id_module, $width, $height, $period = 0, $homeu else { $xdata_display = $max_xdata_display; } - + $step = round($ndata/$xdata_display); - + $colors = array(1 => '#38B800', 2 => '#FFFF00', 3 => '#FF0000', 4 => '#C3C3C3'); - + // Draw slicebar graph if ($config['flash_charts']) { - echo flot_slicesbar_graph($data, $period, $width, 15, $legend, $colors, $config['fontpath'], $config['round_corner'], $homeurl, '', $adapt_key, $stat_win); + echo flot_slicesbar_graph($data, $period, $width, 50, $legend, $colors, $config['fontpath'], $config['round_corner'], $homeurl, '', $adapt_key, $stat_win); } else { echo slicesbar_graph($data, $period, $width, 15, $colors, $config['fontpath'], $config['round_corner'], $homeurl); } } -///Functions for the LOG4X graphs -function grafico_modulo_log4x ($id_agente_modulo, $periodo, $show_event, - $width, $height , $title, $unit_name, $show_alert, $avg_only = 0, $pure=0, - $date = 0) { - - grafico_modulo_log4x_trace("
");
-	
-	if ($date == "")
-		$now = time ();
-	else
-		$now = $date;
-	
-	$fechatope = $now - $periodo; // limit date
-	
-	$nombre_agente = modules_get_agentmodule_agent_name ($id_agente_modulo);
-	$nombre_modulo = modules_get_agentmodule_name ($id_agente_modulo);
-	$id_agente = agents_get_agent_id ($nombre_agente);
-	
-	$adjust_time = SECONDS_1MINUTE;
-	
-
-	if ($periodo == SECONDS_1DAY)
-		$adjust_time = SECONDS_1HOUR;
-	elseif ($periodo == SECONDS_1WEEK)
-		$adjust_time = SECONDS_1DAY;
-	elseif ($periodo == SECONDS_1HOUR)
-		$adjust_time = SECONDS_10MINUTES;
-	elseif ($periodo == SECONDS_1MONTH)
-		$adjust_time = SECONDS_1WEEK;
-	else
-		$adjust_time = $periodo / 12.0;
-	
-	$num_slices = $periodo / $adjust_time;
-	
-	$fechatope_index = grafico_modulo_log4x_index($fechatope, $adjust_time);
-	
-	$sql1="SELECT utimestamp, SEVERITY " .
-			" FROM tagente_datos_log4x " .
-			" WHERE id_agente_modulo = $id_agente_modulo AND utimestamp > $fechatope and utimestamp < $now";
-	
-	$valores = array();
-	
-	$max_count = -1;
-	$min_count = 9999999;
-	
-	grafico_modulo_log4x_trace("$sql1");
-	
-	$rows = 0;
-	
-	$first = true;
-	while ($row = get_db_all_row_by_steps_sql($first, $result, $sql1)) {
-		$first = false;
-		
-		$rows++;
-		$utimestamp = $row[0];
-		$severity = $row[1];
-		$severity_num = $row[2];
-		
-		if (!isset($valores[$severity]))
-			$valores[$severity] = array();
-		
-		$dest = grafico_modulo_log4x_index($utimestamp, $adjust_time);
-		
-		$index = (($dest - $fechatope_index) / $adjust_time) - 1;
-		
-		if (!isset($valores[$severity][$index])) {
-			$valores[$severity][$index] = array();
-			$valores[$severity][$index]['pivot'] = $dest;
-			$valores[$severity][$index]['count'] = 0;
-			$valores[$severity][$index]['alerts'] = 0;
-		}
-		
-		$valores[$severity][$index]['count']++;
-		
-		$max_count = max($max_count, $valores[$severity][$index]['count']);
-		$min_count = min($min_count, $valores[$severity][$index]['count']);
-	}
-	
-	grafico_modulo_log4x_trace("$rows rows");
-	
-	// Create graph
-	// *************
-	
-	grafico_modulo_log4x_trace(__LINE__);
-	
-	//set_error_handler("myErrorHandler");
-	
-	grafico_modulo_log4x_trace(__LINE__);
-	$ds = DIRECTORY_SEPARATOR;
-	set_include_path(get_include_path() . PATH_SEPARATOR . getcwd() . $ds."..".$ds."..".$ds."include");
-	
-	require_once 'Image/Graph.php';
-	
-	grafico_modulo_log4x_trace(__LINE__);
-	
-	$Graph =& Image_Graph::factory('graph', array($width, $height));
-	
-	grafico_modulo_log4x_trace(__LINE__);
-	
-	// add a TrueType font
-	$Font =& $Graph->addNew('font', $config['fontpath']); // C:\WINNT\Fonts\ARIAL.TTF
-	$Font->setSize(7);
-	
-	$Graph->setFont($Font);
-	
-	if ($periodo == SECONDS_1DAY)
-		$title_period = $lang_label["last_day"];
-	elseif ($periodo == SECONDS_1WEEK)
-		$title_period = $lang_label["last_week"];
-	elseif ($periodo == SECONDS_1HOUR)
-		$title_period = $lang_label["last_hour"];
-	elseif ($periodo == SECONDS_1MONTH)
-		$title_period = $lang_label["last_month"];
-	else {
-		$suffix = $lang_label["days"];
-		$graph_extension = $periodo / SECONDS_1DAY;
-		
-		if ($graph_extension < 1) {
-			$graph_extension = $periodo / SECONDS_1HOUR;
-			$suffix = $lang_label["hours"];
-		}
-		//$title_period = "Last ";
-		$title_period = format_numeric($graph_extension,2)." $suffix";
-	}
-	
-	$title_period = html_entity_decode($title_period);
-	
-	grafico_modulo_log4x_trace(__LINE__);
-	
-	if ($pure == 0) {
-		$Graph->add(
-			Image_Graph::horizontal(
-				Image_Graph::vertical(
-					Image_Graph::vertical(
-						$Title = Image_Graph::factory('title', array('   Pandora FMS Graph - '.strtoupper($nombre_agente)." - " .$title_period, 10)),
-						$Subtitle = Image_Graph::factory('title', array('     '.$title, 7)),
-						90
-					),
-					$Plotarea = Image_Graph::factory('plotarea', array('Image_Graph_Axis', 'Image_Graph_Axis')),
-					15 // If you change this, change the 0.85 below
-				),
-				Image_Graph::vertical(
-					$Legend = Image_Graph::factory('legend'),
-					$PlotareaMinMax = Image_Graph::factory('plotarea'),
-					65
-				),
-				85 // If you change this, change the 0.85 below
-			)
-		);
-		
-		$Legend->setPlotarea($Plotarea);
-		$Title->setAlignment(IMAGE_GRAPH_ALIGN_LEFT);
-		$Subtitle->setAlignment(IMAGE_GRAPH_ALIGN_LEFT);
-	}
-	else { // Pure, without title and legends
-		$Graph->add($Plotarea = Image_Graph::factory('plotarea', array('Image_Graph_Axis', 'Image_Graph_Axis')));
-	}
-	
-	grafico_modulo_log4x_trace(__LINE__);
-	
-	$dataset = array();
-	
-	$severities = array("FATAL", "ERROR", "WARN", "INFO", "DEBUG", "TRACE");
-	$colors = array("black", "red", "orange", "yellow", "#3300ff", 'magenta');
-	
-	$max_bubble_radius = $height * 0.6 / (count($severities) + 1); // this is the size for the max_count
-	$y = count($severities) - 1;
-	$i = 0;
-	
-	foreach($severities as $severity) {
-		$dataset[$i] = Image_Graph::factory('dataset');
-		$dataset[$i]->setName($severity);
-		
-		if (isset($valores[$severity])) {
-			$data =& $valores[$severity];
-			while (list($index, $data2) = each($data)) {
-				$count = $data2['count'];
-				$pivot = $data2['pivot'];
-				
-				//$x = $scale * $index;
-				$x = 100.0 * ($pivot - $fechatope) / ($now - $fechatope);
-				if ($x > 100) $x = 100;
-				
-				$size = grafico_modulo_log4x_bubble_size($count, $max_count, $max_bubble_radius);
-				
-				// pivot is the value in the X axis
-				// y is the number of steps (from the bottom of the graphics) (zero based)
-				// x is the position of the bubble, in % from the left (0% = full left, 100% = full right)
-				// size is the radius of the bubble
-				// value is the value associated with the bubble (needed to calculate the leyend)
-				//
-				$dataset[$i]->addPoint($pivot, $y, array("x" => $x, "size" => $size, "value" => $count));
-			}
-		}
-		else {
-			// There's a problem when we have no data ...
-			// This was the first try.. didnt work
-			//$dataset[$i]->addPoint($now, -1, array("x" => 0, "size" => 0));
-		}
-		
-		$y--;
-		$i++;
-	}
-	
-	grafico_modulo_log4x_trace(__LINE__);
-	
-	// create the 1st plot as smoothed area chart using the 1st dataset
-	$Plot =& $Plotarea->addNew('bubble', array(&$dataset));
-	$Plot->setFont($Font);
-	
-	$AxisX =& $Plotarea->getAxis(IMAGE_GRAPH_AXIS_X);
-	$AxisX->setDataPreprocessor(Image_Graph::factory('Image_Graph_DataPreprocessor_Function', 'grafico_modulo_log4x_format_x_axis'));
-	$AxisX->forceMinimum($fechatope);
-	$AxisX->forceMaximum($now);
-	
-	$minIntervalWidth = $Plot->getTextWidth("88/88/8888");
-	$interval_x = $adjust_time;
-	
-	while (true) {
-		$intervalWidth = $width * 0.85 * $interval_x/ $periodo;
-		if ($intervalWidth >= $minIntervalWidth)
-			break;
-		
-		$interval_x *= 2;
-	}
-	
-	$AxisX->setLabelInterval($interval_x);
-	$AxisX->setLabelOption("showtext",true);
-	
-	//*
-	$GridY2 =& $Plotarea->addNew('line_grid');
-	$GridY2->setLineColor('gray');
-	$GridY2->setFillColor('lightgray@0.05');
-	$GridY2->_setPrimaryAxis($AxisX);
-	//$GridY2->setLineStyle(Image_Graph::factory('Image_Graph_Line_Dotted', array("white", "gray", "gray", "gray")));
-	$GridY2->setLineStyle(Image_Graph::factory('Image_Graph_Line_Formatted', array(array("transparent", "transparent", "transparent", "gray"))));
-	//*/
-	//grafico_modulo_log4x_trace(print_r($AxisX, true));
-	
-	$AxisY =& $Plotarea->getAxis(IMAGE_GRAPH_AXIS_Y);
-	$AxisY->setDataPreprocessor(Image_Graph::factory('Image_Graph_DataPreprocessor_Function', 'grafico_modulo_log4x_format_y_axis'));
-	$AxisY->setLabelOption("showtext",true);
-	//$AxisY->setLabelInterval(0);
-	//$AxisY->showLabel(IMAGE_GRAPH_LABEL_ZERO);
-	
-	//*
-	$GridY2 =& $Plotarea->addNew('line_grid');
-	$GridY2->setLineColor('gray');
-	$GridY2->setFillColor('lightgray@0.05');
-	$GridY2->_setPrimaryAxis($AxisY);
-	$GridY2->setLineStyle(Image_Graph::factory('Image_Graph_Line_Formatted', array(array("transparent", "transparent", "transparent", "gray"))));
-	//*/
-	
-	$AxisY->forceMinimum(0);
-	$AxisY->forceMaximum(count($severities) + 1) ;
-	
-	// set line colors
-	$FillArray =& Image_Graph::factory('Image_Graph_Fill_Array');
-	
-	$Plot->setFillStyle($FillArray);
-	foreach($colors as $color)
-		$FillArray->addColor($color);
-	
-	grafico_modulo_log4x_trace(__LINE__);
-	
-	$FillArray->addColor('green@0.6');
-	//$AxisY_Weather =& $Plotarea->getAxis(IMAGE_GRAPH_AXIS_Y);
-	
-	// Show events !
-	if ($show_event == 1) {
-		$Plot =& $Plotarea->addNew('Plot_Impulse', array($dataset_event));
-		$Plot->setLineColor( 'red' );
-		$Marker_event =& Image_Graph::factory('Image_Graph_Marker_Cross');
-		$Plot->setMarker($Marker_event);
-		$Marker_event->setFillColor( 'red' );
-		$Marker_event->setLineColor( 'red' );
-		$Marker_event->setSize ( 5 );
-	}
-	
-	$Axis =& $PlotareaMinMax->getAxis(IMAGE_GRAPH_AXIS_X);
-	$Axis->Hide();
-	$Axis =& $PlotareaMinMax->getAxis(IMAGE_GRAPH_AXIS_Y);
-	$Axis->Hide();
-	
-	$plotMinMax =& $PlotareaMinMax->addNew('bubble', array(&$dataset, true));
-	
-	grafico_modulo_log4x_trace(__LINE__);
-	
-	$Graph->done();
-	
-	grafico_modulo_log4x_trace(__LINE__);
-}
-
-function grafico_modulo_log4x_index($x, $interval)
-{
-	return $x + $interval - (($x - 1) % $interval) - 1;
-}
-
-function grafico_modulo_log4x_trace($str)
-{
-	//echo "$str\n";
-}
-
-function grafico_modulo_log4x_bubble_size($count, $max_count, $max_bubble_radius)
-{
-	//Superformula de ROA
-	$r0 = 1.5;
-	$r1 = $max_bubble_radius;
-	$v2 = pow($max_count,1/2.0);
-	
-	return $r1*pow($count,1/2.0)/($v2)+$r0;
-}
-
-function grafico_modulo_log4x_format_x_axis ( $number , $decimals=2, $dec_point=".", $thousands_sep=",")
-{
-	// $number is the unix time in the local timezone
-	
-	//$dtZone = new DateTimeZone(date_default_timezone_get());
-	//$d = new DateTime("now", $dtZone);
-	//$offset = $dtZone->getOffset($d);
-	//$number -= $offset;
-	
-	return date("d/m", $number) . "\n" . date("H:i", $number);
-}
-
-function grafico_modulo_log4x_format_y_axis ( $number , $decimals=2, $dec_point=".", $thousands_sep=",")
-{
-	
-	switch ($number) {
-		case 6:
-			return "FATAL";
-			break;
-		case 5:
-			return "ERROR";
-			break;
-		case 4:
-			return "WARN";
-			break;
-		case 3:
-			return "INFO";
-			break;
-		case 2:
-			return "DEBUG";
-			break;
-		case 1:
-			return "TRACE";
-			break;
-		default:
-			return "";
-			break;
-	}
-	
-}
-
 function graph_nodata_image($width = 300, $height = 110, $type = 'area', $text = '') {
 	$image = ui_get_full_url('images/image_problem_area_small.png',
 		false, false, false); 
diff --git a/pandora_console/include/functions_modules.php b/pandora_console/include/functions_modules.php
index 6c0811897f..0d1c208e16 100755
--- a/pandora_console/include/functions_modules.php
+++ b/pandora_console/include/functions_modules.php
@@ -1674,26 +1674,27 @@ function modules_get_last_value ($id_agentmodule) {
  * @return mixed The row of tagente_datos of the last period. False if there were no data.
  */
 function modules_get_previous_data ($id_agent_module, $utimestamp = 0, $string = 0) {
-	if (empty ($utimestamp))
+	if (empty ($utimestamp)){
 		$utimestamp = time ();
-	
+	}
+
 	if ($string == 1) {
 		$table = 'tagente_datos_string';
 	}
 	else {
 		$table = 'tagente_datos';
 	}
-	
+
 	$sql = sprintf ('SELECT *
 		FROM ' . $table . '
 		WHERE id_agente_modulo = %d
-			AND utimestamp <= %d 
-			AND utimestamp >= %d 
+			AND utimestamp <= %d
 		ORDER BY utimestamp DESC',
-		$id_agent_module, $utimestamp, $utimestamp - SECONDS_2DAY);
-	
-	$search_in_history_db = db_search_in_history_db($utimestamp);
+		$id_agent_module, $utimestamp,
+		$utimestamp - SECONDS_2DAY
+	);
 
+	$search_in_history_db = db_search_in_history_db($utimestamp);
 	return db_get_row_sql ($sql, $search_in_history_db);
 }
 
@@ -2327,14 +2328,12 @@ function modules_get_first_date($id_agent_module, $datelimit = 0) {
 		$query  = " SELECT max(utimestamp) as utimestamp FROM $table ";
 		$query .= " WHERE id_agente_modulo=$id_agent_module ";
 		$query .= " AND utimestamp < $datelimit ";
-	
 	}
 	else {
 		// get first utimestamp
 		$query  = " SELECT min(utimestamp) as utimestamp FROM $table ";
 		$query .= " WHERE id_agente_modulo=$id_agent_module ";
 	}
-	
 
 	// SEARCH ACTIVE DB
 	$data = db_get_all_rows_sql($query,$search_historydb);
diff --git a/pandora_console/include/functions_reporting.php b/pandora_console/include/functions_reporting.php
index 74329b6fd4..c17b4650af 100755
--- a/pandora_console/include/functions_reporting.php
+++ b/pandora_console/include/functions_reporting.php
@@ -288,7 +288,10 @@ function reporting_make_reporting_data($report = null, $id_report,
 						$content,
 						$type,
 						$force_width_chart,
-						$force_height_chart, 'custom_graph');
+						$force_height_chart,
+						'custom_graph',
+						$pdf
+					);
 				break;
 			case 'automatic_graph':
 				$report['contents'][] =
@@ -297,7 +300,10 @@ function reporting_make_reporting_data($report = null, $id_report,
 						$content,
 						$type,
 						$force_width_chart,
-						$force_height_chart, 'automatic_graph');
+						$force_height_chart,
+						'automatic_graph',
+						$pdf
+					);
 				break;
 			case 'text':
 				$report['contents'][] = reporting_text(
@@ -374,7 +380,9 @@ function reporting_make_reporting_data($report = null, $id_report,
 					$content,
 					$type,
 					$force_width_chart,
-					$force_height_chart);
+					$force_height_chart,
+					$pdf
+				);
 				break;
 			case 'prediction_date':
 				$report['contents'][] = reporting_prediction_date(
@@ -490,7 +498,9 @@ function reporting_make_reporting_data($report = null, $id_report,
 				$report['contents'][] = reporting_network_interfaces_report(
 					$report,
 					$content,
-					$type);
+					$type,
+					$pdf
+				);
 				break;
 			case 'group_configuration':
 				$report['contents'][] = reporting_group_configuration(
@@ -1199,11 +1209,7 @@ function reporting_event_top_n($report, $content, $type = 'dinamic',
 						ui_print_truncate_text($agent_name[$i], $truncate_size, false, true, false, "...") .
 						' - ' . 
 						ui_print_truncate_text($module_name[$i], $truncate_size, false, true, false, "...");
-					
-					
-					
-					//Dirty hack, maybe I am going to apply a job in Apple
-					//https://www.imperialviolet.org/2014/02/22/applebug.html
+
 					$item_name_key_pie = $item_name;
 					$exist_key = true;
 					while ($exist_key) {
@@ -1255,11 +1261,7 @@ function reporting_event_top_n($report, $content, $type = 'dinamic',
 						' - ' . 
 						ui_print_truncate_text($module_name[$i],
 							$truncate_size, false, true, false, "...");
-					
-					
-					
-					//Dirty hack, maybe I am going to apply a job in Apple
-					//https://www.imperialviolet.org/2014/02/22/applebug.html
+
 					$item_name_key_pie = $item_name;
 					$exist_key = true;
 					while ($exist_key) {
@@ -2710,13 +2712,12 @@ function reporting_group_configuration($report, $content) {
 	return reporting_check_structure_content($return);
 }
 
-function reporting_network_interfaces_report($report, $content, $type = 'dinamic') {
-	
+function reporting_network_interfaces_report($report, $content, $type = 'dinamic', $pdf = 0) {
+
 	global $config;
-	
 
 	$return['type'] = 'network_interfaces_report';
-	
+
 	if (empty($content['name'])) {
 		$content['name'] = __('Network interfaces report');
 	}
@@ -2724,16 +2725,16 @@ function reporting_network_interfaces_report($report, $content, $type = 'dinamic
 	if (isset($content['style']['fullscale'])) {
 		$fullscale = (bool) $content['style']['fullscale'];
 	}
-	
+
 	$group_name = groups_get_name($content['id_group']);
-	
+
 	$return['title'] = $content['name'];
 	$return['subtitle'] = $group_name;
 	$return["description"] = $content["description"];
 	$return["date"] = reporting_get_date_text($report, $content);
-	
+
 	include_once($config['homedir'] . "/include/functions_custom_graphs.php");
-	
+
 	$filter = array(
 		'id_grupo' => $content['id_group'],
 		'disabled' => 0);
@@ -2752,7 +2753,15 @@ function reporting_network_interfaces_report($report, $content, $type = 'dinamic
 				}
 				else{
 					$network_interfaces_by_agents = agents_get_network_interfaces(false, $filter);
-					$return = agents_get_network_interfaces_array($network_interfaces_by_agents, $return, $type, $content, $report, $fullscale);
+					$return = agents_get_network_interfaces_array(
+						$network_interfaces_by_agents,
+						$return,
+						$type,
+						$content,
+						$report,
+						$fullscale,
+						$pdf
+					);
 					metaconsole_restore_db();
 				}
 			}
@@ -2760,13 +2769,24 @@ function reporting_network_interfaces_report($report, $content, $type = 'dinamic
 	}
 	else{
 		$network_interfaces_by_agents = agents_get_network_interfaces(false, $filter);
-		$return = agents_get_network_interfaces_array($network_interfaces_by_agents, $return, $type, $content, $report, $fullscale);
+		$return = agents_get_network_interfaces_array(
+			$network_interfaces_by_agents,
+			$return,
+			$type,
+			$content,
+			$report,
+			$fullscale,
+			$pdf
+		);
 	}
 
 	return reporting_check_structure_content($return);
 }
 
-function agents_get_network_interfaces_array($network_interfaces_by_agents, $return, $type, $content, $report, $fullscale){
+function agents_get_network_interfaces_array(
+	$network_interfaces_by_agents, $return,
+	$type, $content, $report, $fullscale, $pdf
+){
 	if (empty($network_interfaces_by_agents)) {
 		$return['failed'] =
 			__('The group has no agents or none of the agents has any network interface');
@@ -2785,75 +2805,45 @@ function agents_get_network_interfaces_array($network_interfaces_by_agents, $ret
 				$row_interface['status'] = $interface['status_image'];
 				$row_interface['chart'] = null;
 
-				// Get chart
-				reporting_set_conf_charts($width, $height, $only_image,
-					$type, $content, $ttl);
+				$width = null;
+				$height = null;
 
-				if (!empty($force_width_chart)) {
-					$width = $force_width_chart;
-				}
+				$params =array(
+					'period'    => $content['period'],
+					'width'     => $width,
+					'height'    => $height,
+					'unit_name' => array_fill(0, count($interface['traffic']), __("bytes/s")),
+					'date'      => $report["datetime"],
+					'only_image'=> $pdf,
+					'homeurl'   => $config['homeurl'],
+					'fullscale' => $fullscale
+				);
 
-				if (!empty($force_height_chart)) {
-					$height = $force_height_chart;
-				}
+				$params_combined = array(
+					'labels'         => array_keys($interface['traffic']),
+					'modules_series' => array_values($interface['traffic'])
+				);
 
 				switch ($type) {
 					case 'dinamic':
-						if (!empty($interface['traffic'])) {
-							$row_interface['chart'] = custom_graphs_print(0,
-								$height,
-								$width,
-								$content['period'],
-								null,
-								true,
-								$report["datetime"],
-								$only_image,
-								'white',
-								array_values($interface['traffic']),
-								$config['homeurl'],
-								array_keys($interface['traffic']),
-								array_fill(0, count($interface['traffic']), __("bytes/s")),
-								false,
-								true,
-								true,
-								true,
-								1,
-								false,
-								false,
-								null,
-								false,
-								false,
-								$fullscale);
-							}
-						break;
-					case 'data':
 					case 'static':
 						if (!empty($interface['traffic'])) {
-							$row_interface['chart'] = custom_graphs_print(0,
-								$height,
-								$width,
-								$content['period'],
-								null,
-								true,
-								$report["datetime"],
-								true,
-								'white',
+							$row_interface['chart'] = graphic_combined_module(
 								array_values($interface['traffic']),
-								$config['homeurl'],
-								array_keys($interface['traffic']),
-								array_fill(0, count($interface['traffic']), __("bytes/s")),
-								false,
-								true,
-								true,
-								true,
-								2,
-								false,
-								false,
-								null,
-								false,
-								false,
-								$fullscale);
-							}
+								$params,
+								$params_combined
+							);
+						}
+						break;
+					case 'data':
+						if (!empty($interface['traffic'])) {
+							$params['return_data'] = true;
+							$row_interface['chart'] = graphic_combined_module(
+								array_values($interface['traffic']),
+								$params,
+								$params_combined
+							);
+						}
 						break;
 				}
 				$row_data['interfaces'][] = $row_interface;
@@ -3485,77 +3475,72 @@ function reporting_netflow($report, $content, $type,
 function reporting_simple_baseline_graph($report, $content,
 	$type = 'dinamic', $force_width_chart = null,
 	$force_height_chart = null) {
-	
+
 	global $config;
-	
+
 	if ($config['metaconsole']) {
 		$id_meta = metaconsole_get_id_server($content["server_name"]);
-		
-		
 		$server = metaconsole_get_connection_by_id ($id_meta);
 		metaconsole_connect($server);
 	}
-	
+
 	$return['type'] = 'simple_baseline_graph';
-	
+
 	if (empty($content['name'])) {
 		$content['name'] = __('Simple baseline graph');
 	}
-	
+
 	$module_name = io_safe_output(
 		modules_get_agentmodule_name($content['id_agent_module']));
 	$agent_name = io_safe_output(
 		modules_get_agentmodule_agent_alias ($content['id_agent_module']));
-	
+
 	$return['title'] = $content['name'];
 	$return['subtitle'] = $agent_name . " - " . $module_name;
 	$return["description"] = $content["description"];
 	$return["date"] = reporting_get_date_text($report, $content);
 	$return['label'] = (isset($content['style']['label'])) ? $content['style']['label'] : '';
-	
+
 	// Get chart
 	reporting_set_conf_charts($width, $height, $only_image, $type,
 		$content, $ttl);
-	
-	if (!empty($force_width_chart)) {
-		$width = $force_width_chart;
+
+	$baseline_data = enterprise_hook(
+		'reporting_enterprise_get_baseline',
+		array (
+			$content['id_agent_module'],
+			$content['period'],
+			$report["datetime"]
+		)
+	);
+
+	if ($baseline_data === ENTERPRISE_NOT_HOOK) {
+		$baseline_data = array ();
 	}
-	
-	if (!empty($force_height_chart)) {
-		$height = $force_height_chart;
-	}
-	
+
 	switch ($type) {
 		case 'dinamic':
 		case 'static':
-			$return['chart'] = grafico_modulo_sparse(
-				$content['id_agent_module'],
-				$content['period'],
-				false,
-				$width,
-				$height,
-				'',
-				'',
-				false,
-				true,
-				true,
-				$report["datetime"],
-				'',
-				true,
-				0,
-				true,
-				$only_image,
-				ui_get_full_url(false, false, false, false),
-				$ttl);
+			$params =array(
+				'agent_module_id'     => $content['id_agent_module'],
+				'period'              => $content['period'],
+				'date'                => $report["datetime"],
+				'only_image'          => $only_image,
+				'homeurl'             => ui_get_full_url(false, false, false, false),
+				'ttl'                 => $ttl,
+				'array_data_create'   => $baseline_data
+			);
+
+			$return['chart'] = grafico_modulo_sparse ($params);
 			break;
 		case 'data':
 			break;
 	}
-	
+
 	if ($config['metaconsole']) {
 		metaconsole_restore_db();
 	}
-	
+
 	return reporting_check_structure_content($return);
 }
 
@@ -3602,85 +3587,70 @@ function reporting_prediction_date($report, $content) {
 
 function reporting_projection_graph($report, $content,
 	$type = 'dinamic', $force_width_chart = null,
-	$force_height_chart = null) {
-	
+	$force_height_chart = null, $pdf = false) {
+
 	global $config;
-	
+
 	if ($config['metaconsole']) {
 		$id_meta = metaconsole_get_id_server($content["server_name"]);
-		
-		
-		$server = metaconsole_get_connection_by_id ($id_meta);
+		$server  = metaconsole_get_connection_by_id ($id_meta);
 		metaconsole_connect($server);
 	}
-	
+
 	$return['type'] = 'projection_graph';
-	
+
 	if (empty($content['name'])) {
 		$content['name'] = __('Projection Graph');
 	}
-	
-	$module_name = io_safe_output(
-		modules_get_agentmodule_name($content['id_agent_module']));
-	$agent_name = io_safe_output(
-		modules_get_agentmodule_agent_alias ($content['id_agent_module']));
-	
-	$return['title'] = $content['name'];
-	$return['subtitle'] = $agent_name . " - " . $module_name;
+
+	$module_name = io_safe_output(modules_get_agentmodule_name($content['id_agent_module']));
+	$agent_name = io_safe_output(modules_get_agentmodule_agent_alias ($content['id_agent_module']));
+
+	$return['title']       = $content['name'];
+	$return['subtitle']    = $agent_name . " - " . $module_name;
 	$return["description"] = $content["description"];
-	$return["date"] = reporting_get_date_text($report, $content);
-	$return['label'] = (isset($content['style']['label'])) ? $content['style']['label'] : '';
-			
-	$return['agent_name'] = $agent_name;
+	$return["date"]        = reporting_get_date_text($report, $content);
+	$return['label']       = (isset($content['style']['label'])) ? $content['style']['label'] : '';
+	$return['agent_name']  = $agent_name;
 	$return['module_name'] = $module_name;
-	
-	
-	
+
 	set_time_limit(500);
-	
-	$output_projection = forecast_projection_graph(
-		$content['id_agent_module'], $content['period'], $content['top_n_value']);
-	
-	// If projection doesn't have data then don't draw graph
-	if ($output_projection ==  NULL) {
-		$output_projection = false;
-	}
-	
-	// Get chart
-	reporting_set_conf_charts($width, $height, $only_image, $type,
-		$content, $ttl);
-	
-	if (!empty($force_width_chart)) {
-		$width = $force_width_chart;
-	}
-	
-	if (!empty($force_height_chart)) {
-		$height = $force_height_chart;
-	}
-	
+
 	switch ($type) {
 		case 'dinamic':
 		case 'static':
+			$output_projection = forecast_projection_graph(
+				$content['id_agent_module'],
+				$content['period'],
+				$content['top_n_value']
+			);
+
+			// If projection doesn't have data then don't draw graph
+			if ($output_projection ==  NULL) {
+				$output_projection = false;
+			}
+
+			$params =array(
+				'period'     => $content['period'],
+				'width'      => $width,
+				'height'     => $height,
+				'date'       => $report["datetime"],
+				'unit'       => '',
+				'only_image' => $pdf,
+				'homeurl'    => ui_get_full_url(false, false, false, false) . '/',
+				'ttl'        => $ttl
+			);
+
+			$params_combined = array(
+				'projection' => $output_projection
+			);
+
 			$return['chart'] = graphic_combined_module(
 				array($content['id_agent_module']),
-				array(),
-				$content['period'],
-				$width,
-				$height,
-				'',
-				'',
-				0,
-				0,
-				0,
-				0,
-				$report["datetime"],
-				$only_image,
-				ui_get_full_url(false, false, false, false) . '/',
-				$ttl,
-				// Important parameter, this tell to graphic_combined_module function that is a projection graph
-				$output_projection,
-				$content['top_n_value']
-				);
+				$params,
+				$params_combined
+			);
+
 			break;
 		case 'data':
 			$return['data'] = forecast_projection_graph(
@@ -3690,11 +3660,11 @@ function reporting_projection_graph($report, $content,
 				false, false, true);
 			break;
 	}
-	
+
 	if ($config['metaconsole']) {
 		metaconsole_restore_db();
 	}
-	
+
 	return reporting_check_structure_content($return);
 }
 
@@ -3819,7 +3789,7 @@ function reporting_agent_configuration($report, $content) {
 
 function reporting_value($report, $content, $type,$pdf) {
 	global $config;
-	
+
 	$return = array();
 	switch ($type) {
 		case 'max':
@@ -3909,7 +3879,6 @@ function reporting_value($report, $content, $type,$pdf) {
 	switch ($type) {
 		case 'max':
 		if($content['lapse_calc'] == 0){
-		
 			$value = reporting_get_agentmodule_data_max(
 				$content['id_agent_module'], $content['period'], $report["datetime"]);
 			if (!$config['simple_module_value']) {
@@ -3918,17 +3887,27 @@ function reporting_value($report, $content, $type,$pdf) {
 			else {
 				$formated_value = format_for_graph($value, $config['graph_precision']) . " " . $unit;
 			}
-			
 		}
 		else{
-			
+			$params =array(
+				'agent_module_id'     => $content['id_agent_module'],
+				'period'              => $content['period'],
+				'width'               => '600px',
+				'pure'                => false,///true
+				'date'                => $report["datetime"],
+				'only_image'          => $only_image,
+				'homeurl'             => ui_get_full_url(false, false, false, false),
+				'ttl'                 => 1,///2
+				'type_graph'          => $config['type_module_charts'],
+				'time_interval'       => $content['lapse']
+			);
+
 			$value = '
 							
+
+				
'; - if($content['visual_format'] == 1 || $content['visual_format'] == 2 || $content['visual_format'] == 3){ - + $value .= ' @@ -3954,62 +3933,28 @@ function reporting_value($report, $content, $type,$pdf) {
'; - } - + $value .= '
'; - + if($content['visual_format'] == 2 || $content['visual_format'] == 3){ - $value .= - grafico_modulo_sparse( - $content['id_agent_module'], - $content['period'], - false, - 600, - 300, - '', - '', - false, - 0, - true, - $report["datetime"], - '', - 0, - 0, - true, - $only_image, - ui_get_full_url(false, false, false, false), - 2, - false, - '', - $time_compare_overlapped, - true, - true, - 'white', - ($content['style']['percentil'] == 1) ? $config['percentil'] : null, - false, - false, - $config['type_module_charts'], - false, - false, - $content['lapse_calc'], - $content['lapse'], - 1); + $params['force_interval'] = 'max_only'; + $value .= grafico_modulo_sparse($params); } - + $value .= ' - -
'; - + if($content['visual_format'] == 1 || $content['visual_format'] == 3){ - + $value .= ' @@ -4023,11 +3968,11 @@ function reporting_value($report, $content, $type,$pdf) { '; $time_begin = db_get_row_sql('select utimestamp from tagente_datos where id_agente_modulo ='.$content['id_agent_module']); $date_reference = getdate(); - + for ($i=$date_reference[0]; $i > ($date_reference[0]-$content["period"]); $i -= $content["lapse"]) { - + $value .= ''; @@ -4035,37 +3980,35 @@ function reporting_value($report, $content, $type,$pdf) { else{ $value .= 'N/A'; } - + } - + $value .='
'. date("Y-m-d H:i:s", ($i-$content["lapse"]+1)).' to '.date("Y-m-d H:i:s",$i).''; - + if($i>$time_begin['utimestamp']){ $value .= format_for_graph(reporting_get_agentmodule_data_max( $content['id_agent_module'], $content["lapse"], $i), $config['graph_precision']) . ' ' . $unit.'
'; } - $value .= ' -
'; - + $formated_value = $value; } - + break; case 'min': if($content['lapse_calc'] == 0){ $value = reporting_get_agentmodule_data_min( $content['id_agent_module'], $content['period'], $report["datetime"]); - + if (!$config['simple_module_value']) { $formated_value = $value; } else { $formated_value = format_for_graph($value, $config['graph_precision']) . " " . $unit; } - + } else{ - + $value = ' @@ -4098,57 +4041,21 @@ function reporting_value($report, $content, $type,$pdf) {
'; - } - + $value .= ' '; - + if($content['visual_format'] == 2 || $content['visual_format'] == 3){ - $value .= - grafico_modulo_sparse( - $content['id_agent_module'], - $content['period'], - false, - 600, - 300, - '', - '', - false, - 0, - true, - $report["datetime"], - '', - 0, - 0, - true, - $only_image, - ui_get_full_url(false, false, false, false), - 2, - false, - '', - $time_compare_overlapped, - true, - true, - 'white', - ($content['style']['percentil'] == 1) ? $config['percentil'] : null, - false, - false, - $config['type_module_charts'], - false, - false, - $content['lapse_calc'], - $content['lapse'], - 0, - 1); + $params['force_interval'] = 'min_only'; + $value .= grafico_modulo_sparse($params); } - + $value .= ' - - + '; @@ -4214,7 +4121,6 @@ function reporting_value($report, $content, $type,$pdf) { '; if($content['visual_format'] == 1 || $content['visual_format'] == 2 || $content['visual_format'] == 3){ - $value .= ' @@ -4250,41 +4156,8 @@ function reporting_value($report, $content, $type,$pdf) { '); + fragments.push(''); + rowStarted = true; + } + + fragments.push( + '' + + '' + ); + } + + if (rowStarted) + fragments.push(''); + + if (fragments.length == 0) + return; + + var table = '
'; if($content['visual_format'] == 2 || $content['visual_format'] == 3){ - $value .= - grafico_modulo_sparse( - $content['id_agent_module'], - $content['period'], - false, - 600, - 300, - '', - '', - false, - 1, - true, - $report["datetime"], - '', - 0, - 0, - true, - $only_image, - ui_get_full_url(false, false, false, false), - 2, - false, - '', - $time_compare_overlapped, - true, - true, - 'white', - ($content['style']['percentil'] == 1) ? $config['percentil'] : null, - false, - false, - $config['type_module_charts'], - false, - false, - $content['lapse_calc'], - $content['lapse'] - ); + $params['force_interval'] = 'avg_only'; + $value .= grafico_modulo_sparse($params); } $value .= ' @@ -6395,16 +6268,17 @@ function reporting_general($report, $content) { } function reporting_custom_graph($report, $content, $type = 'dinamic', - $force_width_chart = null, $force_height_chart = null, $type_report = "custom_graph") { + $force_width_chart = null, $force_height_chart = null, + $type_report = "custom_graph", $pdf = false) { global $config; - + require_once ($config["homedir"] . '/include/functions_graph.php'); - + $graph = db_get_row ("tgraph", "id_graph", $content['id_gs']); $return = array(); $return['type'] = 'custom_graph'; - + if (empty($content['name'])) { if ($type_report == "custom_graph") { $content['name'] = __('Custom graph'); @@ -6413,21 +6287,21 @@ function reporting_custom_graph($report, $content, $type = 'dinamic', $content['name'] = __('Simple graph'); } } - + $return['title'] = $content['name']; $return['subtitle'] = io_safe_output($graph['name']); $return["description"] = $content["description"]; $return["date"] = reporting_get_date_text( $report, $content); - + $graphs = db_get_all_rows_field_filter ("tgraph_source", "id_graph", $content['id_gs']); $modules = array (); $weights = array (); if ($graphs === false) $graphs = array(); - + $labels = array(); foreach ($graphs as $graph_item) { if ($type_report == 'automatic_graph') { @@ -6438,7 +6312,7 @@ function reporting_custom_graph($report, $content, $type = 'dinamic', else { array_push ($modules, $graph_item['id_agent_module']); } - + if (in_array('label',$content['style'])) { if (defined('METACONSOLE')) { $server_name = $content['server_name']; @@ -6456,7 +6330,7 @@ function reporting_custom_graph($report, $content, $type = 'dinamic', 'id_agent' =>modules_get_agentmodule_agent($graph_item['id_agent_module']), 'id_agent_module'=>$graph_item['id_agent_module']); } - + $label = reporting_label_macro($item, $content['style']['label']); $labels[$graph_item['id_agent_module']] = $label; @@ -6468,19 +6342,21 @@ function reporting_custom_graph($report, $content, $type = 'dinamic', array_push ($weights, $graph_item["weight"]); } - + if ($config['metaconsole'] && $type_report != 'automatic_graph') { - $id_meta = metaconsole_get_id_server($content["server_name"]); + $id_meta = metaconsole_get_id_server($content["server_name"]); $server = metaconsole_get_connection_by_id ($id_meta); metaconsole_connect($server); } $return['chart'] = ''; // Get chart - reporting_set_conf_charts($width, $height, $only_image, $type, - $content, $ttl); - + //reporting_set_conf_charts($width, $height, $only_image, $type, + // $content, $ttl); +$width =null; +$height =null; //height for bullet chart + /* if($graph['stacked'] != 4){ $height += count($modules) * REPORTING_CUSTOM_GRAPH_LEGEND_EACH_MODULE_VERTICAL_SIZE; } @@ -6489,50 +6365,44 @@ function reporting_custom_graph($report, $content, $type = 'dinamic', $height = 50; } } - +*/ + switch ($type) { case 'dinamic': case 'static': + + $params =array( + 'period' => $content['period'], + 'width' => $width, + 'height' => $height, + 'date' => $report["datetime"], + 'only_image' => $pdf, + 'homeurl' => ui_get_full_url(false, false, false, false), + 'ttl' => $ttl, + 'percentil' => $graph["percentil"], + 'fullscale' => $graph["fullscale"], + ); + + $params_combined = array( + 'weight_list' => $weights, + 'stacked' => $graph["stacked"], + 'labels' => $labels, + 'summatory' => $graph["summatory_series"], + 'average' => $graph["average_series"], + 'modules_series' => $graph["modules_series"] + ); + $return['chart'] = graphic_combined_module( $modules, - $weights, - $content['period'], - $width, $height, - '', - '', - 0, - 0, - 0, - $graph["stacked"], - $report["datetime"], - $only_image, - ui_get_full_url(false, false, false, false), - $ttl, - false, - false, - 'white', - array(), - array(), - true, - true, - true, - true, - $labels, - false, - false, - $graph["percentil"], - false, - false, - $graph["fullscale"], - $graph["summatory_series"], - $graph["average_series"], - $graph["modules_series"] + $params, + $params_combined ); + break; case 'data': break; } - + if ($config['metaconsole'] && $type_report != 'automatic_graph') { metaconsole_restore_db(); } @@ -6542,36 +6412,28 @@ function reporting_custom_graph($report, $content, $type = 'dinamic', function reporting_simple_graph($report, $content, $type = 'dinamic', $force_width_chart = null, $force_height_chart = null) { - + global $config; - - + if ($config['metaconsole']) { $id_meta = metaconsole_get_id_server($content["server_name"]); - - + $server = metaconsole_get_connection_by_id ($id_meta); metaconsole_connect($server); } - - + $return = array(); $return['type'] = 'simple_graph'; - + if (empty($content['name'])) { $content['name'] = __('Simple graph'); } - - - + $module_name = io_safe_output( modules_get_agentmodule_name($content['id_agent_module'])); $agent_name = io_safe_output( modules_get_agentmodule_agent_alias ($content['id_agent_module'])); - - - - + $return['title'] = $content['name']; $return['subtitle'] = $agent_name . " - " . $module_name; $return['agent_name'] = $agent_name; @@ -6581,10 +6443,10 @@ function reporting_simple_graph($report, $content, $type = 'dinamic', $report, $content); $label = (isset($content['style']['label'])) ? $content['style']['label'] : ''; - if ($label != '') { - $label = reporting_label_macro($content, $label); - } - + if ($label != '') { + $label = reporting_label_macro($content, $label); + } + $only_avg = true; // Due to database compatibility problems, the 'only_avg' value // is stored into the json contained into the 'style' column. @@ -6595,105 +6457,64 @@ function reporting_simple_graph($report, $content, $type = 'dinamic', if (isset($content['style']['fullscale'])) { $fullscale = (bool) $content['style']['fullscale']; } - - $moduletype_name = modules_get_moduletype_name( - modules_get_agentmodule_type( - $content['id_agent_module'])); - + $return['chart'] = ''; + // Get chart - reporting_set_conf_charts($width, $height, $only_image, $type, - $content, $ttl); - + reporting_set_conf_charts($width, $height, $only_image, $type, $content, $ttl); + if (!empty($force_width_chart)) { $width = $force_width_chart; } - + if (!empty($force_height_chart)) { $height = $force_height_chart; } - + switch ($type) { case 'dinamic': case 'static': - if (preg_match ("/string/", $moduletype_name)) { - - $urlImage = ui_get_full_url(false, false, false, false); - - $return['chart'] = grafico_modulo_string( - $content['id_agent_module'], - $content['period'], - false, - $width, - $height, - $label, - '', - false, - $only_avg, - false, - $report["datetime"], - $only_image, - $urlImage, - "", - $ttl); - - } - else { - // HACK it is saved in show_graph field. - $time_compare_overlapped = false; - if ($content['show_graph']) { - $time_compare_overlapped = 'overlapped'; - } - - $return['chart'] = grafico_modulo_sparse( - $content['id_agent_module'], - $content['period'], - false, - $width, - $height, - $label, - '', - false, - $only_avg, - true, - $report["datetime"], - '', - 0, - 0, - true, - $only_image, - ui_get_full_url(false, false, false, false), - $ttl, - false, - '', - $time_compare_overlapped, - true, - true, - 'white', - ($content['style']['percentil'] == 1) ? $config['percentil'] : null, - false, - false, - $config['type_module_charts'], - $fullscale); + // HACK it is saved in show_graph field. + $time_compare_overlapped = false; + if ($content['show_graph']) { + $time_compare_overlapped = 'overlapped'; } + + $params =array( + 'agent_module_id' => $content['id_agent_module'], + 'period' => $content['period'], + 'title' => $label, + 'avg_only' => $only_avg, + 'pure' => false, + 'date' => $report["datetime"], + 'only_image' => $only_image, + 'homeurl' => ui_get_full_url(false, false, false, false), + 'ttl' => $ttl, + 'compare' => $time_compare_overlapped, + 'show_unknown' => true, + 'percentil' => ($content['style']['percentil'] == 1) ? $config['percentil'] : null, + 'fullscale' => $fullscale + ); + + $return['chart'] = grafico_modulo_sparse($params); + break; case 'data': $data = modules_get_agentmodule_data( $content['id_agent_module'], $content['period'], $report["datetime"]); - + foreach ($data as $d) { $return['chart'][$d['utimestamp']] = $d['data']; } - + break; } - + if ($config['metaconsole']) { metaconsole_restore_db(); } - return reporting_check_structure_content($return); } diff --git a/pandora_console/include/functions_treeview.php b/pandora_console/include/functions_treeview.php index cde01c68dd..fda921a44a 100755 --- a/pandora_console/include/functions_treeview.php +++ b/pandora_console/include/functions_treeview.php @@ -727,11 +727,10 @@ function treeview_printTable($id_agente, $server_data = array(), $no_head = fals //echo '
 
'; if ($config["agentaccess"]) { - $access_graph = '
'; - $access_graph .= graphic_agentaccess($id_agente, 290, 110, - SECONDS_1DAY, true); - $access_graph .= '

'; - + $access_graph = '
hjhhjhhj
'; + $access_graph = '
'; + $access_graph .= graphic_agentaccess($id_agente, 380, 180, SECONDS_1DAY, true, true); + $access_graph .= '


'; ui_toggle($access_graph, __('Agent access rate (24h)')); } diff --git a/pandora_console/include/functions_visual_map.php b/pandora_console/include/functions_visual_map.php index 7585002e76..d434723e34 100755 --- a/pandora_console/include/functions_visual_map.php +++ b/pandora_console/include/functions_visual_map.php @@ -944,9 +944,9 @@ function visual_map_print_item($mode = "read", $layoutData, continue; } } - + $only_image = !$graph_javascript && $isExternalLink; - + if ($layoutData['id_custom_graph'] != 0) { // Show only avg on the visual console if (get_parameter('action') == 'edit') { @@ -955,59 +955,54 @@ function visual_map_print_item($mode = "read", $layoutData, } else { $img = ''; - } - } + } + } else { - if ($width == 0 || $height == 0) { - if ($layoutData['label_position']=='left') { - $img = '
'.custom_graphs_print( - $layoutData['id_custom_graph'], 180, 480, - $period, null, true, 0, $only_image, $layoutData['image'], - array(), '', array(), array(), true, - false, false, true, 1, false, true).'
'; - } - elseif ($layoutData['label_position']=='right') { - $img = '
'.custom_graphs_print( - $layoutData['id_custom_graph'], 180, 480, - $period, null, true, 0, $only_image, $layoutData['image'], - array(), '', array(), array(), true, - false, false, true, 1, false, true).'
'; - } - else { - $img = custom_graphs_print( - $layoutData['id_custom_graph'], 180, 480, - $period, null, true, 0, $only_image, $layoutData['image'], - array(), '', array(), array(), true, - false, false, true, 1, false, true); - } + if ($width == 0 ) { + $width = 180; + } + if($height == 0) { + $height = 480; + } + + $params =array( + 'period' => $period, + 'width' => $width, + 'height' => $height, + 'title' => '', + 'unit_name' => null, + 'show_alerts' => false, + 'only_image' => $only_image, + 'vconsole' => true, + 'backgroundColor' => $layoutData['image'] + ); + + $params_combined = array( + 'id_graph' => $layoutData['id_custom_graph'] + ); + + if ($layoutData['label_position']=='left') { + $img = '
'. + graphic_combined_module( + false, + $params, + $params_combined + ).'
'; + } + elseif ($layoutData['label_position']=='right') { + $img = '
'. + graphic_combined_module( + false, + $params, + $params_combined + ).'
'; } else { - if ($width < 480){ - $img = '
'._("Could not draw pie with labels contained inside canvas. Resize widget to 500px width minimum").'
'; - } - else { - if ($layoutData['label_position']=='left') { - $img = '
'.custom_graphs_print( - $layoutData['id_custom_graph'], $height, $width, - $period, null, true, 0, $only_image, $layoutData['image'], - array(), '', array(), array(), true, - false, false, true, 1, false, true).'
'; - } - elseif($layoutData['label_position']=='right') { - $img = '
'.custom_graphs_print( - $layoutData['id_custom_graph'], $height, $width, - $period, null, true, 0, $only_image, $layoutData['image'], - array(), '', array(), array(), true, - false, false, true, 1, false, true).'
'; - } - else { - $img = custom_graphs_print( - $layoutData['id_custom_graph'], $height, $width, - $period, null, true, 0, $only_image, $layoutData['image'], - array(), '', array(), array(), true, - false, false, true, 1, false, true); - } - } + $img = graphic_combined_module( + false, + $params, + $params_combined + ); } } } @@ -1016,7 +1011,7 @@ function visual_map_print_item($mode = "read", $layoutData, $homeurl = $config['homeurl']; else $homeurl = ''; - + if ( (get_parameter('action') == 'edit') || (get_parameter('operation') == 'edit_visualmap') ) { if($width == 0 || $height == 0){ if ($layoutData['id_metaconsole'] != 0) { @@ -1034,85 +1029,62 @@ function visual_map_print_item($mode = "read", $layoutData, else{ $img = ''; } - } - } - else { - if ($width == 0 || $height == 0) { - - if ($layoutData['label_position']=='left') { - $img = '
'. - grafico_modulo_sparse($id_module, $period, - 0, 300, 180, modules_get_agentmodule_name($id_module),null, false, 1, false, 0, - modules_get_unit($id_module), 0, 0, true, $only_image, '', 1, false, '', - false, false, false, $layoutData['image'], - null, true, false, $type_graph) . '
'; - } - elseif($layoutData['label_position']=='right') { - $img = '
' . - grafico_modulo_sparse($id_module, - $period, 0, 300, 180, modules_get_agentmodule_name($id_module),null, false, - 1, false, 0, modules_get_unit($id_module), 0, 0, true, $only_image, '', - 1, false, '', false, false, false, - $layoutData['image'], null, true, - false, $type_graph) . '
'; - } - else { - $img = grafico_modulo_sparse($id_module, - $period, 0, 300, 180, modules_get_agentmodule_name($id_module),null, false, 1, - false, 0, modules_get_unit($id_module), 0, 0, true, $only_image, '', - 1, false, '', false, false, false, - $layoutData['image'], null, true, false, $type_graph); - } - } - else{ - if ($layoutData['label_position']=='left') { - $img = '
' . - grafico_modulo_sparse($id_module, $period, - 0, $width, $height, modules_get_agentmodule_name($id_module), null, false, 1, - false, 0, modules_get_unit($id_module), 0, 0, true, $only_image, '', - 1, false, '', false, false, false, - $layoutData['image'], null, true, - false, $type_graph) . '
'; - } - elseif ($layoutData['label_position']=='right') { - $img = '
' . - grafico_modulo_sparse($id_module, $period, - 0, $width, $height, modules_get_agentmodule_name($id_module), null, false, 1, - false, 0, modules_get_unit($id_module), 0, 0, true, $only_image, - '', 1, false, modules_get_unit($id_module), false, false, false, - $layoutData['image'], null, true, - false, $type_graph) . '
'; - } - else { - $img = grafico_modulo_sparse($id_module, - $period, 0, $width, $height, modules_get_agentmodule_name($id_module), null, - false, 1, false, 0, modules_get_unit($id_module), 0, 0, true, - $only_image, '', 1, false, '', false, - false, false, $layoutData['image'], - null, false, true, $type_graph); - } } } - } - + else { + + if ($width == 0 || $height == 0) { + $width = 300; + $height = 180; + } + + $params =array( + 'agent_module_id' => $id_module, + 'period' => $period, + 'show_events' => false, + 'width' => $width, + 'height' => $height, + 'title' => modules_get_agentmodule_name($id_module), + 'unit' => modules_get_unit($id_module), + 'only_image' => $only_image, + 'menu' => false, + 'backgroundColor' => $layoutData['image'], + 'type_graph' => $type_graph, + 'vconsole' => true + ); + + if ($layoutData['label_position']=='left') { + $img = '
'. + grafico_modulo_sparse($params) . '
'; + } + elseif($layoutData['label_position']=='right') { + + $img = '
' . + grafico_modulo_sparse($params) . '
'; + } + else { + $img = grafico_modulo_sparse($params); + } + + } + } + //Restore db connection if ($layoutData['id_metaconsole'] != 0) { metaconsole_restore_db(); } - break; - + case BARS_GRAPH: - $imgpos = ''; - + if($layoutData['label_position']=='left'){ $imgpos = 'float:right'; } else if($layoutData['label_position']=='right'){ $imgpos = 'float:left'; } - + if (!empty($proportion)) { $width = ((integer)($proportion['proportion_width'] * $width)); diff --git a/pandora_console/include/graphs/fgraph.php b/pandora_console/include/graphs/fgraph.php index ac6816ec58..9639ac210d 100644 --- a/pandora_console/include/graphs/fgraph.php +++ b/pandora_console/include/graphs/fgraph.php @@ -39,11 +39,11 @@ if (!empty($graph_type)) { ob_end_clean (); switch($graph_type) { - case 'histogram': + case 'histogram': $width = get_parameter('width'); $height = get_parameter('height'); $data = json_decode(io_safe_output(get_parameter('data')), true); - + $max = get_parameter('max'); $title = get_parameter('title'); $mode = get_parameter ('mode', 1); @@ -53,19 +53,19 @@ switch($graph_type) { $width = get_parameter('width'); $height = get_parameter('height'); $progress = get_parameter('progress'); - + $out_of_lim_str = io_safe_output(get_parameter('out_of_lim_str', false)); $out_of_lim_image = get_parameter('out_of_lim_image', false); - + $title = get_parameter('title'); - + $mode = get_parameter('mode', 1); - + $fontsize = get_parameter('fontsize', 10); - + $value_text = get_parameter('value_text', ''); $colorRGB = get_parameter('colorRGB', ''); - + gd_progress_bar ($width, $height, $progress, $title, $config['fontpath'], $out_of_lim_str, $out_of_lim_image, $mode, $fontsize, $value_text, $colorRGB); @@ -74,19 +74,19 @@ switch($graph_type) { $width = get_parameter('width'); $height = get_parameter('height'); $progress = get_parameter('progress'); - + $out_of_lim_str = io_safe_output(get_parameter('out_of_lim_str', false)); $out_of_lim_image = get_parameter('out_of_lim_image', false); - + $title = get_parameter('title'); - + $mode = get_parameter('mode', 1); - + $fontsize = get_parameter('fontsize', 7); - + $value_text = get_parameter('value_text', ''); $colorRGB = get_parameter('colorRGB', ''); - + gd_progress_bubble ($width, $height, $progress, $title, $config['fontpath'], $out_of_lim_str, $out_of_lim_image, $mode, $fontsize, $value_text, $colorRGB); @@ -95,7 +95,7 @@ switch($graph_type) { function histogram($chart_data, $width, $height, $font, $max, $title, $mode, $ttl = 1) { - + $graph = array(); $graph['data'] = $chart_data; $graph['width'] = $width; @@ -104,18 +104,18 @@ function histogram($chart_data, $width, $height, $font, $max, $title, $graph['max'] = $max; $graph['title'] = $title; $graph['mode'] = $mode; - + $id_graph = serialize_in_temp($graph, null, $ttl); - + return ""; } function progressbar($progress, $width, $height, $title, $font, $mode = 1, $out_of_lim_str = false, $out_of_lim_image = false, $ttl = 1) { - + $graph = array(); - + $graph['progress'] = $progress; $graph['width'] = $width; $graph['height'] = $height; @@ -124,7 +124,7 @@ function progressbar($progress, $width, $height, $title, $font, $graph['title'] = $title; $graph['font'] = $font; $graph['mode'] = $mode; - + $id_graph = serialize_in_temp($graph, null, $ttl); if (is_metaconsole()) { return ""; @@ -137,7 +137,7 @@ function progressbar($progress, $width, $height, $title, $font, function slicesbar_graph($chart_data, $period, $width, $height, $colors, $font, $round_corner, $home_url = '', $ttl = 1) { - + $graph = array(); $graph['data'] = $chart_data; $graph['period'] = $period; @@ -146,9 +146,9 @@ function slicesbar_graph($chart_data, $period, $width, $height, $colors, $graph['font'] = $font; $graph['round_corner'] = $round_corner; $graph['color'] = $colors; - + $id_graph = serialize_in_temp($graph, null, $ttl); - + return ""; } @@ -172,15 +172,14 @@ function vbar_graph( $backgroundColor = 'white', $from_ux = false, $from_wux = false, - $tick_color = 'white' -) { + $tick_color = 'white') { setup_watermark($water_mark, $water_mark_file, $water_mark_url); - + if (empty($chart_data)) { return ''; } - + if ($flash_chart) { return flot_vcolumn_chart ($chart_data, $width, $height, $color, $legend, $long_index, $homeurl, $unit, $water_mark_url, @@ -213,337 +212,78 @@ function vbar_graph( $graph['water_mark'] = $water_mark_file; $graph['font'] = $font; $graph['font_size'] = $font_size; - + $id_graph = serialize_in_temp($graph, null, $ttl); - + return ""; } } -// NOT USED ACTUALLY -function threshold_graph($flash_chart, $chart_data, $width, $height, - $ttl = 1) { - - if ($flash_chart) { - return flot_area_simple_graph($chart_data, $width, $height); - } - else { - echo ""; - } -} +function area_graph( + $agent_module_id, $array_data, + $legend, $series_type, $color, $date_array, + $data_module_graph, $params, $water_mark, + $array_events_alerts) { + global $config; -function area_graph($flash_chart, $chart_data, $width, $height, $color, - $legend, $long_index, $no_data_image, $xaxisname = "", - $yaxisname = "", $homeurl="", $water_mark = "", $font = '', - $font_size = '', $unit = '', $ttl = 1, $series_type = array(), - $chart_extra_data = array(), $yellow_threshold = 0, - $red_threshold = 0, $adapt_key = '', $force_integer = false, - $series_suffix_str = '', $menu = true, $backgroundColor = 'white', - $dashboard = false, $vconsole = false, $agent_module_id = 0, $percentil_values = array(), - $threshold_data = array()) { - include_once('functions_flot.php'); - - setup_watermark($water_mark, $water_mark_file, $water_mark_url); - - // ATTENTION: The min size is in constants.php - // It's not the same minsize for all graphs, but we are choosed a prudent minsize for all - if ($height <= CHART_DEFAULT_HEIGHT) { - $height = CHART_DEFAULT_HEIGHT; - } - if ($width < CHART_DEFAULT_WIDTH) { - $width = CHART_DEFAULT_WIDTH; - } - - if (empty($chart_data)) { - return graph_nodata_image($width, $height); - return ''; - } - if ($vconsole) $menu = false; - - if ($flash_chart) { - return flot_area_simple_graph( - $chart_data, - $width, - $height, - $color, - $legend, - $long_index, - $homeurl, - $unit, - $water_mark_url, - $series_type, - $chart_extra_data, - $yellow_threshold, - $red_threshold, - $adapt_key, - $force_integer, - $series_suffix_str, - $menu, - $backgroundColor, - $dashboard, - $vconsole, - $agent_module_id, - $font, - $font_size, - $xaxisname, - $percentil_values, - $threshold_data - ); - } - else { - $graph = array(); - $graph['data'] = $chart_data; - $graph['width'] = $width; - $graph['height'] = $height; - $graph['color'] = $color; - $graph['legend'] = $legend; - $graph['xaxisname'] = $xaxisname; - $graph['yaxisname'] = $yaxisname; - $graph['water_mark'] = $water_mark_file; - $graph['font'] = $font; - $graph['font_size'] = $font_size; - $graph['backgroundColor'] = $backgroundColor; - $graph['unit'] = $unit; - $graph['series_type'] = $series_type; - $graph['percentil'] = $percentil_values; - - $id_graph = serialize_in_temp($graph, null, $ttl); - // Warning: This string is used in the function "api_get_module_graph" from 'functions_api.php' with the regec patern "//" - return ""; - } + setup_watermark($water_mark, $water_mark_file, $water_mark_url); + + return flot_area_graph( + $agent_module_id, + $array_data, + $legend, + $series_type, + $color, + $date_array, + $data_module_graph, + $params, + $water_mark, + $array_events_alerts + ); } -function stacked_area_graph($flash_chart, $chart_data, $width, $height, - $color, $legend, $long_index, $no_data_image, $xaxisname = "", - $yaxisname = "", $water_mark = "", $font = '', $font_size = '', - $unit = '', $ttl = 1, $homeurl = '', $backgroundColor = 'white', - $dashboard = false, $vconsole = false, $agent_module_id) { - - setup_watermark($water_mark, $water_mark_file, $water_mark_url); - - if (empty($chart_data)) { - return ''; - } - - $menu = (!$dashboard && !$vconsole); - - if ($flash_chart) { - return flot_area_stacked_graph( - $chart_data, - $width, - $height, - $color, - $legend, - $long_index, - $homeurl, - $font, - $font_size, - $unit, - $water_mark_url, - array(), - array(), - 0, - 0, - '', - false, - '', - $menu, - $backgroundColor, - $dashboard, - $vconsole, - $agent_module_id); - } - else { - //Stack the data - stack_data($chart_data, $legend, $color); - - $graph = array(); - $graph['data'] = $chart_data; - $graph['width'] = $width; - $graph['height'] = $height; - $graph['color'] = $color; - $graph['legend'] = $legend; - $graph['xaxisname'] = $xaxisname; - $graph['yaxisname'] = $yaxisname; - $graph['water_mark'] = $water_mark_file; - $graph['font'] = $font; - $graph['font_size'] = $font_size; - $graph['backgroundColor'] = $backgroundColor; - - $id_graph = serialize_in_temp($graph, null, $ttl); - - return ""; - } -} - -function stacked_line_graph($flash_chart, $chart_data, $width, $height, - $color, $legend, $long_index, $no_data_image, $xaxisname = "", - $yaxisname = "", $water_mark = "", $font = '', $font_size = '', - $unit = '', $ttl = 1, $homeurl = '', $backgroundColor = 'white', - $dashboard = false, $vconsole = false) { - - setup_watermark($water_mark, $water_mark_file, $water_mark_url); - - if (empty($chart_data)) { - return ''; - } - - $menu = (!$dashboard && !$vconsole); - - if ($flash_chart) { - return flot_line_stacked_graph( - $chart_data, - $width, - $height, - $color, - $legend, - $long_index, - $homeurl, - $font, - $font_size, - $unit, - $water_mark_url, - array(), - array(), - 0, - 0, - '', - false, - '', - $menu, - $backgroundColor, - $dashboard, - $vconsole); - } - else { - //Stack the data - stack_data($chart_data, $legend, $color); - - $graph = array(); - $graph['data'] = $chart_data; - $graph['width'] = $width; - $graph['height'] = $height; - $graph['color'] = $color; - $graph['legend'] = $legend; - $graph['xaxisname'] = $xaxisname; - $graph['yaxisname'] = $yaxisname; - $graph['water_mark'] = $water_mark_file; - $graph['font'] = $font; - $graph['font_size'] = $font_size; - $graph['backgroundColor'] = $backgroundColor; - - $id_graph = serialize_in_temp($graph, null, $ttl); - - return ""; - } -} - -function stacked_bullet_chart($flash_chart, $chart_data, $width, $height, +function stacked_bullet_chart($chart_data, $width, $height, $color, $legend, $long_index, $no_data_image, $xaxisname = "", $yaxisname = "", $water_mark = "", $font = '', $font_size = '', $unit = '', $ttl = 1, $homeurl = '', $backgroundColor = 'white') { - - include_once('functions_d3.php'); - - setup_watermark($water_mark, $water_mark_file, $water_mark_url); - - if (empty($chart_data)) { - return ''; - } - if ($flash_chart) { - return d3_bullet_chart( - $chart_data, - $width, - $height, - $color, - $legend, - $homeurl, - $unit, - $font, - $font_size - ); - } - else { - $legend = array(); - $new_data = array(); - foreach($chart_data as $key => $data) { - $temp[] = ($data['min'] != false) ? $data['min'] : 0; - $temp[] = ($data['value'] != false) ? $data['value'] : 0; - $temp[] = ($data['max'] != false) ? $data['max'] : 0; - - $legend[] = $data['label']; - array_push($new_data, $temp); - $temp = array(); - } - $graph = array(); - $graph['data'] = $new_data; - $graph['width'] = $width; - $graph['height'] = $height; - $graph['color'] = $color; - $graph['legend'] = $legend; - $graph['xaxisname'] = $xaxisname; - $graph['yaxisname'] = $yaxisname; - $graph['water_mark'] = $water_mark_file; - $graph['font'] = $font; - $graph['font_size'] = $font_size; - $graph['backgroundColor'] = $backgroundColor; - - $id_graph = serialize_in_temp($graph, null, $ttl); - - return ""; - } - -} -function stacked_gauge($flash_chart, $chart_data, $width, $height, - $color, $legend, $long_index, $no_data_image, $xaxisname = "", - $yaxisname = "", $water_mark = "", $font = '', $font_size = '', - $unit = '', $ttl = 1, $homeurl = '', $backgroundColor = 'white') { - include_once('functions_d3.php'); - + setup_watermark($water_mark, $water_mark_file, $water_mark_url); - + if (empty($chart_data)) { return ''; } - - return d3_gauges( - $chart_data, - $width, - $height, - $color, - $legend, - $homeurl, - $unit, - $font, - $font_size + 2, - $no_data_image - ); + + return d3_bullet_chart( + $chart_data, + $width, + $height, + $color, + $legend, + $homeurl, + $unit, + $font, + $font_size + ); + } function stacked_thermometers($flash_chart, $chart_data, $width, $height, $color, $legend, $long_index, $no_data_image, $xaxisname = "", $yaxisname = "", $water_mark = "", $font = '', $font_size = '', $unit = '', $ttl = 1, $homeurl = '', $backgroundColor = 'white') { - + include_once('functions_d3.php'); - + setup_watermark($water_mark, $water_mark_file, $water_mark_url); - + if (empty($chart_data)) { return ''; } - + return d3_thermometers( $chart_data, $width, @@ -558,127 +298,47 @@ function stacked_thermometers($flash_chart, $chart_data, $width, $height, ); } -function line_graph($flash_chart, $chart_data, $width, $height, $color, - $legend, $long_index, $no_data_image, $xaxisname = "", - $yaxisname = "", $water_mark = "", $font = '', $font_size = '', - $unit = '', $ttl = 1, $homeurl = '', $backgroundColor = 'white', - $dashboard = false, $vconsole = false, $series_type = array(), - $percentil_values = array(), $yellow_threshold = 0, $red_threshold = 0, - $threshold_data = array()) { - - include_once("functions_flot.php"); - - setup_watermark($water_mark, $water_mark_file, $water_mark_url); - +function stacked_gauge($chart_data, $width, $height, + $color, $legend, $no_data_image, $font = '', $font_size = '', + $unit = '', $homeurl = '') { + + include_once('functions_d3.php'); + if (empty($chart_data)) { return ''; } - $menu = (!$dashboard && !$vconsole); - - if ($flash_chart) { - return flot_line_simple_graph( - $chart_data, - $width, - $height, - $color, - $legend, - $long_index, - $homeurl, - $font, - $font_size, - $unit, - $water_mark_url, - $series_type, - array(), - $yellow_threshold, - $red_threshold, - '', - false, - '', - $menu, - $backgroundColor, - $dashboard, - $vconsole, - false, - $percentil_values, - $threshold_data, - $xaxisname); - } - else { - $graph = array(); - $graph['data'] = $chart_data; - $graph['width'] = $width; - $graph['height'] = $height; - $graph['color'] = $color; - $graph['legend'] = $legend; - $graph['xaxisname'] = $xaxisname; - $graph['yaxisname'] = $yaxisname; - $graph['water_mark'] = $water_mark_file; - $graph['font'] = $font; - $graph['font_size'] = $font_size; - $graph['backgroundColor'] = $backgroundColor; - $graph['percentil'] = $percentil_values; - $id_graph = serialize_in_temp($graph, null, $ttl); - - if(empty($homeurl)){ - return ""; - }else{ - return ""; - } - } + return d3_gauges( + $chart_data, + $width, + $height, + $color, + $legend, + $homeurl, + $unit, + $font, + $font_size + 2, + $no_data_image + ); } -function kiviat_graph($graph_type, $flash_chart, $chart_data, $width, - $height, $no_data_image, $ttl = 1, $homedir="") { - - if (empty($chart_data)) { - return ''; - } - - $graph = array(); - $graph['data'] = $chart_data; - $graph['width'] = $width; - $graph['height'] = $height; - - $id_graph = serialize_in_temp($graph, null, $ttl); - - return ""; -} - -function radar_graph($flash_chart, $chart_data, $width, $height, - $no_data_image, $ttl = 1, $homedir="") { - - return kiviat_graph('radar', $flash_chart, $chart_data, $width, - $height, $no_data_image, $ttl, $homedir); -} - -function polar_graph($flash_chart, $chart_data, $width, $height, - $no_data_image, $ttl = 1, $homedir="") { - - return kiviat_graph('polar', $flash_chart, $chart_data, $width, - $height, $no_data_image, $ttl, $homedir=""); -} - - function hbar_graph($flash_chart, $chart_data, $width, $height, $color, $legend, $long_index, $no_data_image, $xaxisname = "", $yaxisname = "", $water_mark = "", $font = '', $font_size = '', $unit = '', $ttl = 1, $homeurl = '', $backgroundColor = 'white', $tick_color = "white", $val_min=null, $val_max=null) { - + setup_watermark($water_mark, $water_mark_file, $water_mark_url); - + if (empty($chart_data)) { return ''; } - + if ($flash_chart) { return flot_hcolumn_chart( $chart_data, $width, $height, $water_mark_url, $font, $font_size, $backgroundColor, $tick_color, $val_min, $val_max); } else { - foreach ($chart_data as $key => $value) { $str_key = io_safe_output($key); if(strlen($str_key) > 40){ @@ -692,8 +352,7 @@ function hbar_graph($flash_chart, $chart_data, $width, $height, unset($chart_data[$key]); } } - - + $graph = array(); $graph['data'] = $chart_data; $graph['width'] = $width; @@ -707,9 +366,9 @@ function hbar_graph($flash_chart, $chart_data, $width, $height, $graph['font'] = $font; $graph['font_size'] = $font_size; $graph['force_steps'] = $force_steps; - + $id_graph = serialize_in_temp($graph, null, $ttl); - + return ""; } } @@ -718,7 +377,7 @@ function pie3d_graph($flash_chart, $chart_data, $width, $height, $others_str = "other", $homedir = "", $water_mark = "", $font = '', $font_size = '', $ttl = 1, $legend_position = false, $colors = '', $hide_labels = false) { - + return pie_graph('3d', $flash_chart, $chart_data, $width, $height, $others_str, $homedir, $water_mark, $font, $font_size, $ttl, $legend_position, $colors, $hide_labels); @@ -728,9 +387,9 @@ function pie2d_graph($flash_chart, $chart_data, $width, $height, $others_str = "other", $homedir="", $water_mark = "", $font = '', $font_size = '', $ttl = 1, $legend_position = false, $colors = '', $hide_labels = false) { - + return pie_graph('2d', $flash_chart, $chart_data, $width, $height, - $others_str, $homedir, $water_mark, $font, $font_size, $ttl, + $others_str, $homedir, $water_mark, $font, $font_size, $ttl, $legend_position, $colors, $hide_labels); } @@ -738,23 +397,23 @@ function pie_graph($graph_type, $flash_chart, $chart_data, $width, $height, $others_str = "other", $homedir="", $water_mark = "", $font = '', $font_size = '', $ttl = 1, $legend_position = false, $colors = '', $hide_labels = false) { - + if (empty($chart_data)) { return graph_nodata_image($width, $height, 'pie'); } - + setup_watermark($water_mark, $water_mark_file, $water_mark_url); - + // This library allows only 8 colors $max_values = 9; - + //Remove the html_entities $temp = array(); foreach ($chart_data as $key => $value) { $temp[io_safe_output($key)] = $value; } $chart_data = $temp; - + if (count($chart_data) > $max_values) { $chart_data_trunc = array(); $n = 1; @@ -772,7 +431,7 @@ function pie_graph($graph_type, $flash_chart, $chart_data, $width, } $chart_data = $chart_data_trunc; } - + if ($flash_chart) { return flot_pie_chart(array_values($chart_data), array_keys($chart_data), $width, $height, $water_mark_url, @@ -780,7 +439,6 @@ function pie_graph($graph_type, $flash_chart, $chart_data, $width, } else { //TODO SET THE LEGEND POSITION - $graph = array(); $graph['data'] = $chart_data; $graph['width'] = $width; @@ -790,9 +448,9 @@ function pie_graph($graph_type, $flash_chart, $chart_data, $width, $graph['font_size'] = $font_size; $graph['legend_position'] = $legend_position; $graph['color'] = $colors; - + $id_graph = serialize_in_temp($graph, null, $ttl); - + switch ($graph_type) { case "2d": return ""; @@ -808,17 +466,16 @@ function ring_graph($flash_chart, $chart_data, $width, $height, $others_str = "other", $homedir="", $water_mark = "", $font = '', $font_size = '', $ttl = 1, $legend_position = false, $colors = '', $hide_labels = false,$background_color = 'white') { - + if (empty($chart_data)) { return graph_nodata_image($width, $height, 'pie'); } - + setup_watermark($water_mark, $water_mark_file, $water_mark_url); - + // This library allows only 8 colors $max_values = 18; - - + if ($flash_chart) { return flot_custom_pie_chart ($flash_chart, $chart_data, $width, $height, $colors, $module_name_list, $long_index, @@ -828,7 +485,7 @@ function ring_graph($flash_chart, $chart_data, $width, else { $total_modules = $chart_data['total_modules']; unset($chart_data['total_modules']); - + $max_values = 9; //Remove the html_entities $n = 0; @@ -842,13 +499,13 @@ function ring_graph($flash_chart, $chart_data, $width, $n++; } $chart_data = $temp; - + $chart_data_trunc = array(); $coloretes = array(); $n = 1; //~ foreach ($chart_data as $key => $value) { //~ if ($n < $max_values) { - + //~ $chart_data_trunc[$key] = $value; //~ } //~ else { @@ -860,9 +517,8 @@ function ring_graph($flash_chart, $chart_data, $width, //~ $n++; //~ } //~ $chart_data = $chart_data_trunc; - + //TODO SET THE LEGEND POSITION - $graph = array(); $graph['data'] = $chart_data; $graph['width'] = $width; @@ -872,11 +528,11 @@ function ring_graph($flash_chart, $chart_data, $width, $graph['font_size'] = $font_size; $graph['legend_position'] = $legend_position; $graph['legend'] = $legend; - + $id_graph = serialize_in_temp($graph, null, $ttl); - + return ""; - + } } diff --git a/pandora_console/include/graphs/flot/excanvas.js b/pandora_console/include/graphs/flot/excanvas.js index 367764b4d6..70a8f25ca8 100755 --- a/pandora_console/include/graphs/flot/excanvas.js +++ b/pandora_console/include/graphs/flot/excanvas.js @@ -15,7 +15,7 @@ // Known Issues: // -// * Patterns are not implemented. +// * Patterns only support repeat. // * Radial gradient are not implemented. The VML version of these look very // different from the canvas one. // * Clipping paths are not implemented. @@ -29,6 +29,7 @@ // or use Box Sizing Behavior from WebFX // (http://webfx.eae.net/dhtml/boxsizing/boxsizing.html) // * Non uniform scaling does not correctly scale strokes. +// * Filling very large shapes (above 5000 points) is buggy. // * Optimize. There is always room for speed improvements. // Only add this code if we do not already have a canvas implementation @@ -48,6 +49,8 @@ if (!document.createElement('canvas').getContext) { var Z = 10; var Z2 = Z / 2; + var IE_VERSION = +navigator.userAgent.match(/MSIE ([\d.]+)?/)[1]; + /** * This funtion is assigned to the elements as element.getContext(). * @this {HTMLElement} @@ -83,41 +86,43 @@ if (!document.createElement('canvas').getContext) { }; } + function encodeHtmlAttribute(s) { + return String(s).replace(/&/g, '&').replace(/"/g, '"'); + } + + function addNamespace(doc, prefix, urn) { + if (!doc.namespaces[prefix]) { + doc.namespaces.add(prefix, urn, '#default#VML'); + } + } + + function addNamespacesAndStylesheet(doc) { + addNamespace(doc, 'g_vml_', 'urn:schemas-microsoft-com:vml'); + addNamespace(doc, 'g_o_', 'urn:schemas-microsoft-com:office:office'); + + // Setup default CSS. Only add one style sheet per document + if (!doc.styleSheets['ex_canvas_']) { + var ss = doc.createStyleSheet(); + ss.owningElement.id = 'ex_canvas_'; + ss.cssText = 'canvas{display:inline-block;overflow:hidden;' + + // default size is 300x150 in Gecko and Opera + 'text-align:left;width:300px;height:150px}'; + } + } + + // Add namespaces and stylesheet at startup. + addNamespacesAndStylesheet(document); + var G_vmlCanvasManager_ = { init: function(opt_doc) { - if (/MSIE/.test(navigator.userAgent) && !window.opera) { - var doc = opt_doc || document; - // Create a dummy element so that IE will allow canvas elements to be - // recognized. - doc.createElement('canvas'); - doc.attachEvent('onreadystatechange', bind(this.init_, this, doc)); - } + var doc = opt_doc || document; + // Create a dummy element so that IE will allow canvas elements to be + // recognized. + doc.createElement('canvas'); + doc.attachEvent('onreadystatechange', bind(this.init_, this, doc)); }, init_: function(doc) { - // create xmlns - if (!doc.namespaces['g_vml_']) { - doc.namespaces.add('g_vml_', 'urn:schemas-microsoft-com:vml', - '#default#VML'); - - } - if (!doc.namespaces['g_o_']) { - doc.namespaces.add('g_o_', 'urn:schemas-microsoft-com:office:office', - '#default#VML'); - } - - // Setup default CSS. Only add one style sheet per document - if (!doc.styleSheets['ex_canvas_']) { - var ss = doc.createStyleSheet(); - ss.owningElement.id = 'ex_canvas_'; - ss.cssText = 'canvas{display:inline-block;overflow:hidden;' + - // default size is 300x150 in Gecko and Opera - 'text-align:left;width:300px;height:150px}' + - 'g_vml_\\:*{behavior:url(#default#VML)}' + - 'g_o_\\:*{behavior:url(#default#VML)}'; - - } - // find all canvas elements var els = doc.getElementsByTagName('canvas'); for (var i = 0; i < els.length; i++) { @@ -135,9 +140,11 @@ if (!document.createElement('canvas').getContext) { */ initElement: function(el) { if (!el.getContext) { - el.getContext = getContext; + // Add namespaces and stylesheet to document of the element. + addNamespacesAndStylesheet(el.ownerDocument); + // Remove fallback content. There is no way to hide text nodes so we // just remove all childNodes. We could hide all elements and remove // text nodes but who really cares about the fallback content. @@ -173,12 +180,15 @@ if (!document.createElement('canvas').getContext) { switch (e.propertyName) { case 'width': - el.style.width = el.attributes.width.nodeValue + 'px'; el.getContext().clearRect(); + el.style.width = el.attributes.width.nodeValue + 'px'; + // In IE8 this does not trigger onresize. + el.firstChild.style.width = el.clientWidth + 'px'; break; case 'height': - el.style.height = el.attributes.height.nodeValue + 'px'; el.getContext().clearRect(); + el.style.height = el.attributes.height.nodeValue + 'px'; + el.firstChild.style.height = el.clientHeight + 'px'; break; } } @@ -194,10 +204,10 @@ if (!document.createElement('canvas').getContext) { G_vmlCanvasManager_.init(); // precompute "00" to "FF" - var dec2hex = []; + var decToHex = []; for (var i = 0; i < 16; i++) { for (var j = 0; j < 16; j++) { - dec2hex[i * 16 + j] = i.toString(16) + j.toString(16); + decToHex[i * 16 + j] = i.toString(16) + j.toString(16); } } @@ -238,54 +248,326 @@ if (!document.createElement('canvas').getContext) { o2.shadowOffsetY = o1.shadowOffsetY; o2.strokeStyle = o1.strokeStyle; o2.globalAlpha = o1.globalAlpha; + o2.font = o1.font; + o2.textAlign = o1.textAlign; + o2.textBaseline = o1.textBaseline; o2.arcScaleX_ = o1.arcScaleX_; o2.arcScaleY_ = o1.arcScaleY_; o2.lineScale_ = o1.lineScale_; } + var colorData = { + aliceblue: '#F0F8FF', + antiquewhite: '#FAEBD7', + aquamarine: '#7FFFD4', + azure: '#F0FFFF', + beige: '#F5F5DC', + bisque: '#FFE4C4', + black: '#000000', + blanchedalmond: '#FFEBCD', + blueviolet: '#8A2BE2', + brown: '#A52A2A', + burlywood: '#DEB887', + cadetblue: '#5F9EA0', + chartreuse: '#7FFF00', + chocolate: '#D2691E', + coral: '#FF7F50', + cornflowerblue: '#6495ED', + cornsilk: '#FFF8DC', + crimson: '#DC143C', + cyan: '#00FFFF', + darkblue: '#00008B', + darkcyan: '#008B8B', + darkgoldenrod: '#B8860B', + darkgray: '#A9A9A9', + darkgreen: '#006400', + darkgrey: '#A9A9A9', + darkkhaki: '#BDB76B', + darkmagenta: '#8B008B', + darkolivegreen: '#556B2F', + darkorange: '#FF8C00', + darkorchid: '#9932CC', + darkred: '#8B0000', + darksalmon: '#E9967A', + darkseagreen: '#8FBC8F', + darkslateblue: '#483D8B', + darkslategray: '#2F4F4F', + darkslategrey: '#2F4F4F', + darkturquoise: '#00CED1', + darkviolet: '#9400D3', + deeppink: '#FF1493', + deepskyblue: '#00BFFF', + dimgray: '#696969', + dimgrey: '#696969', + dodgerblue: '#1E90FF', + firebrick: '#B22222', + floralwhite: '#FFFAF0', + forestgreen: '#228B22', + gainsboro: '#DCDCDC', + ghostwhite: '#F8F8FF', + gold: '#FFD700', + goldenrod: '#DAA520', + grey: '#808080', + greenyellow: '#ADFF2F', + honeydew: '#F0FFF0', + hotpink: '#FF69B4', + indianred: '#CD5C5C', + indigo: '#4B0082', + ivory: '#FFFFF0', + khaki: '#F0E68C', + lavender: '#E6E6FA', + lavenderblush: '#FFF0F5', + lawngreen: '#7CFC00', + lemonchiffon: '#FFFACD', + lightblue: '#ADD8E6', + lightcoral: '#F08080', + lightcyan: '#E0FFFF', + lightgoldenrodyellow: '#FAFAD2', + lightgreen: '#90EE90', + lightgrey: '#D3D3D3', + lightpink: '#FFB6C1', + lightsalmon: '#FFA07A', + lightseagreen: '#20B2AA', + lightskyblue: '#87CEFA', + lightslategray: '#778899', + lightslategrey: '#778899', + lightsteelblue: '#B0C4DE', + lightyellow: '#FFFFE0', + limegreen: '#32CD32', + linen: '#FAF0E6', + magenta: '#FF00FF', + mediumaquamarine: '#66CDAA', + mediumblue: '#0000CD', + mediumorchid: '#BA55D3', + mediumpurple: '#9370DB', + mediumseagreen: '#3CB371', + mediumslateblue: '#7B68EE', + mediumspringgreen: '#00FA9A', + mediumturquoise: '#48D1CC', + mediumvioletred: '#C71585', + midnightblue: '#191970', + mintcream: '#F5FFFA', + mistyrose: '#FFE4E1', + moccasin: '#FFE4B5', + navajowhite: '#FFDEAD', + oldlace: '#FDF5E6', + olivedrab: '#6B8E23', + orange: '#FFA500', + orangered: '#FF4500', + orchid: '#DA70D6', + palegoldenrod: '#EEE8AA', + palegreen: '#98FB98', + paleturquoise: '#AFEEEE', + palevioletred: '#DB7093', + papayawhip: '#FFEFD5', + peachpuff: '#FFDAB9', + peru: '#CD853F', + pink: '#FFC0CB', + plum: '#DDA0DD', + powderblue: '#B0E0E6', + rosybrown: '#BC8F8F', + royalblue: '#4169E1', + saddlebrown: '#8B4513', + salmon: '#FA8072', + sandybrown: '#F4A460', + seagreen: '#2E8B57', + seashell: '#FFF5EE', + sienna: '#A0522D', + skyblue: '#87CEEB', + slateblue: '#6A5ACD', + slategray: '#708090', + slategrey: '#708090', + snow: '#FFFAFA', + springgreen: '#00FF7F', + steelblue: '#4682B4', + tan: '#D2B48C', + thistle: '#D8BFD8', + tomato: '#FF6347', + turquoise: '#40E0D0', + violet: '#EE82EE', + wheat: '#F5DEB3', + whitesmoke: '#F5F5F5', + yellowgreen: '#9ACD32' + }; + + + function getRgbHslContent(styleString) { + var start = styleString.indexOf('(', 3); + var end = styleString.indexOf(')', start + 1); + var parts = styleString.substring(start + 1, end).split(','); + // add alpha if needed + if (parts.length != 4 || styleString.charAt(3) != 'a') { + parts[3] = 1; + } + return parts; + } + + function percent(s) { + return parseFloat(s) / 100; + } + + function clamp(v, min, max) { + return Math.min(max, Math.max(min, v)); + } + + function hslToRgb(parts){ + var r, g, b, h, s, l; + h = parseFloat(parts[0]) / 360 % 360; + if (h < 0) + h++; + s = clamp(percent(parts[1]), 0, 1); + l = clamp(percent(parts[2]), 0, 1); + if (s == 0) { + r = g = b = l; // achromatic + } else { + var q = l < 0.5 ? l * (1 + s) : l + s - l * s; + var p = 2 * l - q; + r = hueToRgb(p, q, h + 1 / 3); + g = hueToRgb(p, q, h); + b = hueToRgb(p, q, h - 1 / 3); + } + + return '#' + decToHex[Math.floor(r * 255)] + + decToHex[Math.floor(g * 255)] + + decToHex[Math.floor(b * 255)]; + } + + function hueToRgb(m1, m2, h) { + if (h < 0) + h++; + if (h > 1) + h--; + + if (6 * h < 1) + return m1 + (m2 - m1) * 6 * h; + else if (2 * h < 1) + return m2; + else if (3 * h < 2) + return m1 + (m2 - m1) * (2 / 3 - h) * 6; + else + return m1; + } + + var processStyleCache = {}; + function processStyle(styleString) { + if (styleString in processStyleCache) { + return processStyleCache[styleString]; + } + var str, alpha = 1; styleString = String(styleString); - if (styleString.substring(0, 3) == 'rgb') { - var start = styleString.indexOf('(', 3); - var end = styleString.indexOf(')', start + 1); - var guts = styleString.substring(start + 1, end).split(','); - - str = '#'; - for (var i = 0; i < 3; i++) { - str += dec2hex[Number(guts[i])]; - } - - if (guts.length == 4 && styleString.substr(3, 1) == 'a') { - alpha = guts[3]; - } - } else { + if (styleString.charAt(0) == '#') { str = styleString; + } else if (/^rgb/.test(styleString)) { + var parts = getRgbHslContent(styleString); + var str = '#', n; + for (var i = 0; i < 3; i++) { + if (parts[i].indexOf('%') != -1) { + n = Math.floor(percent(parts[i]) * 255); + } else { + n = +parts[i]; + } + str += decToHex[clamp(n, 0, 255)]; + } + alpha = +parts[3]; + } else if (/^hsl/.test(styleString)) { + var parts = getRgbHslContent(styleString); + str = hslToRgb(parts); + alpha = parts[3]; + } else { + str = colorData[styleString] || styleString; } - - return {color: str, alpha: alpha}; + return processStyleCache[styleString] = {color: str, alpha: alpha}; } - function processLineCap(lineCap) { - switch (lineCap) { - case 'butt': - return 'flat'; - case 'round': - return 'round'; - case 'square': - default: - return 'square'; + var DEFAULT_STYLE = { + style: 'normal', + variant: 'normal', + weight: 'normal', + size: 10, + family: 'sans-serif' + }; + + // Internal text style cache + var fontStyleCache = {}; + + function processFontStyle(styleString) { + if (fontStyleCache[styleString]) { + return fontStyleCache[styleString]; } + + var el = document.createElement('div'); + var style = el.style; + try { + style.font = styleString; + } catch (ex) { + // Ignore failures to set to invalid font. + } + + return fontStyleCache[styleString] = { + style: style.fontStyle || DEFAULT_STYLE.style, + variant: style.fontVariant || DEFAULT_STYLE.variant, + weight: style.fontWeight || DEFAULT_STYLE.weight, + size: style.fontSize || DEFAULT_STYLE.size, + family: style.fontFamily || DEFAULT_STYLE.family + }; + } + + function getComputedStyle(style, element) { + var computedStyle = {}; + + for (var p in style) { + computedStyle[p] = style[p]; + } + + // Compute the size + var canvasFontSize = parseFloat(element.currentStyle.fontSize), + fontSize = parseFloat(style.size); + + if (typeof style.size == 'number') { + computedStyle.size = style.size; + } else if (style.size.indexOf('px') != -1) { + computedStyle.size = fontSize; + } else if (style.size.indexOf('em') != -1) { + computedStyle.size = canvasFontSize * fontSize; + } else if(style.size.indexOf('%') != -1) { + computedStyle.size = (canvasFontSize / 100) * fontSize; + } else if (style.size.indexOf('pt') != -1) { + computedStyle.size = fontSize / .75; + } else { + computedStyle.size = canvasFontSize; + } + + // Different scaling between normal text and VML text. This was found using + // trial and error to get the same size as non VML text. + computedStyle.size *= 0.981; + + return computedStyle; + } + + function buildStyle(style) { + return style.style + ' ' + style.variant + ' ' + style.weight + ' ' + + style.size + 'px ' + style.family; + } + + var lineCapMap = { + 'butt': 'flat', + 'round': 'round' + }; + + function processLineCap(lineCap) { + return lineCapMap[lineCap] || 'square'; } /** * This class implements CanvasRenderingContext2D interface as described by * the WHATWG. - * @param {HTMLElement} surfaceElement The element that the 2D context should + * @param {HTMLElement} canvasElement The element that the 2D context should * be associated with */ - function CanvasRenderingContext2D_(surfaceElement) { + function CanvasRenderingContext2D_(canvasElement) { this.m_ = createMatrixIdentity(); this.mStack_ = []; @@ -301,14 +583,22 @@ if (!document.createElement('canvas').getContext) { this.lineCap = 'butt'; this.miterLimit = Z * 1; this.globalAlpha = 1; - this.canvas = surfaceElement; + this.font = '10px sans-serif'; + this.textAlign = 'left'; + this.textBaseline = 'alphabetic'; + this.canvas = canvasElement; - var el = surfaceElement.ownerDocument.createElement('div'); - el.style.width = surfaceElement.clientWidth + 'px'; - el.style.height = surfaceElement.clientHeight + 'px'; - el.style.overflow = 'hidden'; - el.style.position = 'absolute'; - surfaceElement.appendChild(el); + var cssText = 'width:' + canvasElement.clientWidth + 'px;height:' + + canvasElement.clientHeight + 'px;overflow:hidden;position:absolute'; + var el = canvasElement.ownerDocument.createElement('div'); + el.style.cssText = cssText; + canvasElement.appendChild(el); + + var overlayEl = el.cloneNode(false); + // Use a non transparent background. + overlayEl.style.backgroundColor = 'red'; + overlayEl.style.filter = 'alpha(opacity=0)'; + canvasElement.appendChild(overlayEl); this.element_ = el; this.arcScaleX_ = 1; @@ -318,6 +608,10 @@ if (!document.createElement('canvas').getContext) { var contextPrototype = CanvasRenderingContext2D_.prototype; contextPrototype.clearRect = function() { + if (this.textMeasureEl_) { + this.textMeasureEl_.removeNode(true); + this.textMeasureEl_ = null; + } this.element_.innerHTML = ''; }; @@ -328,14 +622,14 @@ if (!document.createElement('canvas').getContext) { }; contextPrototype.moveTo = function(aX, aY) { - var p = this.getCoords_(aX, aY); + var p = getCoords(this, aX, aY); this.currentPath_.push({type: 'moveTo', x: p.x, y: p.y}); this.currentX_ = p.x; this.currentY_ = p.y; }; contextPrototype.lineTo = function(aX, aY) { - var p = this.getCoords_(aX, aY); + var p = getCoords(this, aX, aY); this.currentPath_.push({type: 'lineTo', x: p.x, y: p.y}); this.currentX_ = p.x; @@ -345,9 +639,9 @@ if (!document.createElement('canvas').getContext) { contextPrototype.bezierCurveTo = function(aCP1x, aCP1y, aCP2x, aCP2y, aX, aY) { - var p = this.getCoords_(aX, aY); - var cp1 = this.getCoords_(aCP1x, aCP1y); - var cp2 = this.getCoords_(aCP2x, aCP2y); + var p = getCoords(this, aX, aY); + var cp1 = getCoords(this, aCP1x, aCP1y); + var cp2 = getCoords(this, aCP2x, aCP2y); bezierCurveTo(this, cp1, cp2, p); }; @@ -370,8 +664,8 @@ if (!document.createElement('canvas').getContext) { // the following is lifted almost directly from // http://developer.mozilla.org/en/docs/Canvas_tutorial:Drawing_shapes - var cp = this.getCoords_(aCPx, aCPy); - var p = this.getCoords_(aX, aY); + var cp = getCoords(this, aCPx, aCPy); + var p = getCoords(this, aX, aY); var cp1 = { x: this.currentX_ + 2.0 / 3.0 * (cp.x - this.currentX_), @@ -402,9 +696,9 @@ if (!document.createElement('canvas').getContext) { // that can be represented in binary } - var p = this.getCoords_(aX, aY); - var pStart = this.getCoords_(xStart, yStart); - var pEnd = this.getCoords_(xEnd, yEnd); + var p = getCoords(this, aX, aY); + var pStart = getCoords(this, xStart, yStart); + var pEnd = getCoords(this, xEnd, yEnd); this.currentPath_.push({type: arcType, x: p.x, @@ -518,7 +812,7 @@ if (!document.createElement('canvas').getContext) { throw Error('Invalid number of arguments'); } - var d = this.getCoords_(dx, dy); + var d = getCoords(this, dx, dy); var w2 = sw / 2; var h2 = sh / 2; @@ -539,7 +833,8 @@ if (!document.createElement('canvas').getContext) { // The following check doesn't account for skews (which don't exist // in the canvas spec (yet) anyway. - if (this.m_[0][0] != 1 || this.m_[0][1]) { + if (this.m_[0][0] != 1 || this.m_[0][1] || + this.m_[1][1] != 1 || this.m_[1][0]) { var filter = []; // Note the 12/21 reversal @@ -553,16 +848,17 @@ if (!document.createElement('canvas').getContext) { // Bounding box calculation (need to minimize displayed area so that // filters don't waste time on unused pixels. var max = d; - var c2 = this.getCoords_(dx + dw, dy); - var c3 = this.getCoords_(dx, dy + dh); - var c4 = this.getCoords_(dx + dw, dy + dh); + var c2 = getCoords(this, dx + dw, dy); + var c3 = getCoords(this, dx, dy + dh); + var c4 = getCoords(this, dx + dw, dy + dh); max.x = m.max(max.x, c2.x, c3.x, c4.x); max.y = m.max(max.y, c2.y, c3.y, c4.y); vmlStr.push('padding:0 ', mr(max.x / Z), 'px ', mr(max.y / Z), 'px 0;filter:progid:DXImageTransform.Microsoft.Matrix(', - filter.join(''), ", sizingmethod='clip');") + filter.join(''), ", sizingmethod='clip');"); + } else { vmlStr.push('top:', mr(d.y / Z), 'px;left:', mr(d.x / Z), 'px;'); } @@ -570,7 +866,7 @@ if (!document.createElement('canvas').getContext) { vmlStr.push(' ">' , '', ''); - this.element_.insertAdjacentHTML('BeforeEnd', - vmlStr.join('')); + this.element_.insertAdjacentHTML('BeforeEnd', vmlStr.join('')); }; contextPrototype.stroke = function(aFill) { - var lineStr = []; - var lineOpen = false; - var a = processStyle(aFill ? this.fillStyle : this.strokeStyle); - var color = a.color; - var opacity = a.alpha * this.globalAlpha; - var W = 10; var H = 10; + // Divide the shape into chunks if it's too long because IE has a limit + // somewhere for how long a VML shape can be. This simple division does + // not work with fills, only strokes, unfortunately. + var chunkSize = 5000; - lineStr.push(''); + + if (!aFill) { + appendStroke(this, lineStr); + } else { + appendFill(this, lineStr, min, max); + } + + lineStr.push(''); + + this.element_.insertAdjacentHTML('beforeEnd', lineStr.join('')); } - lineStr.push(' ">'); + }; - if (!aFill) { - var lineWidth = this.lineScale_ * this.lineWidth; + function appendStroke(ctx, lineStr) { + var a = processStyle(ctx.strokeStyle); + var color = a.color; + var opacity = a.alpha * ctx.globalAlpha; + var lineWidth = ctx.lineScale_ * ctx.lineWidth; - // VML cannot correctly render a line if the width is less than 1px. - // In that case, we dilute the color to make the line look thinner. - if (lineWidth < 1) { - opacity *= lineWidth; - } + // VML cannot correctly render a line if the width is less than 1px. + // In that case, we dilute the color to make the line look thinner. + if (lineWidth < 1) { + opacity *= lineWidth; + } - lineStr.push( - '' - ); - } else if (typeof this.fillStyle == 'object') { - var fillStyle = this.fillStyle; + lineStr.push( + '' + ); + } + + function appendFill(ctx, lineStr, min, max) { + var fillStyle = ctx.fillStyle; + var arcScaleX = ctx.arcScaleX_; + var arcScaleY = ctx.arcScaleY_; + var width = max.x - min.x; + var height = max.y - min.y; + if (fillStyle instanceof CanvasGradient_) { + // TODO: Gradients transformed with the transformation matrix. var angle = 0; var focus = {x: 0, y: 0}; @@ -689,12 +1015,12 @@ if (!document.createElement('canvas').getContext) { var expansion = 1; if (fillStyle.type_ == 'gradient') { - var x0 = fillStyle.x0_ / this.arcScaleX_; - var y0 = fillStyle.y0_ / this.arcScaleY_; - var x1 = fillStyle.x1_ / this.arcScaleX_; - var y1 = fillStyle.y1_ / this.arcScaleY_; - var p0 = this.getCoords_(x0, y0); - var p1 = this.getCoords_(x1, y1); + var x0 = fillStyle.x0_ / arcScaleX; + var y0 = fillStyle.y0_ / arcScaleY; + var x1 = fillStyle.x1_ / arcScaleX; + var y1 = fillStyle.y1_ / arcScaleY; + var p0 = getCoords(ctx, x0, y0); + var p1 = getCoords(ctx, x1, y1); var dx = p1.x - p0.x; var dy = p1.y - p0.y; angle = Math.atan2(dx, dy) * 180 / Math.PI; @@ -710,16 +1036,14 @@ if (!document.createElement('canvas').getContext) { angle = 0; } } else { - var p0 = this.getCoords_(fillStyle.x0_, fillStyle.y0_); - var width = max.x - min.x; - var height = max.y - min.y; + var p0 = getCoords(ctx, fillStyle.x0_, fillStyle.y0_); focus = { x: (p0.x - min.x) / width, y: (p0.y - min.y) / height }; - width /= this.arcScaleX_ * Z; - height /= this.arcScaleY_ * Z; + width /= arcScaleX * Z; + height /= arcScaleY * Z; var dimension = m.max(width, height); shift = 2 * fillStyle.r0_ / dimension; expansion = 2 * fillStyle.r1_ / dimension - shift; @@ -735,8 +1059,8 @@ if (!document.createElement('canvas').getContext) { var length = stops.length; var color1 = stops[0].color; var color2 = stops[length - 1].color; - var opacity1 = stops[0].alpha * this.globalAlpha; - var opacity2 = stops[length - 1].alpha * this.globalAlpha; + var opacity1 = stops[0].alpha * ctx.globalAlpha; + var opacity2 = stops[length - 1].alpha * ctx.globalAlpha; var colors = []; for (var i = 0; i < length; i++) { @@ -755,33 +1079,42 @@ if (!document.createElement('canvas').getContext) { ' g_o_:opacity2="', opacity1, '"', ' angle="', angle, '"', ' focusposition="', focus.x, ',', focus.y, '" />'); + } else if (fillStyle instanceof CanvasPattern_) { + if (width && height) { + var deltaLeft = -min.x; + var deltaTop = -min.y; + lineStr.push(''); + } } else { + var a = processStyle(ctx.fillStyle); + var color = a.color; + var opacity = a.alpha * ctx.globalAlpha; lineStr.push(''); } - - lineStr.push(''); - - this.element_.insertAdjacentHTML('beforeEnd', lineStr.join('')); - }; + } contextPrototype.fill = function() { this.stroke(true); - } + }; contextPrototype.closePath = function() { this.currentPath_.push({type: 'close'}); }; - /** - * @private - */ - contextPrototype.getCoords_ = function(aX, aY) { - var m = this.m_; + function getCoords(ctx, aX, aY) { + var m = ctx.m_; return { x: Z * (aX * m[0][0] + aY * m[1][0] + m[2][0]) - Z2, y: Z * (aX * m[0][1] + aY * m[1][1] + m[2][1]) - Z2 - } + }; }; contextPrototype.save = function() { @@ -793,19 +1126,16 @@ if (!document.createElement('canvas').getContext) { }; contextPrototype.restore = function() { - copyState(this.aStack_.pop(), this); - this.m_ = this.mStack_.pop(); + if (this.aStack_.length) { + copyState(this.aStack_.pop(), this); + this.m_ = this.mStack_.pop(); + } }; function matrixIsFinite(m) { - for (var j = 0; j < 3; j++) { - for (var k = 0; k < 2; k++) { - if (!isFinite(m[j][k]) || isNaN(m[j][k])) { - return false; - } - } - } - return true; + return isFinite(m[0][0]) && isFinite(m[0][1]) && + isFinite(m[1][0]) && isFinite(m[1][1]) && + isFinite(m[2][0]) && isFinite(m[2][1]); } function setM(ctx, m, updateLineScale) { @@ -879,6 +1209,124 @@ if (!document.createElement('canvas').getContext) { setM(this, m, true); }; + /** + * The text drawing function. + * The maxWidth argument isn't taken in account, since no browser supports + * it yet. + */ + contextPrototype.drawText_ = function(text, x, y, maxWidth, stroke) { + var m = this.m_, + delta = 1000, + left = 0, + right = delta, + offset = {x: 0, y: 0}, + lineStr = []; + + var fontStyle = getComputedStyle(processFontStyle(this.font), + this.element_); + + var fontStyleString = buildStyle(fontStyle); + + var elementStyle = this.element_.currentStyle; + var textAlign = this.textAlign.toLowerCase(); + switch (textAlign) { + case 'left': + case 'center': + case 'right': + break; + case 'end': + textAlign = elementStyle.direction == 'ltr' ? 'right' : 'left'; + break; + case 'start': + textAlign = elementStyle.direction == 'rtl' ? 'right' : 'left'; + break; + default: + textAlign = 'left'; + } + + // 1.75 is an arbitrary number, as there is no info about the text baseline + switch (this.textBaseline) { + case 'hanging': + case 'top': + offset.y = fontStyle.size / 1.75; + break; + case 'middle': + break; + default: + case null: + case 'alphabetic': + case 'ideographic': + case 'bottom': + offset.y = -fontStyle.size / 2.25; + break; + } + + switch(textAlign) { + case 'right': + left = delta; + right = 0.05; + break; + case 'center': + left = right = delta / 2; + break; + } + + var d = getCoords(this, x + offset.x, y + offset.y); + + lineStr.push(''); + + if (stroke) { + appendStroke(this, lineStr); + } else { + // TODO: Fix the min and max params. + appendFill(this, lineStr, {x: -left, y: 0}, + {x: right, y: fontStyle.size}); + } + + var skewM = m[0][0].toFixed(3) + ',' + m[1][0].toFixed(3) + ',' + + m[0][1].toFixed(3) + ',' + m[1][1].toFixed(3) + ',0,0'; + + var skewOffset = mr(d.x / Z) + ',' + mr(d.y / Z); + + lineStr.push('', + '', + ''); + + this.element_.insertAdjacentHTML('beforeEnd', lineStr.join('')); + }; + + contextPrototype.fillText = function(text, x, y, maxWidth) { + this.drawText_(text, x, y, maxWidth, false); + }; + + contextPrototype.strokeText = function(text, x, y, maxWidth) { + this.drawText_(text, x, y, maxWidth, true); + }; + + contextPrototype.measureText = function(text) { + if (!this.textMeasureEl_) { + var s = ''; + this.element_.insertAdjacentHTML('beforeEnd', s); + this.textMeasureEl_ = this.element_.lastChild; + } + var doc = this.element_.ownerDocument; + this.textMeasureEl_.innerHTML = ''; + this.textMeasureEl_.style.font = this.font; + // Don't use innerHTML or innerText because they allow markup/whitespace. + this.textMeasureEl_.appendChild(doc.createTextNode(text)); + return {width: this.textMeasureEl_.offsetWidth}; + }; + /******** STUBS ********/ contextPrototype.clip = function() { // TODO: Implement @@ -888,8 +1336,8 @@ if (!document.createElement('canvas').getContext) { // TODO: Implement }; - contextPrototype.createPattern = function() { - return new CanvasPattern_; + contextPrototype.createPattern = function(image, repetition) { + return new CanvasPattern_(image, repetition); }; // Gradient / Pattern Stubs @@ -911,14 +1359,70 @@ if (!document.createElement('canvas').getContext) { alpha: aColor.alpha}); }; - function CanvasPattern_() {} + function CanvasPattern_(image, repetition) { + assertImageIsValid(image); + switch (repetition) { + case 'repeat': + case null: + case '': + this.repetition_ = 'repeat'; + break + case 'repeat-x': + case 'repeat-y': + case 'no-repeat': + this.repetition_ = repetition; + break; + default: + throwException('SYNTAX_ERR'); + } + + this.src_ = image.src; + this.width_ = image.width; + this.height_ = image.height; + } + + function throwException(s) { + throw new DOMException_(s); + } + + function assertImageIsValid(img) { + if (!img || img.nodeType != 1 || img.tagName != 'IMG') { + throwException('TYPE_MISMATCH_ERR'); + } + if (img.readyState != 'complete') { + throwException('INVALID_STATE_ERR'); + } + } + + function DOMException_(s) { + this.code = this[s]; + this.message = s +': DOM Exception ' + this.code; + } + var p = DOMException_.prototype = new Error; + p.INDEX_SIZE_ERR = 1; + p.DOMSTRING_SIZE_ERR = 2; + p.HIERARCHY_REQUEST_ERR = 3; + p.WRONG_DOCUMENT_ERR = 4; + p.INVALID_CHARACTER_ERR = 5; + p.NO_DATA_ALLOWED_ERR = 6; + p.NO_MODIFICATION_ALLOWED_ERR = 7; + p.NOT_FOUND_ERR = 8; + p.NOT_SUPPORTED_ERR = 9; + p.INUSE_ATTRIBUTE_ERR = 10; + p.INVALID_STATE_ERR = 11; + p.SYNTAX_ERR = 12; + p.INVALID_MODIFICATION_ERR = 13; + p.NAMESPACE_ERR = 14; + p.INVALID_ACCESS_ERR = 15; + p.VALIDATION_ERR = 16; + p.TYPE_MISMATCH_ERR = 17; // set up externs G_vmlCanvasManager = G_vmlCanvasManager_; CanvasRenderingContext2D = CanvasRenderingContext2D_; CanvasGradient = CanvasGradient_; CanvasPattern = CanvasPattern_; - + DOMException = DOMException_; })(); } // if diff --git a/pandora_console/include/graphs/flot/excanvas.min.js b/pandora_console/include/graphs/flot/excanvas.min.js new file mode 100644 index 0000000000..fcf876c749 --- /dev/null +++ b/pandora_console/include/graphs/flot/excanvas.min.js @@ -0,0 +1 @@ +if(!document.createElement("canvas").getContext){(function(){var ab=Math;var n=ab.round;var l=ab.sin;var A=ab.cos;var H=ab.abs;var N=ab.sqrt;var d=10;var f=d/2;var z=+navigator.userAgent.match(/MSIE ([\d.]+)?/)[1];function y(){return this.context_||(this.context_=new D(this))}var t=Array.prototype.slice;function g(j,m,p){var i=t.call(arguments,2);return function(){return j.apply(m,i.concat(t.call(arguments)))}}function af(i){return String(i).replace(/&/g,"&").replace(/"/g,""")}function Y(m,j,i){if(!m.namespaces[j]){m.namespaces.add(j,i,"#default#VML")}}function R(j){Y(j,"g_vml_","urn:schemas-microsoft-com:vml");Y(j,"g_o_","urn:schemas-microsoft-com:office:office");if(!j.styleSheets.ex_canvas_){var i=j.createStyleSheet();i.owningElement.id="ex_canvas_";i.cssText="canvas{display:inline-block;overflow:hidden;text-align:left;width:300px;height:150px}"}}R(document);var e={init:function(i){var j=i||document;j.createElement("canvas");j.attachEvent("onreadystatechange",g(this.init_,this,j))},init_:function(p){var m=p.getElementsByTagName("canvas");for(var j=0;j1){m--}if(6*m<1){return j+(i-j)*6*m}else{if(2*m<1){return i}else{if(3*m<2){return j+(i-j)*(2/3-m)*6}else{return j}}}}var C={};function F(j){if(j in C){return C[j]}var ag,Z=1;j=String(j);if(j.charAt(0)=="#"){ag=j}else{if(/^rgb/.test(j)){var p=M(j);var ag="#",ah;for(var m=0;m<3;m++){if(p[m].indexOf("%")!=-1){ah=Math.floor(c(p[m])*255)}else{ah=+p[m]}ag+=k[r(ah,0,255)]}Z=+p[3]}else{if(/^hsl/.test(j)){var p=M(j);ag=I(p);Z=p[3]}else{ag=b[j]||j}}}return C[j]={color:ag,alpha:Z}}var o={style:"normal",variant:"normal",weight:"normal",size:10,family:"sans-serif"};var L={};function E(i){if(L[i]){return L[i]}var p=document.createElement("div");var m=p.style;try{m.font=i}catch(j){}return L[i]={style:m.fontStyle||o.style,variant:m.fontVariant||o.variant,weight:m.fontWeight||o.weight,size:m.fontSize||o.size,family:m.fontFamily||o.family}}function u(m,j){var i={};for(var ah in m){i[ah]=m[ah]}var ag=parseFloat(j.currentStyle.fontSize),Z=parseFloat(m.size);if(typeof m.size=="number"){i.size=m.size}else{if(m.size.indexOf("px")!=-1){i.size=Z}else{if(m.size.indexOf("em")!=-1){i.size=ag*Z}else{if(m.size.indexOf("%")!=-1){i.size=(ag/100)*Z}else{if(m.size.indexOf("pt")!=-1){i.size=Z/0.75}else{i.size=ag}}}}}i.size*=0.981;return i}function ac(i){return i.style+" "+i.variant+" "+i.weight+" "+i.size+"px "+i.family}var s={butt:"flat",round:"round"};function S(i){return s[i]||"square"}function D(i){this.m_=B();this.mStack_=[];this.aStack_=[];this.currentPath_=[];this.strokeStyle="#000";this.fillStyle="#000";this.lineWidth=1;this.lineJoin="miter";this.lineCap="butt";this.miterLimit=d*1;this.globalAlpha=1;this.font="10px sans-serif";this.textAlign="left";this.textBaseline="alphabetic";this.canvas=i;var m="width:"+i.clientWidth+"px;height:"+i.clientHeight+"px;overflow:hidden;position:absolute";var j=i.ownerDocument.createElement("div");j.style.cssText=m;i.appendChild(j);var p=j.cloneNode(false);p.style.backgroundColor="red";p.style.filter="alpha(opacity=0)";i.appendChild(p);this.element_=j;this.arcScaleX_=1;this.arcScaleY_=1;this.lineScale_=1}var q=D.prototype;q.clearRect=function(){if(this.textMeasureEl_){this.textMeasureEl_.removeNode(true);this.textMeasureEl_=null}this.element_.innerHTML=""};q.beginPath=function(){this.currentPath_=[]};q.moveTo=function(j,i){var m=V(this,j,i);this.currentPath_.push({type:"moveTo",x:m.x,y:m.y});this.currentX_=m.x;this.currentY_=m.y};q.lineTo=function(j,i){var m=V(this,j,i);this.currentPath_.push({type:"lineTo",x:m.x,y:m.y});this.currentX_=m.x;this.currentY_=m.y};q.bezierCurveTo=function(m,j,ak,aj,ai,ag){var i=V(this,ai,ag);var ah=V(this,m,j);var Z=V(this,ak,aj);K(this,ah,Z,i)};function K(i,Z,m,j){i.currentPath_.push({type:"bezierCurveTo",cp1x:Z.x,cp1y:Z.y,cp2x:m.x,cp2y:m.y,x:j.x,y:j.y});i.currentX_=j.x;i.currentY_=j.y}q.quadraticCurveTo=function(ai,m,j,i){var ah=V(this,ai,m);var ag=V(this,j,i);var aj={x:this.currentX_+2/3*(ah.x-this.currentX_),y:this.currentY_+2/3*(ah.y-this.currentY_)};var Z={x:aj.x+(ag.x-this.currentX_)/3,y:aj.y+(ag.y-this.currentY_)/3};K(this,aj,Z,ag)};q.arc=function(al,aj,ak,ag,j,m){ak*=d;var ap=m?"at":"wa";var am=al+A(ag)*ak-f;var ao=aj+l(ag)*ak-f;var i=al+A(j)*ak-f;var an=aj+l(j)*ak-f;if(am==i&&!m){am+=0.125}var Z=V(this,al,aj);var ai=V(this,am,ao);var ah=V(this,i,an);this.currentPath_.push({type:ap,x:Z.x,y:Z.y,radius:ak,xStart:ai.x,yStart:ai.y,xEnd:ah.x,yEnd:ah.y})};q.rect=function(m,j,i,p){this.moveTo(m,j);this.lineTo(m+i,j);this.lineTo(m+i,j+p);this.lineTo(m,j+p);this.closePath()};q.strokeRect=function(m,j,i,p){var Z=this.currentPath_;this.beginPath();this.moveTo(m,j);this.lineTo(m+i,j);this.lineTo(m+i,j+p);this.lineTo(m,j+p);this.closePath();this.stroke();this.currentPath_=Z};q.fillRect=function(m,j,i,p){var Z=this.currentPath_;this.beginPath();this.moveTo(m,j);this.lineTo(m+i,j);this.lineTo(m+i,j+p);this.lineTo(m,j+p);this.closePath();this.fill();this.currentPath_=Z};q.createLinearGradient=function(j,p,i,m){var Z=new U("gradient");Z.x0_=j;Z.y0_=p;Z.x1_=i;Z.y1_=m;return Z};q.createRadialGradient=function(p,ag,m,j,Z,i){var ah=new U("gradientradial");ah.x0_=p;ah.y0_=ag;ah.r0_=m;ah.x1_=j;ah.y1_=Z;ah.r1_=i;return ah};q.drawImage=function(aq,m){var aj,ah,al,ay,ao,am,at,aA;var ak=aq.runtimeStyle.width;var ap=aq.runtimeStyle.height;aq.runtimeStyle.width="auto";aq.runtimeStyle.height="auto";var ai=aq.width;var aw=aq.height;aq.runtimeStyle.width=ak;aq.runtimeStyle.height=ap;if(arguments.length==3){aj=arguments[1];ah=arguments[2];ao=am=0;at=al=ai;aA=ay=aw}else{if(arguments.length==5){aj=arguments[1];ah=arguments[2];al=arguments[3];ay=arguments[4];ao=am=0;at=ai;aA=aw}else{if(arguments.length==9){ao=arguments[1];am=arguments[2];at=arguments[3];aA=arguments[4];aj=arguments[5];ah=arguments[6];al=arguments[7];ay=arguments[8]}else{throw Error("Invalid number of arguments")}}}var az=V(this,aj,ah);var p=at/2;var j=aA/2;var ax=[];var i=10;var ag=10;ax.push(" ','","");this.element_.insertAdjacentHTML("BeforeEnd",ax.join(""))};q.stroke=function(ao){var Z=10;var ap=10;var ag=5000;var ai={x:null,y:null};var an={x:null,y:null};for(var aj=0;ajan.x){an.x=m.x}if(ai.y==null||m.yan.y){an.y=m.y}}}am.push(' ">');if(!ao){w(this,am)}else{G(this,am,ai,an)}am.push("");this.element_.insertAdjacentHTML("beforeEnd",am.join(""))}};function w(m,ag){var j=F(m.strokeStyle);var p=j.color;var Z=j.alpha*m.globalAlpha;var i=m.lineScale_*m.lineWidth;if(i<1){Z*=i}ag.push("')}function G(aq,ai,aK,ar){var aj=aq.fillStyle;var aB=aq.arcScaleX_;var aA=aq.arcScaleY_;var j=ar.x-aK.x;var p=ar.y-aK.y;if(aj instanceof U){var an=0;var aF={x:0,y:0};var ax=0;var am=1;if(aj.type_=="gradient"){var al=aj.x0_/aB;var m=aj.y0_/aA;var ak=aj.x1_/aB;var aM=aj.y1_/aA;var aJ=V(aq,al,m);var aI=V(aq,ak,aM);var ag=aI.x-aJ.x;var Z=aI.y-aJ.y;an=Math.atan2(ag,Z)*180/Math.PI;if(an<0){an+=360}if(an<0.000001){an=0}}else{var aJ=V(aq,aj.x0_,aj.y0_);aF={x:(aJ.x-aK.x)/j,y:(aJ.y-aK.y)/p};j/=aB*d;p/=aA*d;var aD=ab.max(j,p);ax=2*aj.r0_/aD;am=2*aj.r1_/aD-ax}var av=aj.colors_;av.sort(function(aN,i){return aN.offset-i.offset});var ap=av.length;var au=av[0].color;var at=av[ap-1].color;var az=av[0].alpha*aq.globalAlpha;var ay=av[ap-1].alpha*aq.globalAlpha;var aE=[];for(var aH=0;aH')}else{if(aj instanceof T){if(j&&p){var ah=-aK.x;var aC=-aK.y;ai.push("')}}else{var aL=F(aq.fillStyle);var aw=aL.color;var aG=aL.alpha*aq.globalAlpha;ai.push('')}}}q.fill=function(){this.stroke(true)};q.closePath=function(){this.currentPath_.push({type:"close"})};function V(j,Z,p){var i=j.m_;return{x:d*(Z*i[0][0]+p*i[1][0]+i[2][0])-f,y:d*(Z*i[0][1]+p*i[1][1]+i[2][1])-f}}q.save=function(){var i={};v(this,i);this.aStack_.push(i);this.mStack_.push(this.m_);this.m_=J(B(),this.m_)};q.restore=function(){if(this.aStack_.length){v(this.aStack_.pop(),this);this.m_=this.mStack_.pop()}};function h(i){return isFinite(i[0][0])&&isFinite(i[0][1])&&isFinite(i[1][0])&&isFinite(i[1][1])&&isFinite(i[2][0])&&isFinite(i[2][1])}function aa(j,i,p){if(!h(i)){return}j.m_=i;if(p){var Z=i[0][0]*i[1][1]-i[0][1]*i[1][0];j.lineScale_=N(H(Z))}}q.translate=function(m,j){var i=[[1,0,0],[0,1,0],[m,j,1]];aa(this,J(i,this.m_),false)};q.rotate=function(j){var p=A(j);var m=l(j);var i=[[p,m,0],[-m,p,0],[0,0,1]];aa(this,J(i,this.m_),false)};q.scale=function(m,j){this.arcScaleX_*=m;this.arcScaleY_*=j;var i=[[m,0,0],[0,j,0],[0,0,1]];aa(this,J(i,this.m_),true)};q.transform=function(Z,p,ah,ag,j,i){var m=[[Z,p,0],[ah,ag,0],[j,i,1]];aa(this,J(m,this.m_),true)};q.setTransform=function(ag,Z,ai,ah,p,j){var i=[[ag,Z,0],[ai,ah,0],[p,j,1]];aa(this,i,true)};q.drawText_=function(am,ak,aj,ap,ai){var ao=this.m_,at=1000,j=0,ar=at,ah={x:0,y:0},ag=[];var i=u(E(this.font),this.element_);var p=ac(i);var au=this.element_.currentStyle;var Z=this.textAlign.toLowerCase();switch(Z){case"left":case"center":case"right":break;case"end":Z=au.direction=="ltr"?"right":"left";break;case"start":Z=au.direction=="rtl"?"right":"left";break;default:Z="left"}switch(this.textBaseline){case"hanging":case"top":ah.y=i.size/1.75;break;case"middle":break;default:case null:case"alphabetic":case"ideographic":case"bottom":ah.y=-i.size/2.25;break}switch(Z){case"right":j=at;ar=0.05;break;case"center":j=ar=at/2;break}var aq=V(this,ak+ah.x,aj+ah.y);ag.push('');if(ai){w(this,ag)}else{G(this,ag,{x:-j,y:0},{x:ar,y:i.size})}var an=ao[0][0].toFixed(3)+","+ao[1][0].toFixed(3)+","+ao[0][1].toFixed(3)+","+ao[1][1].toFixed(3)+",0,0";var al=n(aq.x/d)+","+n(aq.y/d);ag.push('','','');this.element_.insertAdjacentHTML("beforeEnd",ag.join(""))};q.fillText=function(m,i,p,j){this.drawText_(m,i,p,j,false)};q.strokeText=function(m,i,p,j){this.drawText_(m,i,p,j,true)};q.measureText=function(m){if(!this.textMeasureEl_){var i='';this.element_.insertAdjacentHTML("beforeEnd",i);this.textMeasureEl_=this.element_.lastChild}var j=this.element_.ownerDocument;this.textMeasureEl_.innerHTML="";this.textMeasureEl_.style.font=this.font;this.textMeasureEl_.appendChild(j.createTextNode(m));return{width:this.textMeasureEl_.offsetWidth}};q.clip=function(){};q.arcTo=function(){};q.createPattern=function(j,i){return new T(j,i)};function U(i){this.type_=i;this.x0_=0;this.y0_=0;this.r0_=0;this.x1_=0;this.y1_=0;this.r1_=0;this.colors_=[]}U.prototype.addColorStop=function(j,i){i=F(i);this.colors_.push({offset:j,color:i.color,alpha:i.alpha})};function T(j,i){Q(j);switch(i){case"repeat":case null:case"":this.repetition_="repeat";break;case"repeat-x":case"repeat-y":case"no-repeat":this.repetition_=i;break;default:O("SYNTAX_ERR")}this.src_=j.src;this.width_=j.width;this.height_=j.height}function O(i){throw new P(i)}function Q(i){if(!i||i.nodeType!=1||i.tagName!="IMG"){O("TYPE_MISMATCH_ERR")}if(i.readyState!="complete"){O("INVALID_STATE_ERR")}}function P(i){this.code=this[i];this.message=i+": DOM Exception "+this.code}var X=P.prototype=new Error;X.INDEX_SIZE_ERR=1;X.DOMSTRING_SIZE_ERR=2;X.HIERARCHY_REQUEST_ERR=3;X.WRONG_DOCUMENT_ERR=4;X.INVALID_CHARACTER_ERR=5;X.NO_DATA_ALLOWED_ERR=6;X.NO_MODIFICATION_ALLOWED_ERR=7;X.NOT_FOUND_ERR=8;X.NOT_SUPPORTED_ERR=9;X.INUSE_ATTRIBUTE_ERR=10;X.INVALID_STATE_ERR=11;X.SYNTAX_ERR=12;X.INVALID_MODIFICATION_ERR=13;X.NAMESPACE_ERR=14;X.INVALID_ACCESS_ERR=15;X.VALIDATION_ERR=16;X.TYPE_MISMATCH_ERR=17;G_vmlCanvasManager=e;CanvasRenderingContext2D=D;CanvasGradient=U;CanvasPattern=T;DOMException=P})()}; \ No newline at end of file diff --git a/pandora_console/include/graphs/flot/jquery.colorhelpers.js b/pandora_console/include/graphs/flot/jquery.colorhelpers.js new file mode 100644 index 0000000000..b2f6dc4e43 --- /dev/null +++ b/pandora_console/include/graphs/flot/jquery.colorhelpers.js @@ -0,0 +1,180 @@ +/* Plugin for jQuery for working with colors. + * + * Version 1.1. + * + * Inspiration from jQuery color animation plugin by John Resig. + * + * Released under the MIT license by Ole Laursen, October 2009. + * + * Examples: + * + * $.color.parse("#fff").scale('rgb', 0.25).add('a', -0.5).toString() + * var c = $.color.extract($("#mydiv"), 'background-color'); + * console.log(c.r, c.g, c.b, c.a); + * $.color.make(100, 50, 25, 0.4).toString() // returns "rgba(100,50,25,0.4)" + * + * Note that .scale() and .add() return the same modified object + * instead of making a new one. + * + * V. 1.1: Fix error handling so e.g. parsing an empty string does + * produce a color rather than just crashing. + */ + +(function($) { + $.color = {}; + + // construct color object with some convenient chainable helpers + $.color.make = function (r, g, b, a) { + var o = {}; + o.r = r || 0; + o.g = g || 0; + o.b = b || 0; + o.a = a != null ? a : 1; + + o.add = function (c, d) { + for (var i = 0; i < c.length; ++i) + o[c.charAt(i)] += d; + return o.normalize(); + }; + + o.scale = function (c, f) { + for (var i = 0; i < c.length; ++i) + o[c.charAt(i)] *= f; + return o.normalize(); + }; + + o.toString = function () { + if (o.a >= 1.0) { + return "rgb("+[o.r, o.g, o.b].join(",")+")"; + } else { + return "rgba("+[o.r, o.g, o.b, o.a].join(",")+")"; + } + }; + + o.normalize = function () { + function clamp(min, value, max) { + return value < min ? min: (value > max ? max: value); + } + + o.r = clamp(0, parseInt(o.r), 255); + o.g = clamp(0, parseInt(o.g), 255); + o.b = clamp(0, parseInt(o.b), 255); + o.a = clamp(0, o.a, 1); + return o; + }; + + o.clone = function () { + return $.color.make(o.r, o.b, o.g, o.a); + }; + + return o.normalize(); + } + + // extract CSS color property from element, going up in the DOM + // if it's "transparent" + $.color.extract = function (elem, css) { + var c; + + do { + c = elem.css(css).toLowerCase(); + // keep going until we find an element that has color, or + // we hit the body or root (have no parent) + if (c != '' && c != 'transparent') + break; + elem = elem.parent(); + } while (elem.length && !$.nodeName(elem.get(0), "body")); + + // catch Safari's way of signalling transparent + if (c == "rgba(0, 0, 0, 0)") + c = "transparent"; + + return $.color.parse(c); + } + + // parse CSS color string (like "rgb(10, 32, 43)" or "#fff"), + // returns color object, if parsing failed, you get black (0, 0, + // 0) out + $.color.parse = function (str) { + var res, m = $.color.make; + + // Look for rgb(num,num,num) + if (res = /rgb\(\s*([0-9]{1,3})\s*,\s*([0-9]{1,3})\s*,\s*([0-9]{1,3})\s*\)/.exec(str)) + return m(parseInt(res[1], 10), parseInt(res[2], 10), parseInt(res[3], 10)); + + // Look for rgba(num,num,num,num) + if (res = /rgba\(\s*([0-9]{1,3})\s*,\s*([0-9]{1,3})\s*,\s*([0-9]{1,3})\s*,\s*([0-9]+(?:\.[0-9]+)?)\s*\)/.exec(str)) + return m(parseInt(res[1], 10), parseInt(res[2], 10), parseInt(res[3], 10), parseFloat(res[4])); + + // Look for rgb(num%,num%,num%) + if (res = /rgb\(\s*([0-9]+(?:\.[0-9]+)?)\%\s*,\s*([0-9]+(?:\.[0-9]+)?)\%\s*,\s*([0-9]+(?:\.[0-9]+)?)\%\s*\)/.exec(str)) + return m(parseFloat(res[1])*2.55, parseFloat(res[2])*2.55, parseFloat(res[3])*2.55); + + // Look for rgba(num%,num%,num%,num) + if (res = /rgba\(\s*([0-9]+(?:\.[0-9]+)?)\%\s*,\s*([0-9]+(?:\.[0-9]+)?)\%\s*,\s*([0-9]+(?:\.[0-9]+)?)\%\s*,\s*([0-9]+(?:\.[0-9]+)?)\s*\)/.exec(str)) + return m(parseFloat(res[1])*2.55, parseFloat(res[2])*2.55, parseFloat(res[3])*2.55, parseFloat(res[4])); + + // Look for #a0b1c2 + if (res = /#([a-fA-F0-9]{2})([a-fA-F0-9]{2})([a-fA-F0-9]{2})/.exec(str)) + return m(parseInt(res[1], 16), parseInt(res[2], 16), parseInt(res[3], 16)); + + // Look for #fff + if (res = /#([a-fA-F0-9])([a-fA-F0-9])([a-fA-F0-9])/.exec(str)) + return m(parseInt(res[1]+res[1], 16), parseInt(res[2]+res[2], 16), parseInt(res[3]+res[3], 16)); + + // Otherwise, we're most likely dealing with a named color + var name = $.trim(str).toLowerCase(); + if (name == "transparent") + return m(255, 255, 255, 0); + else { + // default to black + res = lookupColors[name] || [0, 0, 0]; + return m(res[0], res[1], res[2]); + } + } + + var lookupColors = { + aqua:[0,255,255], + azure:[240,255,255], + beige:[245,245,220], + black:[0,0,0], + blue:[0,0,255], + brown:[165,42,42], + cyan:[0,255,255], + darkblue:[0,0,139], + darkcyan:[0,139,139], + darkgrey:[169,169,169], + darkgreen:[0,100,0], + darkkhaki:[189,183,107], + darkmagenta:[139,0,139], + darkolivegreen:[85,107,47], + darkorange:[255,140,0], + darkorchid:[153,50,204], + darkred:[139,0,0], + darksalmon:[233,150,122], + darkviolet:[148,0,211], + fuchsia:[255,0,255], + gold:[255,215,0], + green:[0,128,0], + indigo:[75,0,130], + khaki:[240,230,140], + lightblue:[173,216,230], + lightcyan:[224,255,255], + lightgreen:[144,238,144], + lightgrey:[211,211,211], + lightpink:[255,182,193], + lightyellow:[255,255,224], + lime:[0,255,0], + magenta:[255,0,255], + maroon:[128,0,0], + navy:[0,0,128], + olive:[128,128,0], + orange:[255,165,0], + pink:[255,192,203], + purple:[128,0,128], + violet:[128,0,128], + red:[255,0,0], + silver:[192,192,192], + white:[255,255,255], + yellow:[255,255,0] + }; +})(jQuery); diff --git a/pandora_console/include/graphs/flot/jquery.colorhelpers.min.js b/pandora_console/include/graphs/flot/jquery.colorhelpers.min.js new file mode 100644 index 0000000000..7f42659643 --- /dev/null +++ b/pandora_console/include/graphs/flot/jquery.colorhelpers.min.js @@ -0,0 +1 @@ +(function($){$.color={};$.color.make=function(r,g,b,a){var o={};o.r=r||0;o.g=g||0;o.b=b||0;o.a=a!=null?a:1;o.add=function(c,d){for(var i=0;i=1){return"rgb("+[o.r,o.g,o.b].join(",")+")"}else{return"rgba("+[o.r,o.g,o.b,o.a].join(",")+")"}};o.normalize=function(){function clamp(min,value,max){return valuemax?max:value}o.r=clamp(0,parseInt(o.r),255);o.g=clamp(0,parseInt(o.g),255);o.b=clamp(0,parseInt(o.b),255);o.a=clamp(0,o.a,1);return o};o.clone=function(){return $.color.make(o.r,o.b,o.g,o.a)};return o.normalize()};$.color.extract=function(elem,css){var c;do{c=elem.css(css).toLowerCase();if(c!=""&&c!="transparent")break;elem=elem.parent()}while(elem.length&&!$.nodeName(elem.get(0),"body"));if(c=="rgba(0, 0, 0, 0)")c="transparent";return $.color.parse(c)};$.color.parse=function(str){var res,m=$.color.make;if(res=/rgb\(\s*([0-9]{1,3})\s*,\s*([0-9]{1,3})\s*,\s*([0-9]{1,3})\s*\)/.exec(str))return m(parseInt(res[1],10),parseInt(res[2],10),parseInt(res[3],10));if(res=/rgba\(\s*([0-9]{1,3})\s*,\s*([0-9]{1,3})\s*,\s*([0-9]{1,3})\s*,\s*([0-9]+(?:\.[0-9]+)?)\s*\)/.exec(str))return m(parseInt(res[1],10),parseInt(res[2],10),parseInt(res[3],10),parseFloat(res[4]));if(res=/rgb\(\s*([0-9]+(?:\.[0-9]+)?)\%\s*,\s*([0-9]+(?:\.[0-9]+)?)\%\s*,\s*([0-9]+(?:\.[0-9]+)?)\%\s*\)/.exec(str))return m(parseFloat(res[1])*2.55,parseFloat(res[2])*2.55,parseFloat(res[3])*2.55);if(res=/rgba\(\s*([0-9]+(?:\.[0-9]+)?)\%\s*,\s*([0-9]+(?:\.[0-9]+)?)\%\s*,\s*([0-9]+(?:\.[0-9]+)?)\%\s*,\s*([0-9]+(?:\.[0-9]+)?)\s*\)/.exec(str))return m(parseFloat(res[1])*2.55,parseFloat(res[2])*2.55,parseFloat(res[3])*2.55,parseFloat(res[4]));if(res=/#([a-fA-F0-9]{2})([a-fA-F0-9]{2})([a-fA-F0-9]{2})/.exec(str))return m(parseInt(res[1],16),parseInt(res[2],16),parseInt(res[3],16));if(res=/#([a-fA-F0-9])([a-fA-F0-9])([a-fA-F0-9])/.exec(str))return m(parseInt(res[1]+res[1],16),parseInt(res[2]+res[2],16),parseInt(res[3]+res[3],16));var name=$.trim(str).toLowerCase();if(name=="transparent")return m(255,255,255,0);else{res=lookupColors[name]||[0,0,0];return m(res[0],res[1],res[2])}};var lookupColors={aqua:[0,255,255],azure:[240,255,255],beige:[245,245,220],black:[0,0,0],blue:[0,0,255],brown:[165,42,42],cyan:[0,255,255],darkblue:[0,0,139],darkcyan:[0,139,139],darkgrey:[169,169,169],darkgreen:[0,100,0],darkkhaki:[189,183,107],darkmagenta:[139,0,139],darkolivegreen:[85,107,47],darkorange:[255,140,0],darkorchid:[153,50,204],darkred:[139,0,0],darksalmon:[233,150,122],darkviolet:[148,0,211],fuchsia:[255,0,255],gold:[255,215,0],green:[0,128,0],indigo:[75,0,130],khaki:[240,230,140],lightblue:[173,216,230],lightcyan:[224,255,255],lightgreen:[144,238,144],lightgrey:[211,211,211],lightpink:[255,182,193],lightyellow:[255,255,224],lime:[0,255,0],magenta:[255,0,255],maroon:[128,0,0],navy:[0,0,128],olive:[128,128,0],orange:[255,165,0],pink:[255,192,203],purple:[128,0,128],violet:[128,0,128],red:[255,0,0],silver:[192,192,192],white:[255,255,255],yellow:[255,255,0]}})(jQuery); \ No newline at end of file diff --git a/pandora_console/include/graphs/flot/jquery.flot.axislabels.js b/pandora_console/include/graphs/flot/jquery.flot.axislabels.js index a89dddc2a8..39f92cc227 100755 --- a/pandora_console/include/graphs/flot/jquery.flot.axislabels.js +++ b/pandora_console/include/graphs/flot/jquery.flot.axislabels.js @@ -1,6 +1,11 @@ /* -CAxis Labels Plugin for flot. :P -Copyright (c) 2010 Xuan Luo +Axis Labels Plugin for flot. +http://github.com/markrcote/flot-axislabels + +Original code is Copyright (c) 2010 Xuan Luo. +Original code was released under the GPLv3 license by Xuan Luo, September 2010. +Original code was rereleased under the MIT license by Xuan Luo, April 2012. + 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 @@ -8,8 +13,10 @@ 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 @@ -18,120 +25,441 @@ 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 ($) { - var options = { }; + var options = { + axisLabels: { + show: true + } + }; - function init(plot) { - // This is kind of a hack. There are no hooks in Flot between - // the creation and measuring of the ticks (setTicks, measureTickLabels - // in setupGrid() ) and the drawing of the ticks and plot box - // (insertAxisLabels in setupGrid() ). - // - // Therefore, we use a trick where we run the draw routine twice: - // the first time to get the tick measurements, so that we can change - // them, and then have it draw it again. - var secondPass = false; - plot.hooks.draw.push(function (plot, ctx) { - if (!secondPass) { - // MEASURE AND SET OPTIONS - $.each(plot.getAxes(), function(axisName, axis) { - var opts = axis.options // Flot 0.7 - || plot.getOptions()[axisName]; // Flot 0.6 - if (!opts || !opts.axisLabel) - return; + function canvasSupported() { + return !!document.createElement('canvas').getContext; + } - var w, h; - if (opts.axisLabelUseCanvas != false) - opts.axisLabelUseCanvas = true; + function canvasTextSupported() { + if (!canvasSupported()) { + return false; + } + var dummy_canvas = document.createElement('canvas'); + var context = dummy_canvas.getContext('2d'); + return typeof context.fillText == 'function'; + } - if (opts.axisLabelUseCanvas) { - // canvas text - if (!opts.axisLabelFontSizePixels) - opts.axisLabelFontSizePixels = 14; - if (!opts.axisLabelFontFamily) - opts.axisLabelFontFamily = 'sans-serif'; - // since we currently always display x as horiz. - // and y as vertical, we only care about the height - w = opts.axisLabelFontSizePixels; - h = opts.axisLabelFontSizePixels; - - } else { - // HTML text - var elem = $('
' + opts.axisLabel + '
'); - plot.getPlaceholder().append(elem); - w = elem.outerWidth(true); - h = elem.outerHeight(true); - elem.remove(); - } - - if (axisName.charAt(0) == 'x') - axis.labelHeight += h; - else - axis.labelWidth += w; - opts.labelHeight = axis.labelHeight; - opts.labelWidth = axis.labelWidth; - }); - // re-draw with new label widths and heights - secondPass = true; - plot.setupGrid(); - plot.draw(); - - - } else { - // DRAW - $.each(plot.getAxes(), function(axisName, axis) { - var opts = axis.options // Flot 0.7 - || plot.getOptions()[axisName]; // Flot 0.6 - if (!opts || !opts.axisLabel) - return; - - if (opts.axisLabelUseCanvas) { - // canvas text - var ctx = plot.getCanvas().getContext('2d'); - ctx.save(); - ctx.font = opts.axisLabelFontSizePixels + 'px ' + - opts.axisLabelFontFamily; - var width = ctx.measureText(opts.axisLabel).width; - var height = opts.axisLabelFontSizePixels; - var x, y; - if (axisName.charAt(0) == 'x') { - x = plot.getPlotOffset().left + plot.width()/2 - width/2; - y = plot.getCanvas().height; - } else { - x = height * 0.72; - y = plot.getPlotOffset().top + plot.height()/2 - width/2; - } - ctx.translate(x, y); - ctx.rotate((axisName.charAt(0) == 'x') ? 0 : -Math.PI/2); - ctx.fillText(opts.axisLabel, 0, 0); - ctx.restore(); - - } else { - // HTML text - plot.getPlaceholder().find('#' + axisName + 'Label').remove(); - var elem = $('
' + opts.axisLabel + '
'); - if (axisName.charAt(0) == 'x') { - //~ elem.css('left', plot.getPlotOffset().left + plot.width()/4 - elem.outerWidth()/2 + 'px'); - elem.css('bottom', '0px'); - elem.css('width', '100%'); - } else { - elem.css('top', plot.getPlotOffset().top + plot.height()/2 - elem.outerHeight()/2 + 'px'); - elem.css('left', '0px'); - } - plot.getPlaceholder().append(elem); - } - }); - secondPass = false; - } - }); + function css3TransitionSupported() { + var div = document.createElement('div'); + return typeof div.style.MozTransition != 'undefined' // Gecko + || typeof div.style.OTransition != 'undefined' // Opera + || typeof div.style.webkitTransition != 'undefined' // WebKit + || typeof div.style.transition != 'undefined'; } + function AxisLabel(axisName, position, padding, plot, opts) { + this.axisName = axisName; + this.position = position; + this.padding = padding; + this.plot = plot; + this.opts = opts; + this.width = 0; + this.height = 0; + } + + AxisLabel.prototype.cleanup = function() { + }; + + + CanvasAxisLabel.prototype = new AxisLabel(); + CanvasAxisLabel.prototype.constructor = CanvasAxisLabel; + function CanvasAxisLabel(axisName, position, padding, plot, opts) { + AxisLabel.prototype.constructor.call(this, axisName, position, padding, + plot, opts); + } + + CanvasAxisLabel.prototype.calculateSize = function() { + if (!this.opts.axisLabelFontSizePixels) + this.opts.axisLabelFontSizePixels = 14; + if (!this.opts.axisLabelFontFamily) + this.opts.axisLabelFontFamily = 'sans-serif'; + + var textWidth = this.opts.axisLabelFontSizePixels + this.padding; + var textHeight = this.opts.axisLabelFontSizePixels + this.padding; + if (this.position == 'left' || this.position == 'right') { + this.width = this.opts.axisLabelFontSizePixels + this.padding; + this.height = 0; + } else { + this.width = 0; + this.height = this.opts.axisLabelFontSizePixels + this.padding; + } + }; + + CanvasAxisLabel.prototype.draw = function(box) { + if (!this.opts.axisLabelColour) + this.opts.axisLabelColour = 'black'; + var ctx = this.plot.getCanvas().getContext('2d'); + ctx.save(); + ctx.font = this.opts.axisLabelFontSizePixels + 'px ' + + this.opts.axisLabelFontFamily; + ctx.fillStyle = this.opts.axisLabelColour; + var width = ctx.measureText(this.opts.axisLabel).width; + var height = this.opts.axisLabelFontSizePixels; + var x, y, angle = 0; + if (this.position == 'top') { + x = box.left + box.width/2 - width/2; + y = box.top + height*0.72; + } else if (this.position == 'bottom') { + x = box.left + box.width/2 - width/2; + y = box.top + box.height - height*0.72; + } else if (this.position == 'left') { + x = box.left + height*0.72; + y = box.height/2 + box.top + width/2; + angle = -Math.PI/2; + } else if (this.position == 'right') { + x = box.left + box.width - height*0.72; + y = box.height/2 + box.top - width/2; + angle = Math.PI/2; + } + ctx.translate(x, y); + ctx.rotate(angle); + ctx.fillText(this.opts.axisLabel, 0, 0); + ctx.restore(); + }; + + + HtmlAxisLabel.prototype = new AxisLabel(); + HtmlAxisLabel.prototype.constructor = HtmlAxisLabel; + function HtmlAxisLabel(axisName, position, padding, plot, opts) { + AxisLabel.prototype.constructor.call(this, axisName, position, + padding, plot, opts); + this.elem = null; + } + + HtmlAxisLabel.prototype.calculateSize = function() { + var elem = $('
' + + this.opts.axisLabel + '
'); + this.plot.getPlaceholder().append(elem); + // store height and width of label itself, for use in draw() + this.labelWidth = elem.outerWidth(true); + this.labelHeight = elem.outerHeight(true); + elem.remove(); + + this.width = this.height = 0; + if (this.position == 'left' || this.position == 'right') { + this.width = this.labelWidth + this.padding; + } else { + this.height = this.labelHeight + this.padding; + } + }; + + HtmlAxisLabel.prototype.cleanup = function() { + if (this.elem) { + this.elem.remove(); + } + }; + + HtmlAxisLabel.prototype.draw = function(box) { + this.plot.getPlaceholder().find('#' + this.axisName + 'Label').remove(); + this.elem = $('
' + + this.opts.axisLabel + '
'); + this.plot.getPlaceholder().append(this.elem); + if (this.position == 'top') { + this.elem.css('left', box.left + box.width/2 - this.labelWidth/2 + + 'px'); + this.elem.css('top', box.top + 'px'); + } else if (this.position == 'bottom') { + this.elem.css('left', box.left + box.width/2 - this.labelWidth/2 + + 'px'); + this.elem.css('top', box.top + box.height - this.labelHeight + + 'px'); + } else if (this.position == 'left') { + this.elem.css('top', box.top + box.height/2 - this.labelHeight/2 + + 'px'); + this.elem.css('left', box.left + 'px'); + } else if (this.position == 'right') { + this.elem.css('top', box.top + box.height/2 - this.labelHeight/2 + + 'px'); + this.elem.css('left', box.left + box.width - this.labelWidth + + 'px'); + } + }; + + + CssTransformAxisLabel.prototype = new HtmlAxisLabel(); + CssTransformAxisLabel.prototype.constructor = CssTransformAxisLabel; + function CssTransformAxisLabel(axisName, position, padding, plot, opts) { + HtmlAxisLabel.prototype.constructor.call(this, axisName, position, + padding, plot, opts); + } + + CssTransformAxisLabel.prototype.calculateSize = function() { + HtmlAxisLabel.prototype.calculateSize.call(this); + this.width = this.height = 0; + if (this.position == 'left' || this.position == 'right') { + this.width = this.labelHeight + this.padding; + } else { + this.height = this.labelHeight + this.padding; + } + }; + + CssTransformAxisLabel.prototype.transforms = function(degrees, x, y) { + var stransforms = { + '-moz-transform': '', + '-webkit-transform': '', + '-o-transform': '', + '-ms-transform': '' + }; + if (x != 0 || y != 0) { + var stdTranslate = ' translate(' + x + 'px, ' + y + 'px)'; + stransforms['-moz-transform'] += stdTranslate; + stransforms['-webkit-transform'] += stdTranslate; + stransforms['-o-transform'] += stdTranslate; + stransforms['-ms-transform'] += stdTranslate; + } + if (degrees != 0) { + var rotation = degrees / 90; + var stdRotate = ' rotate(' + degrees + 'deg)'; + stransforms['-moz-transform'] += stdRotate; + stransforms['-webkit-transform'] += stdRotate; + stransforms['-o-transform'] += stdRotate; + stransforms['-ms-transform'] += stdRotate; + } + var s = 'top: 0; left: 0; '; + for (var prop in stransforms) { + if (stransforms[prop]) { + s += prop + ':' + stransforms[prop] + ';'; + } + } + s += ';'; + return s; + }; + + CssTransformAxisLabel.prototype.calculateOffsets = function(box) { + var offsets = { x: 0, y: 0, degrees: 0 }; + if (this.position == 'bottom') { + offsets.x = box.left + box.width/2 - this.labelWidth/2; + offsets.y = box.top + box.height - this.labelHeight; + } else if (this.position == 'top') { + offsets.x = box.left + box.width/2 - this.labelWidth/2; + offsets.y = box.top; + } else if (this.position == 'left') { + offsets.degrees = -90; + offsets.x = box.left - this.labelWidth/2 + this.labelHeight/2; + offsets.y = box.height/2 + box.top; + } else if (this.position == 'right') { + offsets.degrees = 90; + offsets.x = box.left + box.width - this.labelWidth/2 + - this.labelHeight/2; + offsets.y = box.height/2 + box.top; + } + offsets.x = Math.round(offsets.x); + offsets.y = Math.round(offsets.y); + + return offsets; + }; + + CssTransformAxisLabel.prototype.draw = function(box) { + this.plot.getPlaceholder().find("." + this.axisName + "Label").remove(); + var offsets = this.calculateOffsets(box); + this.elem = $('
' + this.opts.axisLabel + '
'); + this.plot.getPlaceholder().append(this.elem); + }; + + + IeTransformAxisLabel.prototype = new CssTransformAxisLabel(); + IeTransformAxisLabel.prototype.constructor = IeTransformAxisLabel; + function IeTransformAxisLabel(axisName, position, padding, plot, opts) { + CssTransformAxisLabel.prototype.constructor.call(this, axisName, + position, padding, + plot, opts); + this.requiresResize = false; + } + + IeTransformAxisLabel.prototype.transforms = function(degrees, x, y) { + // I didn't feel like learning the crazy Matrix stuff, so this uses + // a combination of the rotation transform and CSS positioning. + var s = ''; + if (degrees != 0) { + var rotation = degrees/90; + while (rotation < 0) { + rotation += 4; + } + s += ' filter: progid:DXImageTransform.Microsoft.BasicImage(rotation=' + rotation + '); '; + // see below + this.requiresResize = (this.position == 'right'); + } + if (x != 0) { + s += 'left: ' + x + 'px; '; + } + if (y != 0) { + s += 'top: ' + y + 'px; '; + } + return s; + }; + + IeTransformAxisLabel.prototype.calculateOffsets = function(box) { + var offsets = CssTransformAxisLabel.prototype.calculateOffsets.call( + this, box); + // adjust some values to take into account differences between + // CSS and IE rotations. + if (this.position == 'top') { + // FIXME: not sure why, but placing this exactly at the top causes + // the top axis label to flip to the bottom... + offsets.y = box.top + 1; + } else if (this.position == 'left') { + offsets.x = box.left; + offsets.y = box.height/2 + box.top - this.labelWidth/2; + } else if (this.position == 'right') { + offsets.x = box.left + box.width - this.labelHeight; + offsets.y = box.height/2 + box.top - this.labelWidth/2; + } + return offsets; + }; + + IeTransformAxisLabel.prototype.draw = function(box) { + CssTransformAxisLabel.prototype.draw.call(this, box); + if (this.requiresResize) { + this.elem = this.plot.getPlaceholder().find("." + this.axisName + + "Label"); + // Since we used CSS positioning instead of transforms for + // translating the element, and since the positioning is done + // before any rotations, we have to reset the width and height + // in case the browser wrapped the text (specifically for the + // y2axis). + this.elem.css('width', this.labelWidth); + this.elem.css('height', this.labelHeight); + } + }; + + + function init(plot) { + plot.hooks.processOptions.push(function (plot, options) { + + if (!options.axisLabels.show) + return; + + // This is kind of a hack. There are no hooks in Flot between + // the creation and measuring of the ticks (setTicks, measureTickLabels + // in setupGrid() ) and the drawing of the ticks and plot box + // (insertAxisLabels in setupGrid() ). + // + // Therefore, we use a trick where we run the draw routine twice: + // the first time to get the tick measurements, so that we can change + // them, and then have it draw it again. + var secondPass = false; + + var axisLabels = {}; + var axisOffsetCounts = { left: 0, right: 0, top: 0, bottom: 0 }; + + var defaultPadding = 2; // padding between axis and tick labels + plot.hooks.draw.push(function (plot, ctx) { + var hasAxisLabels = false; + if (!secondPass) { + // MEASURE AND SET OPTIONS + $.each(plot.getAxes(), function(axisName, axis) { + var opts = axis.options // Flot 0.7 + || plot.getOptions()[axisName]; // Flot 0.6 + + // Handle redraws initiated outside of this plug-in. + if (axisName in axisLabels) { + axis.labelHeight = axis.labelHeight - + axisLabels[axisName].height; + axis.labelWidth = axis.labelWidth - + axisLabels[axisName].width; + opts.labelHeight = axis.labelHeight; + opts.labelWidth = axis.labelWidth; + axisLabels[axisName].cleanup(); + delete axisLabels[axisName]; + } + + if (!opts || !opts.axisLabel || !axis.show) + return; + + hasAxisLabels = true; + var renderer = null; + + if (!opts.axisLabelUseHtml && + navigator.appName == 'Microsoft Internet Explorer') { + var ua = navigator.userAgent; + var re = new RegExp("MSIE ([0-9]{1,}[\.0-9]{0,})"); + if (re.exec(ua) != null) { + rv = parseFloat(RegExp.$1); + } + if (rv >= 9 && !opts.axisLabelUseCanvas && !opts.axisLabelUseHtml) { + renderer = CssTransformAxisLabel; + } else if (!opts.axisLabelUseCanvas && !opts.axisLabelUseHtml) { + renderer = IeTransformAxisLabel; + } else if (opts.axisLabelUseCanvas) { + renderer = CanvasAxisLabel; + } else { + renderer = HtmlAxisLabel; + } + } else { + if (opts.axisLabelUseHtml || (!css3TransitionSupported() && !canvasTextSupported()) && !opts.axisLabelUseCanvas) { + renderer = HtmlAxisLabel; + } else if (opts.axisLabelUseCanvas || !css3TransitionSupported()) { + renderer = CanvasAxisLabel; + } else { + renderer = CssTransformAxisLabel; + } + } + + var padding = opts.axisLabelPadding === undefined ? + defaultPadding : opts.axisLabelPadding; + + axisLabels[axisName] = new renderer(axisName, + axis.position, padding, + plot, opts); + + // flot interprets axis.labelHeight and .labelWidth as + // the height and width of the tick labels. We increase + // these values to make room for the axis label and + // padding. + + axisLabels[axisName].calculateSize(); + + // AxisLabel.height and .width are the size of the + // axis label and padding. + // Just set opts here because axis will be sorted out on + // the redraw. + + opts.labelHeight = axis.labelHeight + + axisLabels[axisName].height; + opts.labelWidth = axis.labelWidth + + axisLabels[axisName].width; + }); + + // If there are axis labels, re-draw with new label widths and + // heights. + + if (hasAxisLabels) { + secondPass = true; + plot.setupGrid(); + plot.draw(); + } + } else { + secondPass = false; + // DRAW + $.each(plot.getAxes(), function(axisName, axis) { + var opts = axis.options // Flot 0.7 + || plot.getOptions()[axisName]; // Flot 0.6 + if (!opts || !opts.axisLabel || !axis.show) + return; + + axisLabels[axisName].draw(axis.box); + }); + } + }); + }); + } $.plot.plugins.push({ init: init, options: options, name: 'axisLabels', - version: '1.0' + version: '2.0' }); })(jQuery); \ No newline at end of file diff --git a/pandora_console/include/graphs/flot/jquery.flot.canvas.js b/pandora_console/include/graphs/flot/jquery.flot.canvas.js new file mode 100644 index 0000000000..29328d5812 --- /dev/null +++ b/pandora_console/include/graphs/flot/jquery.flot.canvas.js @@ -0,0 +1,345 @@ +/* Flot plugin for drawing all elements of a plot on the canvas. + +Copyright (c) 2007-2014 IOLA and Ole Laursen. +Licensed under the MIT license. + +Flot normally produces certain elements, like axis labels and the legend, using +HTML elements. This permits greater interactivity and customization, and often +looks better, due to cross-browser canvas text inconsistencies and limitations. + +It can also be desirable to render the plot entirely in canvas, particularly +if the goal is to save it as an image, or if Flot is being used in a context +where the HTML DOM does not exist, as is the case within Node.js. This plugin +switches out Flot's standard drawing operations for canvas-only replacements. + +Currently the plugin supports only axis labels, but it will eventually allow +every element of the plot to be rendered directly to canvas. + +The plugin supports these options: + +{ + canvas: boolean +} + +The "canvas" option controls whether full canvas drawing is enabled, making it +possible to toggle on and off. This is useful when a plot uses HTML text in the +browser, but needs to redraw with canvas text when exporting as an image. + +*/ + +(function($) { + + var options = { + canvas: true + }; + + var render, getTextInfo, addText; + + // Cache the prototype hasOwnProperty for faster access + + var hasOwnProperty = Object.prototype.hasOwnProperty; + + function init(plot, classes) { + + var Canvas = classes.Canvas; + + // We only want to replace the functions once; the second time around + // we would just get our new function back. This whole replacing of + // prototype functions is a disaster, and needs to be changed ASAP. + + if (render == null) { + getTextInfo = Canvas.prototype.getTextInfo, + addText = Canvas.prototype.addText, + render = Canvas.prototype.render; + } + + // Finishes rendering the canvas, including overlaid text + + Canvas.prototype.render = function() { + + if (!plot.getOptions().canvas) { + return render.call(this); + } + + var context = this.context, + cache = this._textCache; + + // For each text layer, render elements marked as active + + context.save(); + context.textBaseline = "middle"; + + for (var layerKey in cache) { + if (hasOwnProperty.call(cache, layerKey)) { + var layerCache = cache[layerKey]; + for (var styleKey in layerCache) { + if (hasOwnProperty.call(layerCache, styleKey)) { + var styleCache = layerCache[styleKey], + updateStyles = true; + for (var key in styleCache) { + if (hasOwnProperty.call(styleCache, key)) { + + var info = styleCache[key], + positions = info.positions, + lines = info.lines; + + // Since every element at this level of the cache have the + // same font and fill styles, we can just change them once + // using the values from the first element. + + if (updateStyles) { + context.fillStyle = info.font.color; + context.font = info.font.definition; + updateStyles = false; + } + + for (var i = 0, position; position = positions[i]; i++) { + if (position.active) { + for (var j = 0, line; line = position.lines[j]; j++) { + context.fillText(lines[j].text, line[0], line[1]); + } + } else { + positions.splice(i--, 1); + } + } + + if (positions.length == 0) { + delete styleCache[key]; + } + } + } + } + } + } + } + + context.restore(); + }; + + // Creates (if necessary) and returns a text info object. + // + // When the canvas option is set, the object looks like this: + // + // { + // width: Width of the text's bounding box. + // height: Height of the text's bounding box. + // positions: Array of positions at which this text is drawn. + // lines: [{ + // height: Height of this line. + // widths: Width of this line. + // text: Text on this line. + // }], + // font: { + // definition: Canvas font property string. + // color: Color of the text. + // }, + // } + // + // The positions array contains objects that look like this: + // + // { + // active: Flag indicating whether the text should be visible. + // lines: Array of [x, y] coordinates at which to draw the line. + // x: X coordinate at which to draw the text. + // y: Y coordinate at which to draw the text. + // } + + Canvas.prototype.getTextInfo = function(layer, text, font, angle, width) { + + if (!plot.getOptions().canvas) { + return getTextInfo.call(this, layer, text, font, angle, width); + } + + var textStyle, layerCache, styleCache, info; + + // Cast the value to a string, in case we were given a number + + text = "" + text; + + // If the font is a font-spec object, generate a CSS definition + + if (typeof font === "object") { + textStyle = font.style + " " + font.variant + " " + font.weight + " " + font.size + "px " + font.family; + } else { + textStyle = font; + } + + // Retrieve (or create) the cache for the text's layer and styles + + layerCache = this._textCache[layer]; + + if (layerCache == null) { + layerCache = this._textCache[layer] = {}; + } + + styleCache = layerCache[textStyle]; + + if (styleCache == null) { + styleCache = layerCache[textStyle] = {}; + } + + info = styleCache[text]; + + if (info == null) { + + var context = this.context; + + // If the font was provided as CSS, create a div with those + // classes and examine it to generate a canvas font spec. + + if (typeof font !== "object") { + + var element = $("
 
") + .css("position", "absolute") + .addClass(typeof font === "string" ? font : null) + .appendTo(this.getTextLayer(layer)); + + font = { + lineHeight: element.height(), + style: element.css("font-style"), + variant: element.css("font-variant"), + weight: element.css("font-weight"), + family: element.css("font-family"), + color: element.css("color") + }; + + // Setting line-height to 1, without units, sets it equal + // to the font-size, even if the font-size is abstract, + // like 'smaller'. This enables us to read the real size + // via the element's height, working around browsers that + // return the literal 'smaller' value. + + font.size = element.css("line-height", 1).height(); + + element.remove(); + } + + textStyle = font.style + " " + font.variant + " " + font.weight + " " + font.size + "px " + font.family; + + // Create a new info object, initializing the dimensions to + // zero so we can count them up line-by-line. + + info = styleCache[text] = { + width: 0, + height: 0, + positions: [], + lines: [], + font: { + definition: textStyle, + color: font.color + } + }; + + context.save(); + context.font = textStyle; + + // Canvas can't handle multi-line strings; break on various + // newlines, including HTML brs, to build a list of lines. + // Note that we could split directly on regexps, but IE < 9 is + // broken; revisit when we drop IE 7/8 support. + + var lines = (text + "").replace(/
|\r\n|\r/g, "\n").split("\n"); + + for (var i = 0; i < lines.length; ++i) { + + var lineText = lines[i], + measured = context.measureText(lineText); + + info.width = Math.max(measured.width, info.width); + info.height += font.lineHeight; + + info.lines.push({ + text: lineText, + width: measured.width, + height: font.lineHeight + }); + } + + context.restore(); + } + + return info; + }; + + // Adds a text string to the canvas text overlay. + + Canvas.prototype.addText = function(layer, x, y, text, font, angle, width, halign, valign) { + + if (!plot.getOptions().canvas) { + return addText.call(this, layer, x, y, text, font, angle, width, halign, valign); + } + + var info = this.getTextInfo(layer, text, font, angle, width), + positions = info.positions, + lines = info.lines; + + // Text is drawn with baseline 'middle', which we need to account + // for by adding half a line's height to the y position. + + y += info.height / lines.length / 2; + + // Tweak the initial y-position to match vertical alignment + + if (valign == "middle") { + y = Math.round(y - info.height / 2); + } else if (valign == "bottom") { + y = Math.round(y - info.height); + } else { + y = Math.round(y); + } + + // FIXME: LEGACY BROWSER FIX + // AFFECTS: Opera < 12.00 + + // Offset the y coordinate, since Opera is off pretty + // consistently compared to the other browsers. + + if (!!(window.opera && window.opera.version().split(".")[0] < 12)) { + y -= 2; + } + + // Determine whether this text already exists at this position. + // If so, mark it for inclusion in the next render pass. + + for (var i = 0, position; position = positions[i]; i++) { + if (position.x == x && position.y == y) { + position.active = true; + return; + } + } + + // If the text doesn't exist at this position, create a new entry + + position = { + active: true, + lines: [], + x: x, + y: y + }; + + positions.push(position); + + // Fill in the x & y positions of each line, adjusting them + // individually for horizontal alignment. + + for (var i = 0, line; line = lines[i]; i++) { + if (halign == "center") { + position.lines.push([Math.round(x - line.width / 2), y]); + } else if (halign == "right") { + position.lines.push([Math.round(x - line.width), y]); + } else { + position.lines.push([Math.round(x), y]); + } + y += line.height; + } + }; + } + + $.plot.plugins.push({ + init: init, + options: options, + name: "canvas", + version: "1.0" + }); + +})(jQuery); diff --git a/pandora_console/include/graphs/flot/jquery.flot.canvas.min.js b/pandora_console/include/graphs/flot/jquery.flot.canvas.min.js new file mode 100644 index 0000000000..40c1051b3c --- /dev/null +++ b/pandora_console/include/graphs/flot/jquery.flot.canvas.min.js @@ -0,0 +1,7 @@ +/* Javascript plotting library for jQuery, version 0.8.3. + +Copyright (c) 2007-2014 IOLA and Ole Laursen. +Licensed under the MIT license. + +*/ +(function($){var options={canvas:true};var render,getTextInfo,addText;var hasOwnProperty=Object.prototype.hasOwnProperty;function init(plot,classes){var Canvas=classes.Canvas;if(render==null){getTextInfo=Canvas.prototype.getTextInfo,addText=Canvas.prototype.addText,render=Canvas.prototype.render}Canvas.prototype.render=function(){if(!plot.getOptions().canvas){return render.call(this)}var context=this.context,cache=this._textCache;context.save();context.textBaseline="middle";for(var layerKey in cache){if(hasOwnProperty.call(cache,layerKey)){var layerCache=cache[layerKey];for(var styleKey in layerCache){if(hasOwnProperty.call(layerCache,styleKey)){var styleCache=layerCache[styleKey],updateStyles=true;for(var key in styleCache){if(hasOwnProperty.call(styleCache,key)){var info=styleCache[key],positions=info.positions,lines=info.lines;if(updateStyles){context.fillStyle=info.font.color;context.font=info.font.definition;updateStyles=false}for(var i=0,position;position=positions[i];i++){if(position.active){for(var j=0,line;line=position.lines[j];j++){context.fillText(lines[j].text,line[0],line[1])}}else{positions.splice(i--,1)}}if(positions.length==0){delete styleCache[key]}}}}}}}context.restore()};Canvas.prototype.getTextInfo=function(layer,text,font,angle,width){if(!plot.getOptions().canvas){return getTextInfo.call(this,layer,text,font,angle,width)}var textStyle,layerCache,styleCache,info;text=""+text;if(typeof font==="object"){textStyle=font.style+" "+font.variant+" "+font.weight+" "+font.size+"px "+font.family}else{textStyle=font}layerCache=this._textCache[layer];if(layerCache==null){layerCache=this._textCache[layer]={}}styleCache=layerCache[textStyle];if(styleCache==null){styleCache=layerCache[textStyle]={}}info=styleCache[text];if(info==null){var context=this.context;if(typeof font!=="object"){var element=$("
 
").css("position","absolute").addClass(typeof font==="string"?font:null).appendTo(this.getTextLayer(layer));font={lineHeight:element.height(),style:element.css("font-style"),variant:element.css("font-variant"),weight:element.css("font-weight"),family:element.css("font-family"),color:element.css("color")};font.size=element.css("line-height",1).height();element.remove()}textStyle=font.style+" "+font.variant+" "+font.weight+" "+font.size+"px "+font.family;info=styleCache[text]={width:0,height:0,positions:[],lines:[],font:{definition:textStyle,color:font.color}};context.save();context.font=textStyle;var lines=(text+"").replace(/
|\r\n|\r/g,"\n").split("\n");for(var i=0;i index) + index = categories[v]; + + return index + 1; + } + + function categoriesTickGenerator(axis) { + var res = []; + for (var label in axis.categories) { + var v = axis.categories[label]; + if (v >= axis.min && v <= axis.max) + res.push([v, label]); + } + + res.sort(function (a, b) { return a[0] - b[0]; }); + + return res; + } + + function setupCategoriesForAxis(series, axis, datapoints) { + if (series[axis].options.mode != "categories") + return; + + if (!series[axis].categories) { + // parse options + var c = {}, o = series[axis].options.categories || {}; + if ($.isArray(o)) { + for (var i = 0; i < o.length; ++i) + c[o[i]] = i; + } + else { + for (var v in o) + c[v] = o[v]; + } + + series[axis].categories = c; + } + + // fix ticks + if (!series[axis].options.ticks) + series[axis].options.ticks = categoriesTickGenerator; + + transformPointsOnAxis(datapoints, axis, series[axis].categories); + } + + function transformPointsOnAxis(datapoints, axis, categories) { + // go through the points, transforming them + var points = datapoints.points, + ps = datapoints.pointsize, + format = datapoints.format, + formatColumn = axis.charAt(0), + index = getNextIndex(categories); + + for (var i = 0; i < points.length; i += ps) { + if (points[i] == null) + continue; + + for (var m = 0; m < ps; ++m) { + var val = points[i + m]; + + if (val == null || !format[m][formatColumn]) + continue; + + if (!(val in categories)) { + categories[val] = index; + ++index; + } + + points[i + m] = categories[val]; + } + } + } + + function processDatapoints(plot, series, datapoints) { + setupCategoriesForAxis(series, "xaxis", datapoints); + setupCategoriesForAxis(series, "yaxis", datapoints); + } + + function init(plot) { + plot.hooks.processRawData.push(processRawData); + plot.hooks.processDatapoints.push(processDatapoints); + } + + $.plot.plugins.push({ + init: init, + options: options, + name: 'categories', + version: '1.0' + }); +})(jQuery); diff --git a/pandora_console/include/graphs/flot/jquery.flot.categories.min.js b/pandora_console/include/graphs/flot/jquery.flot.categories.min.js new file mode 100644 index 0000000000..5bce588e3d --- /dev/null +++ b/pandora_console/include/graphs/flot/jquery.flot.categories.min.js @@ -0,0 +1,7 @@ +/* Javascript plotting library for jQuery, version 0.8.3. + +Copyright (c) 2007-2014 IOLA and Ole Laursen. +Licensed under the MIT license. + +*/ +(function($){var options={xaxis:{categories:null},yaxis:{categories:null}};function processRawData(plot,series,data,datapoints){var xCategories=series.xaxis.options.mode=="categories",yCategories=series.yaxis.options.mode=="categories";if(!(xCategories||yCategories))return;var format=datapoints.format;if(!format){var s=series;format=[];format.push({x:true,number:true,required:true});format.push({y:true,number:true,required:true});if(s.bars.show||s.lines.show&&s.lines.fill){var autoscale=!!(s.bars.show&&s.bars.zero||s.lines.show&&s.lines.zero);format.push({y:true,number:true,required:false,defaultValue:0,autoscale:autoscale});if(s.bars.horizontal){delete format[format.length-1].y;format[format.length-1].x=true}}datapoints.format=format}for(var m=0;mindex)index=categories[v];return index+1}function categoriesTickGenerator(axis){var res=[];for(var label in axis.categories){var v=axis.categories[label];if(v>=axis.min&&v<=axis.max)res.push([v,label])}res.sort(function(a,b){return a[0]-b[0]});return res}function setupCategoriesForAxis(series,axis,datapoints){if(series[axis].options.mode!="categories")return;if(!series[axis].categories){var c={},o=series[axis].options.categories||{};if($.isArray(o)){for(var i=0;i ax[1].max || y < ax[1].min || upper < ax[0].min || lower > ax[0].max) + continue; + if (err[e].err == 'y') + if (x > ax[0].max || x < ax[0].min || upper < ax[1].min || lower > ax[1].max) + continue; + + // prevent errorbars getting out of the canvas + var drawUpper = true, + drawLower = true; + + if (upper > minmax[1]) { + drawUpper = false; + upper = minmax[1]; + } + if (lower < minmax[0]) { + drawLower = false; + lower = minmax[0]; + } + + //sanity check, in case some inverted axis hack is applied to flot + if ((err[e].err == 'x' && invertX) || (err[e].err == 'y' && invertY)) { + //swap coordinates + var tmp = lower; + lower = upper; + upper = tmp; + tmp = drawLower; + drawLower = drawUpper; + drawUpper = tmp; + tmp = minmax[0]; + minmax[0] = minmax[1]; + minmax[1] = tmp; + } + + // convert to pixels + x = ax[0].p2c(x), + y = ax[1].p2c(y), + upper = ax[e].p2c(upper); + lower = ax[e].p2c(lower); + minmax[0] = ax[e].p2c(minmax[0]); + minmax[1] = ax[e].p2c(minmax[1]); + + //same style as points by default + var lw = err[e].lineWidth ? err[e].lineWidth : s.points.lineWidth, + sw = s.points.shadowSize != null ? s.points.shadowSize : s.shadowSize; + + //shadow as for points + if (lw > 0 && sw > 0) { + var w = sw / 2; + ctx.lineWidth = w; + ctx.strokeStyle = "rgba(0,0,0,0.1)"; + drawError(ctx, err[e], x, y, upper, lower, drawUpper, drawLower, radius, w + w/2, minmax); + + ctx.strokeStyle = "rgba(0,0,0,0.2)"; + drawError(ctx, err[e], x, y, upper, lower, drawUpper, drawLower, radius, w/2, minmax); + } + + ctx.strokeStyle = err[e].color? err[e].color: s.color; + ctx.lineWidth = lw; + //draw it + drawError(ctx, err[e], x, y, upper, lower, drawUpper, drawLower, radius, 0, minmax); + } + } + } + } + + function drawError(ctx,err,x,y,upper,lower,drawUpper,drawLower,radius,offset,minmax){ + + //shadow offset + y += offset; + upper += offset; + lower += offset; + + // error bar - avoid plotting over circles + if (err.err == 'x'){ + if (upper > x + radius) drawPath(ctx, [[upper,y],[Math.max(x + radius,minmax[0]),y]]); + else drawUpper = false; + if (lower < x - radius) drawPath(ctx, [[Math.min(x - radius,minmax[1]),y],[lower,y]] ); + else drawLower = false; + } + else { + if (upper < y - radius) drawPath(ctx, [[x,upper],[x,Math.min(y - radius,minmax[0])]] ); + else drawUpper = false; + if (lower > y + radius) drawPath(ctx, [[x,Math.max(y + radius,minmax[1])],[x,lower]] ); + else drawLower = false; + } + + //internal radius value in errorbar, allows to plot radius 0 points and still keep proper sized caps + //this is a way to get errorbars on lines without visible connecting dots + radius = err.radius != null? err.radius: radius; + + // upper cap + if (drawUpper) { + if (err.upperCap == '-'){ + if (err.err=='x') drawPath(ctx, [[upper,y - radius],[upper,y + radius]] ); + else drawPath(ctx, [[x - radius,upper],[x + radius,upper]] ); + } else if ($.isFunction(err.upperCap)){ + if (err.err=='x') err.upperCap(ctx, upper, y, radius); + else err.upperCap(ctx, x, upper, radius); + } + } + // lower cap + if (drawLower) { + if (err.lowerCap == '-'){ + if (err.err=='x') drawPath(ctx, [[lower,y - radius],[lower,y + radius]] ); + else drawPath(ctx, [[x - radius,lower],[x + radius,lower]] ); + } else if ($.isFunction(err.lowerCap)){ + if (err.err=='x') err.lowerCap(ctx, lower, y, radius); + else err.lowerCap(ctx, x, lower, radius); + } + } + } + + function drawPath(ctx, pts){ + ctx.beginPath(); + ctx.moveTo(pts[0][0], pts[0][1]); + for (var p=1; p < pts.length; p++) + ctx.lineTo(pts[p][0], pts[p][1]); + ctx.stroke(); + } + + function draw(plot, ctx){ + var plotOffset = plot.getPlotOffset(); + + ctx.save(); + ctx.translate(plotOffset.left, plotOffset.top); + $.each(plot.getData(), function (i, s) { + if (s.points.errorbars && (s.points.xerr.show || s.points.yerr.show)) + drawSeriesErrors(plot, ctx, s); + }); + ctx.restore(); + } + + function init(plot) { + plot.hooks.processRawData.push(processRawData); + plot.hooks.draw.push(draw); + } + + $.plot.plugins.push({ + init: init, + options: options, + name: 'errorbars', + version: '1.0' + }); +})(jQuery); diff --git a/pandora_console/include/graphs/flot/jquery.flot.errorbars.min.js b/pandora_console/include/graphs/flot/jquery.flot.errorbars.min.js new file mode 100644 index 0000000000..aa79f541a6 --- /dev/null +++ b/pandora_console/include/graphs/flot/jquery.flot.errorbars.min.js @@ -0,0 +1,7 @@ +/* Javascript plotting library for jQuery, version 0.8.3. + +Copyright (c) 2007-2014 IOLA and Ole Laursen. +Licensed under the MIT license. + +*/ +(function($){var options={series:{points:{errorbars:null,xerr:{err:"x",show:null,asymmetric:null,upperCap:null,lowerCap:null,color:null,radius:null},yerr:{err:"y",show:null,asymmetric:null,upperCap:null,lowerCap:null,color:null,radius:null}}}};function processRawData(plot,series,data,datapoints){if(!series.points.errorbars)return;var format=[{x:true,number:true,required:true},{y:true,number:true,required:true}];var errors=series.points.errorbars;if(errors=="x"||errors=="xy"){if(series.points.xerr.asymmetric){format.push({x:true,number:true,required:true});format.push({x:true,number:true,required:true})}else format.push({x:true,number:true,required:true})}if(errors=="y"||errors=="xy"){if(series.points.yerr.asymmetric){format.push({y:true,number:true,required:true});format.push({y:true,number:true,required:true})}else format.push({y:true,number:true,required:true})}datapoints.format=format}function parseErrors(series,i){var points=series.datapoints.points;var exl=null,exu=null,eyl=null,eyu=null;var xerr=series.points.xerr,yerr=series.points.yerr;var eb=series.points.errorbars;if(eb=="x"||eb=="xy"){if(xerr.asymmetric){exl=points[i+2];exu=points[i+3];if(eb=="xy")if(yerr.asymmetric){eyl=points[i+4];eyu=points[i+5]}else eyl=points[i+4]}else{exl=points[i+2];if(eb=="xy")if(yerr.asymmetric){eyl=points[i+3];eyu=points[i+4]}else eyl=points[i+3]}}else if(eb=="y")if(yerr.asymmetric){eyl=points[i+2];eyu=points[i+3]}else eyl=points[i+2];if(exu==null)exu=exl;if(eyu==null)eyu=eyl;var errRanges=[exl,exu,eyl,eyu];if(!xerr.show){errRanges[0]=null;errRanges[1]=null}if(!yerr.show){errRanges[2]=null;errRanges[3]=null}return errRanges}function drawSeriesErrors(plot,ctx,s){var points=s.datapoints.points,ps=s.datapoints.pointsize,ax=[s.xaxis,s.yaxis],radius=s.points.radius,err=[s.points.xerr,s.points.yerr];var invertX=false;if(ax[0].p2c(ax[0].max)ax[1].max||yax[0].max)continue;if(err[e].err=="y")if(x>ax[0].max||xax[1].max)continue;var drawUpper=true,drawLower=true;if(upper>minmax[1]){drawUpper=false;upper=minmax[1]}if(lower0&&sw>0){var w=sw/2;ctx.lineWidth=w;ctx.strokeStyle="rgba(0,0,0,0.1)";drawError(ctx,err[e],x,y,upper,lower,drawUpper,drawLower,radius,w+w/2,minmax);ctx.strokeStyle="rgba(0,0,0,0.2)";drawError(ctx,err[e],x,y,upper,lower,drawUpper,drawLower,radius,w/2,minmax)}ctx.strokeStyle=err[e].color?err[e].color:s.color;ctx.lineWidth=lw;drawError(ctx,err[e],x,y,upper,lower,drawUpper,drawLower,radius,0,minmax)}}}}function drawError(ctx,err,x,y,upper,lower,drawUpper,drawLower,radius,offset,minmax){y+=offset;upper+=offset;lower+=offset;if(err.err=="x"){if(upper>x+radius)drawPath(ctx,[[upper,y],[Math.max(x+radius,minmax[0]),y]]);else drawUpper=false;if(lowery+radius)drawPath(ctx,[[x,Math.max(y+radius,minmax[1])],[x,lower]]);else drawLower=false}radius=err.radius!=null?err.radius:radius;if(drawUpper){if(err.upperCap=="-"){if(err.err=="x")drawPath(ctx,[[upper,y-radius],[upper,y+radius]]);else drawPath(ctx,[[x-radius,upper],[x+radius,upper]])}else if($.isFunction(err.upperCap)){if(err.err=="x")err.upperCap(ctx,upper,y,radius);else err.upperCap(ctx,x,upper,radius)}}if(drawLower){if(err.lowerCap=="-"){if(err.err=="x")drawPath(ctx,[[lower,y-radius],[lower,y+radius]]);else drawPath(ctx,[[x-radius,lower],[x+radius,lower]])}else if($.isFunction(err.lowerCap)){if(err.err=="x")err.lowerCap(ctx,lower,y,radius);else err.lowerCap(ctx,x,lower,radius)}}}function drawPath(ctx,pts){ctx.beginPath();ctx.moveTo(pts[0][0],pts[0][1]);for(var p=1;p", "g"), ""); - clean_label = clean_label.replace( new RegExp(";", "g"), ""); - result.data.push([date, value, clean_label]); + date_format = (d.getDate() <10?'0':'') + d.getDate() + " " + + monthNames[d.getMonth()] + " " + + d.getFullYear() + " " + + (d.getHours()<10?'0':'') + d.getHours() + ":" + + (d.getMinutes()<10?'0':'') + d.getMinutes() + ":" + + (d.getSeconds()<10?'0':'') + d.getSeconds(); + + var date = date_format; + + var value = item[1]; + + var clean_label = plot.getOptions().export.labels_long[dataObject.label]; + clean_label = clean_label.replace( new RegExp(" ", "g"), " "); + result.data.push([timestap, date, value, clean_label]); }); } /* [ @@ -203,10 +213,9 @@ } catch (e) { alert('There was an error exporting the data'); - console.log(e); } } - + plot.exportDataJSON = function (args) { //amount = plot.getOptions().export.type, //options = options || {}; @@ -394,11 +403,10 @@ } catch (e) { alert('There was an error exporting the data'); - console.log(e); } } } - + $.plot.plugins.push({ init: init, options: options, diff --git a/pandora_console/include/graphs/flot/jquery.flot.fillbetween.js b/pandora_console/include/graphs/flot/jquery.flot.fillbetween.js new file mode 100644 index 0000000000..18b15d26db --- /dev/null +++ b/pandora_console/include/graphs/flot/jquery.flot.fillbetween.js @@ -0,0 +1,226 @@ +/* Flot plugin for computing bottoms for filled line and bar charts. + +Copyright (c) 2007-2014 IOLA and Ole Laursen. +Licensed under the MIT license. + +The case: you've got two series that you want to fill the area between. In Flot +terms, you need to use one as the fill bottom of the other. You can specify the +bottom of each data point as the third coordinate manually, or you can use this +plugin to compute it for you. + +In order to name the other series, you need to give it an id, like this: + + var dataset = [ + { data: [ ... ], id: "foo" } , // use default bottom + { data: [ ... ], fillBetween: "foo" }, // use first dataset as bottom + ]; + + $.plot($("#placeholder"), dataset, { lines: { show: true, fill: true }}); + +As a convenience, if the id given is a number that doesn't appear as an id in +the series, it is interpreted as the index in the array instead (so fillBetween: +0 can also mean the first series). + +Internally, the plugin modifies the datapoints in each series. For line series, +extra data points might be inserted through interpolation. Note that at points +where the bottom line is not defined (due to a null point or start/end of line), +the current line will show a gap too. The algorithm comes from the +jquery.flot.stack.js plugin, possibly some code could be shared. + +*/ + +(function ( $ ) { + + var options = { + series: { + fillBetween: null // or number + } + }; + + function init( plot ) { + + function findBottomSeries( s, allseries ) { + + var i; + + for ( i = 0; i < allseries.length; ++i ) { + if ( allseries[ i ].id === s.fillBetween ) { + return allseries[ i ]; + } + } + + if ( typeof s.fillBetween === "number" ) { + if ( s.fillBetween < 0 || s.fillBetween >= allseries.length ) { + return null; + } + return allseries[ s.fillBetween ]; + } + + return null; + } + + function computeFillBottoms( plot, s, datapoints ) { + + if ( s.fillBetween == null ) { + return; + } + + var other = findBottomSeries( s, plot.getData() ); + + if ( !other ) { + return; + } + + var ps = datapoints.pointsize, + points = datapoints.points, + otherps = other.datapoints.pointsize, + otherpoints = other.datapoints.points, + newpoints = [], + px, py, intery, qx, qy, bottom, + withlines = s.lines.show, + withbottom = ps > 2 && datapoints.format[2].y, + withsteps = withlines && s.lines.steps, + fromgap = true, + i = 0, + j = 0, + l, m; + + while ( true ) { + + if ( i >= points.length ) { + break; + } + + l = newpoints.length; + + if ( points[ i ] == null ) { + + // copy gaps + + for ( m = 0; m < ps; ++m ) { + newpoints.push( points[ i + m ] ); + } + + i += ps; + + } else if ( j >= otherpoints.length ) { + + // for lines, we can't use the rest of the points + + if ( !withlines ) { + for ( m = 0; m < ps; ++m ) { + newpoints.push( points[ i + m ] ); + } + } + + i += ps; + + } else if ( otherpoints[ j ] == null ) { + + // oops, got a gap + + for ( m = 0; m < ps; ++m ) { + newpoints.push( null ); + } + + fromgap = true; + j += otherps; + + } else { + + // cases where we actually got two points + + px = points[ i ]; + py = points[ i + 1 ]; + qx = otherpoints[ j ]; + qy = otherpoints[ j + 1 ]; + bottom = 0; + + if ( px === qx ) { + + for ( m = 0; m < ps; ++m ) { + newpoints.push( points[ i + m ] ); + } + + //newpoints[ l + 1 ] += qy; + bottom = qy; + + i += ps; + j += otherps; + + } else if ( px > qx ) { + + // we got past point below, might need to + // insert interpolated extra point + + if ( withlines && i > 0 && points[ i - ps ] != null ) { + intery = py + ( points[ i - ps + 1 ] - py ) * ( qx - px ) / ( points[ i - ps ] - px ); + newpoints.push( qx ); + newpoints.push( intery ); + for ( m = 2; m < ps; ++m ) { + newpoints.push( points[ i + m ] ); + } + bottom = qy; + } + + j += otherps; + + } else { // px < qx + + // if we come from a gap, we just skip this point + + if ( fromgap && withlines ) { + i += ps; + continue; + } + + for ( m = 0; m < ps; ++m ) { + newpoints.push( points[ i + m ] ); + } + + // we might be able to interpolate a point below, + // this can give us a better y + + if ( withlines && j > 0 && otherpoints[ j - otherps ] != null ) { + bottom = qy + ( otherpoints[ j - otherps + 1 ] - qy ) * ( px - qx ) / ( otherpoints[ j - otherps ] - qx ); + } + + //newpoints[l + 1] += bottom; + + i += ps; + } + + fromgap = false; + + if ( l !== newpoints.length && withbottom ) { + newpoints[ l + 2 ] = bottom; + } + } + + // maintain the line steps invariant + + if ( withsteps && l !== newpoints.length && l > 0 && + newpoints[ l ] !== null && + newpoints[ l ] !== newpoints[ l - ps ] && + newpoints[ l + 1 ] !== newpoints[ l - ps + 1 ] ) { + for (m = 0; m < ps; ++m) { + newpoints[ l + ps + m ] = newpoints[ l + m ]; + } + newpoints[ l + 1 ] = newpoints[ l - ps + 1 ]; + } + } + + datapoints.points = newpoints; + } + + plot.hooks.processDatapoints.push( computeFillBottoms ); + } + + $.plot.plugins.push({ + init: init, + options: options, + name: "fillbetween", + version: "1.0" + }); + +})(jQuery); diff --git a/pandora_console/include/graphs/flot/jquery.flot.fillbetween.min.js b/pandora_console/include/graphs/flot/jquery.flot.fillbetween.min.js new file mode 100644 index 0000000000..464bf72c84 --- /dev/null +++ b/pandora_console/include/graphs/flot/jquery.flot.fillbetween.min.js @@ -0,0 +1,7 @@ +/* Javascript plotting library for jQuery, version 0.8.3. + +Copyright (c) 2007-2014 IOLA and Ole Laursen. +Licensed under the MIT license. + +*/ +(function($){var options={series:{fillBetween:null}};function init(plot){function findBottomSeries(s,allseries){var i;for(i=0;i=allseries.length){return null}return allseries[s.fillBetween]}return null}function computeFillBottoms(plot,s,datapoints){if(s.fillBetween==null){return}var other=findBottomSeries(s,plot.getData());if(!other){return}var ps=datapoints.pointsize,points=datapoints.points,otherps=other.datapoints.pointsize,otherpoints=other.datapoints.points,newpoints=[],px,py,intery,qx,qy,bottom,withlines=s.lines.show,withbottom=ps>2&&datapoints.format[2].y,withsteps=withlines&&s.lines.steps,fromgap=true,i=0,j=0,l,m;while(true){if(i>=points.length){break}l=newpoints.length;if(points[i]==null){for(m=0;m=otherpoints.length){if(!withlines){for(m=0;mqx){if(withlines&&i>0&&points[i-ps]!=null){intery=py+(points[i-ps+1]-py)*(qx-px)/(points[i-ps]-px);newpoints.push(qx);newpoints.push(intery);for(m=2;m0&&otherpoints[j-otherps]!=null){bottom=qy+(otherpoints[j-otherps+1]-qy)*(px-qx)/(otherpoints[j-otherps]-qx)}i+=ps}fromgap=false;if(l!==newpoints.length&&withbottom){newpoints[l+2]=bottom}}if(withsteps&&l!==newpoints.length&&l>0&&newpoints[l]!==null&&newpoints[l]!==newpoints[l-ps]&&newpoints[l+1]!==newpoints[l-ps+1]){for(m=0;m').load(handler).error(handler).attr('src', url); + }); + }; + + function drawSeries(plot, ctx, series) { + var plotOffset = plot.getPlotOffset(); + + if (!series.images || !series.images.show) + return; + + var points = series.datapoints.points, + ps = series.datapoints.pointsize; + + for (var i = 0; i < points.length; i += ps) { + var img = points[i], + x1 = points[i + 1], y1 = points[i + 2], + x2 = points[i + 3], y2 = points[i + 4], + xaxis = series.xaxis, yaxis = series.yaxis, + tmp; + + // actually we should check img.complete, but it + // appears to be a somewhat unreliable indicator in + // IE6 (false even after load event) + if (!img || img.width <= 0 || img.height <= 0) + continue; + + if (x1 > x2) { + tmp = x2; + x2 = x1; + x1 = tmp; + } + if (y1 > y2) { + tmp = y2; + y2 = y1; + y1 = tmp; + } + + // if the anchor is at the center of the pixel, expand the + // image by 1/2 pixel in each direction + if (series.images.anchor == "center") { + tmp = 0.5 * (x2-x1) / (img.width - 1); + x1 -= tmp; + x2 += tmp; + tmp = 0.5 * (y2-y1) / (img.height - 1); + y1 -= tmp; + y2 += tmp; + } + + // clip + if (x1 == x2 || y1 == y2 || + x1 >= xaxis.max || x2 <= xaxis.min || + y1 >= yaxis.max || y2 <= yaxis.min) + continue; + + var sx1 = 0, sy1 = 0, sx2 = img.width, sy2 = img.height; + if (x1 < xaxis.min) { + sx1 += (sx2 - sx1) * (xaxis.min - x1) / (x2 - x1); + x1 = xaxis.min; + } + + if (x2 > xaxis.max) { + sx2 += (sx2 - sx1) * (xaxis.max - x2) / (x2 - x1); + x2 = xaxis.max; + } + + if (y1 < yaxis.min) { + sy2 += (sy1 - sy2) * (yaxis.min - y1) / (y2 - y1); + y1 = yaxis.min; + } + + if (y2 > yaxis.max) { + sy1 += (sy1 - sy2) * (yaxis.max - y2) / (y2 - y1); + y2 = yaxis.max; + } + + x1 = xaxis.p2c(x1); + x2 = xaxis.p2c(x2); + y1 = yaxis.p2c(y1); + y2 = yaxis.p2c(y2); + + // the transformation may have swapped us + if (x1 > x2) { + tmp = x2; + x2 = x1; + x1 = tmp; + } + if (y1 > y2) { + tmp = y2; + y2 = y1; + y1 = tmp; + } + + tmp = ctx.globalAlpha; + ctx.globalAlpha *= series.images.alpha; + ctx.drawImage(img, + sx1, sy1, sx2 - sx1, sy2 - sy1, + x1 + plotOffset.left, y1 + plotOffset.top, + x2 - x1, y2 - y1); + ctx.globalAlpha = tmp; + } + } + + function processRawData(plot, series, data, datapoints) { + if (!series.images.show) + return; + + // format is Image, x1, y1, x2, y2 (opposite corners) + datapoints.format = [ + { required: true }, + { x: true, number: true, required: true }, + { y: true, number: true, required: true }, + { x: true, number: true, required: true }, + { y: true, number: true, required: true } + ]; + } + + function init(plot) { + plot.hooks.processRawData.push(processRawData); + plot.hooks.drawSeries.push(drawSeries); + } + + $.plot.plugins.push({ + init: init, + options: options, + name: 'image', + version: '1.1' + }); +})(jQuery); diff --git a/pandora_console/include/graphs/flot/jquery.flot.image.min.js b/pandora_console/include/graphs/flot/jquery.flot.image.min.js new file mode 100644 index 0000000000..09df132f00 --- /dev/null +++ b/pandora_console/include/graphs/flot/jquery.flot.image.min.js @@ -0,0 +1,7 @@ +/* Javascript plotting library for jQuery, version 0.8.3. + +Copyright (c) 2007-2014 IOLA and Ole Laursen. +Licensed under the MIT license. + +*/ +(function($){var options={series:{images:{show:false,alpha:1,anchor:"corner"}}};$.plot.image={};$.plot.image.loadDataImages=function(series,options,callback){var urls=[],points=[];var defaultShow=options.series.images.show;$.each(series,function(i,s){if(!(defaultShow||s.images.show))return;if(s.data)s=s.data;$.each(s,function(i,p){if(typeof p[0]=="string"){urls.push(p[0]);points.push(p)}})});$.plot.image.load(urls,function(loadedImages){$.each(points,function(i,p){var url=p[0];if(loadedImages[url])p[0]=loadedImages[url]});callback()})};$.plot.image.load=function(urls,callback){var missing=urls.length,loaded={};if(missing==0)callback({});$.each(urls,function(i,url){var handler=function(){--missing;loaded[url]=this;if(missing==0)callback(loaded)};$("").load(handler).error(handler).attr("src",url)})};function drawSeries(plot,ctx,series){var plotOffset=plot.getPlotOffset();if(!series.images||!series.images.show)return;var points=series.datapoints.points,ps=series.datapoints.pointsize;for(var i=0;ix2){tmp=x2;x2=x1;x1=tmp}if(y1>y2){tmp=y2;y2=y1;y1=tmp}if(series.images.anchor=="center"){tmp=.5*(x2-x1)/(img.width-1);x1-=tmp;x2+=tmp;tmp=.5*(y2-y1)/(img.height-1);y1-=tmp;y2+=tmp}if(x1==x2||y1==y2||x1>=xaxis.max||x2<=xaxis.min||y1>=yaxis.max||y2<=yaxis.min)continue;var sx1=0,sy1=0,sx2=img.width,sy2=img.height;if(x1xaxis.max){sx2+=(sx2-sx1)*(xaxis.max-x2)/(x2-x1);x2=xaxis.max}if(y1yaxis.max){sy1+=(sy1-sy2)*(yaxis.max-y2)/(y2-y1);y2=yaxis.max}x1=xaxis.p2c(x1);x2=xaxis.p2c(x2);y1=yaxis.p2c(y1);y2=yaxis.p2c(y2);if(x1>x2){tmp=x2;x2=x1;x1=tmp}if(y1>y2){tmp=y2;y2=y1;y1=tmp}tmp=ctx.globalAlpha;ctx.globalAlpha*=series.images.alpha;ctx.drawImage(img,sx1,sy1,sx2-sx1,sy2-sy1,x1+plotOffset.left,y1+plotOffset.top,x2-x1,y2-y1);ctx.globalAlpha=tmp}}function processRawData(plot,series,data,datapoints){if(!series.images.show)return;datapoints.format=[{required:true},{x:true,number:true,required:true},{y:true,number:true,required:true},{x:true,number:true,required:true},{y:true,number:true,required:true}]}function init(plot){plot.hooks.processRawData.push(processRawData);plot.hooks.drawSeries.push(drawSeries)}$.plot.plugins.push({init:init,options:options,name:"image",version:"1.1"})})(jQuery); \ No newline at end of file diff --git a/pandora_console/include/graphs/flot/jquery.flot.js b/pandora_console/include/graphs/flot/jquery.flot.js new file mode 100644 index 0000000000..39f3e4cf3e --- /dev/null +++ b/pandora_console/include/graphs/flot/jquery.flot.js @@ -0,0 +1,3168 @@ +/* Javascript plotting library for jQuery, version 0.8.3. + +Copyright (c) 2007-2014 IOLA and Ole Laursen. +Licensed under the MIT license. + +*/ + +// first an inline dependency, jquery.colorhelpers.js, we inline it here +// for convenience + +/* Plugin for jQuery for working with colors. + * + * Version 1.1. + * + * Inspiration from jQuery color animation plugin by John Resig. + * + * Released under the MIT license by Ole Laursen, October 2009. + * + * Examples: + * + * $.color.parse("#fff").scale('rgb', 0.25).add('a', -0.5).toString() + * var c = $.color.extract($("#mydiv"), 'background-color'); + * console.log(c.r, c.g, c.b, c.a); + * $.color.make(100, 50, 25, 0.4).toString() // returns "rgba(100,50,25,0.4)" + * + * Note that .scale() and .add() return the same modified object + * instead of making a new one. + * + * V. 1.1: Fix error handling so e.g. parsing an empty string does + * produce a color rather than just crashing. + */ +(function($){$.color={};$.color.make=function(r,g,b,a){var o={};o.r=r||0;o.g=g||0;o.b=b||0;o.a=a!=null?a:1;o.add=function(c,d){for(var i=0;i=1){return"rgb("+[o.r,o.g,o.b].join(",")+")"}else{return"rgba("+[o.r,o.g,o.b,o.a].join(",")+")"}};o.normalize=function(){function clamp(min,value,max){return valuemax?max:value}o.r=clamp(0,parseInt(o.r),255);o.g=clamp(0,parseInt(o.g),255);o.b=clamp(0,parseInt(o.b),255);o.a=clamp(0,o.a,1);return o};o.clone=function(){return $.color.make(o.r,o.b,o.g,o.a)};return o.normalize()};$.color.extract=function(elem,css){var c;do{c=elem.css(css).toLowerCase();if(c!=""&&c!="transparent")break;elem=elem.parent()}while(elem.length&&!$.nodeName(elem.get(0),"body"));if(c=="rgba(0, 0, 0, 0)")c="transparent";return $.color.parse(c)};$.color.parse=function(str){var res,m=$.color.make;if(res=/rgb\(\s*([0-9]{1,3})\s*,\s*([0-9]{1,3})\s*,\s*([0-9]{1,3})\s*\)/.exec(str))return m(parseInt(res[1],10),parseInt(res[2],10),parseInt(res[3],10));if(res=/rgba\(\s*([0-9]{1,3})\s*,\s*([0-9]{1,3})\s*,\s*([0-9]{1,3})\s*,\s*([0-9]+(?:\.[0-9]+)?)\s*\)/.exec(str))return m(parseInt(res[1],10),parseInt(res[2],10),parseInt(res[3],10),parseFloat(res[4]));if(res=/rgb\(\s*([0-9]+(?:\.[0-9]+)?)\%\s*,\s*([0-9]+(?:\.[0-9]+)?)\%\s*,\s*([0-9]+(?:\.[0-9]+)?)\%\s*\)/.exec(str))return m(parseFloat(res[1])*2.55,parseFloat(res[2])*2.55,parseFloat(res[3])*2.55);if(res=/rgba\(\s*([0-9]+(?:\.[0-9]+)?)\%\s*,\s*([0-9]+(?:\.[0-9]+)?)\%\s*,\s*([0-9]+(?:\.[0-9]+)?)\%\s*,\s*([0-9]+(?:\.[0-9]+)?)\s*\)/.exec(str))return m(parseFloat(res[1])*2.55,parseFloat(res[2])*2.55,parseFloat(res[3])*2.55,parseFloat(res[4]));if(res=/#([a-fA-F0-9]{2})([a-fA-F0-9]{2})([a-fA-F0-9]{2})/.exec(str))return m(parseInt(res[1],16),parseInt(res[2],16),parseInt(res[3],16));if(res=/#([a-fA-F0-9])([a-fA-F0-9])([a-fA-F0-9])/.exec(str))return m(parseInt(res[1]+res[1],16),parseInt(res[2]+res[2],16),parseInt(res[3]+res[3],16));var name=$.trim(str).toLowerCase();if(name=="transparent")return m(255,255,255,0);else{res=lookupColors[name]||[0,0,0];return m(res[0],res[1],res[2])}};var lookupColors={aqua:[0,255,255],azure:[240,255,255],beige:[245,245,220],black:[0,0,0],blue:[0,0,255],brown:[165,42,42],cyan:[0,255,255],darkblue:[0,0,139],darkcyan:[0,139,139],darkgrey:[169,169,169],darkgreen:[0,100,0],darkkhaki:[189,183,107],darkmagenta:[139,0,139],darkolivegreen:[85,107,47],darkorange:[255,140,0],darkorchid:[153,50,204],darkred:[139,0,0],darksalmon:[233,150,122],darkviolet:[148,0,211],fuchsia:[255,0,255],gold:[255,215,0],green:[0,128,0],indigo:[75,0,130],khaki:[240,230,140],lightblue:[173,216,230],lightcyan:[224,255,255],lightgreen:[144,238,144],lightgrey:[211,211,211],lightpink:[255,182,193],lightyellow:[255,255,224],lime:[0,255,0],magenta:[255,0,255],maroon:[128,0,0],navy:[0,0,128],olive:[128,128,0],orange:[255,165,0],pink:[255,192,203],purple:[128,0,128],violet:[128,0,128],red:[255,0,0],silver:[192,192,192],white:[255,255,255],yellow:[255,255,0]}})(jQuery); + +// the actual Flot code +(function($) { + + // Cache the prototype hasOwnProperty for faster access + + var hasOwnProperty = Object.prototype.hasOwnProperty; + + // A shim to provide 'detach' to jQuery versions prior to 1.4. Using a DOM + // operation produces the same effect as detach, i.e. removing the element + // without touching its jQuery data. + + // Do not merge this into Flot 0.9, since it requires jQuery 1.4.4+. + + if (!$.fn.detach) { + $.fn.detach = function() { + return this.each(function() { + if (this.parentNode) { + this.parentNode.removeChild( this ); + } + }); + }; + } + + /////////////////////////////////////////////////////////////////////////// + // The Canvas object is a wrapper around an HTML5 tag. + // + // @constructor + // @param {string} cls List of classes to apply to the canvas. + // @param {element} container Element onto which to append the canvas. + // + // Requiring a container is a little iffy, but unfortunately canvas + // operations don't work unless the canvas is attached to the DOM. + + function Canvas(cls, container) { + + var element = container.children("." + cls)[0]; + + if (element == null) { + + element = document.createElement("canvas"); + element.className = cls; + + $(element).css({ direction: "ltr", position: "absolute", left: 0, top: 0 }) + .appendTo(container); + + // If HTML5 Canvas isn't available, fall back to [Ex|Flash]canvas + + if (!element.getContext) { + if (window.G_vmlCanvasManager) { + element = window.G_vmlCanvasManager.initElement(element); + } else { + throw new Error("Canvas is not available. If you're using IE with a fall-back such as Excanvas, then there's either a mistake in your conditional include, or the page has no DOCTYPE and is rendering in Quirks Mode."); + } + } + } + + this.element = element; + + var context = this.context = element.getContext("2d"); + + // Determine the screen's ratio of physical to device-independent + // pixels. This is the ratio between the canvas width that the browser + // advertises and the number of pixels actually present in that space. + + // The iPhone 4, for example, has a device-independent width of 320px, + // but its screen is actually 640px wide. It therefore has a pixel + // ratio of 2, while most normal devices have a ratio of 1. + + var devicePixelRatio = window.devicePixelRatio || 1, + backingStoreRatio = + context.webkitBackingStorePixelRatio || + context.mozBackingStorePixelRatio || + context.msBackingStorePixelRatio || + context.oBackingStorePixelRatio || + context.backingStorePixelRatio || 1; + + this.pixelRatio = devicePixelRatio / backingStoreRatio; + + // Size the canvas to match the internal dimensions of its container + + this.resize(container.width(), container.height()); + + // Collection of HTML div layers for text overlaid onto the canvas + + this.textContainer = null; + this.text = {}; + + // Cache of text fragments and metrics, so we can avoid expensively + // re-calculating them when the plot is re-rendered in a loop. + + this._textCache = {}; + } + + // Resizes the canvas to the given dimensions. + // + // @param {number} width New width of the canvas, in pixels. + // @param {number} width New height of the canvas, in pixels. + + Canvas.prototype.resize = function(width, height) { + + if (width <= 0 || height <= 0) { + throw new Error("Invalid dimensions for plot, width = " + width + ", height = " + height); + } + + var element = this.element, + context = this.context, + pixelRatio = this.pixelRatio; + + // Resize the canvas, increasing its density based on the display's + // pixel ratio; basically giving it more pixels without increasing the + // size of its element, to take advantage of the fact that retina + // displays have that many more pixels in the same advertised space. + + // Resizing should reset the state (excanvas seems to be buggy though) + + if (this.width != width) { + element.width = width * pixelRatio; + element.style.width = width + "px"; + this.width = width; + } + + if (this.height != height) { + element.height = height * pixelRatio; + element.style.height = height + "px"; + this.height = height; + } + + // Save the context, so we can reset in case we get replotted. The + // restore ensure that we're really back at the initial state, and + // should be safe even if we haven't saved the initial state yet. + + context.restore(); + context.save(); + + // Scale the coordinate space to match the display density; so even though we + // may have twice as many pixels, we still want lines and other drawing to + // appear at the same size; the extra pixels will just make them crisper. + + context.scale(pixelRatio, pixelRatio); + }; + + // Clears the entire canvas area, not including any overlaid HTML text + + Canvas.prototype.clear = function() { + this.context.clearRect(0, 0, this.width, this.height); + }; + + // Finishes rendering the canvas, including managing the text overlay. + + Canvas.prototype.render = function() { + + var cache = this._textCache; + + // For each text layer, add elements marked as active that haven't + // already been rendered, and remove those that are no longer active. + + for (var layerKey in cache) { + if (hasOwnProperty.call(cache, layerKey)) { + + var layer = this.getTextLayer(layerKey), + layerCache = cache[layerKey]; + + layer.hide(); + + for (var styleKey in layerCache) { + if (hasOwnProperty.call(layerCache, styleKey)) { + var styleCache = layerCache[styleKey]; + for (var key in styleCache) { + if (hasOwnProperty.call(styleCache, key)) { + + var positions = styleCache[key].positions; + + for (var i = 0, position; position = positions[i]; i++) { + if (position.active) { + if (!position.rendered) { + layer.append(position.element); + position.rendered = true; + } + } else { + positions.splice(i--, 1); + if (position.rendered) { + position.element.detach(); + } + } + } + + if (positions.length == 0) { + delete styleCache[key]; + } + } + } + } + } + + layer.show(); + } + } + }; + + // Creates (if necessary) and returns the text overlay container. + // + // @param {string} classes String of space-separated CSS classes used to + // uniquely identify the text layer. + // @return {object} The jQuery-wrapped text-layer div. + + Canvas.prototype.getTextLayer = function(classes) { + + var layer = this.text[classes]; + + // Create the text layer if it doesn't exist + + if (layer == null) { + + // Create the text layer container, if it doesn't exist + + if (this.textContainer == null) { + this.textContainer = $("
") + .css({ + position: "absolute", + top: 0, + left: 0, + bottom: 0, + right: 0, + 'font-size': "smaller", + color: "#545454" + }) + .insertAfter(this.element); + } + + layer = this.text[classes] = $("
") + .addClass(classes) + .css({ + position: "absolute", + top: 0, + left: 0, + bottom: 0, + right: 0 + }) + .appendTo(this.textContainer); + } + + return layer; + }; + + // Creates (if necessary) and returns a text info object. + // + // The object looks like this: + // + // { + // width: Width of the text's wrapper div. + // height: Height of the text's wrapper div. + // element: The jQuery-wrapped HTML div containing the text. + // positions: Array of positions at which this text is drawn. + // } + // + // The positions array contains objects that look like this: + // + // { + // active: Flag indicating whether the text should be visible. + // rendered: Flag indicating whether the text is currently visible. + // element: The jQuery-wrapped HTML div containing the text. + // x: X coordinate at which to draw the text. + // y: Y coordinate at which to draw the text. + // } + // + // Each position after the first receives a clone of the original element. + // + // The idea is that that the width, height, and general 'identity' of the + // text is constant no matter where it is placed; the placements are a + // secondary property. + // + // Canvas maintains a cache of recently-used text info objects; getTextInfo + // either returns the cached element or creates a new entry. + // + // @param {string} layer A string of space-separated CSS classes uniquely + // identifying the layer containing this text. + // @param {string} text Text string to retrieve info for. + // @param {(string|object)=} font Either a string of space-separated CSS + // classes or a font-spec object, defining the text's font and style. + // @param {number=} angle Angle at which to rotate the text, in degrees. + // Angle is currently unused, it will be implemented in the future. + // @param {number=} width Maximum width of the text before it wraps. + // @return {object} a text info object. + + Canvas.prototype.getTextInfo = function(layer, text, font, angle, width) { + + var textStyle, layerCache, styleCache, info; + + // Cast the value to a string, in case we were given a number or such + + text = "" + text; + + // If the font is a font-spec object, generate a CSS font definition + + if (typeof font === "object") { + textStyle = font.style + " " + font.variant + " " + font.weight + " " + font.size + "px/" + font.lineHeight + "px " + font.family; + } else { + textStyle = font; + } + + // Retrieve (or create) the cache for the text's layer and styles + + layerCache = this._textCache[layer]; + + if (layerCache == null) { + layerCache = this._textCache[layer] = {}; + } + + styleCache = layerCache[textStyle]; + + if (styleCache == null) { + styleCache = layerCache[textStyle] = {}; + } + + info = styleCache[text]; + + // If we can't find a matching element in our cache, create a new one + + if (info == null) { + + var element = $("
").html(text) + .css({ + position: "absolute", + 'max-width': width, + top: -9999 + }) + .appendTo(this.getTextLayer(layer)); + + if (typeof font === "object") { + element.css({ + font: textStyle, + color: font.color + }); + } else if (typeof font === "string") { + element.addClass(font); + } + + info = styleCache[text] = { + width: element.outerWidth(true), + height: element.outerHeight(true), + element: element, + positions: [] + }; + + element.detach(); + } + + return info; + }; + + // Adds a text string to the canvas text overlay. + // + // The text isn't drawn immediately; it is marked as rendering, which will + // result in its addition to the canvas on the next render pass. + // + // @param {string} layer A string of space-separated CSS classes uniquely + // identifying the layer containing this text. + // @param {number} x X coordinate at which to draw the text. + // @param {number} y Y coordinate at which to draw the text. + // @param {string} text Text string to draw. + // @param {(string|object)=} font Either a string of space-separated CSS + // classes or a font-spec object, defining the text's font and style. + // @param {number=} angle Angle at which to rotate the text, in degrees. + // Angle is currently unused, it will be implemented in the future. + // @param {number=} width Maximum width of the text before it wraps. + // @param {string=} halign Horizontal alignment of the text; either "left", + // "center" or "right". + // @param {string=} valign Vertical alignment of the text; either "top", + // "middle" or "bottom". + + Canvas.prototype.addText = function(layer, x, y, text, font, angle, width, halign, valign) { + + var info = this.getTextInfo(layer, text, font, angle, width), + positions = info.positions; + + // Tweak the div's position to match the text's alignment + + if (halign == "center") { + x -= info.width / 2; + } else if (halign == "right") { + x -= info.width; + } + + if (valign == "middle") { + y -= info.height / 2; + } else if (valign == "bottom") { + y -= info.height; + } + + // Determine whether this text already exists at this position. + // If so, mark it for inclusion in the next render pass. + + for (var i = 0, position; position = positions[i]; i++) { + if (position.x == x && position.y == y) { + position.active = true; + return; + } + } + + // If the text doesn't exist at this position, create a new entry + + // For the very first position we'll re-use the original element, + // while for subsequent ones we'll clone it. + + position = { + active: true, + rendered: false, + element: positions.length ? info.element.clone() : info.element, + x: x, + y: y + }; + + positions.push(position); + + // Move the element to its final position within the container + + position.element.css({ + top: Math.round(y), + left: Math.round(x), + 'text-align': halign // In case the text wraps + }); + }; + + // Removes one or more text strings from the canvas text overlay. + // + // If no parameters are given, all text within the layer is removed. + // + // Note that the text is not immediately removed; it is simply marked as + // inactive, which will result in its removal on the next render pass. + // This avoids the performance penalty for 'clear and redraw' behavior, + // where we potentially get rid of all text on a layer, but will likely + // add back most or all of it later, as when redrawing axes, for example. + // + // @param {string} layer A string of space-separated CSS classes uniquely + // identifying the layer containing this text. + // @param {number=} x X coordinate of the text. + // @param {number=} y Y coordinate of the text. + // @param {string=} text Text string to remove. + // @param {(string|object)=} font Either a string of space-separated CSS + // classes or a font-spec object, defining the text's font and style. + // @param {number=} angle Angle at which the text is rotated, in degrees. + // Angle is currently unused, it will be implemented in the future. + + Canvas.prototype.removeText = function(layer, x, y, text, font, angle) { + if (text == null) { + var layerCache = this._textCache[layer]; + if (layerCache != null) { + for (var styleKey in layerCache) { + if (hasOwnProperty.call(layerCache, styleKey)) { + var styleCache = layerCache[styleKey]; + for (var key in styleCache) { + if (hasOwnProperty.call(styleCache, key)) { + var positions = styleCache[key].positions; + for (var i = 0, position; position = positions[i]; i++) { + position.active = false; + } + } + } + } + } + } + } else { + var positions = this.getTextInfo(layer, text, font, angle).positions; + for (var i = 0, position; position = positions[i]; i++) { + if (position.x == x && position.y == y) { + position.active = false; + } + } + } + }; + + /////////////////////////////////////////////////////////////////////////// + // The top-level container for the entire plot. + + function Plot(placeholder, data_, options_, plugins) { + // data is on the form: + // [ series1, series2 ... ] + // where series is either just the data as [ [x1, y1], [x2, y2], ... ] + // or { data: [ [x1, y1], [x2, y2], ... ], label: "some label", ... } + + var series = [], + options = { + // the color theme used for graphs + colors: ["#edc240", "#afd8f8", "#cb4b4b", "#4da74d", "#9440ed"], + legend: { + show: true, + noColumns: 1, // number of colums in legend table + labelFormatter: null, // fn: string -> string + labelBoxBorderColor: "#ccc", // border color for the little label boxes + container: null, // container (as jQuery object) to put legend in, null means default on top of graph + position: "ne", // position of default legend container within plot + margin: 5, // distance from grid edge to default legend container within plot + backgroundColor: null, // null means auto-detect + backgroundOpacity: 0.85, // set to 0 to avoid background + sorted: null // default to no legend sorting + }, + xaxis: { + show: null, // null = auto-detect, true = always, false = never + position: "bottom", // or "top" + mode: null, // null or "time" + font: null, // null (derived from CSS in placeholder) or object like { size: 11, lineHeight: 13, style: "italic", weight: "bold", family: "sans-serif", variant: "small-caps" } + color: null, // base color, labels, ticks + tickColor: null, // possibly different color of ticks, e.g. "rgba(0,0,0,0.15)" + transform: null, // null or f: number -> number to transform axis + inverseTransform: null, // if transform is set, this should be the inverse function + min: null, // min. value to show, null means set automatically + max: null, // max. value to show, null means set automatically + autoscaleMargin: null, // margin in % to add if auto-setting min/max + ticks: null, // either [1, 3] or [[1, "a"], 3] or (fn: axis info -> ticks) or app. number of ticks for auto-ticks + tickFormatter: null, // fn: number -> string + labelWidth: null, // size of tick labels in pixels + labelHeight: null, + reserveSpace: null, // whether to reserve space even if axis isn't shown + tickLength: null, // size in pixels of ticks, or "full" for whole line + alignTicksWithAxis: null, // axis number or null for no sync + tickDecimals: null, // no. of decimals, null means auto + tickSize: null, // number or [number, "unit"] + minTickSize: null // number or [number, "unit"] + }, + yaxis: { + autoscaleMargin: 0.02, + position: "left" // or "right" + }, + xaxes: [], + yaxes: [], + series: { + points: { + show: false, + radius: 3, + lineWidth: 2, // in pixels + fill: true, + fillColor: "#ffffff", + symbol: "circle" // or callback + }, + lines: { + // we don't put in show: false so we can see + // whether lines were actively disabled + lineWidth: 2, // in pixels + fill: false, + fillColor: null, + steps: false + // Omit 'zero', so we can later default its value to + // match that of the 'fill' option. + }, + bars: { + show: false, + lineWidth: 2, // in pixels + barWidth: 1, // in units of the x axis + fill: true, + fillColor: null, + align: "left", // "left", "right", or "center" + horizontal: false, + zero: true + }, + shadowSize: 3, + highlightColor: null + }, + grid: { + show: true, + aboveData: false, + color: "#545454", // primary color used for outline and labels + backgroundColor: null, // null for transparent, else color + borderColor: null, // set if different from the grid color + tickColor: null, // color for the ticks, e.g. "rgba(0,0,0,0.15)" + margin: 0, // distance from the canvas edge to the grid + labelMargin: 5, // in pixels + axisMargin: 8, // in pixels + borderWidth: 2, // in pixels + minBorderMargin: null, // in pixels, null means taken from points radius + markings: null, // array of ranges or fn: axes -> array of ranges + markingsColor: "#f4f4f4", + markingsLineWidth: 2, + // interactive stuff + clickable: false, + hoverable: false, + autoHighlight: true, // highlight in case mouse is near + mouseActiveRadius: 10 // how far the mouse can be away to activate an item + }, + interaction: { + redrawOverlayInterval: 1000/60 // time between updates, -1 means in same flow + }, + hooks: {} + }, + surface = null, // the canvas for the plot itself + overlay = null, // canvas for interactive stuff on top of plot + eventHolder = null, // jQuery object that events should be bound to + ctx = null, octx = null, + xaxes = [], yaxes = [], + plotOffset = { left: 0, right: 0, top: 0, bottom: 0}, + plotWidth = 0, plotHeight = 0, + hooks = { + processOptions: [], + processRawData: [], + processDatapoints: [], + processOffset: [], + drawBackground: [], + drawSeries: [], + draw: [], + bindEvents: [], + drawOverlay: [], + shutdown: [] + }, + plot = this; + + // public functions + plot.setData = setData; + plot.setupGrid = setupGrid; + plot.draw = draw; + plot.getPlaceholder = function() { return placeholder; }; + plot.getCanvas = function() { return surface.element; }; + plot.getPlotOffset = function() { return plotOffset; }; + plot.width = function () { return plotWidth; }; + plot.height = function () { return plotHeight; }; + plot.offset = function () { + var o = eventHolder.offset(); + o.left += plotOffset.left; + o.top += plotOffset.top; + return o; + }; + plot.getData = function () { return series; }; + plot.getAxes = function () { + var res = {}, i; + $.each(xaxes.concat(yaxes), function (_, axis) { + if (axis) + res[axis.direction + (axis.n != 1 ? axis.n : "") + "axis"] = axis; + }); + return res; + }; + plot.getXAxes = function () { return xaxes; }; + plot.getYAxes = function () { return yaxes; }; + plot.c2p = canvasToAxisCoords; + plot.p2c = axisToCanvasCoords; + plot.getOptions = function () { return options; }; + plot.highlight = highlight; + plot.unhighlight = unhighlight; + plot.triggerRedrawOverlay = triggerRedrawOverlay; + plot.pointOffset = function(point) { + return { + left: parseInt(xaxes[axisNumber(point, "x") - 1].p2c(+point.x) + plotOffset.left, 10), + top: parseInt(yaxes[axisNumber(point, "y") - 1].p2c(+point.y) + plotOffset.top, 10) + }; + }; + plot.shutdown = shutdown; + plot.destroy = function () { + shutdown(); + placeholder.removeData("plot").empty(); + + series = []; + options = null; + surface = null; + overlay = null; + eventHolder = null; + ctx = null; + octx = null; + xaxes = []; + yaxes = []; + hooks = null; + highlights = []; + plot = null; + }; + plot.resize = function () { + var width = placeholder.width(), + height = placeholder.height(); + surface.resize(width, height); + overlay.resize(width, height); + }; + + // public attributes + plot.hooks = hooks; + + // initialize + initPlugins(plot); + parseOptions(options_); + setupCanvases(); + setData(data_); + setupGrid(); + draw(); + bindEvents(); + + + function executeHooks(hook, args) { + args = [plot].concat(args); + for (var i = 0; i < hook.length; ++i) + hook[i].apply(this, args); + } + + function initPlugins() { + + // References to key classes, allowing plugins to modify them + + var classes = { + Canvas: Canvas + }; + + for (var i = 0; i < plugins.length; ++i) { + var p = plugins[i]; + p.init(plot, classes); + if (p.options) + $.extend(true, options, p.options); + } + } + + function parseOptions(opts) { + + $.extend(true, options, opts); + + // $.extend merges arrays, rather than replacing them. When less + // colors are provided than the size of the default palette, we + // end up with those colors plus the remaining defaults, which is + // not expected behavior; avoid it by replacing them here. + + if (opts && opts.colors) { + options.colors = opts.colors; + } + + if (options.xaxis.color == null) + options.xaxis.color = $.color.parse(options.grid.color).scale('a', 0.22).toString(); + if (options.yaxis.color == null) + options.yaxis.color = $.color.parse(options.grid.color).scale('a', 0.22).toString(); + + if (options.xaxis.tickColor == null) // grid.tickColor for back-compatibility + options.xaxis.tickColor = options.grid.tickColor || options.xaxis.color; + if (options.yaxis.tickColor == null) // grid.tickColor for back-compatibility + options.yaxis.tickColor = options.grid.tickColor || options.yaxis.color; + + if (options.grid.borderColor == null) + options.grid.borderColor = options.grid.color; + if (options.grid.tickColor == null) + options.grid.tickColor = $.color.parse(options.grid.color).scale('a', 0.22).toString(); + + // Fill in defaults for axis options, including any unspecified + // font-spec fields, if a font-spec was provided. + + // If no x/y axis options were provided, create one of each anyway, + // since the rest of the code assumes that they exist. + + var i, axisOptions, axisCount, + fontSize = placeholder.css("font-size"), + fontSizeDefault = fontSize ? +fontSize.replace("px", "") : 13, + fontDefaults = { + style: placeholder.css("font-style"), + size: Math.round(0.8 * fontSizeDefault), + variant: placeholder.css("font-variant"), + weight: placeholder.css("font-weight"), + family: placeholder.css("font-family") + }; + + axisCount = options.xaxes.length || 1; + for (i = 0; i < axisCount; ++i) { + + axisOptions = options.xaxes[i]; + if (axisOptions && !axisOptions.tickColor) { + axisOptions.tickColor = axisOptions.color; + } + + axisOptions = $.extend(true, {}, options.xaxis, axisOptions); + options.xaxes[i] = axisOptions; + + if (axisOptions.font) { + axisOptions.font = $.extend({}, fontDefaults, axisOptions.font); + if (!axisOptions.font.color) { + axisOptions.font.color = axisOptions.color; + } + if (!axisOptions.font.lineHeight) { + axisOptions.font.lineHeight = Math.round(axisOptions.font.size * 1.15); + } + } + } + + axisCount = options.yaxes.length || 1; + for (i = 0; i < axisCount; ++i) { + + axisOptions = options.yaxes[i]; + if (axisOptions && !axisOptions.tickColor) { + axisOptions.tickColor = axisOptions.color; + } + + axisOptions = $.extend(true, {}, options.yaxis, axisOptions); + options.yaxes[i] = axisOptions; + + if (axisOptions.font) { + axisOptions.font = $.extend({}, fontDefaults, axisOptions.font); + if (!axisOptions.font.color) { + axisOptions.font.color = axisOptions.color; + } + if (!axisOptions.font.lineHeight) { + axisOptions.font.lineHeight = Math.round(axisOptions.font.size * 1.15); + } + } + } + + // backwards compatibility, to be removed in future + if (options.xaxis.noTicks && options.xaxis.ticks == null) + options.xaxis.ticks = options.xaxis.noTicks; + if (options.yaxis.noTicks && options.yaxis.ticks == null) + options.yaxis.ticks = options.yaxis.noTicks; + if (options.x2axis) { + options.xaxes[1] = $.extend(true, {}, options.xaxis, options.x2axis); + options.xaxes[1].position = "top"; + // Override the inherit to allow the axis to auto-scale + if (options.x2axis.min == null) { + options.xaxes[1].min = null; + } + if (options.x2axis.max == null) { + options.xaxes[1].max = null; + } + } + if (options.y2axis) { + options.yaxes[1] = $.extend(true, {}, options.yaxis, options.y2axis); + options.yaxes[1].position = "right"; + // Override the inherit to allow the axis to auto-scale + if (options.y2axis.min == null) { + options.yaxes[1].min = null; + } + if (options.y2axis.max == null) { + options.yaxes[1].max = null; + } + } + if (options.grid.coloredAreas) + options.grid.markings = options.grid.coloredAreas; + if (options.grid.coloredAreasColor) + options.grid.markingsColor = options.grid.coloredAreasColor; + if (options.lines) + $.extend(true, options.series.lines, options.lines); + if (options.points) + $.extend(true, options.series.points, options.points); + if (options.bars) + $.extend(true, options.series.bars, options.bars); + if (options.shadowSize != null) + options.series.shadowSize = options.shadowSize; + if (options.highlightColor != null) + options.series.highlightColor = options.highlightColor; + + // save options on axes for future reference + for (i = 0; i < options.xaxes.length; ++i) + getOrCreateAxis(xaxes, i + 1).options = options.xaxes[i]; + for (i = 0; i < options.yaxes.length; ++i) + getOrCreateAxis(yaxes, i + 1).options = options.yaxes[i]; + + // add hooks from options + for (var n in hooks) + if (options.hooks[n] && options.hooks[n].length) + hooks[n] = hooks[n].concat(options.hooks[n]); + + executeHooks(hooks.processOptions, [options]); + } + + function setData(d) { + series = parseData(d); + fillInSeriesOptions(); + processData(); + } + + function parseData(d) { + var res = []; + for (var i = 0; i < d.length; ++i) { + var s = $.extend(true, {}, options.series); + + if (d[i].data != null) { + s.data = d[i].data; // move the data instead of deep-copy + delete d[i].data; + + $.extend(true, s, d[i]); + + d[i].data = s.data; + } + else + s.data = d[i]; + res.push(s); + } + + return res; + } + + function axisNumber(obj, coord) { + var a = obj[coord + "axis"]; + if (typeof a == "object") // if we got a real axis, extract number + a = a.n; + if (typeof a != "number") + a = 1; // default to first axis + return a; + } + + function allAxes() { + // return flat array without annoying null entries + return $.grep(xaxes.concat(yaxes), function (a) { return a; }); + } + + function canvasToAxisCoords(pos) { + // return an object with x/y corresponding to all used axes + var res = {}, i, axis; + for (i = 0; i < xaxes.length; ++i) { + axis = xaxes[i]; + if (axis && axis.used) + res["x" + axis.n] = axis.c2p(pos.left); + } + + for (i = 0; i < yaxes.length; ++i) { + axis = yaxes[i]; + if (axis && axis.used) + res["y" + axis.n] = axis.c2p(pos.top); + } + + if (res.x1 !== undefined) + res.x = res.x1; + if (res.y1 !== undefined) + res.y = res.y1; + + return res; + } + + function axisToCanvasCoords(pos) { + // get canvas coords from the first pair of x/y found in pos + var res = {}, i, axis, key; + + for (i = 0; i < xaxes.length; ++i) { + axis = xaxes[i]; + if (axis && axis.used) { + key = "x" + axis.n; + if (pos[key] == null && axis.n == 1) + key = "x"; + + if (pos[key] != null) { + res.left = axis.p2c(pos[key]); + break; + } + } + } + + for (i = 0; i < yaxes.length; ++i) { + axis = yaxes[i]; + if (axis && axis.used) { + key = "y" + axis.n; + if (pos[key] == null && axis.n == 1) + key = "y"; + + if (pos[key] != null) { + res.top = axis.p2c(pos[key]); + break; + } + } + } + + return res; + } + + function getOrCreateAxis(axes, number) { + if (!axes[number - 1]) + axes[number - 1] = { + n: number, // save the number for future reference + direction: axes == xaxes ? "x" : "y", + options: $.extend(true, {}, axes == xaxes ? options.xaxis : options.yaxis) + }; + + return axes[number - 1]; + } + + function fillInSeriesOptions() { + + var neededColors = series.length, maxIndex = -1, i; + + // Subtract the number of series that already have fixed colors or + // color indexes from the number that we still need to generate. + + for (i = 0; i < series.length; ++i) { + var sc = series[i].color; + if (sc != null) { + neededColors--; + if (typeof sc == "number" && sc > maxIndex) { + maxIndex = sc; + } + } + } + + // If any of the series have fixed color indexes, then we need to + // generate at least as many colors as the highest index. + + if (neededColors <= maxIndex) { + neededColors = maxIndex + 1; + } + + // Generate all the colors, using first the option colors and then + // variations on those colors once they're exhausted. + + var c, colors = [], colorPool = options.colors, + colorPoolSize = colorPool.length, variation = 0; + + for (i = 0; i < neededColors; i++) { + + c = $.color.parse(colorPool[i % colorPoolSize] || "#666"); + + // Each time we exhaust the colors in the pool we adjust + // a scaling factor used to produce more variations on + // those colors. The factor alternates negative/positive + // to produce lighter/darker colors. + + // Reset the variation after every few cycles, or else + // it will end up producing only white or black colors. + + 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); + } + + // Finalize the series options, filling in their colors + + var colori = 0, s; + for (i = 0; i < series.length; ++i) { + s = series[i]; + + // assign colors + if (s.color == null) { + s.color = colors[colori].toString(); + ++colori; + } + else if (typeof s.color == "number") + s.color = colors[s.color].toString(); + + // turn on lines automatically in case nothing is set + if (s.lines.show == null) { + var v, show = true; + for (v in s) + if (s[v] && s[v].show) { + show = false; + break; + } + if (show) + s.lines.show = true; + } + + // If nothing was provided for lines.zero, default it to match + // lines.fill, since areas by default should extend to zero. + + if (s.lines.zero == null) { + s.lines.zero = !!s.lines.fill; + } + + // setup axes + s.xaxis = getOrCreateAxis(xaxes, axisNumber(s, "x")); + s.yaxis = getOrCreateAxis(yaxes, axisNumber(s, "y")); + } + } + + function processData() { + var topSentry = Number.POSITIVE_INFINITY, + bottomSentry = Number.NEGATIVE_INFINITY, + fakeInfinity = Number.MAX_VALUE, + i, j, k, m, length, + s, points, ps, x, y, axis, val, f, p, + data, format; + + function updateAxis(axis, min, max) { + if (min < axis.datamin && min != -fakeInfinity) + axis.datamin = min; + if (max > axis.datamax && max != fakeInfinity) + axis.datamax = max; + } + + $.each(allAxes(), function (_, axis) { + // init axis + axis.datamin = topSentry; + axis.datamax = bottomSentry; + axis.used = false; + }); + + for (i = 0; i < series.length; ++i) { + s = series[i]; + s.datapoints = { points: [] }; + + executeHooks(hooks.processRawData, [ s, s.data, s.datapoints ]); + } + + // first pass: clean and copy data + for (i = 0; i < series.length; ++i) { + s = series[i]; + + data = s.data; + format = s.datapoints.format; + + if (!format) { + format = []; + // find out how to copy + format.push({ x: true, number: true, required: true }); + format.push({ y: true, number: true, required: true }); + + if (s.bars.show || (s.lines.show && s.lines.fill)) { + var autoscale = !!((s.bars.show && s.bars.zero) || (s.lines.show && s.lines.zero)); + format.push({ y: true, number: true, required: false, defaultValue: 0, autoscale: autoscale }); + if (s.bars.horizontal) { + delete format[format.length - 1].y; + format[format.length - 1].x = true; + } + } + + s.datapoints.format = format; + } + + if (s.datapoints.pointsize != null) + continue; // already filled in + + s.datapoints.pointsize = format.length; + + ps = s.datapoints.pointsize; + points = s.datapoints.points; + + var insertSteps = s.lines.show && s.lines.steps; + s.xaxis.used = s.yaxis.used = true; + + for (j = k = 0; j < data.length; ++j, k += ps) { + p = data[j]; + + var nullify = p == null; + if (!nullify) { + for (m = 0; m < ps; ++m) { + val = p[m]; + f = format[m]; + + if (f) { + if (f.number && val != null) { + val = +val; // convert to number + if (isNaN(val)) + val = null; + else if (val == Infinity) + val = fakeInfinity; + else if (val == -Infinity) + val = -fakeInfinity; + } + + if (val == null) { + if (f.required) + nullify = true; + + if (f.defaultValue != null) + val = f.defaultValue; + } + } + + points[k + m] = val; + } + } + + if (nullify) { + for (m = 0; m < ps; ++m) { + val = points[k + m]; + if (val != null) { + f = format[m]; + // extract min/max info + if (f.autoscale !== false) { + if (f.x) { + updateAxis(s.xaxis, val, val); + } + if (f.y) { + updateAxis(s.yaxis, val, val); + } + } + } + points[k + m] = null; + } + } + else { + // a little bit of line specific stuff that + // perhaps shouldn't be here, but lacking + // better means... + if (insertSteps && k > 0 + && points[k - ps] != null + && points[k - ps] != points[k] + && points[k - ps + 1] != points[k + 1]) { + // copy the point to make room for a middle point + for (m = 0; m < ps; ++m) + points[k + ps + m] = points[k + m]; + + // middle point has same y + points[k + 1] = points[k - ps + 1]; + + // we've added a point, better reflect that + k += ps; + } + } + } + } + + // give the hooks a chance to run + for (i = 0; i < series.length; ++i) { + s = series[i]; + + executeHooks(hooks.processDatapoints, [ s, s.datapoints]); + } + + // second pass: find datamax/datamin for auto-scaling + for (i = 0; i < series.length; ++i) { + s = series[i]; + points = s.datapoints.points; + ps = s.datapoints.pointsize; + format = s.datapoints.format; + + var xmin = topSentry, ymin = topSentry, + xmax = bottomSentry, ymax = bottomSentry; + + for (j = 0; j < points.length; j += ps) { + if (points[j] == null) + continue; + + for (m = 0; m < ps; ++m) { + val = points[j + m]; + f = format[m]; + if (!f || f.autoscale === false || val == fakeInfinity || val == -fakeInfinity) + continue; + + if (f.x) { + if (val < xmin) + xmin = val; + if (val > xmax) + xmax = val; + } + if (f.y) { + if (val < ymin) + ymin = val; + if (val > ymax) + ymax = val; + } + } + } + + if (s.bars.show) { + // make sure we got room for the bar on the dancing floor + var delta; + + switch (s.bars.align) { + case "left": + delta = 0; + break; + case "right": + delta = -s.bars.barWidth; + break; + default: + delta = -s.bars.barWidth / 2; + } + + if (s.bars.horizontal) { + ymin += delta; + ymax += delta + s.bars.barWidth; + } + else { + xmin += delta; + xmax += delta + s.bars.barWidth; + } + } + + updateAxis(s.xaxis, xmin, xmax); + updateAxis(s.yaxis, ymin, ymax); + } + + $.each(allAxes(), function (_, axis) { + if (axis.datamin == topSentry) + axis.datamin = null; + if (axis.datamax == bottomSentry) + axis.datamax = null; + }); + } + + function setupCanvases() { + + // Make sure the placeholder is clear of everything except canvases + // from a previous plot in this container that we'll try to re-use. + + placeholder.css("padding", 0) // padding messes up the positioning + .children().filter(function(){ + return !$(this).hasClass("flot-overlay") && !$(this).hasClass('flot-base'); + }).remove(); + + if (placeholder.css("position") == 'static') + placeholder.css("position", "relative"); // for positioning labels and overlay + + surface = new Canvas("flot-base", placeholder); + overlay = new Canvas("flot-overlay", placeholder); // overlay canvas for interactive features + + ctx = surface.context; + octx = overlay.context; + + // define which element we're listening for events on + eventHolder = $(overlay.element).unbind(); + + // If we're re-using a plot object, shut down the old one + + var existing = placeholder.data("plot"); + + if (existing) { + existing.shutdown(); + overlay.clear(); + } + + // save in case we get replotted + placeholder.data("plot", plot); + } + + function bindEvents() { + // bind events + if (options.grid.hoverable) { + eventHolder.mousemove(onMouseMove); + + // Use bind, rather than .mouseleave, because we officially + // still support jQuery 1.2.6, which doesn't define a shortcut + // for mouseenter or mouseleave. This was a bug/oversight that + // was fixed somewhere around 1.3.x. We can return to using + // .mouseleave when we drop support for 1.2.6. + + eventHolder.bind("mouseleave", onMouseLeave); + } + + if (options.grid.clickable) + eventHolder.click(onClick); + + executeHooks(hooks.bindEvents, [eventHolder]); + } + + function shutdown() { + if (redrawTimeout) + clearTimeout(redrawTimeout); + + eventHolder.unbind("mousemove", onMouseMove); + eventHolder.unbind("mouseleave", onMouseLeave); + eventHolder.unbind("click", onClick); + + executeHooks(hooks.shutdown, [eventHolder]); + } + + function setTransformationHelpers(axis) { + // set helper functions on the axis, assumes plot area + // has been computed already + + function identity(x) { return x; } + + var s, m, t = axis.options.transform || identity, + it = axis.options.inverseTransform; + + // precompute how much the axis is scaling a point + // in canvas space + if (axis.direction == "x") { + s = axis.scale = plotWidth / Math.abs(t(axis.max) - t(axis.min)); + m = Math.min(t(axis.max), t(axis.min)); + } + else { + s = axis.scale = plotHeight / Math.abs(t(axis.max) - t(axis.min)); + s = -s; + m = Math.max(t(axis.max), t(axis.min)); + } + + // data point to canvas coordinate + if (t == identity) // slight optimization + axis.p2c = function (p) { return (p - m) * s; }; + else + axis.p2c = function (p) { return (t(p) - m) * s; }; + // canvas coordinate to data point + if (!it) + axis.c2p = function (c) { return m + c / s; }; + else + axis.c2p = function (c) { return it(m + c / s); }; + } + + function measureTickLabels(axis) { + + var opts = axis.options, + ticks = axis.ticks || [], + labelWidth = opts.labelWidth || 0, + labelHeight = opts.labelHeight || 0, + maxWidth = labelWidth || (axis.direction == "x" ? Math.floor(surface.width / (ticks.length || 1)) : null), + legacyStyles = axis.direction + "Axis " + axis.direction + axis.n + "Axis", + layer = "flot-" + axis.direction + "-axis flot-" + axis.direction + axis.n + "-axis " + legacyStyles, + font = opts.font || "flot-tick-label tickLabel"; + + for (var i = 0; i < ticks.length; ++i) { + + var t = ticks[i]; + + if (!t.label) + continue; + + var info = surface.getTextInfo(layer, t.label, font, null, maxWidth); + + labelWidth = Math.max(labelWidth, info.width); + labelHeight = Math.max(labelHeight, info.height); + } + + axis.labelWidth = opts.labelWidth || labelWidth; + axis.labelHeight = opts.labelHeight || labelHeight; + } + + function allocateAxisBoxFirstPhase(axis) { + // find the bounding box of the axis by looking at label + // widths/heights and ticks, make room by diminishing the + // plotOffset; this first phase only looks at one + // dimension per axis, the other dimension depends on the + // other axes so will have to wait + + var lw = axis.labelWidth, + lh = axis.labelHeight, + pos = axis.options.position, + isXAxis = axis.direction === "x", + tickLength = axis.options.tickLength, + axisMargin = options.grid.axisMargin, + padding = options.grid.labelMargin, + innermost = true, + outermost = true, + first = true, + found = false; + + // Determine the axis's position in its direction and on its side + + $.each(isXAxis ? xaxes : yaxes, function(i, a) { + if (a && (a.show || a.reserveSpace)) { + if (a === axis) { + found = true; + } else if (a.options.position === pos) { + if (found) { + outermost = false; + } else { + innermost = false; + } + } + if (!found) { + first = false; + } + } + }); + + // The outermost axis on each side has no margin + + if (outermost) { + axisMargin = 0; + } + + // The ticks for the first axis in each direction stretch across + + if (tickLength == null) { + tickLength = first ? "full" : 5; + } + + if (!isNaN(+tickLength)) + padding += +tickLength; + + if (isXAxis) { + lh += padding; + + if (pos == "bottom") { + plotOffset.bottom += lh + axisMargin; + axis.box = { top: surface.height - plotOffset.bottom, height: lh }; + } + else { + axis.box = { top: plotOffset.top + axisMargin, height: lh }; + plotOffset.top += lh + axisMargin; + } + } + else { + lw += padding; + + if (pos == "left") { + axis.box = { left: plotOffset.left + axisMargin, width: lw }; + plotOffset.left += lw + axisMargin; + } + else { + plotOffset.right += lw + axisMargin; + axis.box = { left: surface.width - plotOffset.right, width: lw }; + } + } + + // save for future reference + axis.position = pos; + axis.tickLength = tickLength; + axis.box.padding = padding; + axis.innermost = innermost; + } + + function allocateAxisBoxSecondPhase(axis) { + // now that all axis boxes have been placed in one + // dimension, we can set the remaining dimension coordinates + if (axis.direction == "x") { + axis.box.left = plotOffset.left - axis.labelWidth / 2; + axis.box.width = surface.width - plotOffset.left - plotOffset.right + axis.labelWidth; + } + else { + axis.box.top = plotOffset.top - axis.labelHeight / 2; + axis.box.height = surface.height - plotOffset.bottom - plotOffset.top + axis.labelHeight; + } + } + + function adjustLayoutForThingsStickingOut() { + // possibly adjust plot offset to ensure everything stays + // inside the canvas and isn't clipped off + + var minMargin = options.grid.minBorderMargin, + axis, i; + + // check stuff from the plot (FIXME: this should just read + // a value from the series, otherwise it's impossible to + // customize) + if (minMargin == null) { + minMargin = 0; + for (i = 0; i < series.length; ++i) + minMargin = Math.max(minMargin, 2 * (series[i].points.radius + series[i].points.lineWidth/2)); + } + + var margins = { + left: minMargin, + right: minMargin, + top: minMargin, + bottom: minMargin + }; + + // check axis labels, note we don't check the actual + // labels but instead use the overall width/height to not + // jump as much around with replots + $.each(allAxes(), function (_, axis) { + if (axis.reserveSpace && axis.ticks && axis.ticks.length) { + if (axis.direction === "x") { + margins.left = Math.max(margins.left, axis.labelWidth / 2); + margins.right = Math.max(margins.right, axis.labelWidth / 2); + } else { + margins.bottom = Math.max(margins.bottom, axis.labelHeight / 2); + margins.top = Math.max(margins.top, axis.labelHeight / 2); + } + } + }); + + plotOffset.left = Math.ceil(Math.max(margins.left, plotOffset.left)); + plotOffset.right = Math.ceil(Math.max(margins.right, plotOffset.right)); + plotOffset.top = Math.ceil(Math.max(margins.top, plotOffset.top)); + plotOffset.bottom = Math.ceil(Math.max(margins.bottom, plotOffset.bottom)); + } + + function setupGrid() { + var i, axes = allAxes(), showGrid = options.grid.show; + + // Initialize the plot's offset from the edge of the canvas + + for (var a in plotOffset) { + var margin = options.grid.margin || 0; + plotOffset[a] = typeof margin == "number" ? margin : margin[a] || 0; + } + + executeHooks(hooks.processOffset, [plotOffset]); + + // If the grid is visible, add its border width to the offset + + for (var a in plotOffset) { + if(typeof(options.grid.borderWidth) == "object") { + plotOffset[a] += showGrid ? options.grid.borderWidth[a] : 0; + } + else { + plotOffset[a] += showGrid ? options.grid.borderWidth : 0; + } + } + + $.each(axes, function (_, axis) { + var axisOpts = axis.options; + axis.show = axisOpts.show == null ? axis.used : axisOpts.show; + axis.reserveSpace = axisOpts.reserveSpace == null ? axis.show : axisOpts.reserveSpace; + setRange(axis); + }); + + if (showGrid) { + + var allocatedAxes = $.grep(axes, function (axis) { + return axis.show || axis.reserveSpace; + }); + + $.each(allocatedAxes, function (_, axis) { + // make the ticks + setupTickGeneration(axis); + setTicks(axis); + snapRangeToTicks(axis, axis.ticks); + // find labelWidth/Height for axis + measureTickLabels(axis); + }); + + // with all dimensions calculated, we can compute the + // axis bounding boxes, start from the outside + // (reverse order) + for (i = allocatedAxes.length - 1; i >= 0; --i) + allocateAxisBoxFirstPhase(allocatedAxes[i]); + + // make sure we've got enough space for things that + // might stick out + adjustLayoutForThingsStickingOut(); + + $.each(allocatedAxes, function (_, axis) { + allocateAxisBoxSecondPhase(axis); + }); + } + + plotWidth = surface.width - plotOffset.left - plotOffset.right; + plotHeight = surface.height - plotOffset.bottom - plotOffset.top; + + // now we got the proper plot dimensions, we can compute the scaling + $.each(axes, function (_, axis) { + setTransformationHelpers(axis); + }); + + if (showGrid) { + drawAxisLabels(); + } + + insertLegend(); + } + + function setRange(axis) { + var opts = axis.options, + min = +(opts.min != null ? opts.min : axis.datamin), + max = +(opts.max != null ? opts.max : axis.datamax), + delta = max - min; + + if (delta == 0.0) { + // degenerate case + var widen = max == 0 ? 1 : 0.01; + + if (opts.min == null) + min -= widen; + // always widen max if we couldn't widen min to ensure we + // don't fall into min == max which doesn't work + if (opts.max == null || opts.min != null) + max += widen; + } + else { + // consider autoscaling + var margin = opts.autoscaleMargin; + if (margin != null) { + if (opts.min == null) { + min -= delta * margin; + // make sure we don't go below zero if all values + // are positive + if (min < 0 && axis.datamin != null && axis.datamin >= 0) + min = 0; + } + if (opts.max == null) { + max += delta * margin; + if (max > 0 && axis.datamax != null && axis.datamax <= 0) + max = 0; + } + } + } + axis.min = min; + axis.max = max; + } + + function setupTickGeneration(axis) { + var opts = axis.options; + + // estimate number of ticks + var noTicks; + if (typeof opts.ticks == "number" && opts.ticks > 0) + noTicks = opts.ticks; + else + // heuristic based on the model a*sqrt(x) fitted to + // some data points that seemed reasonable + noTicks = 0.3 * Math.sqrt(axis.direction == "x" ? surface.width : surface.height); + + var delta = (axis.max - axis.min) / noTicks, + dec = -Math.floor(Math.log(delta) / Math.LN10), + maxDec = opts.tickDecimals; + + if (maxDec != null && dec > maxDec) { + dec = maxDec; + } + + var magn = Math.pow(10, -dec), + norm = delta / magn, // norm is between 1.0 and 10.0 + size; + + if (norm < 1.5) { + size = 1; + } else if (norm < 3) { + size = 2; + // special case for 2.5, requires an extra decimal + if (norm > 2.25 && (maxDec == null || dec + 1 <= maxDec)) { + size = 2.5; + ++dec; + } + } else if (norm < 7.5) { + size = 5; + } else { + size = 10; + } + + size *= magn; + + if (opts.minTickSize != null && size < opts.minTickSize) { + size = opts.minTickSize; + } + + axis.delta = delta; + axis.tickDecimals = Math.max(0, maxDec != null ? maxDec : dec); + axis.tickSize = opts.tickSize || size; + + // Time mode was moved to a plug-in in 0.8, and since so many people use it + // we'll add an especially friendly reminder to make sure they included it. + + if (opts.mode == "time" && !axis.tickGenerator) { + throw new Error("Time mode requires the flot.time plugin."); + } + + // Flot supports base-10 axes; any other mode else is handled by a plug-in, + // like flot.time.js. + + if (!axis.tickGenerator) { + + axis.tickGenerator = function (axis) { + + var ticks = [], + start = floorInBase(axis.min, axis.tickSize), + i = 0, + v = Number.NaN, + prev; + + do { + prev = v; + v = start + i * axis.tickSize; + ticks.push(v); + ++i; + } while (v < axis.max && v != prev); + return ticks; + }; + + axis.tickFormatter = function (value, axis) { + + var factor = axis.tickDecimals ? Math.pow(10, axis.tickDecimals) : 1; + var formatted = "" + Math.round(value * factor) / factor; + + // If tickDecimals was specified, ensure that we have exactly that + // much precision; otherwise default to the value's own precision. + + if (axis.tickDecimals != null) { + var decimal = formatted.indexOf("."); + var precision = decimal == -1 ? 0 : formatted.length - decimal - 1; + if (precision < axis.tickDecimals) { + return (precision ? formatted : formatted + ".") + ("" + factor).substr(1, axis.tickDecimals - precision); + } + } + + return formatted; + }; + } + + if ($.isFunction(opts.tickFormatter)) + axis.tickFormatter = function (v, axis) { return "" + opts.tickFormatter(v, axis); }; + + if (opts.alignTicksWithAxis != null) { + var otherAxis = (axis.direction == "x" ? xaxes : yaxes)[opts.alignTicksWithAxis - 1]; + if (otherAxis && otherAxis.used && otherAxis != axis) { + // consider snapping min/max to outermost nice ticks + var niceTicks = axis.tickGenerator(axis); + if (niceTicks.length > 0) { + if (opts.min == null) + axis.min = Math.min(axis.min, niceTicks[0]); + if (opts.max == null && niceTicks.length > 1) + axis.max = Math.max(axis.max, niceTicks[niceTicks.length - 1]); + } + + axis.tickGenerator = function (axis) { + // copy ticks, scaled to this axis + var ticks = [], v, i; + for (i = 0; i < otherAxis.ticks.length; ++i) { + v = (otherAxis.ticks[i].v - otherAxis.min) / (otherAxis.max - otherAxis.min); + v = axis.min + v * (axis.max - axis.min); + ticks.push(v); + } + return ticks; + }; + + // we might need an extra decimal since forced + // ticks don't necessarily fit naturally + if (!axis.mode && opts.tickDecimals == null) { + var extraDec = Math.max(0, -Math.floor(Math.log(axis.delta) / Math.LN10) + 1), + ts = axis.tickGenerator(axis); + + // only proceed if the tick interval rounded + // with an extra decimal doesn't give us a + // zero at end + if (!(ts.length > 1 && /\..*0$/.test((ts[1] - ts[0]).toFixed(extraDec)))) + axis.tickDecimals = extraDec; + } + } + } + } + + function setTicks(axis) { + var oticks = axis.options.ticks, ticks = []; + if (oticks == null || (typeof oticks == "number" && oticks > 0)) + ticks = axis.tickGenerator(axis); + else if (oticks) { + if ($.isFunction(oticks)) + // generate the ticks + ticks = oticks(axis); + else + ticks = oticks; + } + + // clean up/labelify the supplied ticks, copy them over + var i, v; + axis.ticks = []; + for (i = 0; i < ticks.length; ++i) { + var label = null; + var t = ticks[i]; + if (typeof t == "object") { + v = +t[0]; + if (t.length > 1) + label = t[1]; + } + else + v = +t; + if (label == null) + label = axis.tickFormatter(v, axis); + if (!isNaN(v)) + axis.ticks.push({ v: v, label: label }); + } + } + + function snapRangeToTicks(axis, ticks) { + if (axis.options.autoscaleMargin && ticks.length > 0) { + // snap to ticks + if (axis.options.min == null) + axis.min = Math.min(axis.min, ticks[0].v); + if (axis.options.max == null && ticks.length > 1) + axis.max = Math.max(axis.max, ticks[ticks.length - 1].v); + } + } + + function draw() { + + surface.clear(); + + executeHooks(hooks.drawBackground, [ctx]); + + var grid = options.grid; + + // draw background, if any + if (grid.show && grid.backgroundColor) + drawBackground(); + + if (grid.show && !grid.aboveData) { + drawGrid(); + } + + for (var i = 0; i < series.length; ++i) { + executeHooks(hooks.drawSeries, [ctx, series[i]]); + drawSeries(series[i]); + } + + executeHooks(hooks.draw, [ctx]); + + if (grid.show && grid.aboveData) { + drawGrid(); + } + + surface.render(); + + // A draw implies that either the axes or data have changed, so we + // should probably update the overlay highlights as well. + + triggerRedrawOverlay(); + } + + function extractRange(ranges, coord) { + var axis, from, to, key, axes = allAxes(); + + for (var i = 0; i < axes.length; ++i) { + axis = axes[i]; + if (axis.direction == coord) { + key = coord + axis.n + "axis"; + if (!ranges[key] && axis.n == 1) + key = coord + "axis"; // support x1axis as xaxis + if (ranges[key]) { + from = ranges[key].from; + to = ranges[key].to; + break; + } + } + } + + // backwards-compat stuff - to be removed in future + if (!ranges[key]) { + axis = coord == "x" ? xaxes[0] : yaxes[0]; + from = ranges[coord + "1"]; + to = ranges[coord + "2"]; + } + + // auto-reverse as an added bonus + if (from != null && to != null && from > to) { + var tmp = from; + from = to; + to = tmp; + } + + return { from: from, to: to, axis: axis }; + } + + function drawBackground() { + ctx.save(); + ctx.translate(plotOffset.left, plotOffset.top); + + ctx.fillStyle = getColorOrGradient(options.grid.backgroundColor, plotHeight, 0, "rgba(255, 255, 255, 0)"); + ctx.fillRect(0, 0, plotWidth, plotHeight); + ctx.restore(); + } + + function drawGrid() { + var i, axes, bw, bc; + + ctx.save(); + ctx.translate(plotOffset.left, plotOffset.top); + + // draw markings + var markings = options.grid.markings; + if (markings) { + if ($.isFunction(markings)) { + axes = plot.getAxes(); + // xmin etc. is backwards compatibility, to be + // removed in the future + axes.xmin = axes.xaxis.min; + axes.xmax = axes.xaxis.max; + axes.ymin = axes.yaxis.min; + axes.ymax = axes.yaxis.max; + + markings = markings(axes); + } + + for (i = 0; i < markings.length; ++i) { + var m = markings[i], + xrange = extractRange(m, "x"), + yrange = extractRange(m, "y"); + + // fill in missing + if (xrange.from == null) + xrange.from = xrange.axis.min; + if (xrange.to == null) + xrange.to = xrange.axis.max; + if (yrange.from == null) + yrange.from = yrange.axis.min; + if (yrange.to == null) + yrange.to = yrange.axis.max; + + // clip + if (xrange.to < xrange.axis.min || xrange.from > xrange.axis.max || + yrange.to < yrange.axis.min || yrange.from > yrange.axis.max) + continue; + + xrange.from = Math.max(xrange.from, xrange.axis.min); + xrange.to = Math.min(xrange.to, xrange.axis.max); + yrange.from = Math.max(yrange.from, yrange.axis.min); + yrange.to = Math.min(yrange.to, yrange.axis.max); + + var xequal = xrange.from === xrange.to, + yequal = yrange.from === yrange.to; + + if (xequal && yequal) { + continue; + } + + // then draw + xrange.from = Math.floor(xrange.axis.p2c(xrange.from)); + xrange.to = Math.floor(xrange.axis.p2c(xrange.to)); + yrange.from = Math.floor(yrange.axis.p2c(yrange.from)); + yrange.to = Math.floor(yrange.axis.p2c(yrange.to)); + + if (xequal || yequal) { + var lineWidth = m.lineWidth || options.grid.markingsLineWidth, + subPixel = lineWidth % 2 ? 0.5 : 0; + ctx.beginPath(); + ctx.strokeStyle = m.color || options.grid.markingsColor; + ctx.lineWidth = lineWidth; + if (xequal) { + ctx.moveTo(xrange.to + subPixel, yrange.from); + ctx.lineTo(xrange.to + subPixel, yrange.to); + } else { + ctx.moveTo(xrange.from, yrange.to + subPixel); + ctx.lineTo(xrange.to, yrange.to + subPixel); + } + ctx.stroke(); + } else { + ctx.fillStyle = m.color || options.grid.markingsColor; + ctx.fillRect(xrange.from, yrange.to, + xrange.to - xrange.from, + yrange.from - yrange.to); + } + } + } + + // draw the ticks + axes = allAxes(); + bw = options.grid.borderWidth; + + for (var j = 0; j < axes.length; ++j) { + var axis = axes[j], box = axis.box, + t = axis.tickLength, x, y, xoff, yoff; + if (!axis.show || axis.ticks.length == 0) + continue; + + ctx.lineWidth = 1; + + // find the edges + if (axis.direction == "x") { + x = 0; + if (t == "full") + y = (axis.position == "top" ? 0 : plotHeight); + else + y = box.top - plotOffset.top + (axis.position == "top" ? box.height : 0); + } + else { + y = 0; + if (t == "full") + x = (axis.position == "left" ? 0 : plotWidth); + else + x = box.left - plotOffset.left + (axis.position == "left" ? box.width : 0); + } + + // draw tick bar + if (!axis.innermost) { + ctx.strokeStyle = axis.options.color; + ctx.beginPath(); + xoff = yoff = 0; + if (axis.direction == "x") + xoff = plotWidth + 1; + else + yoff = plotHeight + 1; + + if (ctx.lineWidth == 1) { + if (axis.direction == "x") { + y = Math.floor(y) + 0.5; + } else { + x = Math.floor(x) + 0.5; + } + } + + ctx.moveTo(x, y); + ctx.lineTo(x + xoff, y + yoff); + ctx.stroke(); + } + + // draw ticks + + ctx.strokeStyle = axis.options.tickColor; + + ctx.beginPath(); + for (i = 0; i < axis.ticks.length; ++i) { + var v = axis.ticks[i].v; + + xoff = yoff = 0; + + if (isNaN(v) || v < axis.min || v > axis.max + // skip those lying on the axes if we got a border + || (t == "full" + && ((typeof bw == "object" && bw[axis.position] > 0) || bw > 0) + && (v == axis.min || v == axis.max))) + continue; + + if (axis.direction == "x") { + x = axis.p2c(v); + yoff = t == "full" ? -plotHeight : t; + + if (axis.position == "top") + yoff = -yoff; + } + else { + y = axis.p2c(v); + xoff = t == "full" ? -plotWidth : t; + + if (axis.position == "left") + xoff = -xoff; + } + + if (ctx.lineWidth == 1) { + if (axis.direction == "x") + x = Math.floor(x) + 0.5; + else + y = Math.floor(y) + 0.5; + } + + ctx.moveTo(x, y); + ctx.lineTo(x + xoff, y + yoff); + } + + ctx.stroke(); + } + + + // draw border + if (bw) { + // If either borderWidth or borderColor is an object, then draw the border + // line by line instead of as one rectangle + bc = options.grid.borderColor; + if(typeof bw == "object" || typeof bc == "object") { + if (typeof bw !== "object") { + bw = {top: bw, right: bw, bottom: bw, left: bw}; + } + if (typeof bc !== "object") { + bc = {top: bc, right: bc, bottom: bc, left: bc}; + } + + if (bw.top > 0) { + ctx.strokeStyle = bc.top; + ctx.lineWidth = bw.top; + ctx.beginPath(); + ctx.moveTo(0 - bw.left, 0 - bw.top/2); + ctx.lineTo(plotWidth, 0 - bw.top/2); + ctx.stroke(); + } + + if (bw.right > 0) { + ctx.strokeStyle = bc.right; + ctx.lineWidth = bw.right; + ctx.beginPath(); + ctx.moveTo(plotWidth + bw.right / 2, 0 - bw.top); + ctx.lineTo(plotWidth + bw.right / 2, plotHeight); + ctx.stroke(); + } + + if (bw.bottom > 0) { + ctx.strokeStyle = bc.bottom; + ctx.lineWidth = bw.bottom; + ctx.beginPath(); + ctx.moveTo(plotWidth + bw.right, plotHeight + bw.bottom / 2); + ctx.lineTo(0, plotHeight + bw.bottom / 2); + ctx.stroke(); + } + + if (bw.left > 0) { + ctx.strokeStyle = bc.left; + ctx.lineWidth = bw.left; + ctx.beginPath(); + ctx.moveTo(0 - bw.left/2, plotHeight + bw.bottom); + ctx.lineTo(0- bw.left/2, 0); + ctx.stroke(); + } + } + else { + ctx.lineWidth = bw; + ctx.strokeStyle = options.grid.borderColor; + ctx.strokeRect(-bw/2, -bw/2, plotWidth + bw, plotHeight + bw); + } + } + + ctx.restore(); + } + + function drawAxisLabels() { + + $.each(allAxes(), function (_, axis) { + var box = axis.box, + legacyStyles = axis.direction + "Axis " + axis.direction + axis.n + "Axis", + layer = "flot-" + axis.direction + "-axis flot-" + axis.direction + axis.n + "-axis " + legacyStyles, + font = axis.options.font || "flot-tick-label tickLabel", + tick, x, y, halign, valign; + + // Remove text before checking for axis.show and ticks.length; + // otherwise plugins, like flot-tickrotor, that draw their own + // tick labels will end up with both theirs and the defaults. + + surface.removeText(layer); + + if (!axis.show || axis.ticks.length == 0) + return; + + for (var i = 0; i < axis.ticks.length; ++i) { + + tick = axis.ticks[i]; + if (!tick.label || tick.v < axis.min || tick.v > axis.max) + continue; + + if (axis.direction == "x") { + halign = "center"; + x = plotOffset.left + axis.p2c(tick.v); + if (axis.position == "bottom") { + y = box.top + box.padding; + } else { + y = box.top + box.height - box.padding; + valign = "bottom"; + } + } else { + valign = "middle"; + y = plotOffset.top + axis.p2c(tick.v); + if (axis.position == "left") { + x = box.left + box.width - box.padding; + halign = "right"; + } else { + x = box.left + box.padding; + } + } + + surface.addText(layer, x, y, tick.label, font, null, null, halign, valign); + } + }); + } + + function drawSeries(series) { + if (series.lines.show) + drawSeriesLines(series); + if (series.bars.show) + drawSeriesBars(series); + if (series.points.show) + drawSeriesPoints(series); + } + + function drawSeriesLines(series) { + function plotLine(datapoints, xoffset, yoffset, axisx, axisy) { + var points = datapoints.points, + ps = datapoints.pointsize, + prevx = null, prevy = null; + + ctx.beginPath(); + for (var i = ps; i < points.length; i += ps) { + var x1 = points[i - ps], y1 = points[i - ps + 1], + x2 = points[i], y2 = points[i + 1]; + + if (x1 == null || x2 == null) + continue; + + // clip with ymin + if (y1 <= y2 && y1 < axisy.min) { + if (y2 < axisy.min) + continue; // line segment is outside + // compute new intersection point + x1 = (axisy.min - y1) / (y2 - y1) * (x2 - x1) + x1; + y1 = axisy.min; + } + else if (y2 <= y1 && y2 < axisy.min) { + if (y1 < axisy.min) + continue; + x2 = (axisy.min - y1) / (y2 - y1) * (x2 - x1) + x1; + y2 = axisy.min; + } + + // clip with ymax + if (y1 >= y2 && y1 > axisy.max) { + if (y2 > axisy.max) + continue; + x1 = (axisy.max - y1) / (y2 - y1) * (x2 - x1) + x1; + y1 = axisy.max; + } + else if (y2 >= y1 && y2 > axisy.max) { + if (y1 > axisy.max) + continue; + x2 = (axisy.max - y1) / (y2 - y1) * (x2 - x1) + x1; + y2 = axisy.max; + } + + // clip with xmin + if (x1 <= x2 && x1 < axisx.min) { + if (x2 < axisx.min) + continue; + y1 = (axisx.min - x1) / (x2 - x1) * (y2 - y1) + y1; + x1 = axisx.min; + } + else if (x2 <= x1 && x2 < axisx.min) { + if (x1 < axisx.min) + continue; + y2 = (axisx.min - x1) / (x2 - x1) * (y2 - y1) + y1; + x2 = axisx.min; + } + + // clip with xmax + if (x1 >= x2 && x1 > axisx.max) { + if (x2 > axisx.max) + continue; + y1 = (axisx.max - x1) / (x2 - x1) * (y2 - y1) + y1; + x1 = axisx.max; + } + else if (x2 >= x1 && x2 > axisx.max) { + if (x1 > axisx.max) + continue; + y2 = (axisx.max - x1) / (x2 - x1) * (y2 - y1) + y1; + x2 = axisx.max; + } + + if (x1 != prevx || y1 != prevy) + ctx.moveTo(axisx.p2c(x1) + xoffset, axisy.p2c(y1) + yoffset); + + prevx = x2; + prevy = y2; + ctx.lineTo(axisx.p2c(x2) + xoffset, axisy.p2c(y2) + yoffset); + } + ctx.stroke(); + } + + function plotLineArea(datapoints, axisx, axisy) { + var points = datapoints.points, + ps = datapoints.pointsize, + bottom = Math.min(Math.max(0, axisy.min), axisy.max), + i = 0, top, areaOpen = false, + ypos = 1, segmentStart = 0, segmentEnd = 0; + + // we process each segment in two turns, first forward + // direction to sketch out top, then once we hit the + // end we go backwards to sketch the bottom + while (true) { + if (ps > 0 && i > points.length + ps) + break; + + i += ps; // ps is negative if going backwards + + var x1 = points[i - ps], + y1 = points[i - ps + ypos], + x2 = points[i], y2 = points[i + ypos]; + + if (areaOpen) { + if (ps > 0 && x1 != null && x2 == null) { + // at turning point + segmentEnd = i; + ps = -ps; + ypos = 2; + continue; + } + + if (ps < 0 && i == segmentStart + ps) { + // done with the reverse sweep + ctx.fill(); + areaOpen = false; + ps = -ps; + ypos = 1; + i = segmentStart = segmentEnd + ps; + continue; + } + } + + if (x1 == null || x2 == null) + continue; + + // clip x values + + // clip with xmin + if (x1 <= x2 && x1 < axisx.min) { + if (x2 < axisx.min) + continue; + y1 = (axisx.min - x1) / (x2 - x1) * (y2 - y1) + y1; + x1 = axisx.min; + } + else if (x2 <= x1 && x2 < axisx.min) { + if (x1 < axisx.min) + continue; + y2 = (axisx.min - x1) / (x2 - x1) * (y2 - y1) + y1; + x2 = axisx.min; + } + + // clip with xmax + if (x1 >= x2 && x1 > axisx.max) { + if (x2 > axisx.max) + continue; + y1 = (axisx.max - x1) / (x2 - x1) * (y2 - y1) + y1; + x1 = axisx.max; + } + else if (x2 >= x1 && x2 > axisx.max) { + if (x1 > axisx.max) + continue; + y2 = (axisx.max - x1) / (x2 - x1) * (y2 - y1) + y1; + x2 = axisx.max; + } + + if (!areaOpen) { + // open area + ctx.beginPath(); + ctx.moveTo(axisx.p2c(x1), axisy.p2c(bottom)); + areaOpen = true; + } + + // now first check the case where both is outside + if (y1 >= axisy.max && y2 >= axisy.max) { + ctx.lineTo(axisx.p2c(x1), axisy.p2c(axisy.max)); + ctx.lineTo(axisx.p2c(x2), axisy.p2c(axisy.max)); + continue; + } + else if (y1 <= axisy.min && y2 <= axisy.min) { + ctx.lineTo(axisx.p2c(x1), axisy.p2c(axisy.min)); + ctx.lineTo(axisx.p2c(x2), axisy.p2c(axisy.min)); + continue; + } + + // else it's a bit more complicated, there might + // be a flat maxed out rectangle first, then a + // triangular cutout or reverse; to find these + // keep track of the current x values + var x1old = x1, x2old = x2; + + // clip the y values, without shortcutting, we + // go through all cases in turn + + // clip with ymin + if (y1 <= y2 && y1 < axisy.min && y2 >= axisy.min) { + x1 = (axisy.min - y1) / (y2 - y1) * (x2 - x1) + x1; + y1 = axisy.min; + } + else if (y2 <= y1 && y2 < axisy.min && y1 >= axisy.min) { + x2 = (axisy.min - y1) / (y2 - y1) * (x2 - x1) + x1; + y2 = axisy.min; + } + + // clip with ymax + if (y1 >= y2 && y1 > axisy.max && y2 <= axisy.max) { + x1 = (axisy.max - y1) / (y2 - y1) * (x2 - x1) + x1; + y1 = axisy.max; + } + else if (y2 >= y1 && y2 > axisy.max && y1 <= axisy.max) { + x2 = (axisy.max - y1) / (y2 - y1) * (x2 - x1) + x1; + y2 = axisy.max; + } + + // if the x value was changed we got a rectangle + // to fill + if (x1 != x1old) { + ctx.lineTo(axisx.p2c(x1old), axisy.p2c(y1)); + // it goes to (x1, y1), but we fill that below + } + + // fill triangular section, this sometimes result + // in redundant points if (x1, y1) hasn't changed + // from previous line to, but we just ignore that + ctx.lineTo(axisx.p2c(x1), axisy.p2c(y1)); + ctx.lineTo(axisx.p2c(x2), axisy.p2c(y2)); + + // fill the other rectangle if it's there + if (x2 != x2old) { + ctx.lineTo(axisx.p2c(x2), axisy.p2c(y2)); + ctx.lineTo(axisx.p2c(x2old), axisy.p2c(y2)); + } + } + } + + ctx.save(); + ctx.translate(plotOffset.left, plotOffset.top); + ctx.lineJoin = "round"; + + var lw = series.lines.lineWidth, + sw = series.shadowSize; + // FIXME: consider another form of shadow when filling is turned on + if (lw > 0 && sw > 0) { + // draw shadow as a thick and thin line with transparency + ctx.lineWidth = sw; + ctx.strokeStyle = "rgba(0,0,0,0.1)"; + // position shadow at angle from the mid of line + var angle = Math.PI/18; + plotLine(series.datapoints, Math.sin(angle) * (lw/2 + sw/2), Math.cos(angle) * (lw/2 + sw/2), series.xaxis, series.yaxis); + ctx.lineWidth = sw/2; + plotLine(series.datapoints, Math.sin(angle) * (lw/2 + sw/4), Math.cos(angle) * (lw/2 + sw/4), series.xaxis, series.yaxis); + } + + ctx.lineWidth = lw; + ctx.strokeStyle = series.color; + var fillStyle = getFillStyle(series.lines, series.color, 0, plotHeight); + if (fillStyle) { + ctx.fillStyle = fillStyle; + plotLineArea(series.datapoints, series.xaxis, series.yaxis); + } + + if (lw > 0) + plotLine(series.datapoints, 0, 0, series.xaxis, series.yaxis); + ctx.restore(); + } + + function drawSeriesPoints(series) { + function plotPoints(datapoints, radius, fillStyle, offset, shadow, axisx, axisy, symbol) { + var points = datapoints.points, ps = datapoints.pointsize; + + for (var i = 0; i < points.length; i += ps) { + var x = points[i], y = points[i + 1]; + if (x == null || x < axisx.min || x > axisx.max || y < axisy.min || y > axisy.max) + continue; + + ctx.beginPath(); + x = axisx.p2c(x); + y = axisy.p2c(y) + offset; + if (symbol == "circle") + ctx.arc(x, y, radius, 0, shadow ? Math.PI : Math.PI * 2, false); + else + symbol(ctx, x, y, radius, shadow); + ctx.closePath(); + + if (fillStyle) { + ctx.fillStyle = fillStyle; + ctx.fill(); + } + ctx.stroke(); + } + } + + ctx.save(); + ctx.translate(plotOffset.left, plotOffset.top); + + var lw = series.points.lineWidth, + sw = series.shadowSize, + radius = series.points.radius, + symbol = series.points.symbol; + + // If the user sets the line width to 0, we change it to a very + // small value. A line width of 0 seems to force the default of 1. + // Doing the conditional here allows the shadow setting to still be + // optional even with a lineWidth of 0. + + if( lw == 0 ) + lw = 0.0001; + + if (lw > 0 && sw > 0) { + // draw shadow in two steps + var w = sw / 2; + ctx.lineWidth = w; + ctx.strokeStyle = "rgba(0,0,0,0.1)"; + plotPoints(series.datapoints, radius, null, w + w/2, true, + series.xaxis, series.yaxis, symbol); + + ctx.strokeStyle = "rgba(0,0,0,0.2)"; + plotPoints(series.datapoints, radius, null, w/2, true, + series.xaxis, series.yaxis, symbol); + } + + ctx.lineWidth = lw; + ctx.strokeStyle = series.color; + plotPoints(series.datapoints, radius, + getFillStyle(series.points, series.color), 0, false, + series.xaxis, series.yaxis, symbol); + ctx.restore(); + } + + function drawBar(x, y, b, barLeft, barRight, fillStyleCallback, axisx, axisy, c, horizontal, lineWidth) { + var left, right, bottom, top, + drawLeft, drawRight, drawTop, drawBottom, + tmp; + + // in horizontal mode, we start the bar from the left + // instead of from the bottom so it appears to be + // horizontal rather than vertical + if (horizontal) { + drawBottom = drawRight = drawTop = true; + drawLeft = false; + left = b; + right = x; + top = y + barLeft; + bottom = y + barRight; + + // account for negative bars + if (right < left) { + tmp = right; + right = left; + left = tmp; + drawLeft = true; + drawRight = false; + } + } + else { + drawLeft = drawRight = drawTop = true; + drawBottom = false; + left = x + barLeft; + right = x + barRight; + bottom = b; + top = y; + + // account for negative bars + if (top < bottom) { + tmp = top; + top = bottom; + bottom = tmp; + drawBottom = true; + drawTop = false; + } + } + + // clip + if (right < axisx.min || left > axisx.max || + top < axisy.min || bottom > axisy.max) + return; + + if (left < axisx.min) { + left = axisx.min; + drawLeft = false; + } + + if (right > axisx.max) { + right = axisx.max; + drawRight = false; + } + + if (bottom < axisy.min) { + bottom = axisy.min; + drawBottom = false; + } + + if (top > axisy.max) { + top = axisy.max; + drawTop = false; + } + + left = axisx.p2c(left); + bottom = axisy.p2c(bottom); + right = axisx.p2c(right); + top = axisy.p2c(top); + + // fill the bar + if (fillStyleCallback) { + c.fillStyle = fillStyleCallback(bottom, top); + c.fillRect(left, top, right - left, bottom - top) + } + + // draw outline + if (lineWidth > 0 && (drawLeft || drawRight || drawTop || drawBottom)) { + c.beginPath(); + + // FIXME: inline moveTo is buggy with excanvas + c.moveTo(left, bottom); + if (drawLeft) + c.lineTo(left, top); + else + c.moveTo(left, top); + if (drawTop) + c.lineTo(right, top); + else + c.moveTo(right, top); + if (drawRight) + c.lineTo(right, bottom); + else + c.moveTo(right, bottom); + if (drawBottom) + c.lineTo(left, bottom); + else + c.moveTo(left, bottom); + c.stroke(); + } + } + + function drawSeriesBars(series) { + function plotBars(datapoints, barLeft, barRight, fillStyleCallback, axisx, axisy) { + var points = datapoints.points, ps = datapoints.pointsize; + + for (var i = 0; i < points.length; i += ps) { + if (points[i] == null) + continue; + drawBar(points[i], points[i + 1], points[i + 2], barLeft, barRight, fillStyleCallback, axisx, axisy, ctx, series.bars.horizontal, series.bars.lineWidth); + } + } + + ctx.save(); + ctx.translate(plotOffset.left, plotOffset.top); + + // FIXME: figure out a way to add shadows (for instance along the right edge) + ctx.lineWidth = series.bars.lineWidth; + ctx.strokeStyle = series.color; + + var barLeft; + + switch (series.bars.align) { + case "left": + barLeft = 0; + break; + case "right": + barLeft = -series.bars.barWidth; + break; + default: + barLeft = -series.bars.barWidth / 2; + } + + var fillStyleCallback = series.bars.fill ? function (bottom, top) { return getFillStyle(series.bars, series.color, bottom, top); } : null; + plotBars(series.datapoints, barLeft, barLeft + series.bars.barWidth, fillStyleCallback, series.xaxis, series.yaxis); + ctx.restore(); + } + + function getFillStyle(filloptions, seriesColor, bottom, top) { + var fill = filloptions.fill; + if (!fill) + return null; + + if (filloptions.fillColor) + return getColorOrGradient(filloptions.fillColor, bottom, top, seriesColor); + + var c = $.color.parse(seriesColor); + c.a = typeof fill == "number" ? fill : 0.4; + c.normalize(); + return c.toString(); + } + + function insertLegend() { + + if (options.legend.container != null) { + $(options.legend.container).html(""); + } else { + placeholder.find(".legend").remove(); + } + + if (!options.legend.show) { + return; + } + + var fragments = [], entries = [], rowStarted = false, + lf = options.legend.labelFormatter, s, label; + + // Build a list of legend entries, with each having a label and a color + + for (var i = 0; i < series.length; ++i) { + s = series[i]; + if (s.label) { + label = lf ? lf(s.label, s) : s.label; + if (label) { + entries.push({ + label: label, + color: s.color + }); + } + } + } + + // Sort the legend using either the default or a custom comparator + + if (options.legend.sorted) { + if ($.isFunction(options.legend.sorted)) { + entries.sort(options.legend.sorted); + } else if (options.legend.sorted == "reverse") { + entries.reverse(); + } else { + var ascending = options.legend.sorted != "descending"; + entries.sort(function(a, b) { + return a.label == b.label ? 0 : ( + (a.label < b.label) != ascending ? 1 : -1 // Logical XOR + ); + }); + } + } + + // Generate markup for the list of entries, in their final order + + for (var i = 0; i < entries.length; ++i) { + + var entry = entries[i]; + + if (i % options.legend.noColumns == 0) { + if (rowStarted) + fragments.push('
' + entry.label + '
' + fragments.join("") + '
'; + if (options.legend.container != null) + $(options.legend.container).html(table); + else { + var pos = "", + p = options.legend.position, + m = options.legend.margin; + if (m[0] == null) + m = [m, m]; + if (p.charAt(0) == "n") + pos += 'top:' + (m[1] + plotOffset.top) + 'px;'; + else if (p.charAt(0) == "s") + pos += 'bottom:' + (m[1] + plotOffset.bottom) + 'px;'; + if (p.charAt(1) == "e") + pos += 'right:' + (m[0] + plotOffset.right) + 'px;'; + else if (p.charAt(1) == "w") + pos += 'left:' + (m[0] + plotOffset.left) + 'px;'; + var legend = $('
' + table.replace('style="', 'style="position:absolute;' + pos +';') + '
').appendTo(placeholder); + if (options.legend.backgroundOpacity != 0.0) { + // put in the transparent background + // separately to avoid blended labels and + // label boxes + var c = options.legend.backgroundColor; + if (c == null) { + c = options.grid.backgroundColor; + if (c && typeof c == "string") + c = $.color.parse(c); + else + c = $.color.extract(legend, 'background-color'); + c.a = 1; + c = c.toString(); + } + var div = legend.children(); + $('
').prependTo(legend).css('opacity', options.legend.backgroundOpacity); + } + } + } + + + // interactive features + + var highlights = [], + redrawTimeout = null; + + // returns the data item the mouse is over, or null if none is found + function findNearbyItem(mouseX, mouseY, seriesFilter) { + var maxDistance = options.grid.mouseActiveRadius, + smallestDistance = maxDistance * maxDistance + 1, + item = null, foundPoint = false, i, j, ps; + + for (i = series.length - 1; i >= 0; --i) { + if (!seriesFilter(series[i])) + continue; + + var s = series[i], + axisx = s.xaxis, + axisy = s.yaxis, + points = s.datapoints.points, + mx = axisx.c2p(mouseX), // precompute some stuff to make the loop faster + my = axisy.c2p(mouseY), + maxx = maxDistance / axisx.scale, + maxy = maxDistance / axisy.scale; + + ps = s.datapoints.pointsize; + // with inverse transforms, we can't use the maxx/maxy + // optimization, sadly + if (axisx.options.inverseTransform) + maxx = Number.MAX_VALUE; + if (axisy.options.inverseTransform) + maxy = Number.MAX_VALUE; + + if (s.lines.show || s.points.show) { + for (j = 0; j < points.length; j += ps) { + var x = points[j], y = points[j + 1]; + if (x == null) + continue; + + // For points and lines, the cursor must be within a + // certain distance to the data point + if (x - mx > maxx || x - mx < -maxx || + y - my > maxy || y - my < -maxy) + continue; + + // We have to calculate distances in pixels, not in + // data units, because the scales of the axes may be different + var dx = Math.abs(axisx.p2c(x) - mouseX), + dy = Math.abs(axisy.p2c(y) - mouseY), + dist = dx * dx + dy * dy; // we save the sqrt + + // use <= to ensure last point takes precedence + // (last generally means on top of) + if (dist < smallestDistance) { + smallestDistance = dist; + item = [i, j / ps]; + } + } + } + + if (s.bars.show && !item) { // no other point can be nearby + + var barLeft, barRight; + + switch (s.bars.align) { + case "left": + barLeft = 0; + break; + case "right": + barLeft = -s.bars.barWidth; + break; + default: + barLeft = -s.bars.barWidth / 2; + } + + barRight = barLeft + s.bars.barWidth; + + for (j = 0; j < points.length; j += ps) { + var x = points[j], y = points[j + 1], b = points[j + 2]; + if (x == null) + continue; + + // for a bar graph, the cursor must be inside the bar + if (series[i].bars.horizontal ? + (mx <= Math.max(b, x) && mx >= Math.min(b, x) && + my >= y + barLeft && my <= y + barRight) : + (mx >= x + barLeft && mx <= x + barRight && + my >= Math.min(b, y) && my <= Math.max(b, y))) + item = [i, j / ps]; + } + } + } + + if (item) { + i = item[0]; + j = item[1]; + ps = series[i].datapoints.pointsize; + + return { datapoint: series[i].datapoints.points.slice(j * ps, (j + 1) * ps), + dataIndex: j, + series: series[i], + seriesIndex: i }; + } + + return null; + } + + function onMouseMove(e) { + if (options.grid.hoverable) + triggerClickHoverEvent("plothover", e, + function (s) { return s["hoverable"] != false; }); + } + + function onMouseLeave(e) { + if (options.grid.hoverable) + triggerClickHoverEvent("plothover", e, + function (s) { return false; }); + } + + function onClick(e) { + triggerClickHoverEvent("plotclick", e, + function (s) { return s["clickable"] != false; }); + } + + // trigger click or hover event (they send the same parameters + // so we share their code) + function triggerClickHoverEvent(eventname, event, seriesFilter) { + var offset = eventHolder.offset(), + canvasX = event.pageX - offset.left - plotOffset.left, + canvasY = event.pageY - offset.top - plotOffset.top, + pos = canvasToAxisCoords({ left: canvasX, top: canvasY }); + + pos.pageX = event.pageX; + pos.pageY = event.pageY; + + var item = findNearbyItem(canvasX, canvasY, seriesFilter); + + if (item) { + // fill in mouse pos for any listeners out there + item.pageX = parseInt(item.series.xaxis.p2c(item.datapoint[0]) + offset.left + plotOffset.left, 10); + item.pageY = parseInt(item.series.yaxis.p2c(item.datapoint[1]) + offset.top + plotOffset.top, 10); + } + + if (options.grid.autoHighlight) { + // clear auto-highlights + for (var i = 0; i < highlights.length; ++i) { + var h = highlights[i]; + if (h.auto == eventname && + !(item && h.series == item.series && + h.point[0] == item.datapoint[0] && + h.point[1] == item.datapoint[1])) + unhighlight(h.series, h.point); + } + + if (item) + highlight(item.series, item.datapoint, eventname); + } + + placeholder.trigger(eventname, [ pos, item ]); + } + + function triggerRedrawOverlay() { + var t = options.interaction.redrawOverlayInterval; + if (t == -1) { // skip event queue + drawOverlay(); + return; + } + + if (!redrawTimeout) + redrawTimeout = setTimeout(drawOverlay, t); + } + + function drawOverlay() { + redrawTimeout = null; + + // draw highlights + octx.save(); + overlay.clear(); + octx.translate(plotOffset.left, plotOffset.top); + + var i, hi; + for (i = 0; i < highlights.length; ++i) { + hi = highlights[i]; + + if (hi.series.bars.show) + drawBarHighlight(hi.series, hi.point); + else + drawPointHighlight(hi.series, hi.point); + } + octx.restore(); + + executeHooks(hooks.drawOverlay, [octx]); + } + + function highlight(s, point, auto) { + if (typeof s == "number") + s = series[s]; + + if (typeof point == "number") { + var ps = s.datapoints.pointsize; + point = s.datapoints.points.slice(ps * point, ps * (point + 1)); + } + + var i = indexOfHighlight(s, point); + if (i == -1) { + highlights.push({ series: s, point: point, auto: auto }); + + triggerRedrawOverlay(); + } + else if (!auto) + highlights[i].auto = false; + } + + function unhighlight(s, point) { + if (s == null && point == null) { + highlights = []; + triggerRedrawOverlay(); + return; + } + + if (typeof s == "number") + s = series[s]; + + if (typeof point == "number") { + var ps = s.datapoints.pointsize; + point = s.datapoints.points.slice(ps * point, ps * (point + 1)); + } + + var i = indexOfHighlight(s, point); + if (i != -1) { + highlights.splice(i, 1); + + triggerRedrawOverlay(); + } + } + + function indexOfHighlight(s, p) { + for (var i = 0; i < highlights.length; ++i) { + var h = highlights[i]; + if (h.series == s && h.point[0] == p[0] + && h.point[1] == p[1]) + return i; + } + return -1; + } + + function drawPointHighlight(series, point) { + var x = point[0], y = point[1], + axisx = series.xaxis, axisy = series.yaxis, + highlightColor = (typeof series.highlightColor === "string") ? series.highlightColor : $.color.parse(series.color).scale('a', 0.5).toString(); + + if (x < axisx.min || x > axisx.max || y < axisy.min || y > axisy.max) + return; + + var pointRadius = series.points.radius + series.points.lineWidth / 2; + octx.lineWidth = pointRadius; + octx.strokeStyle = highlightColor; + var radius = 1.5 * pointRadius; + x = axisx.p2c(x); + y = axisy.p2c(y); + + octx.beginPath(); + if (series.points.symbol == "circle") + octx.arc(x, y, radius, 0, 2 * Math.PI, false); + else + series.points.symbol(octx, x, y, radius, false); + octx.closePath(); + octx.stroke(); + } + + function drawBarHighlight(series, point) { + var highlightColor = (typeof series.highlightColor === "string") ? series.highlightColor : $.color.parse(series.color).scale('a', 0.5).toString(), + fillStyle = highlightColor, + barLeft; + + switch (series.bars.align) { + case "left": + barLeft = 0; + break; + case "right": + barLeft = -series.bars.barWidth; + break; + default: + barLeft = -series.bars.barWidth / 2; + } + + octx.lineWidth = series.bars.lineWidth; + octx.strokeStyle = highlightColor; + + drawBar(point[0], point[1], point[2] || 0, barLeft, barLeft + series.bars.barWidth, + function () { return fillStyle; }, series.xaxis, series.yaxis, octx, series.bars.horizontal, series.bars.lineWidth); + } + + function getColorOrGradient(spec, bottom, top, defaultColor) { + if (typeof spec == "string") + return spec; + else { + // assume this is a gradient spec; IE currently only + // supports a simple vertical gradient properly, so that's + // what we support too + var gradient = ctx.createLinearGradient(0, top, 0, bottom); + + for (var i = 0, l = spec.colors.length; i < l; ++i) { + var c = spec.colors[i]; + if (typeof c != "string") { + var co = $.color.parse(defaultColor); + if (c.brightness != null) + co = co.scale('rgb', c.brightness); + if (c.opacity != null) + co.a *= c.opacity; + c = co.toString(); + } + gradient.addColorStop(i / (l - 1), c); + } + + return gradient; + } + } + } + + // Add the plot function to the top level of the jQuery object + + $.plot = function(placeholder, data, options) { + //var t0 = new Date(); + var plot = new Plot($(placeholder), data, options, $.plot.plugins); + //(window.console ? console.log : alert)("time used (msecs): " + ((new Date()).getTime() - t0.getTime())); + return plot; + }; + + $.plot.version = "0.8.3"; + + $.plot.plugins = []; + + // Also add the plot function as a chainable property + + $.fn.plot = function(data, options) { + return this.each(function() { + $.plot(this, data, options); + }); + }; + + // round to nearby lower multiple of base + function floorInBase(n, base) { + return base * Math.floor(n / base); + } + +})(jQuery); diff --git a/pandora_console/include/graphs/flot/jquery.flot.min.js b/pandora_console/include/graphs/flot/jquery.flot.min.js index a25407dd43..968d3ebd99 100644 --- a/pandora_console/include/graphs/flot/jquery.flot.min.js +++ b/pandora_console/include/graphs/flot/jquery.flot.min.js @@ -1,6 +1,8 @@ -/* Javascript plotting library for jQuery, v. 0.7. - * - * Released under the MIT license by IOLA, December 2007. - * - */ -(function(b){b.color={};b.color.make=function(d,e,g,f){var c={};c.r=d||0;c.g=e||0;c.b=g||0;c.a=f!=null?f:1;c.add=function(h,j){for(var k=0;k=1){return"rgb("+[c.r,c.g,c.b].join(",")+")"}else{return"rgba("+[c.r,c.g,c.b,c.a].join(",")+")"}};c.normalize=function(){function h(k,j,l){return jl?l:j)}c.r=h(0,parseInt(c.r),255);c.g=h(0,parseInt(c.g),255);c.b=h(0,parseInt(c.b),255);c.a=h(0,c.a,1);return c};c.clone=function(){return b.color.make(c.r,c.b,c.g,c.a)};return c.normalize()};b.color.extract=function(d,e){var c;do{c=d.css(e).toLowerCase();if(c!=""&&c!="transparent"){break}d=d.parent()}while(!b.nodeName(d.get(0),"body"));if(c=="rgba(0, 0, 0, 0)"){c="transparent"}return b.color.parse(c)};b.color.parse=function(c){var d,f=b.color.make;if(d=/rgb\(\s*([0-9]{1,3})\s*,\s*([0-9]{1,3})\s*,\s*([0-9]{1,3})\s*\)/.exec(c)){return f(parseInt(d[1],10),parseInt(d[2],10),parseInt(d[3],10))}if(d=/rgba\(\s*([0-9]{1,3})\s*,\s*([0-9]{1,3})\s*,\s*([0-9]{1,3})\s*,\s*([0-9]+(?:\.[0-9]+)?)\s*\)/.exec(c)){return f(parseInt(d[1],10),parseInt(d[2],10),parseInt(d[3],10),parseFloat(d[4]))}if(d=/rgb\(\s*([0-9]+(?:\.[0-9]+)?)\%\s*,\s*([0-9]+(?:\.[0-9]+)?)\%\s*,\s*([0-9]+(?:\.[0-9]+)?)\%\s*\)/.exec(c)){return f(parseFloat(d[1])*2.55,parseFloat(d[2])*2.55,parseFloat(d[3])*2.55)}if(d=/rgba\(\s*([0-9]+(?:\.[0-9]+)?)\%\s*,\s*([0-9]+(?:\.[0-9]+)?)\%\s*,\s*([0-9]+(?:\.[0-9]+)?)\%\s*,\s*([0-9]+(?:\.[0-9]+)?)\s*\)/.exec(c)){return f(parseFloat(d[1])*2.55,parseFloat(d[2])*2.55,parseFloat(d[3])*2.55,parseFloat(d[4]))}if(d=/#([a-fA-F0-9]{2})([a-fA-F0-9]{2})([a-fA-F0-9]{2})/.exec(c)){return f(parseInt(d[1],16),parseInt(d[2],16),parseInt(d[3],16))}if(d=/#([a-fA-F0-9])([a-fA-F0-9])([a-fA-F0-9])/.exec(c)){return f(parseInt(d[1]+d[1],16),parseInt(d[2]+d[2],16),parseInt(d[3]+d[3],16))}var e=b.trim(c).toLowerCase();if(e=="transparent"){return f(255,255,255,0)}else{d=a[e]||[0,0,0];return f(d[0],d[1],d[2])}};var a={aqua:[0,255,255],azure:[240,255,255],beige:[245,245,220],black:[0,0,0],blue:[0,0,255],brown:[165,42,42],cyan:[0,255,255],darkblue:[0,0,139],darkcyan:[0,139,139],darkgrey:[169,169,169],darkgreen:[0,100,0],darkkhaki:[189,183,107],darkmagenta:[139,0,139],darkolivegreen:[85,107,47],darkorange:[255,140,0],darkorchid:[153,50,204],darkred:[139,0,0],darksalmon:[233,150,122],darkviolet:[148,0,211],fuchsia:[255,0,255],gold:[255,215,0],green:[0,128,0],indigo:[75,0,130],khaki:[240,230,140],lightblue:[173,216,230],lightcyan:[224,255,255],lightgreen:[144,238,144],lightgrey:[211,211,211],lightpink:[255,182,193],lightyellow:[255,255,224],lime:[0,255,0],magenta:[255,0,255],maroon:[128,0,0],navy:[0,0,128],olive:[128,128,0],orange:[255,165,0],pink:[255,192,203],purple:[128,0,128],violet:[128,0,128],red:[255,0,0],silver:[192,192,192],white:[255,255,255],yellow:[255,255,0]}})(jQuery);(function(c){function b(av,ai,J,af){var Q=[],O={colors:["#edc240","#afd8f8","#cb4b4b","#4da74d","#9440ed"],legend:{show:true,noColumns:1,labelFormatter:null,labelBoxBorderColor:"#ccc",container:null,position:"ne",margin:5,backgroundColor:null,backgroundOpacity:0.85},xaxis:{show:null,position:"bottom",mode:null,color:null,tickColor:null,transform:null,inverseTransform:null,min:null,max:null,autoscaleMargin:null,ticks:null,tickFormatter:null,labelWidth:null,labelHeight:null,reserveSpace:null,tickLength:null,alignTicksWithAxis:null,tickDecimals:null,tickSize:null,minTickSize:null,monthNames:null,timeformat:null,twelveHourClock:false},yaxis:{autoscaleMargin:0.02,position:"left"},xaxes:[],yaxes:[],series:{points:{show:false,radius:3,lineWidth:2,fill:true,fillColor:"#ffffff",symbol:"circle"},lines:{lineWidth:2,fill:false,fillColor:null,steps:false},bars:{show:false,lineWidth:2,barWidth:1,fill:true,fillColor:null,align:"left",horizontal:false},shadowSize:3},grid:{show:true,aboveData:false,backgroundColor:null,borderColor:null,tickColor:null,labelMargin:5,axisMargin:8,borderWidth:2,minBorderMargin:null,markings:null,markingsColor:"#f4f4f4",markingsLineWidth:2,clickable:false,hoverable:false,autoHighlight:true,mouseActiveRadius:10},hooks:{}},az=null,ad=null,y=null,H=null,A=null,p=[],aw=[],q={left:0,right:0,top:0,bottom:0},G=0,I=0,h=0,w=0,ak={processOptions:[],processRawData:[],processDatapoints:[],drawSeries:[],draw:[],bindEvents:[],drawOverlay:[],shutdown:[]},aq=this;aq.setData=aj;aq.setupGrid=t;aq.draw=W;aq.getPlaceholder=function(){return av};aq.getCanvas=function(){return az};aq.getPlotOffset=function(){return q};aq.width=function(){return h};aq.height=function(){return w};aq.offset=function(){var aB=y.offset();aB.left+=q.left;aB.top+=q.top;return aB};aq.getData=function(){return Q};aq.getAxes=function(){var aC={},aB;c.each(p.concat(aw),function(aD,aE){if(aE){aC[aE.direction+(aE.n!=1?aE.n:"")+"axis"]=aE}});return aC};aq.getXAxes=function(){return p};aq.getYAxes=function(){return aw};aq.c2p=C;aq.p2c=ar;aq.getOptions=function(){return O};aq.highlight=x;aq.unhighlight=T;aq.triggerRedrawOverlay=f;aq.pointOffset=function(aB){return{left:parseInt(p[aA(aB,"x")-1].p2c(+aB.x)+q.left),top:parseInt(aw[aA(aB,"y")-1].p2c(+aB.y)+q.top)}};aq.shutdown=ag;aq.resize=function(){B();g(az);g(ad)};aq.hooks=ak;F(aq);Z(J);X();aj(ai);t();W();ah();function an(aD,aB){aB=[aq].concat(aB);for(var aC=0;aC=O.colors.length){aG=0;++aF}}var aH=0,aN;for(aG=0;aGa3.datamax&&a1!=aB){a3.datamax=a1}}c.each(m(),function(a1,a2){a2.datamin=aO;a2.datamax=aI;a2.used=false});for(aU=0;aU0&&aT[aR-aP]!=null&&aT[aR-aP]!=aT[aR]&&aT[aR-aP+1]!=aT[aR+1]){for(aN=0;aNaM){aM=a0}}if(aX.y){if(a0aV){aV=a0}}}}if(aJ.bars.show){var aY=aJ.bars.align=="left"?0:-aJ.bars.barWidth/2;if(aJ.bars.horizontal){aQ+=aY;aV+=aY+aJ.bars.barWidth}else{aK+=aY;aM+=aY+aJ.bars.barWidth}}aF(aJ.xaxis,aK,aM);aF(aJ.yaxis,aQ,aV)}c.each(m(),function(a1,a2){if(a2.datamin==aO){a2.datamin=null}if(a2.datamax==aI){a2.datamax=null}})}function j(aB,aC){var aD=document.createElement("canvas");aD.className=aC;aD.width=G;aD.height=I;if(!aB){c(aD).css({position:"absolute",left:0,top:0})}c(aD).appendTo(av);if(!aD.getContext){aD=window.G_vmlCanvasManager.initElement(aD)}aD.getContext("2d").save();return aD}function B(){G=av.width();I=av.height();if(G<=0||I<=0){throw"Invalid dimensions for plot, width = "+G+", height = "+I}}function g(aC){if(aC.width!=G){aC.width=G}if(aC.height!=I){aC.height=I}var aB=aC.getContext("2d");aB.restore();aB.save()}function X(){var aC,aB=av.children("canvas.base"),aD=av.children("canvas.overlay");if(aB.length==0||aD==0){av.html("");av.css({padding:0});if(av.css("position")=="static"){av.css("position","relative")}B();az=j(true,"base");ad=j(false,"overlay");aC=false}else{az=aB.get(0);ad=aD.get(0);aC=true}H=az.getContext("2d");A=ad.getContext("2d");y=c([ad,az]);if(aC){av.data("plot").shutdown();aq.resize();A.clearRect(0,0,G,I);y.unbind();av.children().not([az,ad]).remove()}av.data("plot",aq)}function ah(){if(O.grid.hoverable){y.mousemove(aa);y.mouseleave(l)}if(O.grid.clickable){y.click(R)}an(ak.bindEvents,[y])}function ag(){if(M){clearTimeout(M)}y.unbind("mousemove",aa);y.unbind("mouseleave",l);y.unbind("click",R);an(ak.shutdown,[y])}function r(aG){function aC(aH){return aH}var aF,aB,aD=aG.options.transform||aC,aE=aG.options.inverseTransform;if(aG.direction=="x"){aF=aG.scale=h/Math.abs(aD(aG.max)-aD(aG.min));aB=Math.min(aD(aG.max),aD(aG.min))}else{aF=aG.scale=w/Math.abs(aD(aG.max)-aD(aG.min));aF=-aF;aB=Math.max(aD(aG.max),aD(aG.min))}if(aD==aC){aG.p2c=function(aH){return(aH-aB)*aF}}else{aG.p2c=function(aH){return(aD(aH)-aB)*aF}}if(!aE){aG.c2p=function(aH){return aB+aH/aF}}else{aG.c2p=function(aH){return aE(aB+aH/aF)}}}function L(aD){var aB=aD.options,aF,aJ=aD.ticks||[],aI=[],aE,aK=aB.labelWidth,aG=aB.labelHeight,aC;function aH(aM,aL){return c('
'+aM.join("")+"
").appendTo(av)}if(aD.direction=="x"){if(aK==null){aK=Math.floor(G/(aJ.length>0?aJ.length:1))}if(aG==null){aI=[];for(aF=0;aF'+aE+"
")}}if(aI.length>0){aI.push('
');aC=aH(aI,"width:10000px;");aG=aC.height();aC.remove()}}}else{if(aK==null||aG==null){for(aF=0;aF'+aE+"")}}if(aI.length>0){aC=aH(aI,"");if(aK==null){aK=aC.children().width()}if(aG==null){aG=aC.find("div.tickLabel").height()}aC.remove()}}}if(aK==null){aK=0}if(aG==null){aG=0}aD.labelWidth=aK;aD.labelHeight=aG}function au(aD){var aC=aD.labelWidth,aL=aD.labelHeight,aH=aD.options.position,aF=aD.options.tickLength,aG=O.grid.axisMargin,aJ=O.grid.labelMargin,aK=aD.direction=="x"?p:aw,aE;var aB=c.grep(aK,function(aN){return aN&&aN.options.position==aH&&aN.reserveSpace});if(c.inArray(aD,aB)==aB.length-1){aG=0}if(aF==null){aF="full"}var aI=c.grep(aK,function(aN){return aN&&aN.reserveSpace});var aM=c.inArray(aD,aI)==0;if(!aM&&aF=="full"){aF=5}if(!isNaN(+aF)){aJ+=+aF}if(aD.direction=="x"){aL+=aJ;if(aH=="bottom"){q.bottom+=aL+aG;aD.box={top:I-q.bottom,height:aL}}else{aD.box={top:q.top+aG,height:aL};q.top+=aL+aG}}else{aC+=aJ;if(aH=="left"){aD.box={left:q.left+aG,width:aC};q.left+=aC+aG}else{q.right+=aC+aG;aD.box={left:G-q.right,width:aC}}}aD.position=aH;aD.tickLength=aF;aD.box.padding=aJ;aD.innermost=aM}function U(aB){if(aB.direction=="x"){aB.box.left=q.left;aB.box.width=h}else{aB.box.top=q.top;aB.box.height=w}}function t(){var aC,aE=m();c.each(aE,function(aF,aG){aG.show=aG.options.show;if(aG.show==null){aG.show=aG.used}aG.reserveSpace=aG.show||aG.options.reserveSpace;n(aG)});allocatedAxes=c.grep(aE,function(aF){return aF.reserveSpace});q.left=q.right=q.top=q.bottom=0;if(O.grid.show){c.each(allocatedAxes,function(aF,aG){S(aG);P(aG);ap(aG,aG.ticks);L(aG)});for(aC=allocatedAxes.length-1;aC>=0;--aC){au(allocatedAxes[aC])}var aD=O.grid.minBorderMargin;if(aD==null){aD=0;for(aC=0;aC=0){aD=0}}if(aF.max==null){aB+=aH*aG;if(aB>0&&aE.datamax!=null&&aE.datamax<=0){aB=0}}}}aE.min=aD;aE.max=aB}function S(aG){var aM=aG.options;var aH;if(typeof aM.ticks=="number"&&aM.ticks>0){aH=aM.ticks}else{aH=0.3*Math.sqrt(aG.direction=="x"?G:I)}var aT=(aG.max-aG.min)/aH,aO,aB,aN,aR,aS,aQ,aI;if(aM.mode=="time"){var aJ={second:1000,minute:60*1000,hour:60*60*1000,day:24*60*60*1000,month:30*24*60*60*1000,year:365.2425*24*60*60*1000};var aK=[[1,"second"],[2,"second"],[5,"second"],[10,"second"],[30,"second"],[1,"minute"],[2,"minute"],[5,"minute"],[10,"minute"],[30,"minute"],[1,"hour"],[2,"hour"],[4,"hour"],[8,"hour"],[12,"hour"],[1,"day"],[2,"day"],[3,"day"],[0.25,"month"],[0.5,"month"],[1,"month"],[2,"month"],[3,"month"],[6,"month"],[1,"year"]];var aC=0;if(aM.minTickSize!=null){if(typeof aM.tickSize=="number"){aC=aM.tickSize}else{aC=aM.minTickSize[0]*aJ[aM.minTickSize[1]]}}for(var aS=0;aS=aC){break}}aO=aK[aS][0];aN=aK[aS][1];if(aN=="year"){aQ=Math.pow(10,Math.floor(Math.log(aT/aJ.year)/Math.LN10));aI=(aT/aJ.year)/aQ;if(aI<1.5){aO=1}else{if(aI<3){aO=2}else{if(aI<7.5){aO=5}else{aO=10}}}aO*=aQ}aG.tickSize=aM.tickSize||[aO,aN];aB=function(aX){var a2=[],a0=aX.tickSize[0],a3=aX.tickSize[1],a1=new Date(aX.min);var aW=a0*aJ[a3];if(a3=="second"){a1.setUTCSeconds(a(a1.getUTCSeconds(),a0))}if(a3=="minute"){a1.setUTCMinutes(a(a1.getUTCMinutes(),a0))}if(a3=="hour"){a1.setUTCHours(a(a1.getUTCHours(),a0))}if(a3=="month"){a1.setUTCMonth(a(a1.getUTCMonth(),a0))}if(a3=="year"){a1.setUTCFullYear(a(a1.getUTCFullYear(),a0))}a1.setUTCMilliseconds(0);if(aW>=aJ.minute){a1.setUTCSeconds(0)}if(aW>=aJ.hour){a1.setUTCMinutes(0)}if(aW>=aJ.day){a1.setUTCHours(0)}if(aW>=aJ.day*4){a1.setUTCDate(1)}if(aW>=aJ.year){a1.setUTCMonth(0)}var a5=0,a4=Number.NaN,aY;do{aY=a4;a4=a1.getTime();a2.push(a4);if(a3=="month"){if(a0<1){a1.setUTCDate(1);var aV=a1.getTime();a1.setUTCMonth(a1.getUTCMonth()+1);var aZ=a1.getTime();a1.setTime(a4+a5*aJ.hour+(aZ-aV)*a0);a5=a1.getUTCHours();a1.setUTCHours(0)}else{a1.setUTCMonth(a1.getUTCMonth()+a0)}}else{if(a3=="year"){a1.setUTCFullYear(a1.getUTCFullYear()+a0)}else{a1.setTime(a4+aW)}}}while(a4aU){aP=aU}aQ=Math.pow(10,-aP);aI=aT/aQ;if(aI<1.5){aO=1}else{if(aI<3){aO=2;if(aI>2.25&&(aU==null||aP+1<=aU)){aO=2.5;++aP}}else{if(aI<7.5){aO=5}else{aO=10}}}aO*=aQ;if(aM.minTickSize!=null&&aO0){if(aM.min==null){aG.min=Math.min(aG.min,aL[0])}if(aM.max==null&&aL.length>1){aG.max=Math.max(aG.max,aL[aL.length-1])}}aB=function(aX){var aY=[],aV,aW;for(aW=0;aW1&&/\..*0$/.test((aD[1]-aD[0]).toFixed(aE)))){aG.tickDecimals=aE}}}}aG.tickGenerator=aB;if(c.isFunction(aM.tickFormatter)){aG.tickFormatter=function(aV,aW){return""+aM.tickFormatter(aV,aW)}}else{aG.tickFormatter=aR}}function P(aF){var aH=aF.options.ticks,aG=[];if(aH==null||(typeof aH=="number"&&aH>0)){aG=aF.tickGenerator(aF)}else{if(aH){if(c.isFunction(aH)){aG=aH({min:aF.min,max:aF.max})}else{aG=aH}}}var aE,aB;aF.ticks=[];for(aE=0;aE1){aC=aD[1]}}else{aB=+aD}if(aC==null){aC=aF.tickFormatter(aB,aF)}if(!isNaN(aB)){aF.ticks.push({v:aB,label:aC})}}}function ap(aB,aC){if(aB.options.autoscaleMargin&&aC.length>0){if(aB.options.min==null){aB.min=Math.min(aB.min,aC[0].v)}if(aB.options.max==null&&aC.length>1){aB.max=Math.max(aB.max,aC[aC.length-1].v)}}}function W(){H.clearRect(0,0,G,I);var aC=O.grid;if(aC.show&&aC.backgroundColor){N()}if(aC.show&&!aC.aboveData){ac()}for(var aB=0;aBaG){var aC=aH;aH=aG;aG=aC}return{from:aH,to:aG,axis:aE}}function N(){H.save();H.translate(q.left,q.top);H.fillStyle=am(O.grid.backgroundColor,w,0,"rgba(255, 255, 255, 0)");H.fillRect(0,0,h,w);H.restore()}function ac(){var aF;H.save();H.translate(q.left,q.top);var aH=O.grid.markings;if(aH){if(c.isFunction(aH)){var aK=aq.getAxes();aK.xmin=aK.xaxis.min;aK.xmax=aK.xaxis.max;aK.ymin=aK.yaxis.min;aK.ymax=aK.yaxis.max;aH=aH(aK)}for(aF=0;aFaC.axis.max||aI.toaI.axis.max){continue}aC.from=Math.max(aC.from,aC.axis.min);aC.to=Math.min(aC.to,aC.axis.max);aI.from=Math.max(aI.from,aI.axis.min);aI.to=Math.min(aI.to,aI.axis.max);if(aC.from==aC.to&&aI.from==aI.to){continue}aC.from=aC.axis.p2c(aC.from);aC.to=aC.axis.p2c(aC.to);aI.from=aI.axis.p2c(aI.from);aI.to=aI.axis.p2c(aI.to);if(aC.from==aC.to||aI.from==aI.to){H.beginPath();H.strokeStyle=aD.color||O.grid.markingsColor;H.lineWidth=aD.lineWidth||O.grid.markingsLineWidth;H.moveTo(aC.from,aI.from);H.lineTo(aC.to,aI.to);H.stroke()}else{H.fillStyle=aD.color||O.grid.markingsColor;H.fillRect(aC.from,aI.to,aC.to-aC.from,aI.from-aI.to)}}}var aK=m(),aM=O.grid.borderWidth;for(var aE=0;aEaB.max||(aQ=="full"&&aM>0&&(aO==aB.min||aO==aB.max))){continue}if(aB.direction=="x"){aN=aB.p2c(aO);aJ=aQ=="full"?-w:aQ;if(aB.position=="top"){aJ=-aJ}}else{aL=aB.p2c(aO);aP=aQ=="full"?-h:aQ;if(aB.position=="left"){aP=-aP}}if(H.lineWidth==1){if(aB.direction=="x"){aN=Math.floor(aN)+0.5}else{aL=Math.floor(aL)+0.5}}H.moveTo(aN,aL);H.lineTo(aN+aP,aL+aJ)}H.stroke()}if(aM){H.lineWidth=aM;H.strokeStyle=O.grid.borderColor;H.strokeRect(-aM/2,-aM/2,h+aM,w+aM)}H.restore()}function k(){av.find(".tickLabels").remove();var aG=['
'];var aJ=m();for(var aD=0;aD');for(var aE=0;aEaC.max){continue}var aK={},aI;if(aC.direction=="x"){aI="center";aK.left=Math.round(q.left+aC.p2c(aH.v)-aC.labelWidth/2);if(aC.position=="bottom"){aK.top=aF.top+aF.padding}else{aK.bottom=I-(aF.top+aF.height-aF.padding)}}else{aK.top=Math.round(q.top+aC.p2c(aH.v)-aC.labelHeight/2);if(aC.position=="left"){aK.right=G-(aF.left+aF.width-aF.padding);aI="right"}else{aK.left=aF.left+aF.padding;aI="left"}}aK.width=aC.labelWidth;var aB=["position:absolute","text-align:"+aI];for(var aL in aK){aB.push(aL+":"+aK[aL]+"px")}aG.push('
'+aH.label+"
")}aG.push("
")}aG.push("");av.append(aG.join(""))}function d(aB){if(aB.lines.show){at(aB)}if(aB.bars.show){e(aB)}if(aB.points.show){ao(aB)}}function at(aE){function aD(aP,aQ,aI,aU,aT){var aV=aP.points,aJ=aP.pointsize,aN=null,aM=null;H.beginPath();for(var aO=aJ;aO=aR&&aS>aT.max){if(aR>aT.max){continue}aL=(aT.max-aS)/(aR-aS)*(aK-aL)+aL;aS=aT.max}else{if(aR>=aS&&aR>aT.max){if(aS>aT.max){continue}aK=(aT.max-aS)/(aR-aS)*(aK-aL)+aL;aR=aT.max}}if(aL<=aK&&aL=aK&&aL>aU.max){if(aK>aU.max){continue}aS=(aU.max-aL)/(aK-aL)*(aR-aS)+aS;aL=aU.max}else{if(aK>=aL&&aK>aU.max){if(aL>aU.max){continue}aR=(aU.max-aL)/(aK-aL)*(aR-aS)+aS;aK=aU.max}}if(aL!=aN||aS!=aM){H.moveTo(aU.p2c(aL)+aQ,aT.p2c(aS)+aI)}aN=aK;aM=aR;H.lineTo(aU.p2c(aK)+aQ,aT.p2c(aR)+aI)}H.stroke()}function aF(aI,aQ,aP){var aW=aI.points,aV=aI.pointsize,aN=Math.min(Math.max(0,aP.min),aP.max),aX=0,aU,aT=false,aM=1,aL=0,aR=0;while(true){if(aV>0&&aX>aW.length+aV){break}aX+=aV;var aZ=aW[aX-aV],aK=aW[aX-aV+aM],aY=aW[aX],aJ=aW[aX+aM];if(aT){if(aV>0&&aZ!=null&&aY==null){aR=aX;aV=-aV;aM=2;continue}if(aV<0&&aX==aL+aV){H.fill();aT=false;aV=-aV;aM=1;aX=aL=aR+aV;continue}}if(aZ==null||aY==null){continue}if(aZ<=aY&&aZ=aY&&aZ>aQ.max){if(aY>aQ.max){continue}aK=(aQ.max-aZ)/(aY-aZ)*(aJ-aK)+aK;aZ=aQ.max}else{if(aY>=aZ&&aY>aQ.max){if(aZ>aQ.max){continue}aJ=(aQ.max-aZ)/(aY-aZ)*(aJ-aK)+aK;aY=aQ.max}}if(!aT){H.beginPath();H.moveTo(aQ.p2c(aZ),aP.p2c(aN));aT=true}if(aK>=aP.max&&aJ>=aP.max){H.lineTo(aQ.p2c(aZ),aP.p2c(aP.max));H.lineTo(aQ.p2c(aY),aP.p2c(aP.max));continue}else{if(aK<=aP.min&&aJ<=aP.min){H.lineTo(aQ.p2c(aZ),aP.p2c(aP.min));H.lineTo(aQ.p2c(aY),aP.p2c(aP.min));continue}}var aO=aZ,aS=aY;if(aK<=aJ&&aK=aP.min){aZ=(aP.min-aK)/(aJ-aK)*(aY-aZ)+aZ;aK=aP.min}else{if(aJ<=aK&&aJ=aP.min){aY=(aP.min-aK)/(aJ-aK)*(aY-aZ)+aZ;aJ=aP.min}}if(aK>=aJ&&aK>aP.max&&aJ<=aP.max){aZ=(aP.max-aK)/(aJ-aK)*(aY-aZ)+aZ;aK=aP.max}else{if(aJ>=aK&&aJ>aP.max&&aK<=aP.max){aY=(aP.max-aK)/(aJ-aK)*(aY-aZ)+aZ;aJ=aP.max}}if(aZ!=aO){H.lineTo(aQ.p2c(aO),aP.p2c(aK))}H.lineTo(aQ.p2c(aZ),aP.p2c(aK));H.lineTo(aQ.p2c(aY),aP.p2c(aJ));if(aY!=aS){H.lineTo(aQ.p2c(aY),aP.p2c(aJ));H.lineTo(aQ.p2c(aS),aP.p2c(aJ))}}}H.save();H.translate(q.left,q.top);H.lineJoin="round";var aG=aE.lines.lineWidth,aB=aE.shadowSize;if(aG>0&&aB>0){H.lineWidth=aB;H.strokeStyle="rgba(0,0,0,0.1)";var aH=Math.PI/18;aD(aE.datapoints,Math.sin(aH)*(aG/2+aB/2),Math.cos(aH)*(aG/2+aB/2),aE.xaxis,aE.yaxis);H.lineWidth=aB/2;aD(aE.datapoints,Math.sin(aH)*(aG/2+aB/4),Math.cos(aH)*(aG/2+aB/4),aE.xaxis,aE.yaxis)}H.lineWidth=aG;H.strokeStyle=aE.color;var aC=ae(aE.lines,aE.color,0,w);if(aC){H.fillStyle=aC;aF(aE.datapoints,aE.xaxis,aE.yaxis)}if(aG>0){aD(aE.datapoints,0,0,aE.xaxis,aE.yaxis)}H.restore()}function ao(aE){function aH(aN,aM,aU,aK,aS,aT,aQ,aJ){var aR=aN.points,aI=aN.pointsize;for(var aL=0;aLaT.max||aOaQ.max){continue}H.beginPath();aP=aT.p2c(aP);aO=aQ.p2c(aO)+aK;if(aJ=="circle"){H.arc(aP,aO,aM,0,aS?Math.PI:Math.PI*2,false)}else{aJ(H,aP,aO,aM,aS)}H.closePath();if(aU){H.fillStyle=aU;H.fill()}H.stroke()}}H.save();H.translate(q.left,q.top);var aG=aE.points.lineWidth,aC=aE.shadowSize,aB=aE.points.radius,aF=aE.points.symbol;if(aG>0&&aC>0){var aD=aC/2;H.lineWidth=aD;H.strokeStyle="rgba(0,0,0,0.1)";aH(aE.datapoints,aB,null,aD+aD/2,true,aE.xaxis,aE.yaxis,aF);H.strokeStyle="rgba(0,0,0,0.2)";aH(aE.datapoints,aB,null,aD/2,true,aE.xaxis,aE.yaxis,aF)}H.lineWidth=aG;H.strokeStyle=aE.color;aH(aE.datapoints,aB,ae(aE.points,aE.color),0,false,aE.xaxis,aE.yaxis,aF);H.restore()}function E(aN,aM,aV,aI,aQ,aF,aD,aL,aK,aU,aR,aC){var aE,aT,aJ,aP,aG,aB,aO,aH,aS;if(aR){aH=aB=aO=true;aG=false;aE=aV;aT=aN;aP=aM+aI;aJ=aM+aQ;if(aTaL.max||aPaK.max){return}if(aEaL.max){aT=aL.max;aB=false}if(aJaK.max){aP=aK.max;aO=false}aE=aL.p2c(aE);aJ=aK.p2c(aJ);aT=aL.p2c(aT);aP=aK.p2c(aP);if(aD){aU.beginPath();aU.moveTo(aE,aJ);aU.lineTo(aE,aP);aU.lineTo(aT,aP);aU.lineTo(aT,aJ);aU.fillStyle=aD(aJ,aP);aU.fill()}if(aC>0&&(aG||aB||aO||aH)){aU.beginPath();aU.moveTo(aE,aJ+aF);if(aG){aU.lineTo(aE,aP+aF)}else{aU.moveTo(aE,aP+aF)}if(aO){aU.lineTo(aT,aP+aF)}else{aU.moveTo(aT,aP+aF)}if(aB){aU.lineTo(aT,aJ+aF)}else{aU.moveTo(aT,aJ+aF)}if(aH){aU.lineTo(aE,aJ+aF)}else{aU.moveTo(aE,aJ+aF)}aU.stroke()}}function e(aD){function aC(aJ,aI,aL,aG,aK,aN,aM){var aO=aJ.points,aF=aJ.pointsize;for(var aH=0;aH")}aH.push("");aF=true}if(aN){aJ=aN(aJ,aM)}aH.push('
'+aJ+"")}if(aF){aH.push("")}if(aH.length==0){return}var aL=''+aH.join("")+"
";if(O.legend.container!=null){c(O.legend.container).html(aL)}else{var aI="",aC=O.legend.position,aD=O.legend.margin;if(aD[0]==null){aD=[aD,aD]}if(aC.charAt(0)=="n"){aI+="top:"+(aD[1]+q.top)+"px;"}else{if(aC.charAt(0)=="s"){aI+="bottom:"+(aD[1]+q.bottom)+"px;"}}if(aC.charAt(1)=="e"){aI+="right:"+(aD[0]+q.right)+"px;"}else{if(aC.charAt(1)=="w"){aI+="left:"+(aD[0]+q.left)+"px;"}}var aK=c('
'+aL.replace('style="','style="position:absolute;'+aI+";")+"
").appendTo(av);if(O.legend.backgroundOpacity!=0){var aG=O.legend.backgroundColor;if(aG==null){aG=O.grid.backgroundColor;if(aG&&typeof aG=="string"){aG=c.color.parse(aG)}else{aG=c.color.extract(aK,"background-color")}aG.a=1;aG=aG.toString()}var aB=aK.children();c('
').prependTo(aK).css("opacity",O.legend.backgroundOpacity)}}}var ab=[],M=null;function K(aI,aG,aD){var aO=O.grid.mouseActiveRadius,a0=aO*aO+1,aY=null,aR=false,aW,aU;for(aW=Q.length-1;aW>=0;--aW){if(!aD(Q[aW])){continue}var aP=Q[aW],aH=aP.xaxis,aF=aP.yaxis,aV=aP.datapoints.points,aT=aP.datapoints.pointsize,aQ=aH.c2p(aI),aN=aF.c2p(aG),aC=aO/aH.scale,aB=aO/aF.scale;if(aH.options.inverseTransform){aC=Number.MAX_VALUE}if(aF.options.inverseTransform){aB=Number.MAX_VALUE}if(aP.lines.show||aP.points.show){for(aU=0;aUaC||aK-aQ<-aC||aJ-aN>aB||aJ-aN<-aB){continue}var aM=Math.abs(aH.p2c(aK)-aI),aL=Math.abs(aF.p2c(aJ)-aG),aS=aM*aM+aL*aL;if(aS=Math.min(aZ,aK)&&aN>=aJ+aE&&aN<=aJ+aX):(aQ>=aK+aE&&aQ<=aK+aX&&aN>=Math.min(aZ,aJ)&&aN<=Math.max(aZ,aJ))){aY=[aW,aU/aT]}}}}if(aY){aW=aY[0];aU=aY[1];aT=Q[aW].datapoints.pointsize;return{datapoint:Q[aW].datapoints.points.slice(aU*aT,(aU+1)*aT),dataIndex:aU,series:Q[aW],seriesIndex:aW}}return null}function aa(aB){if(O.grid.hoverable){u("plothover",aB,function(aC){return aC.hoverable!=false})}}function l(aB){if(O.grid.hoverable){u("plothover",aB,function(aC){return false})}}function R(aB){u("plotclick",aB,function(aC){return aC.clickable!=false})}function u(aC,aB,aD){var aE=y.offset(),aH=aB.pageX-aE.left-q.left,aF=aB.pageY-aE.top-q.top,aJ=C({left:aH,top:aF});aJ.pageX=aB.pageX;aJ.pageY=aB.pageY;var aK=K(aH,aF,aD);if(aK){aK.pageX=parseInt(aK.series.xaxis.p2c(aK.datapoint[0])+aE.left+q.left);aK.pageY=parseInt(aK.series.yaxis.p2c(aK.datapoint[1])+aE.top+q.top)}if(O.grid.autoHighlight){for(var aG=0;aGaH.max||aIaG.max){return}var aF=aE.points.radius+aE.points.lineWidth/2;A.lineWidth=aF;A.strokeStyle=c.color.parse(aE.color).scale("a",0.5).toString();var aB=1.5*aF,aC=aH.p2c(aC),aI=aG.p2c(aI);A.beginPath();if(aE.points.symbol=="circle"){A.arc(aC,aI,aB,0,2*Math.PI,false)}else{aE.points.symbol(A,aC,aI,aB,false)}A.closePath();A.stroke()}function v(aE,aB){A.lineWidth=aE.bars.lineWidth;A.strokeStyle=c.color.parse(aE.color).scale("a",0.5).toString();var aD=c.color.parse(aE.color).scale("a",0.5).toString();var aC=aE.bars.align=="left"?0:-aE.bars.barWidth/2;E(aB[0],aB[1],aB[2]||0,aC,aC+aE.bars.barWidth,0,function(){return aD},aE.xaxis,aE.yaxis,A,aE.bars.horizontal,aE.bars.lineWidth)}function am(aJ,aB,aH,aC){if(typeof aJ=="string"){return aJ}else{var aI=H.createLinearGradient(0,aH,0,aB);for(var aE=0,aD=aJ.colors.length;aE12){n=n-12}else{if(n==0){n=12}}}for(var g=0;g=1){return"rgb("+[o.r,o.g,o.b].join(",")+")"}else{return"rgba("+[o.r,o.g,o.b,o.a].join(",")+")"}};o.normalize=function(){function clamp(min,value,max){return valuemax?max:value}o.r=clamp(0,parseInt(o.r),255);o.g=clamp(0,parseInt(o.g),255);o.b=clamp(0,parseInt(o.b),255);o.a=clamp(0,o.a,1);return o};o.clone=function(){return $.color.make(o.r,o.b,o.g,o.a)};return o.normalize()};$.color.extract=function(elem,css){var c;do{c=elem.css(css).toLowerCase();if(c!=""&&c!="transparent")break;elem=elem.parent()}while(elem.length&&!$.nodeName(elem.get(0),"body"));if(c=="rgba(0, 0, 0, 0)")c="transparent";return $.color.parse(c)};$.color.parse=function(str){var res,m=$.color.make;if(res=/rgb\(\s*([0-9]{1,3})\s*,\s*([0-9]{1,3})\s*,\s*([0-9]{1,3})\s*\)/.exec(str))return m(parseInt(res[1],10),parseInt(res[2],10),parseInt(res[3],10));if(res=/rgba\(\s*([0-9]{1,3})\s*,\s*([0-9]{1,3})\s*,\s*([0-9]{1,3})\s*,\s*([0-9]+(?:\.[0-9]+)?)\s*\)/.exec(str))return m(parseInt(res[1],10),parseInt(res[2],10),parseInt(res[3],10),parseFloat(res[4]));if(res=/rgb\(\s*([0-9]+(?:\.[0-9]+)?)\%\s*,\s*([0-9]+(?:\.[0-9]+)?)\%\s*,\s*([0-9]+(?:\.[0-9]+)?)\%\s*\)/.exec(str))return m(parseFloat(res[1])*2.55,parseFloat(res[2])*2.55,parseFloat(res[3])*2.55);if(res=/rgba\(\s*([0-9]+(?:\.[0-9]+)?)\%\s*,\s*([0-9]+(?:\.[0-9]+)?)\%\s*,\s*([0-9]+(?:\.[0-9]+)?)\%\s*,\s*([0-9]+(?:\.[0-9]+)?)\s*\)/.exec(str))return m(parseFloat(res[1])*2.55,parseFloat(res[2])*2.55,parseFloat(res[3])*2.55,parseFloat(res[4]));if(res=/#([a-fA-F0-9]{2})([a-fA-F0-9]{2})([a-fA-F0-9]{2})/.exec(str))return m(parseInt(res[1],16),parseInt(res[2],16),parseInt(res[3],16));if(res=/#([a-fA-F0-9])([a-fA-F0-9])([a-fA-F0-9])/.exec(str))return m(parseInt(res[1]+res[1],16),parseInt(res[2]+res[2],16),parseInt(res[3]+res[3],16));var name=$.trim(str).toLowerCase();if(name=="transparent")return m(255,255,255,0);else{res=lookupColors[name]||[0,0,0];return m(res[0],res[1],res[2])}};var lookupColors={aqua:[0,255,255],azure:[240,255,255],beige:[245,245,220],black:[0,0,0],blue:[0,0,255],brown:[165,42,42],cyan:[0,255,255],darkblue:[0,0,139],darkcyan:[0,139,139],darkgrey:[169,169,169],darkgreen:[0,100,0],darkkhaki:[189,183,107],darkmagenta:[139,0,139],darkolivegreen:[85,107,47],darkorange:[255,140,0],darkorchid:[153,50,204],darkred:[139,0,0],darksalmon:[233,150,122],darkviolet:[148,0,211],fuchsia:[255,0,255],gold:[255,215,0],green:[0,128,0],indigo:[75,0,130],khaki:[240,230,140],lightblue:[173,216,230],lightcyan:[224,255,255],lightgreen:[144,238,144],lightgrey:[211,211,211],lightpink:[255,182,193],lightyellow:[255,255,224],lime:[0,255,0],magenta:[255,0,255],maroon:[128,0,0],navy:[0,0,128],olive:[128,128,0],orange:[255,165,0],pink:[255,192,203],purple:[128,0,128],violet:[128,0,128],red:[255,0,0],silver:[192,192,192],white:[255,255,255],yellow:[255,255,0]}})(jQuery);(function($){var hasOwnProperty=Object.prototype.hasOwnProperty;if(!$.fn.detach){$.fn.detach=function(){return this.each(function(){if(this.parentNode){this.parentNode.removeChild(this)}})}}function Canvas(cls,container){var element=container.children("."+cls)[0];if(element==null){element=document.createElement("canvas");element.className=cls;$(element).css({direction:"ltr",position:"absolute",left:0,top:0}).appendTo(container);if(!element.getContext){if(window.G_vmlCanvasManager){element=window.G_vmlCanvasManager.initElement(element)}else{throw new Error("Canvas is not available. If you're using IE with a fall-back such as Excanvas, then there's either a mistake in your conditional include, or the page has no DOCTYPE and is rendering in Quirks Mode.")}}}this.element=element;var context=this.context=element.getContext("2d");var devicePixelRatio=window.devicePixelRatio||1,backingStoreRatio=context.webkitBackingStorePixelRatio||context.mozBackingStorePixelRatio||context.msBackingStorePixelRatio||context.oBackingStorePixelRatio||context.backingStorePixelRatio||1;this.pixelRatio=devicePixelRatio/backingStoreRatio;this.resize(container.width(),container.height());this.textContainer=null;this.text={};this._textCache={}}Canvas.prototype.resize=function(width,height){if(width<=0||height<=0){throw new Error("Invalid dimensions for plot, width = "+width+", height = "+height)}var element=this.element,context=this.context,pixelRatio=this.pixelRatio;if(this.width!=width){element.width=width*pixelRatio;element.style.width=width+"px";this.width=width}if(this.height!=height){element.height=height*pixelRatio;element.style.height=height+"px";this.height=height}context.restore();context.save();context.scale(pixelRatio,pixelRatio)};Canvas.prototype.clear=function(){this.context.clearRect(0,0,this.width,this.height)};Canvas.prototype.render=function(){var cache=this._textCache;for(var layerKey in cache){if(hasOwnProperty.call(cache,layerKey)){var layer=this.getTextLayer(layerKey),layerCache=cache[layerKey];layer.hide();for(var styleKey in layerCache){if(hasOwnProperty.call(layerCache,styleKey)){var styleCache=layerCache[styleKey];for(var key in styleCache){if(hasOwnProperty.call(styleCache,key)){var positions=styleCache[key].positions;for(var i=0,position;position=positions[i];i++){if(position.active){if(!position.rendered){layer.append(position.element);position.rendered=true}}else{positions.splice(i--,1);if(position.rendered){position.element.detach()}}}if(positions.length==0){delete styleCache[key]}}}}}layer.show()}}};Canvas.prototype.getTextLayer=function(classes){var layer=this.text[classes];if(layer==null){if(this.textContainer==null){this.textContainer=$("
").css({position:"absolute",top:0,left:0,bottom:0,right:0,"font-size":"smaller",color:"#545454"}).insertAfter(this.element)}layer=this.text[classes]=$("
").addClass(classes).css({position:"absolute",top:0,left:0,bottom:0,right:0}).appendTo(this.textContainer)}return layer};Canvas.prototype.getTextInfo=function(layer,text,font,angle,width){var textStyle,layerCache,styleCache,info;text=""+text;if(typeof font==="object"){textStyle=font.style+" "+font.variant+" "+font.weight+" "+font.size+"px/"+font.lineHeight+"px "+font.family}else{textStyle=font}layerCache=this._textCache[layer];if(layerCache==null){layerCache=this._textCache[layer]={}}styleCache=layerCache[textStyle];if(styleCache==null){styleCache=layerCache[textStyle]={}}info=styleCache[text];if(info==null){var element=$("
").html(text).css({position:"absolute","max-width":width,top:-9999}).appendTo(this.getTextLayer(layer));if(typeof font==="object"){element.css({font:textStyle,color:font.color})}else if(typeof font==="string"){element.addClass(font)}info=styleCache[text]={width:element.outerWidth(true),height:element.outerHeight(true),element:element,positions:[]};element.detach()}return info};Canvas.prototype.addText=function(layer,x,y,text,font,angle,width,halign,valign){var info=this.getTextInfo(layer,text,font,angle,width),positions=info.positions;if(halign=="center"){x-=info.width/2}else if(halign=="right"){x-=info.width}if(valign=="middle"){y-=info.height/2}else if(valign=="bottom"){y-=info.height}for(var i=0,position;position=positions[i];i++){if(position.x==x&&position.y==y){position.active=true;return}}position={active:true,rendered:false,element:positions.length?info.element.clone():info.element,x:x,y:y};positions.push(position);position.element.css({top:Math.round(y),left:Math.round(x),"text-align":halign})};Canvas.prototype.removeText=function(layer,x,y,text,font,angle){if(text==null){var layerCache=this._textCache[layer];if(layerCache!=null){for(var styleKey in layerCache){if(hasOwnProperty.call(layerCache,styleKey)){var styleCache=layerCache[styleKey];for(var key in styleCache){if(hasOwnProperty.call(styleCache,key)){var positions=styleCache[key].positions;for(var i=0,position;position=positions[i];i++){position.active=false}}}}}}}else{var positions=this.getTextInfo(layer,text,font,angle).positions;for(var i=0,position;position=positions[i];i++){if(position.x==x&&position.y==y){position.active=false}}}};function Plot(placeholder,data_,options_,plugins){var series=[],options={colors:["#edc240","#afd8f8","#cb4b4b","#4da74d","#9440ed"],legend:{show:true,noColumns:1,labelFormatter:null,labelBoxBorderColor:"#ccc",container:null,position:"ne",margin:5,backgroundColor:null,backgroundOpacity:.85,sorted:null},xaxis:{show:null,position:"bottom",mode:null,font:null,color:null,tickColor:null,transform:null,inverseTransform:null,min:null,max:null,autoscaleMargin:null,ticks:null,tickFormatter:null,labelWidth:null,labelHeight:null,reserveSpace:null,tickLength:null,alignTicksWithAxis:null,tickDecimals:null,tickSize:null,minTickSize:null},yaxis:{autoscaleMargin:.02,position:"left"},xaxes:[],yaxes:[],series:{points:{show:false,radius:3,lineWidth:2,fill:true,fillColor:"#ffffff",symbol:"circle"},lines:{lineWidth:2,fill:false,fillColor:null,steps:false},bars:{show:false,lineWidth:2,barWidth:1,fill:true,fillColor:null,align:"left",horizontal:false,zero:true},shadowSize:3,highlightColor:null},grid:{show:true,aboveData:false,color:"#545454",backgroundColor:null,borderColor:null,tickColor:null,margin:0,labelMargin:5,axisMargin:8,borderWidth:2,minBorderMargin:null,markings:null,markingsColor:"#f4f4f4",markingsLineWidth:2,clickable:false,hoverable:false,autoHighlight:true,mouseActiveRadius:10},interaction:{redrawOverlayInterval:1e3/60},hooks:{}},surface=null,overlay=null,eventHolder=null,ctx=null,octx=null,xaxes=[],yaxes=[],plotOffset={left:0,right:0,top:0,bottom:0},plotWidth=0,plotHeight=0,hooks={processOptions:[],processRawData:[],processDatapoints:[],processOffset:[],drawBackground:[],drawSeries:[],draw:[],bindEvents:[],drawOverlay:[],shutdown:[]},plot=this;plot.setData=setData;plot.setupGrid=setupGrid;plot.draw=draw;plot.getPlaceholder=function(){return placeholder};plot.getCanvas=function(){return surface.element};plot.getPlotOffset=function(){return plotOffset};plot.width=function(){return plotWidth};plot.height=function(){return plotHeight};plot.offset=function(){var o=eventHolder.offset();o.left+=plotOffset.left;o.top+=plotOffset.top;return o};plot.getData=function(){return series};plot.getAxes=function(){var res={},i;$.each(xaxes.concat(yaxes),function(_,axis){if(axis)res[axis.direction+(axis.n!=1?axis.n:"")+"axis"]=axis});return res};plot.getXAxes=function(){return xaxes};plot.getYAxes=function(){return yaxes};plot.c2p=canvasToAxisCoords;plot.p2c=axisToCanvasCoords;plot.getOptions=function(){return options};plot.highlight=highlight;plot.unhighlight=unhighlight;plot.triggerRedrawOverlay=triggerRedrawOverlay;plot.pointOffset=function(point){return{left:parseInt(xaxes[axisNumber(point,"x")-1].p2c(+point.x)+plotOffset.left,10),top:parseInt(yaxes[axisNumber(point,"y")-1].p2c(+point.y)+plotOffset.top,10)}};plot.shutdown=shutdown;plot.destroy=function(){shutdown();placeholder.removeData("plot").empty();series=[];options=null;surface=null;overlay=null;eventHolder=null;ctx=null;octx=null;xaxes=[];yaxes=[];hooks=null;highlights=[];plot=null};plot.resize=function(){var width=placeholder.width(),height=placeholder.height();surface.resize(width,height);overlay.resize(width,height)};plot.hooks=hooks;initPlugins(plot);parseOptions(options_);setupCanvases();setData(data_);setupGrid();draw();bindEvents();function executeHooks(hook,args){args=[plot].concat(args);for(var i=0;imaxIndex){maxIndex=sc}}}if(neededColors<=maxIndex){neededColors=maxIndex+1}var c,colors=[],colorPool=options.colors,colorPoolSize=colorPool.length,variation=0;for(i=0;i=0){if(variation<.5){variation=-variation-.2}else variation=0}else variation=-variation}colors[i]=c.scale("rgb",1+variation)}var colori=0,s;for(i=0;iaxis.datamax&&max!=fakeInfinity)axis.datamax=max}$.each(allAxes(),function(_,axis){axis.datamin=topSentry;axis.datamax=bottomSentry;axis.used=false});for(i=0;i0&&points[k-ps]!=null&&points[k-ps]!=points[k]&&points[k-ps+1]!=points[k+1]){for(m=0;mxmax)xmax=val}if(f.y){if(valymax)ymax=val}}}if(s.bars.show){var delta;switch(s.bars.align){case"left":delta=0;break;case"right":delta=-s.bars.barWidth;break;default:delta=-s.bars.barWidth/2}if(s.bars.horizontal){ymin+=delta;ymax+=delta+s.bars.barWidth}else{xmin+=delta;xmax+=delta+s.bars.barWidth}}updateAxis(s.xaxis,xmin,xmax);updateAxis(s.yaxis,ymin,ymax)}$.each(allAxes(),function(_,axis){if(axis.datamin==topSentry)axis.datamin=null;if(axis.datamax==bottomSentry)axis.datamax=null})}function setupCanvases(){placeholder.css("padding",0).children().filter(function(){return!$(this).hasClass("flot-overlay")&&!$(this).hasClass("flot-base")}).remove();if(placeholder.css("position")=="static")placeholder.css("position","relative");surface=new Canvas("flot-base",placeholder);overlay=new Canvas("flot-overlay",placeholder);ctx=surface.context;octx=overlay.context;eventHolder=$(overlay.element).unbind();var existing=placeholder.data("plot");if(existing){existing.shutdown();overlay.clear()}placeholder.data("plot",plot)}function bindEvents(){if(options.grid.hoverable){eventHolder.mousemove(onMouseMove);eventHolder.bind("mouseleave",onMouseLeave)}if(options.grid.clickable)eventHolder.click(onClick);executeHooks(hooks.bindEvents,[eventHolder])}function shutdown(){if(redrawTimeout)clearTimeout(redrawTimeout);eventHolder.unbind("mousemove",onMouseMove);eventHolder.unbind("mouseleave",onMouseLeave);eventHolder.unbind("click",onClick);executeHooks(hooks.shutdown,[eventHolder])}function setTransformationHelpers(axis){function identity(x){return x}var s,m,t=axis.options.transform||identity,it=axis.options.inverseTransform;if(axis.direction=="x"){s=axis.scale=plotWidth/Math.abs(t(axis.max)-t(axis.min));m=Math.min(t(axis.max),t(axis.min))}else{s=axis.scale=plotHeight/Math.abs(t(axis.max)-t(axis.min));s=-s;m=Math.max(t(axis.max),t(axis.min))}if(t==identity)axis.p2c=function(p){return(p-m)*s};else axis.p2c=function(p){return(t(p)-m)*s};if(!it)axis.c2p=function(c){return m+c/s};else axis.c2p=function(c){return it(m+c/s)}}function measureTickLabels(axis){var opts=axis.options,ticks=axis.ticks||[],labelWidth=opts.labelWidth||0,labelHeight=opts.labelHeight||0,maxWidth=labelWidth||(axis.direction=="x"?Math.floor(surface.width/(ticks.length||1)):null),legacyStyles=axis.direction+"Axis "+axis.direction+axis.n+"Axis",layer="flot-"+axis.direction+"-axis flot-"+axis.direction+axis.n+"-axis "+legacyStyles,font=opts.font||"flot-tick-label tickLabel";for(var i=0;i=0;--i)allocateAxisBoxFirstPhase(allocatedAxes[i]);adjustLayoutForThingsStickingOut();$.each(allocatedAxes,function(_,axis){allocateAxisBoxSecondPhase(axis)})}plotWidth=surface.width-plotOffset.left-plotOffset.right;plotHeight=surface.height-plotOffset.bottom-plotOffset.top;$.each(axes,function(_,axis){setTransformationHelpers(axis)});if(showGrid){drawAxisLabels()}insertLegend()}function setRange(axis){var opts=axis.options,min=+(opts.min!=null?opts.min:axis.datamin),max=+(opts.max!=null?opts.max:axis.datamax),delta=max-min;if(delta==0){var widen=max==0?1:.01;if(opts.min==null)min-=widen;if(opts.max==null||opts.min!=null)max+=widen}else{var margin=opts.autoscaleMargin;if(margin!=null){if(opts.min==null){min-=delta*margin;if(min<0&&axis.datamin!=null&&axis.datamin>=0)min=0}if(opts.max==null){max+=delta*margin;if(max>0&&axis.datamax!=null&&axis.datamax<=0)max=0}}}axis.min=min;axis.max=max}function setupTickGeneration(axis){var opts=axis.options;var noTicks;if(typeof opts.ticks=="number"&&opts.ticks>0)noTicks=opts.ticks;else noTicks=.3*Math.sqrt(axis.direction=="x"?surface.width:surface.height);var delta=(axis.max-axis.min)/noTicks,dec=-Math.floor(Math.log(delta)/Math.LN10),maxDec=opts.tickDecimals;if(maxDec!=null&&dec>maxDec){dec=maxDec}var magn=Math.pow(10,-dec),norm=delta/magn,size;if(norm<1.5){size=1}else if(norm<3){size=2;if(norm>2.25&&(maxDec==null||dec+1<=maxDec)){size=2.5;++dec}}else if(norm<7.5){size=5}else{size=10}size*=magn;if(opts.minTickSize!=null&&size0){if(opts.min==null)axis.min=Math.min(axis.min,niceTicks[0]);if(opts.max==null&&niceTicks.length>1)axis.max=Math.max(axis.max,niceTicks[niceTicks.length-1])}axis.tickGenerator=function(axis){var ticks=[],v,i;for(i=0;i1&&/\..*0$/.test((ts[1]-ts[0]).toFixed(extraDec))))axis.tickDecimals=extraDec}}}}function setTicks(axis){var oticks=axis.options.ticks,ticks=[];if(oticks==null||typeof oticks=="number"&&oticks>0)ticks=axis.tickGenerator(axis);else if(oticks){if($.isFunction(oticks))ticks=oticks(axis);else ticks=oticks}var i,v;axis.ticks=[];for(i=0;i1)label=t[1]}else v=+t;if(label==null)label=axis.tickFormatter(v,axis);if(!isNaN(v))axis.ticks.push({v:v,label:label})}}function snapRangeToTicks(axis,ticks){if(axis.options.autoscaleMargin&&ticks.length>0){if(axis.options.min==null)axis.min=Math.min(axis.min,ticks[0].v);if(axis.options.max==null&&ticks.length>1)axis.max=Math.max(axis.max,ticks[ticks.length-1].v)}}function draw(){surface.clear();executeHooks(hooks.drawBackground,[ctx]);var grid=options.grid;if(grid.show&&grid.backgroundColor)drawBackground();if(grid.show&&!grid.aboveData){drawGrid()}for(var i=0;ito){var tmp=from;from=to;to=tmp}return{from:from,to:to,axis:axis}}function drawBackground(){ctx.save();ctx.translate(plotOffset.left,plotOffset.top);ctx.fillStyle=getColorOrGradient(options.grid.backgroundColor,plotHeight,0,"rgba(255, 255, 255, 0)");ctx.fillRect(0,0,plotWidth,plotHeight);ctx.restore()}function drawGrid(){var i,axes,bw,bc;ctx.save();ctx.translate(plotOffset.left,plotOffset.top);var markings=options.grid.markings;if(markings){if($.isFunction(markings)){axes=plot.getAxes();axes.xmin=axes.xaxis.min;axes.xmax=axes.xaxis.max;axes.ymin=axes.yaxis.min;axes.ymax=axes.yaxis.max;markings=markings(axes)}for(i=0;ixrange.axis.max||yrange.toyrange.axis.max)continue;xrange.from=Math.max(xrange.from,xrange.axis.min);xrange.to=Math.min(xrange.to,xrange.axis.max);yrange.from=Math.max(yrange.from,yrange.axis.min);yrange.to=Math.min(yrange.to,yrange.axis.max);var xequal=xrange.from===xrange.to,yequal=yrange.from===yrange.to;if(xequal&&yequal){continue}xrange.from=Math.floor(xrange.axis.p2c(xrange.from));xrange.to=Math.floor(xrange.axis.p2c(xrange.to));yrange.from=Math.floor(yrange.axis.p2c(yrange.from));yrange.to=Math.floor(yrange.axis.p2c(yrange.to));if(xequal||yequal){var lineWidth=m.lineWidth||options.grid.markingsLineWidth,subPixel=lineWidth%2?.5:0;ctx.beginPath();ctx.strokeStyle=m.color||options.grid.markingsColor;ctx.lineWidth=lineWidth;if(xequal){ctx.moveTo(xrange.to+subPixel,yrange.from);ctx.lineTo(xrange.to+subPixel,yrange.to)}else{ctx.moveTo(xrange.from,yrange.to+subPixel);ctx.lineTo(xrange.to,yrange.to+subPixel)}ctx.stroke()}else{ctx.fillStyle=m.color||options.grid.markingsColor;ctx.fillRect(xrange.from,yrange.to,xrange.to-xrange.from,yrange.from-yrange.to)}}}axes=allAxes();bw=options.grid.borderWidth;for(var j=0;jaxis.max||t=="full"&&(typeof bw=="object"&&bw[axis.position]>0||bw>0)&&(v==axis.min||v==axis.max))continue;if(axis.direction=="x"){x=axis.p2c(v);yoff=t=="full"?-plotHeight:t;if(axis.position=="top")yoff=-yoff}else{y=axis.p2c(v);xoff=t=="full"?-plotWidth:t;if(axis.position=="left")xoff=-xoff}if(ctx.lineWidth==1){if(axis.direction=="x")x=Math.floor(x)+.5;else y=Math.floor(y)+.5}ctx.moveTo(x,y);ctx.lineTo(x+xoff,y+yoff)}ctx.stroke()}if(bw){bc=options.grid.borderColor;if(typeof bw=="object"||typeof bc=="object"){if(typeof bw!=="object"){bw={top:bw,right:bw,bottom:bw,left:bw}}if(typeof bc!=="object"){bc={top:bc,right:bc,bottom:bc,left:bc}}if(bw.top>0){ctx.strokeStyle=bc.top;ctx.lineWidth=bw.top;ctx.beginPath();ctx.moveTo(0-bw.left,0-bw.top/2);ctx.lineTo(plotWidth,0-bw.top/2);ctx.stroke()}if(bw.right>0){ctx.strokeStyle=bc.right;ctx.lineWidth=bw.right;ctx.beginPath();ctx.moveTo(plotWidth+bw.right/2,0-bw.top);ctx.lineTo(plotWidth+bw.right/2,plotHeight);ctx.stroke()}if(bw.bottom>0){ctx.strokeStyle=bc.bottom;ctx.lineWidth=bw.bottom;ctx.beginPath();ctx.moveTo(plotWidth+bw.right,plotHeight+bw.bottom/2);ctx.lineTo(0,plotHeight+bw.bottom/2);ctx.stroke()}if(bw.left>0){ctx.strokeStyle=bc.left;ctx.lineWidth=bw.left;ctx.beginPath();ctx.moveTo(0-bw.left/2,plotHeight+bw.bottom);ctx.lineTo(0-bw.left/2,0);ctx.stroke()}}else{ctx.lineWidth=bw;ctx.strokeStyle=options.grid.borderColor;ctx.strokeRect(-bw/2,-bw/2,plotWidth+bw,plotHeight+bw)}}ctx.restore()}function drawAxisLabels(){$.each(allAxes(),function(_,axis){var box=axis.box,legacyStyles=axis.direction+"Axis "+axis.direction+axis.n+"Axis",layer="flot-"+axis.direction+"-axis flot-"+axis.direction+axis.n+"-axis "+legacyStyles,font=axis.options.font||"flot-tick-label tickLabel",tick,x,y,halign,valign;surface.removeText(layer);if(!axis.show||axis.ticks.length==0)return;for(var i=0;iaxis.max)continue;if(axis.direction=="x"){halign="center";x=plotOffset.left+axis.p2c(tick.v);if(axis.position=="bottom"){y=box.top+box.padding}else{y=box.top+box.height-box.padding;valign="bottom"}}else{valign="middle";y=plotOffset.top+axis.p2c(tick.v);if(axis.position=="left"){x=box.left+box.width-box.padding;halign="right"}else{x=box.left+box.padding}}surface.addText(layer,x,y,tick.label,font,null,null,halign,valign)}})}function drawSeries(series){if(series.lines.show)drawSeriesLines(series);if(series.bars.show)drawSeriesBars(series);if(series.points.show)drawSeriesPoints(series)}function drawSeriesLines(series){function plotLine(datapoints,xoffset,yoffset,axisx,axisy){var points=datapoints.points,ps=datapoints.pointsize,prevx=null,prevy=null;ctx.beginPath();for(var i=ps;i=y2&&y1>axisy.max){if(y2>axisy.max)continue;x1=(axisy.max-y1)/(y2-y1)*(x2-x1)+x1;y1=axisy.max}else if(y2>=y1&&y2>axisy.max){if(y1>axisy.max)continue;x2=(axisy.max-y1)/(y2-y1)*(x2-x1)+x1;y2=axisy.max}if(x1<=x2&&x1=x2&&x1>axisx.max){if(x2>axisx.max)continue;y1=(axisx.max-x1)/(x2-x1)*(y2-y1)+y1;x1=axisx.max}else if(x2>=x1&&x2>axisx.max){if(x1>axisx.max)continue;y2=(axisx.max-x1)/(x2-x1)*(y2-y1)+y1;x2=axisx.max}if(x1!=prevx||y1!=prevy)ctx.moveTo(axisx.p2c(x1)+xoffset,axisy.p2c(y1)+yoffset);prevx=x2;prevy=y2;ctx.lineTo(axisx.p2c(x2)+xoffset,axisy.p2c(y2)+yoffset)}ctx.stroke()}function plotLineArea(datapoints,axisx,axisy){var points=datapoints.points,ps=datapoints.pointsize,bottom=Math.min(Math.max(0,axisy.min),axisy.max),i=0,top,areaOpen=false,ypos=1,segmentStart=0,segmentEnd=0;while(true){if(ps>0&&i>points.length+ps)break;i+=ps;var x1=points[i-ps],y1=points[i-ps+ypos],x2=points[i],y2=points[i+ypos];if(areaOpen){if(ps>0&&x1!=null&&x2==null){segmentEnd=i;ps=-ps;ypos=2;continue}if(ps<0&&i==segmentStart+ps){ctx.fill();areaOpen=false;ps=-ps;ypos=1;i=segmentStart=segmentEnd+ps;continue}}if(x1==null||x2==null)continue;if(x1<=x2&&x1=x2&&x1>axisx.max){if(x2>axisx.max)continue;y1=(axisx.max-x1)/(x2-x1)*(y2-y1)+y1;x1=axisx.max}else if(x2>=x1&&x2>axisx.max){if(x1>axisx.max)continue;y2=(axisx.max-x1)/(x2-x1)*(y2-y1)+y1;x2=axisx.max}if(!areaOpen){ctx.beginPath();ctx.moveTo(axisx.p2c(x1),axisy.p2c(bottom));areaOpen=true}if(y1>=axisy.max&&y2>=axisy.max){ctx.lineTo(axisx.p2c(x1),axisy.p2c(axisy.max));ctx.lineTo(axisx.p2c(x2),axisy.p2c(axisy.max));continue}else if(y1<=axisy.min&&y2<=axisy.min){ctx.lineTo(axisx.p2c(x1),axisy.p2c(axisy.min));ctx.lineTo(axisx.p2c(x2),axisy.p2c(axisy.min));continue}var x1old=x1,x2old=x2;if(y1<=y2&&y1=axisy.min){x1=(axisy.min-y1)/(y2-y1)*(x2-x1)+x1;y1=axisy.min}else if(y2<=y1&&y2=axisy.min){x2=(axisy.min-y1)/(y2-y1)*(x2-x1)+x1;y2=axisy.min}if(y1>=y2&&y1>axisy.max&&y2<=axisy.max){x1=(axisy.max-y1)/(y2-y1)*(x2-x1)+x1;y1=axisy.max}else if(y2>=y1&&y2>axisy.max&&y1<=axisy.max){x2=(axisy.max-y1)/(y2-y1)*(x2-x1)+x1;y2=axisy.max}if(x1!=x1old){ctx.lineTo(axisx.p2c(x1old),axisy.p2c(y1))}ctx.lineTo(axisx.p2c(x1),axisy.p2c(y1));ctx.lineTo(axisx.p2c(x2),axisy.p2c(y2));if(x2!=x2old){ctx.lineTo(axisx.p2c(x2),axisy.p2c(y2));ctx.lineTo(axisx.p2c(x2old),axisy.p2c(y2))}}}ctx.save();ctx.translate(plotOffset.left,plotOffset.top);ctx.lineJoin="round";var lw=series.lines.lineWidth,sw=series.shadowSize;if(lw>0&&sw>0){ctx.lineWidth=sw;ctx.strokeStyle="rgba(0,0,0,0.1)";var angle=Math.PI/18;plotLine(series.datapoints,Math.sin(angle)*(lw/2+sw/2),Math.cos(angle)*(lw/2+sw/2),series.xaxis,series.yaxis);ctx.lineWidth=sw/2;plotLine(series.datapoints,Math.sin(angle)*(lw/2+sw/4),Math.cos(angle)*(lw/2+sw/4),series.xaxis,series.yaxis)}ctx.lineWidth=lw;ctx.strokeStyle=series.color;var fillStyle=getFillStyle(series.lines,series.color,0,plotHeight);if(fillStyle){ctx.fillStyle=fillStyle;plotLineArea(series.datapoints,series.xaxis,series.yaxis)}if(lw>0)plotLine(series.datapoints,0,0,series.xaxis,series.yaxis);ctx.restore()}function drawSeriesPoints(series){function plotPoints(datapoints,radius,fillStyle,offset,shadow,axisx,axisy,symbol){var points=datapoints.points,ps=datapoints.pointsize;for(var i=0;iaxisx.max||yaxisy.max)continue;ctx.beginPath();x=axisx.p2c(x);y=axisy.p2c(y)+offset;if(symbol=="circle")ctx.arc(x,y,radius,0,shadow?Math.PI:Math.PI*2,false);else symbol(ctx,x,y,radius,shadow);ctx.closePath();if(fillStyle){ctx.fillStyle=fillStyle;ctx.fill()}ctx.stroke()}}ctx.save();ctx.translate(plotOffset.left,plotOffset.top);var lw=series.points.lineWidth,sw=series.shadowSize,radius=series.points.radius,symbol=series.points.symbol;if(lw==0)lw=1e-4;if(lw>0&&sw>0){var w=sw/2;ctx.lineWidth=w;ctx.strokeStyle="rgba(0,0,0,0.1)";plotPoints(series.datapoints,radius,null,w+w/2,true,series.xaxis,series.yaxis,symbol);ctx.strokeStyle="rgba(0,0,0,0.2)";plotPoints(series.datapoints,radius,null,w/2,true,series.xaxis,series.yaxis,symbol)}ctx.lineWidth=lw;ctx.strokeStyle=series.color;plotPoints(series.datapoints,radius,getFillStyle(series.points,series.color),0,false,series.xaxis,series.yaxis,symbol);ctx.restore()}function drawBar(x,y,b,barLeft,barRight,fillStyleCallback,axisx,axisy,c,horizontal,lineWidth){var left,right,bottom,top,drawLeft,drawRight,drawTop,drawBottom,tmp;if(horizontal){drawBottom=drawRight=drawTop=true;drawLeft=false;left=b;right=x;top=y+barLeft;bottom=y+barRight;if(rightaxisx.max||topaxisy.max)return;if(leftaxisx.max){right=axisx.max;drawRight=false}if(bottomaxisy.max){top=axisy.max;drawTop=false}left=axisx.p2c(left);bottom=axisy.p2c(bottom);right=axisx.p2c(right);top=axisy.p2c(top);if(fillStyleCallback){c.fillStyle=fillStyleCallback(bottom,top);c.fillRect(left,top,right-left,bottom-top)}if(lineWidth>0&&(drawLeft||drawRight||drawTop||drawBottom)){c.beginPath();c.moveTo(left,bottom);if(drawLeft)c.lineTo(left,top);else c.moveTo(left,top);if(drawTop)c.lineTo(right,top);else c.moveTo(right,top);if(drawRight)c.lineTo(right,bottom);else c.moveTo(right,bottom);if(drawBottom)c.lineTo(left,bottom);else c.moveTo(left,bottom);c.stroke()}}function drawSeriesBars(series){function plotBars(datapoints,barLeft,barRight,fillStyleCallback,axisx,axisy){var points=datapoints.points,ps=datapoints.pointsize;for(var i=0;i");fragments.push("");rowStarted=true}fragments.push('
'+''+entry.label+"")}if(rowStarted)fragments.push("");if(fragments.length==0)return;var table=''+fragments.join("")+"
";if(options.legend.container!=null)$(options.legend.container).html(table);else{var pos="",p=options.legend.position,m=options.legend.margin;if(m[0]==null)m=[m,m];if(p.charAt(0)=="n")pos+="top:"+(m[1]+plotOffset.top)+"px;";else if(p.charAt(0)=="s")pos+="bottom:"+(m[1]+plotOffset.bottom)+"px;";if(p.charAt(1)=="e")pos+="right:"+(m[0]+plotOffset.right)+"px;";else if(p.charAt(1)=="w")pos+="left:"+(m[0]+plotOffset.left)+"px;";var legend=$('
'+table.replace('style="','style="position:absolute;'+pos+";")+"
").appendTo(placeholder);if(options.legend.backgroundOpacity!=0){var c=options.legend.backgroundColor;if(c==null){c=options.grid.backgroundColor;if(c&&typeof c=="string")c=$.color.parse(c);else c=$.color.extract(legend,"background-color");c.a=1;c=c.toString()}var div=legend.children();$('
').prependTo(legend).css("opacity",options.legend.backgroundOpacity)}}}var highlights=[],redrawTimeout=null;function findNearbyItem(mouseX,mouseY,seriesFilter){var maxDistance=options.grid.mouseActiveRadius,smallestDistance=maxDistance*maxDistance+1,item=null,foundPoint=false,i,j,ps;for(i=series.length-1;i>=0;--i){if(!seriesFilter(series[i]))continue;var s=series[i],axisx=s.xaxis,axisy=s.yaxis,points=s.datapoints.points,mx=axisx.c2p(mouseX),my=axisy.c2p(mouseY),maxx=maxDistance/axisx.scale,maxy=maxDistance/axisy.scale;ps=s.datapoints.pointsize;if(axisx.options.inverseTransform)maxx=Number.MAX_VALUE;if(axisy.options.inverseTransform)maxy=Number.MAX_VALUE;if(s.lines.show||s.points.show){for(j=0;jmaxx||x-mx<-maxx||y-my>maxy||y-my<-maxy)continue;var dx=Math.abs(axisx.p2c(x)-mouseX),dy=Math.abs(axisy.p2c(y)-mouseY),dist=dx*dx+dy*dy;if(dist=Math.min(b,x)&&my>=y+barLeft&&my<=y+barRight:mx>=x+barLeft&&mx<=x+barRight&&my>=Math.min(b,y)&&my<=Math.max(b,y))item=[i,j/ps]}}}if(item){i=item[0];j=item[1];ps=series[i].datapoints.pointsize;return{datapoint:series[i].datapoints.points.slice(j*ps,(j+1)*ps),dataIndex:j,series:series[i],seriesIndex:i}}return null}function onMouseMove(e){if(options.grid.hoverable)triggerClickHoverEvent("plothover",e,function(s){return s["hoverable"]!=false})}function onMouseLeave(e){if(options.grid.hoverable)triggerClickHoverEvent("plothover",e,function(s){return false})}function onClick(e){triggerClickHoverEvent("plotclick",e,function(s){return s["clickable"]!=false})}function triggerClickHoverEvent(eventname,event,seriesFilter){var offset=eventHolder.offset(),canvasX=event.pageX-offset.left-plotOffset.left,canvasY=event.pageY-offset.top-plotOffset.top,pos=canvasToAxisCoords({left:canvasX,top:canvasY});pos.pageX=event.pageX;pos.pageY=event.pageY;var item=findNearbyItem(canvasX,canvasY,seriesFilter);if(item){item.pageX=parseInt(item.series.xaxis.p2c(item.datapoint[0])+offset.left+plotOffset.left,10);item.pageY=parseInt(item.series.yaxis.p2c(item.datapoint[1])+offset.top+plotOffset.top,10)}if(options.grid.autoHighlight){for(var i=0;iaxisx.max||yaxisy.max)return;var pointRadius=series.points.radius+series.points.lineWidth/2;octx.lineWidth=pointRadius;octx.strokeStyle=highlightColor;var radius=1.5*pointRadius;x=axisx.p2c(x);y=axisy.p2c(y);octx.beginPath();if(series.points.symbol=="circle")octx.arc(x,y,radius,0,2*Math.PI,false);else series.points.symbol(octx,x,y,radius,false);octx.closePath();octx.stroke()}function drawBarHighlight(series,point){var highlightColor=typeof series.highlightColor==="string"?series.highlightColor:$.color.parse(series.color).scale("a",.5).toString(),fillStyle=highlightColor,barLeft;switch(series.bars.align){case"left":barLeft=0;break;case"right":barLeft=-series.bars.barWidth;break;default:barLeft=-series.bars.barWidth/2}octx.lineWidth=series.bars.lineWidth;octx.strokeStyle=highlightColor;drawBar(point[0],point[1],point[2]||0,barLeft,barLeft+series.bars.barWidth,function(){return fillStyle},series.xaxis,series.yaxis,octx,series.bars.horizontal,series.bars.lineWidth)}function getColorOrGradient(spec,bottom,top,defaultColor){if(typeof spec=="string")return spec;else{var gradient=ctx.createLinearGradient(0,top,0,bottom);for(var i=0,l=spec.colors.length;i0&&h.which!=l.which||a(h.target).is(l.not))return;switch(h.type){case"mousedown":return a.extend(l,a(j).offset(),{elem:j,target:h.target,pageX:h.pageX,pageY:h.pageY}),b.add(document,"mousemove mouseup",e,l),i(j,!1),d.dragging=null,!1;case!d.dragging&&"mousemove":if(g(h.pageX-l.pageX)+g(h.pageY-l.pageY) max) { + // make sure min < max + var tmp = min; + min = max; + max = tmp; + } + + //Check that we are in panRange + if (pr) { + if (pr[0] != null && min < pr[0]) { + min = pr[0]; + } + if (pr[1] != null && max > pr[1]) { + max = pr[1]; + } + } + + var range = max - min; + if (zr && + ((zr[0] != null && range < zr[0] && amount >1) || + (zr[1] != null && range > zr[1] && amount <1))) + return; + + opts.min = min; + opts.max = max; + }); + + plot.setupGrid(); + plot.draw(); + + if (!args.preventEvent) + plot.getPlaceholder().trigger("plotzoom", [ plot, args ]); + }; + + plot.pan = function (args) { + var delta = { + x: +args.left, + y: +args.top + }; + + if (isNaN(delta.x)) + delta.x = 0; + if (isNaN(delta.y)) + delta.y = 0; + + $.each(plot.getAxes(), function (_, axis) { + var opts = axis.options, + min, max, d = delta[axis.direction]; + + min = axis.c2p(axis.p2c(axis.min) + d), + max = axis.c2p(axis.p2c(axis.max) + d); + + var pr = opts.panRange; + if (pr === false) // no panning on this axis + return; + + if (pr) { + // check whether we hit the wall + if (pr[0] != null && pr[0] > min) { + d = pr[0] - min; + min += d; + max += d; + } + + if (pr[1] != null && pr[1] < max) { + d = pr[1] - max; + min += d; + max += d; + } + } + + opts.min = min; + opts.max = max; + }); + + plot.setupGrid(); + plot.draw(); + + if (!args.preventEvent) + plot.getPlaceholder().trigger("plotpan", [ plot, args ]); + }; + + function shutdown(plot, eventHolder) { + eventHolder.unbind(plot.getOptions().zoom.trigger, onZoomClick); + eventHolder.unbind("mousewheel", onMouseWheel); + eventHolder.unbind("dragstart", onDragStart); + eventHolder.unbind("drag", onDrag); + eventHolder.unbind("dragend", onDragEnd); + if (panTimeout) + clearTimeout(panTimeout); + } + + plot.hooks.bindEvents.push(bindEvents); + plot.hooks.shutdown.push(shutdown); + } + + $.plot.plugins.push({ + init: init, + options: options, + name: 'navigate', + version: '1.3' + }); +})(jQuery); diff --git a/pandora_console/include/graphs/flot/jquery.flot.navigate.min.js b/pandora_console/include/graphs/flot/jquery.flot.navigate.min.js new file mode 100644 index 0000000000..7288a23fab --- /dev/null +++ b/pandora_console/include/graphs/flot/jquery.flot.navigate.min.js @@ -0,0 +1,7 @@ +/* Javascript plotting library for jQuery, version 0.8.3. + +Copyright (c) 2007-2014 IOLA and Ole Laursen. +Licensed under the MIT license. + +*/ +(function(a){function e(h){var k,j=this,l=h.data||{};if(l.elem)j=h.dragTarget=l.elem,h.dragProxy=d.proxy||j,h.cursorOffsetX=l.pageX-l.left,h.cursorOffsetY=l.pageY-l.top,h.offsetX=h.pageX-h.cursorOffsetX,h.offsetY=h.pageY-h.cursorOffsetY;else if(d.dragging||l.which>0&&h.which!=l.which||a(h.target).is(l.not))return;switch(h.type){case"mousedown":return a.extend(l,a(j).offset(),{elem:j,target:h.target,pageX:h.pageX,pageY:h.pageY}),b.add(document,"mousemove mouseup",e,l),i(j,!1),d.dragging=null,!1;case!d.dragging&&"mousemove":if(g(h.pageX-l.pageX)+g(h.pageY-l.pageY)max){var tmp=min;min=max;max=tmp}if(pr){if(pr[0]!=null&&minpr[1]){max=pr[1]}}var range=max-min;if(zr&&(zr[0]!=null&&range1||zr[1]!=null&&range>zr[1]&&amount<1))return;opts.min=min;opts.max=max});plot.setupGrid();plot.draw();if(!args.preventEvent)plot.getPlaceholder().trigger("plotzoom",[plot,args])};plot.pan=function(args){var delta={x:+args.left,y:+args.top};if(isNaN(delta.x))delta.x=0;if(isNaN(delta.y))delta.y=0;$.each(plot.getAxes(),function(_,axis){var opts=axis.options,min,max,d=delta[axis.direction];min=axis.c2p(axis.p2c(axis.min)+d),max=axis.c2p(axis.p2c(axis.max)+d);var pr=opts.panRange;if(pr===false)return;if(pr){if(pr[0]!=null&&pr[0]>min){d=pr[0]-min;min+=d;max+=d}if(pr[1]!=null&&pr[1]1) - options.series.pie.tilt=1; - if (options.series.pie.tilt<0) - options.series.pie.tilt=0; - - // add processData hook to do transformations on the data - plot.hooks.processDatapoints.push(processDatapoints); - plot.hooks.drawOverlay.push(drawOverlay); - - // add draw hook - plot.hooks.draw.push(draw); - } - } - - // bind hoverable events - function bindEvents(plot, eventHolder) - { - var options = plot.getOptions(); - - if (options.series.pie.show && options.grid.hoverable) - eventHolder.unbind('mousemove').mousemove(onMouseMove); - - if (options.series.pie.show && options.grid.clickable) - eventHolder.unbind('click').click(onClick); - } - - - // debugging function that prints out an object - function alertObject(obj) - { - var msg = ''; - function traverse(obj, depth) - { - if (!depth) - depth = 0; - for (var i = 0; i < obj.length; ++i) - { - for (var j=0; jcanvas.width-maxRadius) - centerLeft = canvas.width-maxRadius; - } - - function fixData(data) - { - for (var i = 0; i < data.length; ++i) - { - if (typeof(data[i].data)=='number') - data[i].data = [[1,data[i].data]]; - else if (typeof(data[i].data)=='undefined' || typeof(data[i].data[0])=='undefined') - { - if (typeof(data[i].data)!='undefined' && typeof(data[i].data.label)!='undefined') - data[i].label = data[i].data.label; // fix weirdness coming from flot - data[i].data = [[1,0]]; - - } - } - return data; - } - - function combine(data) - { - data = fixData(data); - calcTotal(data); - var combined = 0; - var numCombined = 0; - var color = options.series.pie.combine.color; - - var newdata = []; - for (var i = 0; i < data.length; ++i) - { - // make sure its a number - data[i].data[0][1] = parseFloat(data[i].data[0][1]); - if (!data[i].data[0][1]) - data[i].data[0][1] = 0; - - if (data[i].data[0][1]/total<=options.series.pie.combine.threshold) - { - combined += data[i].data[0][1]; - numCombined++; - if (!color) - color = data[i].color; - } - else - { - newdata.push({ - data: [[1,data[i].data[0][1]]], - color: data[i].color, - label: data[i].label, - angle: (data[i].data[0][1]*(Math.PI*2))/total, - percent: (data[i].data[0][1]/total*100) - }); - } - } - if (numCombined>0) - newdata.push({ - data: [[1,combined]], - color: color, - label: options.series.pie.combine.label, - angle: (combined*(Math.PI*2))/total, - percent: (combined/total*100) - }); - return newdata; - } - - function draw(plot, newCtx) - { - if (!target) return; // if no series were passed - ctx = newCtx; - - setupPie(); - var slices = plot.getData(); - - var attempts = 0; - while (redraw && attempts0) - maxRadius *= shrink; - attempts += 1; - clear(); - if (options.series.pie.tilt<=0.8) - drawShadow(); - drawPie(); - } - if (attempts >= redrawAttempts) { - clear(); - target.prepend('
Could not draw pie with labels contained inside canvas
'); - } - - if ( plot.setSeries && plot.insertLegend ) - { - plot.setSeries(slices); - plot.insertLegend(); - } - - // we're actually done at this point, just defining internal functions at this point - - function clear() - { - ctx.clearRect(0,0,canvas.width,canvas.height); - target.children().filter('.pieLabel, .pieLabelBackground').remove(); - } - - function drawShadow() - { - var shadowLeft = 5; - var shadowTop = 15; - var edge = 10; - var alpha = 0.02; - - // set radius - if (options.series.pie.radius>1) - var radius = options.series.pie.radius; - else - var radius = maxRadius * options.series.pie.radius; - - if (radius>=(canvas.width/2)-shadowLeft || radius*options.series.pie.tilt>=(canvas.height/2)-shadowTop || radius<=edge) - return; // shadow would be outside canvas, so don't draw it - - ctx.save(); - ctx.translate(shadowLeft,shadowTop); - ctx.globalAlpha = alpha; - ctx.fillStyle = '#000'; - - // center and rotate to starting position - ctx.translate(centerLeft,centerTop); - ctx.scale(1, options.series.pie.tilt); - - //radius -= edge; - for (var i=1; i<=edge; i++) - { - ctx.beginPath(); - ctx.arc(0,0,radius,0,Math.PI*2,false); - ctx.fill(); - radius -= i; - } - - ctx.restore(); - } - - function drawPie() - { - startAngle = Math.PI*options.series.pie.startAngle; - - // set radius - if (options.series.pie.radius>1) - var radius = options.series.pie.radius; - else - var radius = maxRadius * options.series.pie.radius; - - // center and rotate to starting position - ctx.save(); - ctx.translate(centerLeft,centerTop); - ctx.scale(1, options.series.pie.tilt); - //ctx.rotate(startAngle); // start at top; -- This doesn't work properly in Opera - - // draw slices - ctx.save(); - var currentAngle = startAngle; - for (var i = 0; i < slices.length; ++i) - { - slices[i].startAngle = currentAngle; - drawSlice(slices[i].angle, slices[i].color, true); - } - ctx.restore(); - - // draw slice outlines - ctx.save(); - ctx.lineWidth = options.series.pie.stroke.width; - currentAngle = startAngle; - for (var i = 0; i < slices.length; ++i) - drawSlice(slices[i].angle, options.series.pie.stroke.color, false); - ctx.restore(); - - // draw donut hole - drawDonutHole(ctx); - - // draw labels - if (options.series.pie.label.show) - drawLabels(); - - // restore to original state - ctx.restore(); - - function drawSlice(angle, color, fill) - { - if (angle<=0) - return; - - if (fill) - ctx.fillStyle = color; - else - { - ctx.strokeStyle = color; - ctx.lineJoin = 'round'; - } - - ctx.beginPath(); - if (Math.abs(angle - Math.PI*2) > 0.000000001) - ctx.moveTo(0,0); // Center of the pie - - //-------------------------------------------------- - //JQUERY 1.9 DOES NOT SUPPORT THE BROWSER - //PANDORA FMS TEAM DISABLED THIS LINES - //else if ($.browser.msie) - // angle -= 0.0001; - //-------------------------------------------------- - - //ctx.arc(0,0,radius,0,angle,false); // This doesn't work properly in Opera - ctx.arc(0,0,radius,currentAngle,currentAngle+angle,false); - ctx.closePath(); - //ctx.rotate(angle); // This doesn't work properly in Opera - currentAngle += angle; - - if (fill) - ctx.fill(); - else - ctx.stroke(); - } - - function drawLabels() - { - var currentAngle = startAngle; - - // set radius - if (options.series.pie.label.radius>1) - var radius = options.series.pie.label.radius; - else - var radius = maxRadius * options.series.pie.label.radius; - - for (var i = 0; i < slices.length; ++i) - { - if (slices[i].percent >= options.series.pie.label.threshold*100) - drawLabel(slices[i], currentAngle, i); - currentAngle += slices[i].angle; - } - - function drawLabel(slice, startAngle, index) - { - if (slice.data[0][1]==0) - return; - - // format label text - var lf = options.legend.labelFormatter, text, plf = options.series.pie.label.formatter; - if (lf) - text = lf(slice.label, slice); - else - text = slice.label; - if (plf) - text = plf(text, slice); - - var halfAngle = ((startAngle+slice.angle) + startAngle)/2; - var x = centerLeft + Math.round(Math.cos(halfAngle) * radius); - var y = centerTop + Math.round(Math.sin(halfAngle) * radius) * options.series.pie.tilt; - - var html = '' + text + ""; - target.append(html); - var label = target.children('#pieLabel'+index); - var labelTop = (y - label.height()/2); - var labelLeft = (x - label.width()/2); - label.css('top', labelTop); - label.css('left', labelLeft); - - // check to make sure that the label is not outside the canvas - if (0-labelTop>0 || 0-labelLeft>0 || canvas.height-(labelTop+label.height())<0 || canvas.width-(labelLeft+label.width())<0) - redraw = true; - - if (options.series.pie.label.background.opacity != 0) { - // put in the transparent background separately to avoid blended labels and label boxes - var c = options.series.pie.label.background.color; - if (c == null) { - c = slice.color; - } - var pos = 'top:'+labelTop+'px;left:'+labelLeft+'px;'; - $('
').insertBefore(label).css('opacity', options.series.pie.label.background.opacity); - } - } // end individual label function - } // end drawLabels function - } // end drawPie function - } // end draw function - - // Placed here because it needs to be accessed from multiple locations - function drawDonutHole(layer) - { - // draw donut hole - if(options.series.pie.innerRadius > 0) - { - // subtract the center - layer.save(); - innerRadius = options.series.pie.innerRadius > 1 ? options.series.pie.innerRadius : maxRadius * options.series.pie.innerRadius; - layer.globalCompositeOperation = 'destination-out'; // this does not work with excanvas, but it will fall back to using the stroke color - layer.beginPath(); - layer.fillStyle = options.series.pie.stroke.color; - layer.arc(0,0,innerRadius,0,Math.PI*2,false); - layer.fill(); - layer.closePath(); - layer.restore(); - - // add inner stroke - layer.save(); - layer.beginPath(); - layer.strokeStyle = options.series.pie.stroke.color; - layer.arc(0,0,innerRadius,0,Math.PI*2,false); - layer.stroke(); - layer.closePath(); - layer.restore(); - // TODO: add extra shadow inside hole (with a mask) if the pie is tilted. - } - } - - //-- Additional Interactive related functions -- - - function isPointInPoly(poly, pt) - { - for(var c = false, i = -1, l = poly.length, j = l - 1; ++i < l; j = i) - ((poly[i][1] <= pt[1] && pt[1] < poly[j][1]) || (poly[j][1] <= pt[1] && pt[1]< poly[i][1])) - && (pt[0] < (poly[j][0] - poly[i][0]) * (pt[1] - poly[i][1]) / (poly[j][1] - poly[i][1]) + poly[i][0]) - && (c = !c); - return c; - } - - function findNearbySlice(mouseX, mouseY) - { - var slices = plot.getData(), - options = plot.getOptions(), - radius = options.series.pie.radius > 1 ? options.series.pie.radius : maxRadius * options.series.pie.radius; - - for (var i = 0; i < slices.length; ++i) - { - var s = slices[i]; - - if(s.pie.show) - { - ctx.save(); - ctx.beginPath(); - ctx.moveTo(0,0); // Center of the pie - //ctx.scale(1, options.series.pie.tilt); // this actually seems to break everything when here. - ctx.arc(0,0,radius,s.startAngle,s.startAngle+s.angle,false); - ctx.closePath(); - x = mouseX-centerLeft; - y = mouseY-centerTop; - if(ctx.isPointInPath) - { - if (ctx.isPointInPath(mouseX-centerLeft, mouseY-centerTop)) - { - //alert('found slice!'); - ctx.restore(); - return {datapoint: [s.percent, s.data], dataIndex: 0, series: s, seriesIndex: i}; - } - } - else - { - // excanvas for IE doesn;t support isPointInPath, this is a workaround. - p1X = (radius * Math.cos(s.startAngle)); - p1Y = (radius * Math.sin(s.startAngle)); - p2X = (radius * Math.cos(s.startAngle+(s.angle/4))); - p2Y = (radius * Math.sin(s.startAngle+(s.angle/4))); - p3X = (radius * Math.cos(s.startAngle+(s.angle/2))); - p3Y = (radius * Math.sin(s.startAngle+(s.angle/2))); - p4X = (radius * Math.cos(s.startAngle+(s.angle/1.5))); - p4Y = (radius * Math.sin(s.startAngle+(s.angle/1.5))); - p5X = (radius * Math.cos(s.startAngle+s.angle)); - p5Y = (radius * Math.sin(s.startAngle+s.angle)); - arrPoly = [[0,0],[p1X,p1Y],[p2X,p2Y],[p3X,p3Y],[p4X,p4Y],[p5X,p5Y]]; - arrPoint = [x,y]; - // TODO: perhaps do some mathmatical trickery here with the Y-coordinate to compensate for pie tilt? - if(isPointInPoly(arrPoly, arrPoint)) - { - ctx.restore(); - return {datapoint: [s.percent, s.data], dataIndex: 0, series: s, seriesIndex: i}; - } - } - ctx.restore(); - } - } - - return null; - } - - function onMouseMove(e) - { - triggerClickHoverEvent('plothover', e); - } - - function onClick(e) - { - triggerClickHoverEvent('plotclick', e); - } - - // trigger click or hover event (they send the same parameters so we share their code) - function triggerClickHoverEvent(eventname, e) - { - var offset = plot.offset(), - canvasX = parseInt(e.pageX - offset.left), - canvasY = parseInt(e.pageY - offset.top), - item = findNearbySlice(canvasX, canvasY); - - if (options.grid.autoHighlight) - { - // clear auto-highlights - for (var i = 0; i < highlights.length; ++i) - { - var h = highlights[i]; - if (h.auto == eventname && !(item && h.series == item.series)) - unhighlight(h.series); - } - } - - // highlight the slice - if (item) - highlight(item.series, eventname); - - // trigger any hover bind events - var pos = { pageX: e.pageX, pageY: e.pageY }; - target.trigger(eventname, [ pos, item ]); - } - - function highlight(s, auto) - { - if (typeof s == "number") - s = series[s]; - - var i = indexOfHighlight(s); - if (i == -1) - { - highlights.push({ series: s, auto: auto }); - plot.triggerRedrawOverlay(); - } - else if (!auto) - highlights[i].auto = false; - } - - function unhighlight(s) - { - if (s == null) - { - highlights = []; - plot.triggerRedrawOverlay(); - } - - if (typeof s == "number") - s = series[s]; - - var i = indexOfHighlight(s); - if (i != -1) - { - highlights.splice(i, 1); - plot.triggerRedrawOverlay(); - } - } - - function indexOfHighlight(s) - { - for (var i = 0; i < highlights.length; ++i) - { - var h = highlights[i]; - if (h.series == s) - return i; - } - return -1; - } - - function drawOverlay(plot, octx) - { - //alert(options.series.pie.radius); - var options = plot.getOptions(); - //alert(options.series.pie.radius); - - var radius = options.series.pie.radius > 1 ? options.series.pie.radius : maxRadius * options.series.pie.radius; - - octx.save(); - octx.translate(centerLeft, centerTop); - octx.scale(1, options.series.pie.tilt); - - for (i = 0; i < highlights.length; ++i) - drawHighlight(highlights[i].series); - - drawDonutHole(octx); - - octx.restore(); - - function drawHighlight(series) - { - if (series.angle < 0) return; - - //octx.fillStyle = parseColor(options.series.pie.highlight.color).scale(null, null, null, options.series.pie.highlight.opacity).toString(); - octx.fillStyle = "rgba(255, 255, 255, "+options.series.pie.highlight.opacity+")"; // this is temporary until we have access to parseColor - - octx.beginPath(); - if (Math.abs(series.angle - Math.PI*2) > 0.000000001) - octx.moveTo(0,0); // Center of the pie - octx.arc(0,0,radius,series.startAngle,series.startAngle+series.angle,false); - octx.closePath(); - octx.fill(); - } - - } - - } // end init (plugin body) - - // define pie specific options and their default values - var options = { - series: { - pie: { - show: false, - radius: 'auto', // actual radius of the visible pie (based on full calculated radius if <=1, or hard pixel value) - innerRadius:0, /* for donut */ - startAngle: 3/2, - tilt: 1, - offset: { - top: 0, - left: 'auto' - }, - stroke: { - color: '#FFF', - width: 1 - }, - label: { - show: 'auto', - formatter: function(label, slice){ - return '
'+label+'
'+Math.round(slice.percent)+'%
'; - }, // formatter function - radius: 1, // radius at which to place the labels (based on full calculated radius if <=1, or hard pixel value) - background: { - color: null, - opacity: 0 - }, - threshold: 0 // percentage at which to hide the label (i.e. the slice is too narrow) - }, - combine: { - threshold: -1, // percentage at which to combine little slices into one larger slice - color: null, // color to give the new slice (auto-generated if null) - label: 'Other' // label to give the new slice - }, - highlight: { - //color: '#FFF', // will add this functionality once parseColor is available - opacity: 0.5 - } - } - } - }; - - $.plot.plugins.push({ - init: init, - options: options, - name: "pie", - version: "1.0" - }); -})(jQuery); +/* Flot plugin for rendering pie charts. + +Copyright (c) 2007-2014 IOLA and Ole Laursen. +Licensed under the MIT license. + +The plugin assumes that each series has a single data value, and that each +value is a positive integer or zero. Negative numbers don't make sense for a +pie chart, and have unpredictable results. The values do NOT need to be +passed in as percentages; the plugin will calculate the total and per-slice +percentages internally. + +* Created by Brian Medendorp + +* Updated with contributions from btburnett3, Anthony Aragues and Xavi Ivars + +The plugin supports these options: + + series: { + pie: { + show: true/false + radius: 0-1 for percentage of fullsize, or a specified pixel length, or 'auto' + innerRadius: 0-1 for percentage of fullsize or a specified pixel length, for creating a donut effect + startAngle: 0-2 factor of PI used for starting angle (in radians) i.e 3/2 starts at the top, 0 and 2 have the same result + tilt: 0-1 for percentage to tilt the pie, where 1 is no tilt, and 0 is completely flat (nothing will show) + offset: { + top: integer value to move the pie up or down + left: integer value to move the pie left or right, or 'auto' + }, + stroke: { + color: any hexidecimal color value (other formats may or may not work, so best to stick with something like '#FFF') + width: integer pixel width of the stroke + }, + label: { + show: true/false, or 'auto' + formatter: a user-defined function that modifies the text/style of the label text + radius: 0-1 for percentage of fullsize, or a specified pixel length + background: { + color: any hexidecimal color value (other formats may or may not work, so best to stick with something like '#000') + opacity: 0-1 + }, + threshold: 0-1 for the percentage value at which to hide labels (if they're too small) + }, + combine: { + threshold: 0-1 for the percentage value at which to combine slices (if they're too small) + color: any hexidecimal color value (other formats may or may not work, so best to stick with something like '#CCC'), if null, the plugin will automatically use the color of the first slice to be combined + label: any text value of what the combined slice should be labeled + } + highlight: { + opacity: 0-1 + } + } + } + +More detail and specific examples can be found in the included HTML file. + +*/ + +(function($) { + + // Maximum redraw attempts when fitting labels within the plot + + var REDRAW_ATTEMPTS = 10; + + // Factor by which to shrink the pie when fitting labels within the plot + + var REDRAW_SHRINK = 0.95; + + function init(plot) { + + var canvas = null, + target = null, + options = null, + maxRadius = null, + centerLeft = null, + centerTop = null, + processed = false, + ctx = null; + + // interactive variables + + var highlights = []; + + // add hook to determine if pie plugin in enabled, and then perform necessary operations + + plot.hooks.processOptions.push(function(plot, options) { + if (options.series.pie.show) { + + options.grid.show = false; + + // set labels.show + + if (options.series.pie.label.show == "auto") { + if (options.legend.show) { + options.series.pie.label.show = false; + } else { + options.series.pie.label.show = true; + } + } + + // set radius + + if (options.series.pie.radius == "auto") { + if (options.series.pie.label.show) { + options.series.pie.radius = 3/4; + } else { + options.series.pie.radius = 1; + } + } + + // ensure sane tilt + + if (options.series.pie.tilt > 1) { + options.series.pie.tilt = 1; + } else if (options.series.pie.tilt < 0) { + options.series.pie.tilt = 0; + } + } + }); + + plot.hooks.bindEvents.push(function(plot, eventHolder) { + var options = plot.getOptions(); + if (options.series.pie.show) { + if (options.grid.hoverable) { + eventHolder.unbind("mousemove").mousemove(onMouseMove); + } + if (options.grid.clickable) { + eventHolder.unbind("click").click(onClick); + } + } + }); + + plot.hooks.processDatapoints.push(function(plot, series, data, datapoints) { + var options = plot.getOptions(); + if (options.series.pie.show) { + processDatapoints(plot, series, data, datapoints); + } + }); + + plot.hooks.drawOverlay.push(function(plot, octx) { + var options = plot.getOptions(); + if (options.series.pie.show) { + drawOverlay(plot, octx); + } + }); + + plot.hooks.draw.push(function(plot, newCtx) { + var options = plot.getOptions(); + if (options.series.pie.show) { + draw(plot, newCtx); + } + }); + + function processDatapoints(plot, series, datapoints) { + if (!processed) { + processed = true; + canvas = plot.getCanvas(); + target = $(canvas).parent(); + options = plot.getOptions(); + plot.setData(combine(plot.getData())); + } + } + + function combine(data) { + + var total = 0, + combined = 0, + numCombined = 0, + color = options.series.pie.combine.color, + newdata = []; + + // Fix up the raw data from Flot, ensuring the data is numeric + + for (var i = 0; i < data.length; ++i) { + + var value = data[i].data; + + // If the data is an array, we'll assume that it's a standard + // Flot x-y pair, and are concerned only with the second value. + + // Note how we use the original array, rather than creating a + // new one; this is more efficient and preserves any extra data + // that the user may have stored in higher indexes. + + if ($.isArray(value) && value.length == 1) { + value = value[0]; + } + + if ($.isArray(value)) { + // Equivalent to $.isNumeric() but compatible with jQuery < 1.7 + if (!isNaN(parseFloat(value[1])) && isFinite(value[1])) { + value[1] = +value[1]; + } else { + value[1] = 0; + } + } else if (!isNaN(parseFloat(value)) && isFinite(value)) { + value = [1, +value]; + } else { + value = [1, 0]; + } + + data[i].data = [value]; + } + + // Sum up all the slices, so we can calculate percentages for each + + for (var i = 0; i < data.length; ++i) { + total += data[i].data[0][1]; + } + + // Count the number of slices with percentages below the combine + // threshold; if it turns out to be just one, we won't combine. + + for (var i = 0; i < data.length; ++i) { + var value = data[i].data[0][1]; + if (value / total <= options.series.pie.combine.threshold) { + combined += value; + numCombined++; + if (!color) { + color = data[i].color; + } + } + } + + for (var i = 0; i < data.length; ++i) { + var value = data[i].data[0][1]; + if (numCombined < 2 || value / total > options.series.pie.combine.threshold) { + newdata.push( + $.extend(data[i], { /* extend to allow keeping all other original data values + and using them e.g. in labelFormatter. */ + data: [[1, value]], + color: data[i].color, + label: data[i].label, + angle: value * Math.PI * 2 / total, + percent: value / (total / 100) + }) + ); + } + } + + if (numCombined > 1) { + newdata.push({ + data: [[1, combined]], + color: color, + label: options.series.pie.combine.label, + angle: combined * Math.PI * 2 / total, + percent: combined / (total / 100) + }); + } + + return newdata; + } + + function draw(plot, newCtx) { + + if (!target) { + return; // if no series were passed + } + + var canvasWidth = plot.getPlaceholder().width(), + canvasHeight = plot.getPlaceholder().height(), + legendWidth = target.children().filter(".legend").children().width() || 0; + + ctx = newCtx; + + // WARNING: HACK! REWRITE THIS CODE AS SOON AS POSSIBLE! + + // When combining smaller slices into an 'other' slice, we need to + // add a new series. Since Flot gives plugins no way to modify the + // list of series, the pie plugin uses a hack where the first call + // to processDatapoints results in a call to setData with the new + // list of series, then subsequent processDatapoints do nothing. + + // The plugin-global 'processed' flag is used to control this hack; + // it starts out false, and is set to true after the first call to + // processDatapoints. + + // Unfortunately this turns future setData calls into no-ops; they + // call processDatapoints, the flag is true, and nothing happens. + + // To fix this we'll set the flag back to false here in draw, when + // all series have been processed, so the next sequence of calls to + // processDatapoints once again starts out with a slice-combine. + // This is really a hack; in 0.9 we need to give plugins a proper + // way to modify series before any processing begins. + + processed = false; + + // calculate maximum radius and center point + + maxRadius = Math.min(canvasWidth, canvasHeight / options.series.pie.tilt) / 2; + centerTop = canvasHeight / 2 + options.series.pie.offset.top; + centerLeft = canvasWidth / 2; + + if (options.series.pie.offset.left == "auto") { + if (options.legend.position.match("w")) { + centerLeft += legendWidth / 2; + } else { + centerLeft -= legendWidth / 2; + } + if (centerLeft < maxRadius) { + centerLeft = maxRadius; + } else if (centerLeft > canvasWidth - maxRadius) { + centerLeft = canvasWidth - maxRadius; + } + } else { + centerLeft += options.series.pie.offset.left; + } + + var slices = plot.getData(), + attempts = 0; + + // Keep shrinking the pie's radius until drawPie returns true, + // indicating that all the labels fit, or we try too many times. + + do { + if (attempts > 0) { + maxRadius *= REDRAW_SHRINK; + } + attempts += 1; + clear(); + if (options.series.pie.tilt <= 0.8) { + drawShadow(); + } + } while (!drawPie() && attempts < REDRAW_ATTEMPTS) + + if (attempts >= REDRAW_ATTEMPTS) { + clear(); + target.prepend("
Could not draw pie with labels contained inside canvas
"); + } + + if (plot.setSeries && plot.insertLegend) { + plot.setSeries(slices); + plot.insertLegend(); + } + + // we're actually done at this point, just defining internal functions at this point + + function clear() { + ctx.clearRect(0, 0, canvasWidth, canvasHeight); + target.children().filter(".pieLabel, .pieLabelBackground").remove(); + } + + function drawShadow() { + + var shadowLeft = options.series.pie.shadow.left; + var shadowTop = options.series.pie.shadow.top; + var edge = 10; + var alpha = options.series.pie.shadow.alpha; + var radius = options.series.pie.radius > 1 ? options.series.pie.radius : maxRadius * options.series.pie.radius; + + if (radius >= canvasWidth / 2 - shadowLeft || radius * options.series.pie.tilt >= canvasHeight / 2 - shadowTop || radius <= edge) { + return; // shadow would be outside canvas, so don't draw it + } + + ctx.save(); + ctx.translate(shadowLeft,shadowTop); + ctx.globalAlpha = alpha; + ctx.fillStyle = "#000"; + + // center and rotate to starting position + + ctx.translate(centerLeft,centerTop); + ctx.scale(1, options.series.pie.tilt); + + //radius -= edge; + + for (var i = 1; i <= edge; i++) { + ctx.beginPath(); + ctx.arc(0, 0, radius, 0, Math.PI * 2, false); + ctx.fill(); + radius -= i; + } + + ctx.restore(); + } + + function drawPie() { + + var startAngle = Math.PI * options.series.pie.startAngle; + var radius = options.series.pie.radius > 1 ? options.series.pie.radius : maxRadius * options.series.pie.radius; + + // center and rotate to starting position + + ctx.save(); + ctx.translate(centerLeft,centerTop); + ctx.scale(1, options.series.pie.tilt); + //ctx.rotate(startAngle); // start at top; -- This doesn't work properly in Opera + + // draw slices + + ctx.save(); + var currentAngle = startAngle; + for (var i = 0; i < slices.length; ++i) { + slices[i].startAngle = currentAngle; + drawSlice(slices[i].angle, slices[i].color, true); + } + ctx.restore(); + + // draw slice outlines + + if (options.series.pie.stroke.width > 0) { + ctx.save(); + ctx.lineWidth = options.series.pie.stroke.width; + currentAngle = startAngle; + for (var i = 0; i < slices.length; ++i) { + drawSlice(slices[i].angle, options.series.pie.stroke.color, false); + } + ctx.restore(); + } + + // draw donut hole + + drawDonutHole(ctx); + + ctx.restore(); + + // Draw the labels, returning true if they fit within the plot + + if (options.series.pie.label.show) { + return drawLabels(); + } else return true; + + function drawSlice(angle, color, fill) { + + if (angle <= 0 || isNaN(angle)) { + return; + } + + if (fill) { + ctx.fillStyle = color; + } else { + ctx.strokeStyle = color; + ctx.lineJoin = "round"; + } + + ctx.beginPath(); + if (Math.abs(angle - Math.PI * 2) > 0.000000001) { + ctx.moveTo(0, 0); // Center of the pie + } + + //ctx.arc(0, 0, radius, 0, angle, false); // This doesn't work properly in Opera + ctx.arc(0, 0, radius,currentAngle, currentAngle + angle / 2, false); + ctx.arc(0, 0, radius,currentAngle + angle / 2, currentAngle + angle, false); + ctx.closePath(); + //ctx.rotate(angle); // This doesn't work properly in Opera + currentAngle += angle; + + if (fill) { + ctx.fill(); + } else { + ctx.stroke(); + } + } + + function drawLabels() { + + var currentAngle = startAngle; + var radius = options.series.pie.label.radius > 1 ? options.series.pie.label.radius : maxRadius * options.series.pie.label.radius; + + for (var i = 0; i < slices.length; ++i) { + if (slices[i].percent >= options.series.pie.label.threshold * 100) { + if (!drawLabel(slices[i], currentAngle, i)) { + return false; + } + } + currentAngle += slices[i].angle; + } + + return true; + + function drawLabel(slice, startAngle, index) { + + if (slice.data[0][1] == 0) { + return true; + } + + // format label text + + var lf = options.legend.labelFormatter, text, plf = options.series.pie.label.formatter; + + if (lf) { + text = lf(slice.label, slice); + } else { + text = slice.label; + } + + if (plf) { + text = plf(text, slice); + } + + var halfAngle = ((startAngle + slice.angle) + startAngle) / 2; + var x = centerLeft + Math.round(Math.cos(halfAngle) * radius); + var y = centerTop + Math.round(Math.sin(halfAngle) * radius) * options.series.pie.tilt; + + var html = "" + text + ""; + target.append(html); + + var label = target.children("#pieLabel" + index); + var labelTop = (y - label.height() / 2); + var labelLeft = (x - label.width() / 2); + + label.css("top", labelTop); + label.css("left", labelLeft); + + // check to make sure that the label is not outside the canvas + + if (0 - labelTop > 0 || 0 - labelLeft > 0 || canvasHeight - (labelTop + label.height()) < 0 || canvasWidth - (labelLeft + label.width()) < 0) { + return false; + } + + if (options.series.pie.label.background.opacity != 0) { + + // put in the transparent background separately to avoid blended labels and label boxes + + var c = options.series.pie.label.background.color; + + if (c == null) { + c = slice.color; + } + + var pos = "top:" + labelTop + "px;left:" + labelLeft + "px;"; + $("
") + .css("opacity", options.series.pie.label.background.opacity) + .insertBefore(label); + } + + return true; + } // end individual label function + } // end drawLabels function + } // end drawPie function + } // end draw function + + // Placed here because it needs to be accessed from multiple locations + + function drawDonutHole(layer) { + if (options.series.pie.innerRadius > 0) { + + // subtract the center + + layer.save(); + var innerRadius = options.series.pie.innerRadius > 1 ? options.series.pie.innerRadius : maxRadius * options.series.pie.innerRadius; + layer.globalCompositeOperation = "destination-out"; // this does not work with excanvas, but it will fall back to using the stroke color + layer.beginPath(); + layer.fillStyle = options.series.pie.stroke.color; + layer.arc(0, 0, innerRadius, 0, Math.PI * 2, false); + layer.fill(); + layer.closePath(); + layer.restore(); + + // add inner stroke + + layer.save(); + layer.beginPath(); + layer.strokeStyle = options.series.pie.stroke.color; + layer.arc(0, 0, innerRadius, 0, Math.PI * 2, false); + layer.stroke(); + layer.closePath(); + layer.restore(); + + // TODO: add extra shadow inside hole (with a mask) if the pie is tilted. + } + } + + //-- Additional Interactive related functions -- + + function isPointInPoly(poly, pt) { + for(var c = false, i = -1, l = poly.length, j = l - 1; ++i < l; j = i) + ((poly[i][1] <= pt[1] && pt[1] < poly[j][1]) || (poly[j][1] <= pt[1] && pt[1]< poly[i][1])) + && (pt[0] < (poly[j][0] - poly[i][0]) * (pt[1] - poly[i][1]) / (poly[j][1] - poly[i][1]) + poly[i][0]) + && (c = !c); + return c; + } + + function findNearbySlice(mouseX, mouseY) { + + var slices = plot.getData(), + options = plot.getOptions(), + radius = options.series.pie.radius > 1 ? options.series.pie.radius : maxRadius * options.series.pie.radius, + x, y; + + for (var i = 0; i < slices.length; ++i) { + + var s = slices[i]; + + if (s.pie.show) { + + ctx.save(); + ctx.beginPath(); + ctx.moveTo(0, 0); // Center of the pie + //ctx.scale(1, options.series.pie.tilt); // this actually seems to break everything when here. + ctx.arc(0, 0, radius, s.startAngle, s.startAngle + s.angle / 2, false); + ctx.arc(0, 0, radius, s.startAngle + s.angle / 2, s.startAngle + s.angle, false); + ctx.closePath(); + x = mouseX - centerLeft; + y = mouseY - centerTop; + + if (ctx.isPointInPath) { + if (ctx.isPointInPath(mouseX - centerLeft, mouseY - centerTop)) { + ctx.restore(); + return { + datapoint: [s.percent, s.data], + dataIndex: 0, + series: s, + seriesIndex: i + }; + } + } else { + + // excanvas for IE doesn;t support isPointInPath, this is a workaround. + + var p1X = radius * Math.cos(s.startAngle), + p1Y = radius * Math.sin(s.startAngle), + p2X = radius * Math.cos(s.startAngle + s.angle / 4), + p2Y = radius * Math.sin(s.startAngle + s.angle / 4), + p3X = radius * Math.cos(s.startAngle + s.angle / 2), + p3Y = radius * Math.sin(s.startAngle + s.angle / 2), + p4X = radius * Math.cos(s.startAngle + s.angle / 1.5), + p4Y = radius * Math.sin(s.startAngle + s.angle / 1.5), + p5X = radius * Math.cos(s.startAngle + s.angle), + p5Y = radius * Math.sin(s.startAngle + s.angle), + arrPoly = [[0, 0], [p1X, p1Y], [p2X, p2Y], [p3X, p3Y], [p4X, p4Y], [p5X, p5Y]], + arrPoint = [x, y]; + + // TODO: perhaps do some mathmatical trickery here with the Y-coordinate to compensate for pie tilt? + + if (isPointInPoly(arrPoly, arrPoint)) { + ctx.restore(); + return { + datapoint: [s.percent, s.data], + dataIndex: 0, + series: s, + seriesIndex: i + }; + } + } + + ctx.restore(); + } + } + + return null; + } + + function onMouseMove(e) { + triggerClickHoverEvent("plothover", e); + } + + function onClick(e) { + triggerClickHoverEvent("plotclick", e); + } + + // trigger click or hover event (they send the same parameters so we share their code) + + function triggerClickHoverEvent(eventname, e) { + + var offset = plot.offset(); + var canvasX = parseInt(e.pageX - offset.left); + var canvasY = parseInt(e.pageY - offset.top); + var item = findNearbySlice(canvasX, canvasY); + + if (options.grid.autoHighlight) { + + // clear auto-highlights + + for (var i = 0; i < highlights.length; ++i) { + var h = highlights[i]; + if (h.auto == eventname && !(item && h.series == item.series)) { + unhighlight(h.series); + } + } + } + + // highlight the slice + + if (item) { + highlight(item.series, eventname); + } + + // trigger any hover bind events + + var pos = { pageX: e.pageX, pageY: e.pageY }; + target.trigger(eventname, [pos, item]); + } + + function highlight(s, auto) { + //if (typeof s == "number") { + // s = series[s]; + //} + + var i = indexOfHighlight(s); + + if (i == -1) { + highlights.push({ series: s, auto: auto }); + plot.triggerRedrawOverlay(); + } else if (!auto) { + highlights[i].auto = false; + } + } + + function unhighlight(s) { + if (s == null) { + highlights = []; + plot.triggerRedrawOverlay(); + } + + //if (typeof s == "number") { + // s = series[s]; + //} + + var i = indexOfHighlight(s); + + if (i != -1) { + highlights.splice(i, 1); + plot.triggerRedrawOverlay(); + } + } + + function indexOfHighlight(s) { + for (var i = 0; i < highlights.length; ++i) { + var h = highlights[i]; + if (h.series == s) + return i; + } + return -1; + } + + function drawOverlay(plot, octx) { + + var options = plot.getOptions(); + + var radius = options.series.pie.radius > 1 ? options.series.pie.radius : maxRadius * options.series.pie.radius; + + octx.save(); + octx.translate(centerLeft, centerTop); + octx.scale(1, options.series.pie.tilt); + + for (var i = 0; i < highlights.length; ++i) { + drawHighlight(highlights[i].series); + } + + drawDonutHole(octx); + + octx.restore(); + + function drawHighlight(series) { + + if (series.angle <= 0 || isNaN(series.angle)) { + return; + } + + //octx.fillStyle = parseColor(options.series.pie.highlight.color).scale(null, null, null, options.series.pie.highlight.opacity).toString(); + octx.fillStyle = "rgba(255, 255, 255, " + options.series.pie.highlight.opacity + ")"; // this is temporary until we have access to parseColor + octx.beginPath(); + if (Math.abs(series.angle - Math.PI * 2) > 0.000000001) { + octx.moveTo(0, 0); // Center of the pie + } + octx.arc(0, 0, radius, series.startAngle, series.startAngle + series.angle / 2, false); + octx.arc(0, 0, radius, series.startAngle + series.angle / 2, series.startAngle + series.angle, false); + octx.closePath(); + octx.fill(); + } + } + } // end init (plugin body) + + // define pie specific options and their default values + + var options = { + series: { + pie: { + show: false, + radius: "auto", // actual radius of the visible pie (based on full calculated radius if <=1, or hard pixel value) + innerRadius: 0, /* for donut */ + startAngle: 3/2, + tilt: 1, + shadow: { + left: 5, // shadow left offset + top: 15, // shadow top offset + alpha: 0.02 // shadow alpha + }, + offset: { + top: 0, + left: "auto" + }, + stroke: { + color: "#fff", + width: 1 + }, + label: { + show: "auto", + formatter: function(label, slice) { + return "
" + label + "
" + Math.round(slice.percent) + "%
"; + }, // formatter function + radius: 1, // radius at which to place the labels (based on full calculated radius if <=1, or hard pixel value) + background: { + color: null, + opacity: 0 + }, + threshold: 0 // percentage at which to hide the label (i.e. the slice is too narrow) + }, + combine: { + threshold: -1, // percentage at which to combine little slices into one larger slice + color: null, // color to give the new slice (auto-generated if null) + label: "Other" // label to give the new slice + }, + highlight: { + //color: "#fff", // will add this functionality once parseColor is available + opacity: 0.5 + } + } + } + }; + + $.plot.plugins.push({ + init: init, + options: options, + name: "pie", + version: "1.1" + }); + +})(jQuery); diff --git a/pandora_console/include/graphs/flot/jquery.flot.pie.min.js b/pandora_console/include/graphs/flot/jquery.flot.pie.min.js new file mode 100644 index 0000000000..9bc488b154 --- /dev/null +++ b/pandora_console/include/graphs/flot/jquery.flot.pie.min.js @@ -0,0 +1,7 @@ +/* Javascript plotting library for jQuery, version 0.8.3. + +Copyright (c) 2007-2014 IOLA and Ole Laursen. +Licensed under the MIT license. + +*/ +(function($){var REDRAW_ATTEMPTS=10;var REDRAW_SHRINK=.95;function init(plot){var canvas=null,target=null,options=null,maxRadius=null,centerLeft=null,centerTop=null,processed=false,ctx=null;var highlights=[];plot.hooks.processOptions.push(function(plot,options){if(options.series.pie.show){options.grid.show=false;if(options.series.pie.label.show=="auto"){if(options.legend.show){options.series.pie.label.show=false}else{options.series.pie.label.show=true}}if(options.series.pie.radius=="auto"){if(options.series.pie.label.show){options.series.pie.radius=3/4}else{options.series.pie.radius=1}}if(options.series.pie.tilt>1){options.series.pie.tilt=1}else if(options.series.pie.tilt<0){options.series.pie.tilt=0}}});plot.hooks.bindEvents.push(function(plot,eventHolder){var options=plot.getOptions();if(options.series.pie.show){if(options.grid.hoverable){eventHolder.unbind("mousemove").mousemove(onMouseMove)}if(options.grid.clickable){eventHolder.unbind("click").click(onClick)}}});plot.hooks.processDatapoints.push(function(plot,series,data,datapoints){var options=plot.getOptions();if(options.series.pie.show){processDatapoints(plot,series,data,datapoints)}});plot.hooks.drawOverlay.push(function(plot,octx){var options=plot.getOptions();if(options.series.pie.show){drawOverlay(plot,octx)}});plot.hooks.draw.push(function(plot,newCtx){var options=plot.getOptions();if(options.series.pie.show){draw(plot,newCtx)}});function processDatapoints(plot,series,datapoints){if(!processed){processed=true;canvas=plot.getCanvas();target=$(canvas).parent();options=plot.getOptions();plot.setData(combine(plot.getData()))}}function combine(data){var total=0,combined=0,numCombined=0,color=options.series.pie.combine.color,newdata=[];for(var i=0;ioptions.series.pie.combine.threshold){newdata.push($.extend(data[i],{data:[[1,value]],color:data[i].color,label:data[i].label,angle:value*Math.PI*2/total,percent:value/(total/100)}))}}if(numCombined>1){newdata.push({data:[[1,combined]],color:color,label:options.series.pie.combine.label,angle:combined*Math.PI*2/total,percent:combined/(total/100)})}return newdata}function draw(plot,newCtx){if(!target){return}var canvasWidth=plot.getPlaceholder().width(),canvasHeight=plot.getPlaceholder().height(),legendWidth=target.children().filter(".legend").children().width()||0;ctx=newCtx;processed=false;maxRadius=Math.min(canvasWidth,canvasHeight/options.series.pie.tilt)/2;centerTop=canvasHeight/2+options.series.pie.offset.top;centerLeft=canvasWidth/2;if(options.series.pie.offset.left=="auto"){if(options.legend.position.match("w")){centerLeft+=legendWidth/2}else{centerLeft-=legendWidth/2}if(centerLeftcanvasWidth-maxRadius){centerLeft=canvasWidth-maxRadius}}else{centerLeft+=options.series.pie.offset.left}var slices=plot.getData(),attempts=0;do{if(attempts>0){maxRadius*=REDRAW_SHRINK}attempts+=1;clear();if(options.series.pie.tilt<=.8){drawShadow()}}while(!drawPie()&&attempts=REDRAW_ATTEMPTS){clear();target.prepend("
Could not draw pie with labels contained inside canvas
")}if(plot.setSeries&&plot.insertLegend){plot.setSeries(slices);plot.insertLegend()}function clear(){ctx.clearRect(0,0,canvasWidth,canvasHeight);target.children().filter(".pieLabel, .pieLabelBackground").remove()}function drawShadow(){var shadowLeft=options.series.pie.shadow.left;var shadowTop=options.series.pie.shadow.top;var edge=10;var alpha=options.series.pie.shadow.alpha;var radius=options.series.pie.radius>1?options.series.pie.radius:maxRadius*options.series.pie.radius;if(radius>=canvasWidth/2-shadowLeft||radius*options.series.pie.tilt>=canvasHeight/2-shadowTop||radius<=edge){return}ctx.save();ctx.translate(shadowLeft,shadowTop);ctx.globalAlpha=alpha;ctx.fillStyle="#000";ctx.translate(centerLeft,centerTop);ctx.scale(1,options.series.pie.tilt);for(var i=1;i<=edge;i++){ctx.beginPath();ctx.arc(0,0,radius,0,Math.PI*2,false);ctx.fill();radius-=i}ctx.restore()}function drawPie(){var startAngle=Math.PI*options.series.pie.startAngle;var radius=options.series.pie.radius>1?options.series.pie.radius:maxRadius*options.series.pie.radius;ctx.save();ctx.translate(centerLeft,centerTop);ctx.scale(1,options.series.pie.tilt);ctx.save();var currentAngle=startAngle;for(var i=0;i0){ctx.save();ctx.lineWidth=options.series.pie.stroke.width;currentAngle=startAngle;for(var i=0;i1e-9){ctx.moveTo(0,0)}ctx.arc(0,0,radius,currentAngle,currentAngle+angle/2,false);ctx.arc(0,0,radius,currentAngle+angle/2,currentAngle+angle,false);ctx.closePath();currentAngle+=angle;if(fill){ctx.fill()}else{ctx.stroke()}}function drawLabels(){var currentAngle=startAngle;var radius=options.series.pie.label.radius>1?options.series.pie.label.radius:maxRadius*options.series.pie.label.radius;for(var i=0;i=options.series.pie.label.threshold*100){if(!drawLabel(slices[i],currentAngle,i)){return false}}currentAngle+=slices[i].angle}return true;function drawLabel(slice,startAngle,index){if(slice.data[0][1]==0){return true}var lf=options.legend.labelFormatter,text,plf=options.series.pie.label.formatter;if(lf){text=lf(slice.label,slice)}else{text=slice.label}if(plf){text=plf(text,slice)}var halfAngle=(startAngle+slice.angle+startAngle)/2;var x=centerLeft+Math.round(Math.cos(halfAngle)*radius);var y=centerTop+Math.round(Math.sin(halfAngle)*radius)*options.series.pie.tilt;var html=""+text+"";target.append(html);var label=target.children("#pieLabel"+index);var labelTop=y-label.height()/2;var labelLeft=x-label.width()/2;label.css("top",labelTop);label.css("left",labelLeft);if(0-labelTop>0||0-labelLeft>0||canvasHeight-(labelTop+label.height())<0||canvasWidth-(labelLeft+label.width())<0){return false}if(options.series.pie.label.background.opacity!=0){var c=options.series.pie.label.background.color;if(c==null){c=slice.color}var pos="top:"+labelTop+"px;left:"+labelLeft+"px;";$("
").css("opacity",options.series.pie.label.background.opacity).insertBefore(label)}return true}}}}function drawDonutHole(layer){if(options.series.pie.innerRadius>0){layer.save();var innerRadius=options.series.pie.innerRadius>1?options.series.pie.innerRadius:maxRadius*options.series.pie.innerRadius;layer.globalCompositeOperation="destination-out";layer.beginPath();layer.fillStyle=options.series.pie.stroke.color;layer.arc(0,0,innerRadius,0,Math.PI*2,false);layer.fill();layer.closePath();layer.restore();layer.save();layer.beginPath();layer.strokeStyle=options.series.pie.stroke.color;layer.arc(0,0,innerRadius,0,Math.PI*2,false);layer.stroke();layer.closePath();layer.restore()}}function isPointInPoly(poly,pt){for(var c=false,i=-1,l=poly.length,j=l-1;++i1?options.series.pie.radius:maxRadius*options.series.pie.radius,x,y;for(var i=0;i1?options.series.pie.radius:maxRadius*options.series.pie.radius;octx.save();octx.translate(centerLeft,centerTop);octx.scale(1,options.series.pie.tilt);for(var i=0;i1e-9){octx.moveTo(0,0)}octx.arc(0,0,radius,series.startAngle,series.startAngle+series.angle/2,false);octx.arc(0,0,radius,series.startAngle+series.angle/2,series.startAngle+series.angle,false);octx.closePath();octx.fill()}}}var options={series:{pie:{show:false,radius:"auto",innerRadius:0,startAngle:3/2,tilt:1,shadow:{left:5,top:15,alpha:.02},offset:{top:0,left:"auto"},stroke:{color:"#fff",width:1},label:{show:"auto",formatter:function(label,slice){return"
"+label+"
"+Math.round(slice.percent)+"%
"},radius:1,background:{color:null,opacity:0},threshold:0},combine:{threshold:-1,color:null,label:"Other"},highlight:{opacity:.5}}}};$.plot.plugins.push({init:init,options:options,name:"pie",version:"1.1"})})(jQuery); \ No newline at end of file diff --git a/pandora_console/include/graphs/flot/jquery.flot.resize.js b/pandora_console/include/graphs/flot/jquery.flot.resize.js new file mode 100644 index 0000000000..8a626dda0a --- /dev/null +++ b/pandora_console/include/graphs/flot/jquery.flot.resize.js @@ -0,0 +1,59 @@ +/* Flot plugin for automatically redrawing plots as the placeholder resizes. + +Copyright (c) 2007-2014 IOLA and Ole Laursen. +Licensed under the MIT license. + +It works by listening for changes on the placeholder div (through the jQuery +resize event plugin) - if the size changes, it will redraw the plot. + +There are no options. If you need to disable the plugin for some plots, you +can just fix the size of their placeholders. + +*/ + +/* Inline dependency: + * jQuery resize event - v1.1 - 3/14/2010 + * http://benalman.com/projects/jquery-resize-plugin/ + * + * Copyright (c) 2010 "Cowboy" Ben Alman + * Dual licensed under the MIT and GPL licenses. + * http://benalman.com/about/license/ + */ +(function($,e,t){"$:nomunge";var i=[],n=$.resize=$.extend($.resize,{}),a,r=false,s="setTimeout",u="resize",m=u+"-special-event",o="pendingDelay",l="activeDelay",f="throttleWindow";n[o]=200;n[l]=20;n[f]=true;$.event.special[u]={setup:function(){if(!n[f]&&this[s]){return false}var e=$(this);i.push(this);e.data(m,{w:e.width(),h:e.height()});if(i.length===1){a=t;h()}},teardown:function(){if(!n[f]&&this[s]){return false}var e=$(this);for(var t=i.length-1;t>=0;t--){if(i[t]==this){i.splice(t,1);break}}e.removeData(m);if(!i.length){if(r){cancelAnimationFrame(a)}else{clearTimeout(a)}a=null}},add:function(e){if(!n[f]&&this[s]){return false}var i;function a(e,n,a){var r=$(this),s=r.data(m)||{};s.w=n!==t?n:r.width();s.h=a!==t?a:r.height();i.apply(this,arguments)}if($.isFunction(e)){i=e;return a}else{i=e.handler;e.handler=a}}};function h(t){if(r===true){r=t||1}for(var s=i.length-1;s>=0;s--){var l=$(i[s]);if(l[0]==e||l.is(":visible")){var f=l.width(),c=l.height(),d=l.data(m);if(d&&(f!==d.w||c!==d.h)){l.trigger(u,[d.w=f,d.h=c]);r=t||true}}else{d=l.data(m);d.w=0;d.h=0}}if(a!==null){if(r&&(t==null||t-r<1e3)){a=e.requestAnimationFrame(h)}else{a=setTimeout(h,n[o]);r=false}}}if(!e.requestAnimationFrame){e.requestAnimationFrame=function(){return e.webkitRequestAnimationFrame||e.mozRequestAnimationFrame||e.oRequestAnimationFrame||e.msRequestAnimationFrame||function(t,i){return e.setTimeout(function(){t((new Date).getTime())},n[l])}}()}if(!e.cancelAnimationFrame){e.cancelAnimationFrame=function(){return e.webkitCancelRequestAnimationFrame||e.mozCancelRequestAnimationFrame||e.oCancelRequestAnimationFrame||e.msCancelRequestAnimationFrame||clearTimeout}()}})(jQuery,this); + +(function ($) { + var options = { }; // no options + + function init(plot) { + function onResize() { + var placeholder = plot.getPlaceholder(); + + // somebody might have hidden us and we can't plot + // when we don't have the dimensions + if (placeholder.width() == 0 || placeholder.height() == 0) + return; + + plot.resize(); + plot.setupGrid(); + plot.draw(); + } + + function bindEvents(plot, eventHolder) { + plot.getPlaceholder().resize(onResize); + } + + function shutdown(plot, eventHolder) { + plot.getPlaceholder().unbind("resize", onResize); + } + + plot.hooks.bindEvents.push(bindEvents); + plot.hooks.shutdown.push(shutdown); + } + + $.plot.plugins.push({ + init: init, + options: options, + name: 'resize', + version: '1.0' + }); +})(jQuery); diff --git a/pandora_console/include/graphs/flot/jquery.flot.resize.min.js b/pandora_console/include/graphs/flot/jquery.flot.resize.min.js index 1fa0771f57..7e92aa681c 100644 --- a/pandora_console/include/graphs/flot/jquery.flot.resize.min.js +++ b/pandora_console/include/graphs/flot/jquery.flot.resize.min.js @@ -1 +1,7 @@ -(function(n,p,u){var w=n([]),s=n.resize=n.extend(n.resize,{}),o,l="setTimeout",m="resize",t=m+"-special-event",v="delay",r="throttleWindow";s[v]=250;s[r]=true;n.event.special[m]={setup:function(){if(!s[r]&&this[l]){return false}var a=n(this);w=w.add(a);n.data(this,t,{w:a.width(),h:a.height()});if(w.length===1){q()}},teardown:function(){if(!s[r]&&this[l]){return false}var a=n(this);w=w.not(a);a.removeData(t);if(!w.length){clearTimeout(o)}},add:function(b){if(!s[r]&&this[l]){return false}var c;function a(d,h,g){var f=n(this),e=n.data(this,t);e.w=h!==u?h:f.width();e.h=g!==u?g:f.height();c.apply(this,arguments)}if(n.isFunction(b)){c=b;return a}else{c=b.handler;b.handler=a}}};function q(){o=p[l](function(){w.each(function(){var d=n(this),a=d.width(),b=d.height(),c=n.data(this,t);if(a!==c.w||b!==c.h){d.trigger(m,[c.w=a,c.h=b])}});q()},s[v])}})(jQuery,this);(function(b){var a={};function c(f){function e(){var h=f.getPlaceholder();if(h.width()==0||h.height()==0){return}f.resize();f.setupGrid();f.draw()}function g(i,h){i.getPlaceholder().resize(e)}function d(i,h){i.getPlaceholder().unbind("resize",e)}f.hooks.bindEvents.push(g);f.hooks.shutdown.push(d)}b.plot.plugins.push({init:c,options:a,name:"resize",version:"1.0"})})(jQuery); \ No newline at end of file +/* Javascript plotting library for jQuery, version 0.8.3. + +Copyright (c) 2007-2014 IOLA and Ole Laursen. +Licensed under the MIT license. + +*/ +(function($,e,t){"$:nomunge";var i=[],n=$.resize=$.extend($.resize,{}),a,r=false,s="setTimeout",u="resize",m=u+"-special-event",o="pendingDelay",l="activeDelay",f="throttleWindow";n[o]=200;n[l]=20;n[f]=true;$.event.special[u]={setup:function(){if(!n[f]&&this[s]){return false}var e=$(this);i.push(this);e.data(m,{w:e.width(),h:e.height()});if(i.length===1){a=t;h()}},teardown:function(){if(!n[f]&&this[s]){return false}var e=$(this);for(var t=i.length-1;t>=0;t--){if(i[t]==this){i.splice(t,1);break}}e.removeData(m);if(!i.length){if(r){cancelAnimationFrame(a)}else{clearTimeout(a)}a=null}},add:function(e){if(!n[f]&&this[s]){return false}var i;function a(e,n,a){var r=$(this),s=r.data(m)||{};s.w=n!==t?n:r.width();s.h=a!==t?a:r.height();i.apply(this,arguments)}if($.isFunction(e)){i=e;return a}else{i=e.handler;e.handler=a}}};function h(t){if(r===true){r=t||1}for(var s=i.length-1;s>=0;s--){var l=$(i[s]);if(l[0]==e||l.is(":visible")){var f=l.width(),c=l.height(),d=l.data(m);if(d&&(f!==d.w||c!==d.h)){l.trigger(u,[d.w=f,d.h=c]);r=t||true}}else{d=l.data(m);d.w=0;d.h=0}}if(a!==null){if(r&&(t==null||t-r<1e3)){a=e.requestAnimationFrame(h)}else{a=setTimeout(h,n[o]);r=false}}}if(!e.requestAnimationFrame){e.requestAnimationFrame=function(){return e.webkitRequestAnimationFrame||e.mozRequestAnimationFrame||e.oRequestAnimationFrame||e.msRequestAnimationFrame||function(t,i){return e.setTimeout(function(){t((new Date).getTime())},n[l])}}()}if(!e.cancelAnimationFrame){e.cancelAnimationFrame=function(){return e.webkitCancelRequestAnimationFrame||e.mozCancelRequestAnimationFrame||e.oCancelRequestAnimationFrame||e.msCancelRequestAnimationFrame||clearTimeout}()}})(jQuery,this);(function($){var options={};function init(plot){function onResize(){var placeholder=plot.getPlaceholder();if(placeholder.width()==0||placeholder.height()==0)return;plot.resize();plot.setupGrid();plot.draw()}function bindEvents(plot,eventHolder){plot.getPlaceholder().resize(onResize)}function shutdown(plot,eventHolder){plot.getPlaceholder().unbind("resize",onResize)}plot.hooks.bindEvents.push(bindEvents);plot.hooks.shutdown.push(shutdown)}$.plot.plugins.push({init:init,options:options,name:"resize",version:"1.0"})})(jQuery); \ No newline at end of file diff --git a/pandora_console/include/graphs/flot/jquery.flot.selection.js b/pandora_console/include/graphs/flot/jquery.flot.selection.js new file mode 100644 index 0000000000..d3c20fa4e1 --- /dev/null +++ b/pandora_console/include/graphs/flot/jquery.flot.selection.js @@ -0,0 +1,360 @@ +/* Flot plugin for selecting regions of a plot. + +Copyright (c) 2007-2014 IOLA and Ole Laursen. +Licensed under the MIT license. + +The plugin supports these options: + +selection: { + mode: null or "x" or "y" or "xy", + color: color, + shape: "round" or "miter" or "bevel", + minSize: number of pixels +} + +Selection support is enabled by setting the mode to one of "x", "y" or "xy". +In "x" mode, the user will only be able to specify the x range, similarly for +"y" mode. For "xy", the selection becomes a rectangle where both ranges can be +specified. "color" is color of the selection (if you need to change the color +later on, you can get to it with plot.getOptions().selection.color). "shape" +is the shape of the corners of the selection. + +"minSize" is the minimum size a selection can be in pixels. This value can +be customized to determine the smallest size a selection can be and still +have the selection rectangle be displayed. When customizing this value, the +fact that it refers to pixels, not axis units must be taken into account. +Thus, for example, if there is a bar graph in time mode with BarWidth set to 1 +minute, setting "minSize" to 1 will not make the minimum selection size 1 +minute, but rather 1 pixel. Note also that setting "minSize" to 0 will prevent +"plotunselected" events from being fired when the user clicks the mouse without +dragging. + +When selection support is enabled, a "plotselected" event will be emitted on +the DOM element you passed into the plot function. The event handler gets a +parameter with the ranges selected on the axes, like this: + + placeholder.bind( "plotselected", function( event, ranges ) { + alert("You selected " + ranges.xaxis.from + " to " + ranges.xaxis.to) + // similar for yaxis - with multiple axes, the extra ones are in + // x2axis, x3axis, ... + }); + +The "plotselected" event is only fired when the user has finished making the +selection. A "plotselecting" event is fired during the process with the same +parameters as the "plotselected" event, in case you want to know what's +happening while it's happening, + +A "plotunselected" event with no arguments is emitted when the user clicks the +mouse to remove the selection. As stated above, setting "minSize" to 0 will +destroy this behavior. + +The plugin allso adds the following methods to the plot object: + +- setSelection( ranges, preventEvent ) + + Set the selection rectangle. The passed in ranges is on the same form as + returned in the "plotselected" event. If the selection mode is "x", you + should put in either an xaxis range, if the mode is "y" you need to put in + an yaxis range and both xaxis and yaxis if the selection mode is "xy", like + this: + + setSelection({ xaxis: { from: 0, to: 10 }, yaxis: { from: 40, to: 60 } }); + + setSelection will trigger the "plotselected" event when called. If you don't + want that to happen, e.g. if you're inside a "plotselected" handler, pass + true as the second parameter. If you are using multiple axes, you can + specify the ranges on any of those, e.g. as x2axis/x3axis/... instead of + xaxis, the plugin picks the first one it sees. + +- clearSelection( preventEvent ) + + Clear the selection rectangle. Pass in true to avoid getting a + "plotunselected" event. + +- getSelection() + + Returns the current selection in the same format as the "plotselected" + event. If there's currently no selection, the function returns null. + +*/ + +(function ($) { + function init(plot) { + var selection = { + first: { x: -1, y: -1}, second: { x: -1, y: -1}, + show: false, + active: false + }; + + // FIXME: The drag handling implemented here should be + // abstracted out, there's some similar code from a library in + // the navigation plugin, this should be massaged a bit to fit + // the Flot cases here better and reused. Doing this would + // make this plugin much slimmer. + var savedhandlers = {}; + + var mouseUpHandler = null; + + function onMouseMove(e) { + if (selection.active) { + updateSelection(e); + + plot.getPlaceholder().trigger("plotselecting", [ getSelection() ]); + } + } + + function onMouseDown(e) { + if (e.which != 1) // only accept left-click + return; + + // cancel out any text selections + document.body.focus(); + + // prevent text selection and drag in old-school browsers + if (document.onselectstart !== undefined && savedhandlers.onselectstart == null) { + savedhandlers.onselectstart = document.onselectstart; + document.onselectstart = function () { return false; }; + } + if (document.ondrag !== undefined && savedhandlers.ondrag == null) { + savedhandlers.ondrag = document.ondrag; + document.ondrag = function () { return false; }; + } + + setSelectionPos(selection.first, e); + + selection.active = true; + + // this is a bit silly, but we have to use a closure to be + // able to whack the same handler again + mouseUpHandler = function (e) { onMouseUp(e); }; + + $(document).one("mouseup", mouseUpHandler); + } + + function onMouseUp(e) { + mouseUpHandler = null; + + // revert drag stuff for old-school browsers + if (document.onselectstart !== undefined) + document.onselectstart = savedhandlers.onselectstart; + if (document.ondrag !== undefined) + document.ondrag = savedhandlers.ondrag; + + // no more dragging + selection.active = false; + updateSelection(e); + + if (selectionIsSane()) + triggerSelectedEvent(); + else { + // this counts as a clear + plot.getPlaceholder().trigger("plotunselected", [ ]); + plot.getPlaceholder().trigger("plotselecting", [ null ]); + } + + return false; + } + + function getSelection() { + if (!selectionIsSane()) + return null; + + if (!selection.show) return null; + + var r = {}, c1 = selection.first, c2 = selection.second; + $.each(plot.getAxes(), function (name, axis) { + if (axis.used) { + var p1 = axis.c2p(c1[axis.direction]), p2 = axis.c2p(c2[axis.direction]); + r[name] = { from: Math.min(p1, p2), to: Math.max(p1, p2) }; + } + }); + return r; + } + + function triggerSelectedEvent() { + var r = getSelection(); + + plot.getPlaceholder().trigger("plotselected", [ r ]); + + // backwards-compat stuff, to be removed in future + if (r.xaxis && r.yaxis) + plot.getPlaceholder().trigger("selected", [ { x1: r.xaxis.from, y1: r.yaxis.from, x2: r.xaxis.to, y2: r.yaxis.to } ]); + } + + function clamp(min, value, max) { + return value < min ? min: (value > max ? max: value); + } + + function setSelectionPos(pos, e) { + var o = plot.getOptions(); + var offset = plot.getPlaceholder().offset(); + var plotOffset = plot.getPlotOffset(); + pos.x = clamp(0, e.pageX - offset.left - plotOffset.left, plot.width()); + pos.y = clamp(0, e.pageY - offset.top - plotOffset.top, plot.height()); + + if (o.selection.mode == "y") + pos.x = pos == selection.first ? 0 : plot.width(); + + if (o.selection.mode == "x") + pos.y = pos == selection.first ? 0 : plot.height(); + } + + function updateSelection(pos) { + if (pos.pageX == null) + return; + + setSelectionPos(selection.second, pos); + if (selectionIsSane()) { + selection.show = true; + plot.triggerRedrawOverlay(); + } + else + clearSelection(true); + } + + function clearSelection(preventEvent) { + if (selection.show) { + selection.show = false; + plot.triggerRedrawOverlay(); + if (!preventEvent) + plot.getPlaceholder().trigger("plotunselected", [ ]); + } + } + + // function taken from markings support in Flot + function extractRange(ranges, coord) { + var axis, from, to, key, axes = plot.getAxes(); + + for (var k in axes) { + axis = axes[k]; + if (axis.direction == coord) { + key = coord + axis.n + "axis"; + if (!ranges[key] && axis.n == 1) + key = coord + "axis"; // support x1axis as xaxis + if (ranges[key]) { + from = ranges[key].from; + to = ranges[key].to; + break; + } + } + } + + // backwards-compat stuff - to be removed in future + if (!ranges[key]) { + axis = coord == "x" ? plot.getXAxes()[0] : plot.getYAxes()[0]; + from = ranges[coord + "1"]; + to = ranges[coord + "2"]; + } + + // auto-reverse as an added bonus + if (from != null && to != null && from > to) { + var tmp = from; + from = to; + to = tmp; + } + + return { from: from, to: to, axis: axis }; + } + + function setSelection(ranges, preventEvent) { + var axis, range, o = plot.getOptions(); + + if (o.selection.mode == "y") { + selection.first.x = 0; + selection.second.x = plot.width(); + } + else { + range = extractRange(ranges, "x"); + + selection.first.x = range.axis.p2c(range.from); + selection.second.x = range.axis.p2c(range.to); + } + + if (o.selection.mode == "x") { + selection.first.y = 0; + selection.second.y = plot.height(); + } + else { + range = extractRange(ranges, "y"); + + selection.first.y = range.axis.p2c(range.from); + selection.second.y = range.axis.p2c(range.to); + } + + selection.show = true; + plot.triggerRedrawOverlay(); + if (!preventEvent && selectionIsSane()) + triggerSelectedEvent(); + } + + function selectionIsSane() { + var minSize = plot.getOptions().selection.minSize; + return Math.abs(selection.second.x - selection.first.x) >= minSize && + Math.abs(selection.second.y - selection.first.y) >= minSize; + } + + plot.clearSelection = clearSelection; + plot.setSelection = setSelection; + plot.getSelection = getSelection; + + plot.hooks.bindEvents.push(function(plot, eventHolder) { + var o = plot.getOptions(); + if (o.selection.mode != null) { + eventHolder.mousemove(onMouseMove); + eventHolder.mousedown(onMouseDown); + } + }); + + + plot.hooks.drawOverlay.push(function (plot, ctx) { + // draw selection + if (selection.show && selectionIsSane()) { + var plotOffset = plot.getPlotOffset(); + var o = plot.getOptions(); + + ctx.save(); + ctx.translate(plotOffset.left, plotOffset.top); + + var c = $.color.parse(o.selection.color); + + ctx.strokeStyle = c.scale('a', 0.8).toString(); + ctx.lineWidth = 1; + ctx.lineJoin = o.selection.shape; + ctx.fillStyle = c.scale('a', 0.4).toString(); + + var x = Math.min(selection.first.x, selection.second.x) + 0.5, + y = Math.min(selection.first.y, selection.second.y) + 0.5, + w = Math.abs(selection.second.x - selection.first.x) - 1, + h = Math.abs(selection.second.y - selection.first.y) - 1; + + ctx.fillRect(x, y, w, h); + ctx.strokeRect(x, y, w, h); + + ctx.restore(); + } + }); + + plot.hooks.shutdown.push(function (plot, eventHolder) { + eventHolder.unbind("mousemove", onMouseMove); + eventHolder.unbind("mousedown", onMouseDown); + + if (mouseUpHandler) + $(document).unbind("mouseup", mouseUpHandler); + }); + + } + + $.plot.plugins.push({ + init: init, + options: { + selection: { + mode: null, // one of null, "x", "y" or "xy" + color: "#e8cfac", + shape: "round", // one of "round", "miter", or "bevel" + minSize: 5 // minimum number of pixels + } + }, + name: 'selection', + version: '1.1' + }); +})(jQuery); diff --git a/pandora_console/include/graphs/flot/jquery.flot.selection.min.js b/pandora_console/include/graphs/flot/jquery.flot.selection.min.js index badc0052db..a0154fbc5b 100644 --- a/pandora_console/include/graphs/flot/jquery.flot.selection.min.js +++ b/pandora_console/include/graphs/flot/jquery.flot.selection.min.js @@ -1 +1,7 @@ -(function(a){function b(k){var p={first:{x:-1,y:-1},second:{x:-1,y:-1},show:false,active:false};var m={};var r=null;function e(s){if(p.active){l(s);k.getPlaceholder().trigger("plotselecting",[g()])}}function n(s){if(s.which!=1){return}document.body.focus();if(document.onselectstart!==undefined&&m.onselectstart==null){m.onselectstart=document.onselectstart;document.onselectstart=function(){return false}}if(document.ondrag!==undefined&&m.ondrag==null){m.ondrag=document.ondrag;document.ondrag=function(){return false}}d(p.first,s);p.active=true;r=function(t){j(t)};a(document).one("mouseup",r)}function j(s){r=null;if(document.onselectstart!==undefined){document.onselectstart=m.onselectstart}if(document.ondrag!==undefined){document.ondrag=m.ondrag}p.active=false;l(s);if(f()){i()}else{k.getPlaceholder().trigger("plotunselected",[]);k.getPlaceholder().trigger("plotselecting",[null])}return false}function g(){if(!f()){return null}var u={},t=p.first,s=p.second;a.each(k.getAxes(),function(v,w){if(w.used){var y=w.c2p(t[w.direction]),x=w.c2p(s[w.direction]);u[v]={from:Math.min(y,x),to:Math.max(y,x)}}});return u}function i(){var s=g();k.getPlaceholder().trigger("plotselected",[s]);if(s.xaxis&&s.yaxis){k.getPlaceholder().trigger("selected",[{x1:s.xaxis.from,y1:s.yaxis.from,x2:s.xaxis.to,y2:s.yaxis.to}])}}function h(t,u,s){return us?s:u)}function d(w,t){var v=k.getOptions();var u=k.getPlaceholder().offset();var s=k.getPlotOffset();w.x=h(0,t.pageX-u.left-s.left,k.width());w.y=h(0,t.pageY-u.top-s.top,k.height());if(v.selection.mode=="y"){w.x=w==p.first?0:k.width()}if(v.selection.mode=="x"){w.y=w==p.first?0:k.height()}}function l(s){if(s.pageX==null){return}d(p.second,s);if(f()){p.show=true;k.triggerRedrawOverlay()}else{q(true)}}function q(s){if(p.show){p.show=false;k.triggerRedrawOverlay();if(!s){k.getPlaceholder().trigger("plotunselected",[])}}}function c(s,w){var t,y,z,A,x=k.getAxes();for(var u in x){t=x[u];if(t.direction==w){A=w+t.n+"axis";if(!s[A]&&t.n==1){A=w+"axis"}if(s[A]){y=s[A].from;z=s[A].to;break}}}if(!s[A]){t=w=="x"?k.getXAxes()[0]:k.getYAxes()[0];y=s[w+"1"];z=s[w+"2"]}if(y!=null&&z!=null&&y>z){var v=y;y=z;z=v}return{from:y,to:z,axis:t}}function o(t,s){var v,u,w=k.getOptions();if(w.selection.mode=="y"){p.first.x=0;p.second.x=k.width()}else{u=c(t,"x");p.first.x=u.axis.p2c(u.from);p.second.x=u.axis.p2c(u.to)}if(w.selection.mode=="x"){p.first.y=0;p.second.y=k.height()}else{u=c(t,"y");p.first.y=u.axis.p2c(u.from);p.second.y=u.axis.p2c(u.to)}p.show=true;k.triggerRedrawOverlay();if(!s&&f()){i()}}function f(){var s=5;return Math.abs(p.second.x-p.first.x)>=s&&Math.abs(p.second.y-p.first.y)>=s}k.clearSelection=q;k.setSelection=o;k.getSelection=g;k.hooks.bindEvents.push(function(t,s){var u=t.getOptions();if(u.selection.mode!=null){s.mousemove(e);s.mousedown(n)}});k.hooks.drawOverlay.push(function(v,D){if(p.show&&f()){var t=v.getPlotOffset();var s=v.getOptions();D.save();D.translate(t.left,t.top);var z=a.color.parse(s.selection.color);D.strokeStyle=z.scale("a",0.8).toString();D.lineWidth=1;D.lineJoin="round";D.fillStyle=z.scale("a",0.4).toString();var B=Math.min(p.first.x,p.second.x),A=Math.min(p.first.y,p.second.y),C=Math.abs(p.second.x-p.first.x),u=Math.abs(p.second.y-p.first.y);D.fillRect(B,A,C,u);D.strokeRect(B,A,C,u);D.restore()}});k.hooks.shutdown.push(function(t,s){s.unbind("mousemove",e);s.unbind("mousedown",n);if(r){a(document).unbind("mouseup",r)}})}a.plot.plugins.push({init:b,options:{selection:{mode:null,color:"#e8cfac"}},name:"selection",version:"1.1"})})(jQuery); \ No newline at end of file +/* Javascript plotting library for jQuery, version 0.8.3. + +Copyright (c) 2007-2014 IOLA and Ole Laursen. +Licensed under the MIT license. + +*/ +(function($){function init(plot){var selection={first:{x:-1,y:-1},second:{x:-1,y:-1},show:false,active:false};var savedhandlers={};var mouseUpHandler=null;function onMouseMove(e){if(selection.active){updateSelection(e);plot.getPlaceholder().trigger("plotselecting",[getSelection()])}}function onMouseDown(e){if(e.which!=1)return;document.body.focus();if(document.onselectstart!==undefined&&savedhandlers.onselectstart==null){savedhandlers.onselectstart=document.onselectstart;document.onselectstart=function(){return false}}if(document.ondrag!==undefined&&savedhandlers.ondrag==null){savedhandlers.ondrag=document.ondrag;document.ondrag=function(){return false}}setSelectionPos(selection.first,e);selection.active=true;mouseUpHandler=function(e){onMouseUp(e)};$(document).one("mouseup",mouseUpHandler)}function onMouseUp(e){mouseUpHandler=null;if(document.onselectstart!==undefined)document.onselectstart=savedhandlers.onselectstart;if(document.ondrag!==undefined)document.ondrag=savedhandlers.ondrag;selection.active=false;updateSelection(e);if(selectionIsSane())triggerSelectedEvent();else{plot.getPlaceholder().trigger("plotunselected",[]);plot.getPlaceholder().trigger("plotselecting",[null])}return false}function getSelection(){if(!selectionIsSane())return null;if(!selection.show)return null;var r={},c1=selection.first,c2=selection.second;$.each(plot.getAxes(),function(name,axis){if(axis.used){var p1=axis.c2p(c1[axis.direction]),p2=axis.c2p(c2[axis.direction]);r[name]={from:Math.min(p1,p2),to:Math.max(p1,p2)}}});return r}function triggerSelectedEvent(){var r=getSelection();plot.getPlaceholder().trigger("plotselected",[r]);if(r.xaxis&&r.yaxis)plot.getPlaceholder().trigger("selected",[{x1:r.xaxis.from,y1:r.yaxis.from,x2:r.xaxis.to,y2:r.yaxis.to}])}function clamp(min,value,max){return valuemax?max:value}function setSelectionPos(pos,e){var o=plot.getOptions();var offset=plot.getPlaceholder().offset();var plotOffset=plot.getPlotOffset();pos.x=clamp(0,e.pageX-offset.left-plotOffset.left,plot.width());pos.y=clamp(0,e.pageY-offset.top-plotOffset.top,plot.height());if(o.selection.mode=="y")pos.x=pos==selection.first?0:plot.width();if(o.selection.mode=="x")pos.y=pos==selection.first?0:plot.height()}function updateSelection(pos){if(pos.pageX==null)return;setSelectionPos(selection.second,pos);if(selectionIsSane()){selection.show=true;plot.triggerRedrawOverlay()}else clearSelection(true)}function clearSelection(preventEvent){if(selection.show){selection.show=false;plot.triggerRedrawOverlay();if(!preventEvent)plot.getPlaceholder().trigger("plotunselected",[])}}function extractRange(ranges,coord){var axis,from,to,key,axes=plot.getAxes();for(var k in axes){axis=axes[k];if(axis.direction==coord){key=coord+axis.n+"axis";if(!ranges[key]&&axis.n==1)key=coord+"axis";if(ranges[key]){from=ranges[key].from;to=ranges[key].to;break}}}if(!ranges[key]){axis=coord=="x"?plot.getXAxes()[0]:plot.getYAxes()[0];from=ranges[coord+"1"];to=ranges[coord+"2"]}if(from!=null&&to!=null&&from>to){var tmp=from;from=to;to=tmp}return{from:from,to:to,axis:axis}}function setSelection(ranges,preventEvent){var axis,range,o=plot.getOptions();if(o.selection.mode=="y"){selection.first.x=0;selection.second.x=plot.width()}else{range=extractRange(ranges,"x");selection.first.x=range.axis.p2c(range.from);selection.second.x=range.axis.p2c(range.to)}if(o.selection.mode=="x"){selection.first.y=0;selection.second.y=plot.height()}else{range=extractRange(ranges,"y");selection.first.y=range.axis.p2c(range.from);selection.second.y=range.axis.p2c(range.to)}selection.show=true;plot.triggerRedrawOverlay();if(!preventEvent&&selectionIsSane())triggerSelectedEvent()}function selectionIsSane(){var minSize=plot.getOptions().selection.minSize;return Math.abs(selection.second.x-selection.first.x)>=minSize&&Math.abs(selection.second.y-selection.first.y)>=minSize}plot.clearSelection=clearSelection;plot.setSelection=setSelection;plot.getSelection=getSelection;plot.hooks.bindEvents.push(function(plot,eventHolder){var o=plot.getOptions();if(o.selection.mode!=null){eventHolder.mousemove(onMouseMove);eventHolder.mousedown(onMouseDown)}});plot.hooks.drawOverlay.push(function(plot,ctx){if(selection.show&&selectionIsSane()){var plotOffset=plot.getPlotOffset();var o=plot.getOptions();ctx.save();ctx.translate(plotOffset.left,plotOffset.top);var c=$.color.parse(o.selection.color);ctx.strokeStyle=c.scale("a",.8).toString();ctx.lineWidth=1;ctx.lineJoin=o.selection.shape;ctx.fillStyle=c.scale("a",.4).toString();var x=Math.min(selection.first.x,selection.second.x)+.5,y=Math.min(selection.first.y,selection.second.y)+.5,w=Math.abs(selection.second.x-selection.first.x)-1,h=Math.abs(selection.second.y-selection.first.y)-1;ctx.fillRect(x,y,w,h);ctx.strokeRect(x,y,w,h);ctx.restore()}});plot.hooks.shutdown.push(function(plot,eventHolder){eventHolder.unbind("mousemove",onMouseMove);eventHolder.unbind("mousedown",onMouseDown);if(mouseUpHandler)$(document).unbind("mouseup",mouseUpHandler)})}$.plot.plugins.push({init:init,options:{selection:{mode:null,color:"#e8cfac",shape:"round",minSize:5}},name:"selection",version:"1.1"})})(jQuery); \ No newline at end of file diff --git a/pandora_console/include/graphs/flot/jquery.flot.stack.js b/pandora_console/include/graphs/flot/jquery.flot.stack.js new file mode 100644 index 0000000000..e75a7dfc07 --- /dev/null +++ b/pandora_console/include/graphs/flot/jquery.flot.stack.js @@ -0,0 +1,188 @@ +/* Flot plugin for stacking data sets rather than overlyaing them. + +Copyright (c) 2007-2014 IOLA and Ole Laursen. +Licensed under the MIT license. + +The plugin assumes the data is sorted on x (or y if stacking horizontally). +For line charts, it is assumed that if a line has an undefined gap (from a +null point), then the line above it should have the same gap - insert zeros +instead of "null" if you want another behaviour. This also holds for the start +and end of the chart. Note that stacking a mix of positive and negative values +in most instances doesn't make sense (so it looks weird). + +Two or more series are stacked when their "stack" attribute is set to the same +key (which can be any number or string or just "true"). To specify the default +stack, you can set the stack option like this: + + series: { + stack: null/false, true, or a key (number/string) + } + +You can also specify it for a single series, like this: + + $.plot( $("#placeholder"), [{ + data: [ ... ], + stack: true + }]) + +The stacking order is determined by the order of the data series in the array +(later series end up on top of the previous). + +Internally, the plugin modifies the datapoints in each series, adding an +offset to the y value. For line series, extra data points are inserted through +interpolation. If there's a second y value, it's also adjusted (e.g for bar +charts or filled areas). + +*/ + +(function ($) { + var options = { + series: { stack: null } // or number/string + }; + + function init(plot) { + function findMatchingSeries(s, allseries) { + var res = null; + for (var i = 0; i < allseries.length; ++i) { + if (s == allseries[i]) + break; + + if (allseries[i].stack == s.stack) + res = allseries[i]; + } + + return res; + } + + function stackData(plot, s, datapoints) { + if (s.stack == null || s.stack === false) + return; + + var other = findMatchingSeries(s, plot.getData()); + if (!other) + return; + + var ps = datapoints.pointsize, + points = datapoints.points, + otherps = other.datapoints.pointsize, + otherpoints = other.datapoints.points, + newpoints = [], + px, py, intery, qx, qy, bottom, + withlines = s.lines.show, + horizontal = s.bars.horizontal, + withbottom = ps > 2 && (horizontal ? datapoints.format[2].x : datapoints.format[2].y), + withsteps = withlines && s.lines.steps, + fromgap = true, + keyOffset = horizontal ? 1 : 0, + accumulateOffset = horizontal ? 0 : 1, + i = 0, j = 0, l, m; + + while (true) { + if (i >= points.length) + break; + + l = newpoints.length; + + if (points[i] == null) { + // copy gaps + for (m = 0; m < ps; ++m) + newpoints.push(points[i + m]); + i += ps; + } + else if (j >= otherpoints.length) { + // for lines, we can't use the rest of the points + if (!withlines) { + for (m = 0; m < ps; ++m) + newpoints.push(points[i + m]); + } + i += ps; + } + else if (otherpoints[j] == null) { + // oops, got a gap + for (m = 0; m < ps; ++m) + newpoints.push(null); + fromgap = true; + j += otherps; + } + else { + // cases where we actually got two points + px = points[i + keyOffset]; + py = points[i + accumulateOffset]; + qx = otherpoints[j + keyOffset]; + qy = otherpoints[j + accumulateOffset]; + bottom = 0; + + if (px == qx) { + for (m = 0; m < ps; ++m) + newpoints.push(points[i + m]); + + newpoints[l + accumulateOffset] += qy; + bottom = qy; + + i += ps; + j += otherps; + } + else if (px > qx) { + // we got past point below, might need to + // insert interpolated extra point + if (withlines && i > 0 && points[i - ps] != null) { + intery = py + (points[i - ps + accumulateOffset] - py) * (qx - px) / (points[i - ps + keyOffset] - px); + newpoints.push(qx); + newpoints.push(intery + qy); + for (m = 2; m < ps; ++m) + newpoints.push(points[i + m]); + bottom = qy; + } + + j += otherps; + } + else { // px < qx + if (fromgap && withlines) { + // if we come from a gap, we just skip this point + i += ps; + continue; + } + + for (m = 0; m < ps; ++m) + newpoints.push(points[i + m]); + + // we might be able to interpolate a point below, + // this can give us a better y + if (withlines && j > 0 && otherpoints[j - otherps] != null) + bottom = qy + (otherpoints[j - otherps + accumulateOffset] - qy) * (px - qx) / (otherpoints[j - otherps + keyOffset] - qx); + + newpoints[l + accumulateOffset] += bottom; + + i += ps; + } + + fromgap = false; + + if (l != newpoints.length && withbottom) + newpoints[l + 2] += bottom; + } + + // maintain the line steps invariant + if (withsteps && l != newpoints.length && l > 0 + && newpoints[l] != null + && newpoints[l] != newpoints[l - ps] + && newpoints[l + 1] != newpoints[l - ps + 1]) { + for (m = 0; m < ps; ++m) + newpoints[l + ps + m] = newpoints[l + m]; + newpoints[l + 1] = newpoints[l - ps + 1]; + } + } + + datapoints.points = newpoints; + } + + plot.hooks.processDatapoints.push(stackData); + } + + $.plot.plugins.push({ + init: init, + options: options, + name: 'stack', + version: '1.2' + }); +})(jQuery); diff --git a/pandora_console/include/graphs/flot/jquery.flot.stack.min.js b/pandora_console/include/graphs/flot/jquery.flot.stack.min.js index bba2a0e5ff..920764f5e7 100644 --- a/pandora_console/include/graphs/flot/jquery.flot.stack.min.js +++ b/pandora_console/include/graphs/flot/jquery.flot.stack.min.js @@ -1 +1,7 @@ -(function(b){var a={series:{stack:null}};function c(f){function d(k,j){var h=null;for(var g=0;g2&&(G?g.format[2].x:g.format[2].y),n=u&&v.lines.steps,E=true,q=G?1:0,H=G?0:1,D=0,B=0,A;while(true){if(D>=F.length){break}A=t.length;if(F[D]==null){for(m=0;m=y.length){if(!u){for(m=0;mJ){if(u&&D>0&&F[D-z]!=null){k=w+(F[D-z+H]-w)*(J-x)/(F[D-z+q]-x);t.push(J);t.push(k+I);for(m=2;m0&&y[B-h]!=null){r=I+(y[B-h+H]-I)*(x-J)/(y[B-h+q]-J)}t[A+H]+=r;D+=z}}E=false;if(A!=t.length&&o){t[A+2]+=r}}}}if(n&&A!=t.length&&A>0&&t[A]!=null&&t[A]!=t[A-z]&&t[A+1]!=t[A-z+1]){for(m=0;m2&&(horizontal?datapoints.format[2].x:datapoints.format[2].y),withsteps=withlines&&s.lines.steps,fromgap=true,keyOffset=horizontal?1:0,accumulateOffset=horizontal?0:1,i=0,j=0,l,m;while(true){if(i>=points.length)break;l=newpoints.length;if(points[i]==null){for(m=0;m=otherpoints.length){if(!withlines){for(m=0;mqx){if(withlines&&i>0&&points[i-ps]!=null){intery=py+(points[i-ps+accumulateOffset]-py)*(qx-px)/(points[i-ps+keyOffset]-px);newpoints.push(qx);newpoints.push(intery+qy);for(m=2;m0&&otherpoints[j-otherps]!=null)bottom=qy+(otherpoints[j-otherps+accumulateOffset]-qy)*(px-qx)/(otherpoints[j-otherps+keyOffset]-qx);newpoints[l+accumulateOffset]+=bottom;i+=ps}fromgap=false;if(l!=newpoints.length&&withbottom)newpoints[l+2]+=bottom}if(withsteps&&l!=newpoints.length&&l>0&&newpoints[l]!=null&&newpoints[l]!=newpoints[l-ps]&&newpoints[l+1]!=newpoints[l-ps+1]){for(m=0;m s = r * sqrt(pi)/2 + var size = radius * Math.sqrt(Math.PI) / 2; + ctx.rect(x - size, y - size, size + size, size + size); + }, + diamond: function (ctx, x, y, radius, shadow) { + // pi * r^2 = 2s^2 => s = r * sqrt(pi/2) + var size = radius * Math.sqrt(Math.PI / 2); + ctx.moveTo(x - size, y); + ctx.lineTo(x, y - size); + ctx.lineTo(x + size, y); + ctx.lineTo(x, y + size); + ctx.lineTo(x - size, y); + }, + triangle: function (ctx, x, y, radius, shadow) { + // pi * r^2 = 1/2 * s^2 * sin (pi / 3) => s = r * sqrt(2 * pi / sin(pi / 3)) + var size = radius * Math.sqrt(2 * Math.PI / Math.sin(Math.PI / 3)); + var height = size * Math.sin(Math.PI / 3); + ctx.moveTo(x - size/2, y + height/2); + ctx.lineTo(x + size/2, y + height/2); + if (!shadow) { + ctx.lineTo(x, y - height/2); + ctx.lineTo(x - size/2, y + height/2); + } + }, + cross: function (ctx, x, y, radius, shadow) { + // pi * r^2 = (2s)^2 => s = r * sqrt(pi)/2 + var size = radius * Math.sqrt(Math.PI) / 2; + ctx.moveTo(x - size, y - size); + ctx.lineTo(x + size, y + size); + ctx.moveTo(x - size, y + size); + ctx.lineTo(x + size, y - size); + } + }; + + var s = series.points.symbol; + if (handlers[s]) + series.points.symbol = handlers[s]; + } + + function init(plot) { + plot.hooks.processDatapoints.push(processRawData); + } + + $.plot.plugins.push({ + init: init, + name: 'symbols', + version: '1.0' + }); +})(jQuery); diff --git a/pandora_console/include/graphs/flot/jquery.flot.symbol.min.js b/pandora_console/include/graphs/flot/jquery.flot.symbol.min.js index 272e003ab4..f4a3430136 100644 --- a/pandora_console/include/graphs/flot/jquery.flot.symbol.min.js +++ b/pandora_console/include/graphs/flot/jquery.flot.symbol.min.js @@ -1 +1,7 @@ -(function(b){function a(h,e,g){var d={square:function(k,j,n,i,m){var l=i*Math.sqrt(Math.PI)/2;k.rect(j-l,n-l,l+l,l+l)},diamond:function(k,j,n,i,m){var l=i*Math.sqrt(Math.PI/2);k.moveTo(j-l,n);k.lineTo(j,n-l);k.lineTo(j+l,n);k.lineTo(j,n+l);k.lineTo(j-l,n)},triangle:function(l,k,o,j,n){var m=j*Math.sqrt(2*Math.PI/Math.sin(Math.PI/3));var i=m*Math.sin(Math.PI/3);l.moveTo(k-m/2,o+i/2);l.lineTo(k+m/2,o+i/2);if(!n){l.lineTo(k,o-i/2);l.lineTo(k-m/2,o+i/2)}},cross:function(k,j,n,i,m){var l=i*Math.sqrt(Math.PI)/2;k.moveTo(j-l,n-l);k.lineTo(j+l,n+l);k.moveTo(j-l,n+l);k.lineTo(j+l,n-l)}};var f=e.points.symbol;if(d[f]){e.points.symbol=d[f]}}function c(d){d.hooks.processDatapoints.push(a)}b.plot.plugins.push({init:c,name:"symbols",version:"1.0"})})(jQuery); \ No newline at end of file +/* Javascript plotting library for jQuery, version 0.8.3. + +Copyright (c) 2007-2014 IOLA and Ole Laursen. +Licensed under the MIT license. + +*/ +(function($){function processRawData(plot,series,datapoints){var handlers={square:function(ctx,x,y,radius,shadow){var size=radius*Math.sqrt(Math.PI)/2;ctx.rect(x-size,y-size,size+size,size+size)},diamond:function(ctx,x,y,radius,shadow){var size=radius*Math.sqrt(Math.PI/2);ctx.moveTo(x-size,y);ctx.lineTo(x,y-size);ctx.lineTo(x+size,y);ctx.lineTo(x,y+size);ctx.lineTo(x-size,y)},triangle:function(ctx,x,y,radius,shadow){var size=radius*Math.sqrt(2*Math.PI/Math.sin(Math.PI/3));var height=size*Math.sin(Math.PI/3);ctx.moveTo(x-size/2,y+height/2);ctx.lineTo(x+size/2,y+height/2);if(!shadow){ctx.lineTo(x,y-height/2);ctx.lineTo(x-size/2,y+height/2)}},cross:function(ctx,x,y,radius,shadow){var size=radius*Math.sqrt(Math.PI)/2;ctx.moveTo(x-size,y-size);ctx.lineTo(x+size,y+size);ctx.moveTo(x-size,y+size);ctx.lineTo(x+size,y-size)}};var s=series.points.symbol;if(handlers[s])series.points.symbol=handlers[s]}function init(plot){plot.hooks.processDatapoints.push(processRawData)}$.plot.plugins.push({init:init,name:"symbols",version:"1.0"})})(jQuery); \ No newline at end of file diff --git a/pandora_console/include/graphs/flot/jquery.flot.threshold.js b/pandora_console/include/graphs/flot/jquery.flot.threshold.js index fa79f79877..5167b6421f 100644 --- a/pandora_console/include/graphs/flot/jquery.flot.threshold.js +++ b/pandora_console/include/graphs/flot/jquery.flot.threshold.js @@ -1,132 +1,154 @@ -/* -Flot plugin for thresholding data. Controlled through the option -"threshold" in either the global series options - - series: { - threshold: { - below: number - color: colorspec +/* Flot plugin for thresholding data. +Copyright (c) 2007-2014 IOLA and Ole Laursen. +Licensed under the MIT license. +The plugin supports these options: + series: { + threshold: { + below: number, + above: mumber, + color: colorspec + } } - } - -or in a specific series - - $.plot($("#placeholder"), [{ data: [ ... ], threshold: { ... }}]) - -An array can be passed for multiple thresholding - - threshold: [{ - below: number1 - color: color1 - },{ - below: number2 - color: color2 - }] - -These multiple threshold objects can be passed in any order since they -are sorted by the processing function. - -The data points below "below" are drawn with the specified color. This -makes it easy to mark points below 0, e.g. for budget data. - -Internally, the plugin works by splitting the data into two series, -above and below the threshold. The extra series below the threshold -will have its label cleared and the special "originSeries" attribute -set to the original series. You may need to check for this in hover -events. +It can also be applied to a single series, like this: + $.plot( $("#placeholder"), [{ + data: [ ... ], + threshold: { ... } + }]) +An array can be passed for multiple thresholding, like this: + threshold: [{ + below: number1, + color: color1 + },{ + above: number2, + color: color2 + }] +These multiple threshold objects can be passed in any order since they are +sorted by the processing function. +The data points below "below" are drawn with the specified color. This makes +it easy to mark points below 0, e.g. for budget data. +Internally, the plugin works by splitting the data into two series, above and +below the threshold. The extra series below the threshold will have its label +cleared and the special "originSeries" attribute set to the original series. +You may need to check for this in hover events. */ -(function ($) { +(function($) { var options = { series: { threshold: null } // or { below: number, color: color spec} }; - - function init(plot) { - function thresholdData(plot, s, datapoints, below, color) { - var ps = datapoints.pointsize, i, x, y, p, prevp, - thresholded = $.extend({}, s); // note: shallow copy - thresholded.datapoints = { points: [], pointsize: ps }; + function init(plot) { + function thresholdData(plot, s, datapoints, below, above, color) { + + var origpoints = datapoints.points, + ps = datapoints.pointsize, + addCrossingPoints = s.lines.show, + thresholded = $.extend({}, s), // note: shallow copy + threspoints = [], + newpoints = [], + prevp, i, x, y, p, m; + + thresholded.datapoints = { points: [], pointsize: ps, format: datapoints.format }; thresholded.label = null; thresholded.color = color; thresholded.threshold = null; thresholded.originSeries = s; thresholded.data = []; - var origpoints = datapoints.points, - addCrossingPoints = s.lines.show; - - threspoints = []; - newpoints = []; - for (i = 0; i < origpoints.length; i += ps) { - x = origpoints[i] + x = origpoints[i]; y = origpoints[i + 1]; prevp = p; - if (y < below) + if (y < below || y > above) { p = threspoints; - else + } else { p = newpoints; + } - if (addCrossingPoints && prevp != p && x != null - && i > 0 && origpoints[i - ps] != null) { - var interx = (x - origpoints[i - ps]) / (y - origpoints[i - ps + 1]) * (below - y) + x; + if (addCrossingPoints && prevp !== p && x != null && i > 0 && origpoints[i - ps] != null) { + var interx = x + (below - y) * (x - origpoints[i - ps]) / (y - origpoints[i - ps + 1]); prevp.push(interx); prevp.push(below); - for (m = 2; m < ps; ++m) + for (m = 2; m < ps; ++m) { prevp.push(origpoints[i + m]); - + } + p.push(null); // start new segment p.push(null); - for (m = 2; m < ps; ++m) + for (m = 2; m < ps; ++m) { p.push(origpoints[i + m]); + } p.push(interx); p.push(below); - for (m = 2; m < ps; ++m) + for (m = 2; m < ps; ++m) { p.push(origpoints[i + m]); + } } p.push(x); p.push(y); - if (thresholded.lines.fill) { - p.push(0) - } + for (m = 2; m < ps; ++m) { + p.push(origpoints[i + m]); + } } datapoints.points = newpoints; thresholded.datapoints.points = threspoints; - - if (thresholded.datapoints.points.length > 0) - plot.getData().push(thresholded); - + + if (thresholded.datapoints.points.length > 0) { + var origIndex = $.inArray(s, plot.getData()); + // Insert newly-generated series right after original one (to prevent it from becoming top-most) + plot.getData().splice(origIndex + 1, 0, thresholded); + } + // FIXME: there are probably some edge cases left in bars } - - function iterateThresholds(plot, s, datapoints) { - if(!s.threshold) + + function processThresholds(plot, s, datapoints) { + if (!s.threshold) { return; - - if(s.threshold instanceof Array) { + } + + if (s.threshold instanceof Array) { s.threshold.sort(function(a, b) { return a.below - b.below; }); - + $(s.threshold).each(function(i, th) { - thresholdData(plot, s, datapoints, th.below, th.color); + thresholdData(plot, s, datapoints, th.below, th.above, th.color); }); } else { - thresholdData(plot, s, datapoints, s.threshold.below, s.threshold.color); + thresholdData(plot, s, datapoints, s.threshold.below, s.threshold.above, s.threshold.color); } } - - plot.hooks.processDatapoints.push(iterateThresholds); + + plot.hooks.processDatapoints.push(processThresholds); + + function processThresholdsLegend(ctx, canvas, s) { + if (!s.threshold) { + return; + } + + var color = s.threshold.color ? s.threshold.color : "black"; + + $(".legendLabel").each(function() { + if ($(this).text() === s.label) + { + var legend = $(this).prev().find("div > div"); + legend.css("border-right-color", color); + legend.css("border-bottom-color", color); + } + }); + } + + plot.hooks.drawSeries.push(processThresholdsLegend); } - + $.plot.plugins.push({ init: init, options: options, - name: 'threshold', - version: '1.0' + name: "threshold", + version: "1.3" }); -})(jQuery); +})(jQuery); \ No newline at end of file diff --git a/pandora_console/include/graphs/flot/jquery.flot.threshold.min.js b/pandora_console/include/graphs/flot/jquery.flot.threshold.min.js new file mode 100644 index 0000000000..ce93e0f480 --- /dev/null +++ b/pandora_console/include/graphs/flot/jquery.flot.threshold.min.js @@ -0,0 +1,7 @@ +/* Javascript plotting library for jQuery, version 0.8.3. + +Copyright (c) 2007-2014 IOLA and Ole Laursen. +Licensed under the MIT license. + +*/ +(function($){var options={series:{threshold:null}};function init(plot){function thresholdData(plot,s,datapoints,below,color){var ps=datapoints.pointsize,i,x,y,p,prevp,thresholded=$.extend({},s);thresholded.datapoints={points:[],pointsize:ps,format:datapoints.format};thresholded.label=null;thresholded.color=color;thresholded.threshold=null;thresholded.originSeries=s;thresholded.data=[];var origpoints=datapoints.points,addCrossingPoints=s.lines.show;var threspoints=[];var newpoints=[];var m;for(i=0;i0&&origpoints[i-ps]!=null){var interx=x+(below-y)*(x-origpoints[i-ps])/(y-origpoints[i-ps+1]);prevp.push(interx);prevp.push(below);for(m=2;m0){var origIndex=$.inArray(s,plot.getData());plot.getData().splice(origIndex+1,0,thresholded)}}function processThresholds(plot,s,datapoints){if(!s.threshold)return;if(s.threshold instanceof Array){s.threshold.sort(function(a,b){return a.below-b.below});$(s.threshold).each(function(i,th){thresholdData(plot,s,datapoints,th.below,th.color)})}else{thresholdData(plot,s,datapoints,s.threshold.below,s.threshold.color)}}plot.hooks.processDatapoints.push(processThresholds)}$.plot.plugins.push({init:init,options:options,name:"threshold",version:"1.2"})})(jQuery); \ No newline at end of file diff --git a/pandora_console/include/graphs/flot/jquery.flot.threshold.multiple.js b/pandora_console/include/graphs/flot/jquery.flot.threshold.multiple.js new file mode 100644 index 0000000000..627eba3e4d --- /dev/null +++ b/pandora_console/include/graphs/flot/jquery.flot.threshold.multiple.js @@ -0,0 +1,189 @@ +/* +Flot plugin for specifying range of thresolds on data. +Controlled through the option "constraints" in a specific series + +usage - + $.plot($("#placeholder"), [{ data: [ ... ], constraints: [constraint1, constraint2]},{data:[...],constraints:[...]}]) + + where constraint1 = { + threshold: 2, + color: "rgb(0,0,0)", + evaluate : function(y,threshold){ return y < threshold; } + } + threshold -> y-limit on the plot. + evaluate -> the function which defines the limit.This function defines wheteher y < threshold or y > threshold. + color -> the color with which to plot portion of the graph which satisfies the limit condition. + + + +Internally, the plugin works by splitting the data into different series, one for each constraint. + +*/ +(function($){ + + + function init(plot){ + + function plotWithMultipleThresholds(plot,s,datapoints){ + if(s.data && s.data.length > 0 && s.constraints && s.constraints.length>0){ + var series = new Graph(s.data,s.constraints).getPlotData(); + for(var i=0;i= 0 ; i--) { + var constraint = this._constraints[i]; + if(null != constraint.threshold){ + var set = new Resolve(this._dataset).using(constraint.threshold, constraint.evaluate); + this._plotData.push( { + data : set, + color : constraint.color + }); + } + } + return this._plotData; + }; + + function Resolve(originalPonits) { + this._originalPoints = originalPonits; + this._data = []; + this._getPointOnThreshold = _getPointOnThreshold; + this.using = using ; + + function using(threshold, evaluate) { + var count = 0; + for ( var i = 0; i < this._originalPoints.length; i++) { + var currentPoint = this._originalPoints[i]; + if (evaluate(currentPoint[1],threshold)) { + if (i > 0 + && (this._data.length == 0 || this._data[count - 1] == null)) { + this._data[count++] = this._getPointOnThreshold(threshold,this._originalPoints[i - 1], currentPoint); + } + this._data[count++] = currentPoint; + } else { + if (this._data.length > 0 && this._data[count - 1] != null) { + this._data[count++] = this._getPointOnThreshold(threshold,this._originalPoints[i - 1], currentPoint); + this._data[count++] = null; + this._data[count++] = null; + } + } + } + return this._data; + } + + function _getPointOnThreshold(threshold, prevP, currP) { + var currentX = currP[0]; + var currentY = currP[1]; + + var prevX = prevP[0]; + var prevY = prevP[1]; + + var slope = (threshold - currentY) + / (prevY - currentY); + var xOnConstraintLine = slope * (prevX - currentX) + currentX; + + return [ xOnConstraintLine, threshold ]; + } + } + + function _getSortedConstraints(originalpoints,constraints){ + + var dataRange = _findMaxAndMin(originalpoints); + + if(undefined == dataRange)return []; + + var max = dataRange.max; + var min = dataRange.min; + var thresholdRanges = []; + var sortedConstraints = []; + for(var i = 0; i < constraints.length ; i++){ + var constraint = constraints[i]; + var range = 0; + if(constraint.evaluate(min,constraint.threshold)){ + range = Math.abs(constraint.threshold - min); + }else{ + range = Math.abs(max - constraint.threshold); + } + thresholdRanges.push({constraint:constraint,range:range}); + } + QuickSort(thresholdRanges,function(obj1,obj2){ return obj1.range < obj2.range;}); + for(var i=thresholdRanges.length-1;i>=0;i--){ + sortedConstraints[i] = thresholdRanges[i].constraint; + } + return sortedConstraints; + } + + function _findMaxAndMin(dataset){ + if(undefined == dataset)return undefined; + var arr = []; + for( var i=0;i left){ + var pivotIndex = Math.floor(( left + right )/2); + var pivotNewIndex = partition(array,left,right,pivotIndex,comparator); + sort(array, left, pivotNewIndex - 1,comparator); + sort(array, pivotNewIndex + 1, right,comparator); + + } + } + + function partition(array,left,right,pivotIndex,comparator){ + var pivot = array[pivotIndex]; + swap(array,pivotIndex,right); + var storeIndex = left; + for( var i= left ; i < right ; i++){ + if(comparator(array[i] , pivot)){ + swap(array,i,storeIndex); + storeIndex = storeIndex + 1; + } + } + swap(array,storeIndex,right); + + return storeIndex; + } + + function swap(array,index1,index2){ + var temp = array[index1]; + array[index1] = array[index2]; + array[index2] = temp; + } + } + + + plot.hooks.processRawData.push(plotWithMultipleThresholds); + } + +$.plot.plugins.push({ + init: init, + name: 'multiple.threshold', + version: '1.0' + }); +})(jQuery); + + diff --git a/pandora_console/include/graphs/flot/jquery.flot.time.js b/pandora_console/include/graphs/flot/jquery.flot.time.js new file mode 100644 index 0000000000..34c1d12125 --- /dev/null +++ b/pandora_console/include/graphs/flot/jquery.flot.time.js @@ -0,0 +1,432 @@ +/* Pretty handling of time axes. + +Copyright (c) 2007-2014 IOLA and Ole Laursen. +Licensed under the MIT license. + +Set axis.mode to "time" to enable. See the section "Time series data" in +API.txt for details. + +*/ + +(function($) { + + var options = { + xaxis: { + timezone: null, // "browser" for local to the client or timezone for timezone-js + timeformat: null, // format string to use + twelveHourClock: false, // 12 or 24 time in time mode + monthNames: null // list of names of months + } + }; + + // round to nearby lower multiple of base + + function floorInBase(n, base) { + return base * Math.floor(n / base); + } + + // Returns a string with the date d formatted according to fmt. + // A subset of the Open Group's strftime format is supported. + + function formatDate(d, fmt, monthNames, dayNames) { + + if (typeof d.strftime == "function") { + return d.strftime(fmt); + } + + var leftPad = function(n, pad) { + n = "" + n; + pad = "" + (pad == null ? "0" : pad); + return n.length == 1 ? pad + n : n; + }; + + var r = []; + var escape = false; + var hours = d.getHours(); + var isAM = hours < 12; + + if (monthNames == null) { + monthNames = ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"]; + } + + if (dayNames == null) { + dayNames = ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"]; + } + + var hours12; + + if (hours > 12) { + hours12 = hours - 12; + } else if (hours == 0) { + hours12 = 12; + } else { + hours12 = hours; + } + + for (var i = 0; i < fmt.length; ++i) { + + var c = fmt.charAt(i); + + if (escape) { + switch (c) { + case 'a': c = "" + dayNames[d.getDay()]; break; + case 'b': c = "" + monthNames[d.getMonth()]; break; + case 'd': c = leftPad(d.getDate()); break; + case 'e': c = leftPad(d.getDate(), " "); break; + case 'h': // For back-compat with 0.7; remove in 1.0 + case 'H': c = leftPad(hours); break; + case 'I': c = leftPad(hours12); break; + case 'l': c = leftPad(hours12, " "); break; + case 'm': c = leftPad(d.getMonth() + 1); break; + case 'M': c = leftPad(d.getMinutes()); break; + // quarters not in Open Group's strftime specification + case 'q': + c = "" + (Math.floor(d.getMonth() / 3) + 1); break; + case 'S': c = leftPad(d.getSeconds()); break; + case 'y': c = leftPad(d.getFullYear() % 100); break; + case 'Y': c = "" + d.getFullYear(); break; + case 'p': c = (isAM) ? ("" + "am") : ("" + "pm"); break; + case 'P': c = (isAM) ? ("" + "AM") : ("" + "PM"); break; + case 'w': c = "" + d.getDay(); break; + } + r.push(c); + escape = false; + } else { + if (c == "%") { + escape = true; + } else { + r.push(c); + } + } + } + + return r.join(""); + } + + // To have a consistent view of time-based data independent of which time + // zone the client happens to be in we need a date-like object independent + // of time zones. This is done through a wrapper that only calls the UTC + // versions of the accessor methods. + + function makeUtcWrapper(d) { + + function addProxyMethod(sourceObj, sourceMethod, targetObj, targetMethod) { + sourceObj[sourceMethod] = function() { + return targetObj[targetMethod].apply(targetObj, arguments); + }; + }; + + var utc = { + date: d + }; + + // support strftime, if found + + if (d.strftime != undefined) { + addProxyMethod(utc, "strftime", d, "strftime"); + } + + addProxyMethod(utc, "getTime", d, "getTime"); + addProxyMethod(utc, "setTime", d, "setTime"); + + var props = ["Date", "Day", "FullYear", "Hours", "Milliseconds", "Minutes", "Month", "Seconds"]; + + for (var p = 0; p < props.length; p++) { + addProxyMethod(utc, "get" + props[p], d, "getUTC" + props[p]); + addProxyMethod(utc, "set" + props[p], d, "setUTC" + props[p]); + } + + return utc; + }; + + // select time zone strategy. This returns a date-like object tied to the + // desired timezone + + function dateGenerator(ts, opts) { + if (opts.timezone == "browser") { + return new Date(ts); + } else if (!opts.timezone || opts.timezone == "utc") { + return makeUtcWrapper(new Date(ts)); + } else if (typeof timezoneJS != "undefined" && typeof timezoneJS.Date != "undefined") { + var d = new timezoneJS.Date(); + // timezone-js is fickle, so be sure to set the time zone before + // setting the time. + d.setTimezone(opts.timezone); + d.setTime(ts); + return d; + } else { + return makeUtcWrapper(new Date(ts)); + } + } + + // map of app. size of time units in milliseconds + + var timeUnitSize = { + "second": 1000, + "minute": 60 * 1000, + "hour": 60 * 60 * 1000, + "day": 24 * 60 * 60 * 1000, + "month": 30 * 24 * 60 * 60 * 1000, + "quarter": 3 * 30 * 24 * 60 * 60 * 1000, + "year": 365.2425 * 24 * 60 * 60 * 1000 + }; + + // the allowed tick sizes, after 1 year we use + // an integer algorithm + + var baseSpec = [ + [1, "second"], [2, "second"], [5, "second"], [10, "second"], + [30, "second"], + [1, "minute"], [2, "minute"], [5, "minute"], [10, "minute"], + [30, "minute"], + [1, "hour"], [2, "hour"], [4, "hour"], + [8, "hour"], [12, "hour"], + [1, "day"], [2, "day"], [3, "day"], + [0.25, "month"], [0.5, "month"], [1, "month"], + [2, "month"] + ]; + + // we don't know which variant(s) we'll need yet, but generating both is + // cheap + + var specMonths = baseSpec.concat([[3, "month"], [6, "month"], + [1, "year"]]); + var specQuarters = baseSpec.concat([[1, "quarter"], [2, "quarter"], + [1, "year"]]); + + function init(plot) { + plot.hooks.processOptions.push(function (plot, options) { + $.each(plot.getAxes(), function(axisName, axis) { + + var opts = axis.options; + + if (opts.mode == "time") { + axis.tickGenerator = function(axis) { + + var ticks = []; + var d = dateGenerator(axis.min, opts); + var minSize = 0; + + // make quarter use a possibility if quarters are + // mentioned in either of these options + + var spec = (opts.tickSize && opts.tickSize[1] === + "quarter") || + (opts.minTickSize && opts.minTickSize[1] === + "quarter") ? specQuarters : specMonths; + + if (opts.minTickSize != null) { + if (typeof opts.tickSize == "number") { + minSize = opts.tickSize; + } else { + minSize = opts.minTickSize[0] * timeUnitSize[opts.minTickSize[1]]; + } + } + + for (var i = 0; i < spec.length - 1; ++i) { + if (axis.delta < (spec[i][0] * timeUnitSize[spec[i][1]] + + spec[i + 1][0] * timeUnitSize[spec[i + 1][1]]) / 2 + && spec[i][0] * timeUnitSize[spec[i][1]] >= minSize) { + break; + } + } + + var size = spec[i][0]; + var unit = spec[i][1]; + + // special-case the possibility of several years + + if (unit == "year") { + + // if given a minTickSize in years, just use it, + // ensuring that it's an integer + + if (opts.minTickSize != null && opts.minTickSize[1] == "year") { + size = Math.floor(opts.minTickSize[0]); + } else { + + var magn = Math.pow(10, Math.floor(Math.log(axis.delta / timeUnitSize.year) / Math.LN10)); + var norm = (axis.delta / timeUnitSize.year) / magn; + + if (norm < 1.5) { + size = 1; + } else if (norm < 3) { + size = 2; + } else if (norm < 7.5) { + size = 5; + } else { + size = 10; + } + + size *= magn; + } + + // minimum size for years is 1 + + if (size < 1) { + size = 1; + } + } + + axis.tickSize = opts.tickSize || [size, unit]; + var tickSize = axis.tickSize[0]; + unit = axis.tickSize[1]; + + var step = tickSize * timeUnitSize[unit]; + + if (unit == "second") { + d.setSeconds(floorInBase(d.getSeconds(), tickSize)); + } else if (unit == "minute") { + d.setMinutes(floorInBase(d.getMinutes(), tickSize)); + } else if (unit == "hour") { + d.setHours(floorInBase(d.getHours(), tickSize)); + } else if (unit == "month") { + d.setMonth(floorInBase(d.getMonth(), tickSize)); + } else if (unit == "quarter") { + d.setMonth(3 * floorInBase(d.getMonth() / 3, + tickSize)); + } else if (unit == "year") { + d.setFullYear(floorInBase(d.getFullYear(), tickSize)); + } + + // reset smaller components + + d.setMilliseconds(0); + + if (step >= timeUnitSize.minute) { + d.setSeconds(0); + } + if (step >= timeUnitSize.hour) { + d.setMinutes(0); + } + if (step >= timeUnitSize.day) { + d.setHours(0); + } + if (step >= timeUnitSize.day * 4) { + d.setDate(1); + } + if (step >= timeUnitSize.month * 2) { + d.setMonth(floorInBase(d.getMonth(), 3)); + } + if (step >= timeUnitSize.quarter * 2) { + d.setMonth(floorInBase(d.getMonth(), 6)); + } + if (step >= timeUnitSize.year) { + d.setMonth(0); + } + + var carry = 0; + var v = Number.NaN; + var prev; + + do { + + prev = v; + v = d.getTime(); + ticks.push(v); + + if (unit == "month" || unit == "quarter") { + if (tickSize < 1) { + + // a bit complicated - we'll divide the + // month/quarter up but we need to take + // care of fractions so we don't end up in + // the middle of a day + + d.setDate(1); + var start = d.getTime(); + d.setMonth(d.getMonth() + + (unit == "quarter" ? 3 : 1)); + var end = d.getTime(); + d.setTime(v + carry * timeUnitSize.hour + (end - start) * tickSize); + carry = d.getHours(); + d.setHours(0); + } else { + d.setMonth(d.getMonth() + + tickSize * (unit == "quarter" ? 3 : 1)); + } + } else if (unit == "year") { + d.setFullYear(d.getFullYear() + tickSize); + } else { + d.setTime(v + step); + } + } while (v < axis.max && v != prev); + + return ticks; + }; + + axis.tickFormatter = function (v, axis) { + + var d = dateGenerator(v, axis.options); + + // first check global format + + if (opts.timeformat != null) { + return formatDate(d, opts.timeformat, opts.monthNames, opts.dayNames); + } + + // possibly use quarters if quarters are mentioned in + // any of these places + + var useQuarters = (axis.options.tickSize && + axis.options.tickSize[1] == "quarter") || + (axis.options.minTickSize && + axis.options.minTickSize[1] == "quarter"); + + var t = axis.tickSize[0] * timeUnitSize[axis.tickSize[1]]; + var span = axis.max - axis.min; + var suffix = (opts.twelveHourClock) ? " %p" : ""; + var hourCode = (opts.twelveHourClock) ? "%I" : "%H"; + var fmt; + + if (t < timeUnitSize.minute) { + fmt = hourCode + ":%M:%S" + suffix; + } else if (t < timeUnitSize.day) { + if (span < 2 * timeUnitSize.day) { + fmt = hourCode + ":%M" + suffix; + } else { + fmt = "%b %d " + hourCode + ":%M" + suffix; + } + } else if (t < timeUnitSize.month) { + fmt = "%b %d"; + } else if ((useQuarters && t < timeUnitSize.quarter) || + (!useQuarters && t < timeUnitSize.year)) { + if (span < timeUnitSize.year) { + fmt = "%b"; + } else { + fmt = "%b %Y"; + } + } else if (useQuarters && t < timeUnitSize.year) { + if (span < timeUnitSize.year) { + fmt = "Q%q"; + } else { + fmt = "Q%q %Y"; + } + } else { + fmt = "%Y"; + } + + var rt = formatDate(d, fmt, opts.monthNames, opts.dayNames); + + return rt; + }; + } + }); + }); + } + + $.plot.plugins.push({ + init: init, + options: options, + name: 'time', + version: '1.0' + }); + + // Time-axis support used to be in Flot core, which exposed the + // formatDate function on the plot object. Various plugins depend + // on the function, so we need to re-expose it here. + + $.plot.formatDate = formatDate; + $.plot.dateGenerator = dateGenerator; + +})(jQuery); diff --git a/pandora_console/include/graphs/flot/jquery.flot.time.min.js b/pandora_console/include/graphs/flot/jquery.flot.time.min.js new file mode 100644 index 0000000000..690eb68255 --- /dev/null +++ b/pandora_console/include/graphs/flot/jquery.flot.time.min.js @@ -0,0 +1,7 @@ +/* Javascript plotting library for jQuery, version 0.8.3. + +Copyright (c) 2007-2014 IOLA and Ole Laursen. +Licensed under the MIT license. + +*/ +(function($){var options={xaxis:{timezone:null,timeformat:null,twelveHourClock:false,monthNames:null}};function floorInBase(n,base){return base*Math.floor(n/base)}function formatDate(d,fmt,monthNames,dayNames){if(typeof d.strftime=="function"){return d.strftime(fmt)}var leftPad=function(n,pad){n=""+n;pad=""+(pad==null?"0":pad);return n.length==1?pad+n:n};var r=[];var escape=false;var hours=d.getHours();var isAM=hours<12;if(monthNames==null){monthNames=["Jan","Feb","Mar","Apr","May","Jun","Jul","Aug","Sep","Oct","Nov","Dec"]}if(dayNames==null){dayNames=["Sun","Mon","Tue","Wed","Thu","Fri","Sat"]}var hours12;if(hours>12){hours12=hours-12}else if(hours==0){hours12=12}else{hours12=hours}for(var i=0;i=minSize){break}}var size=spec[i][0];var unit=spec[i][1];if(unit=="year"){if(opts.minTickSize!=null&&opts.minTickSize[1]=="year"){size=Math.floor(opts.minTickSize[0])}else{var magn=Math.pow(10,Math.floor(Math.log(axis.delta/timeUnitSize.year)/Math.LN10));var norm=axis.delta/timeUnitSize.year/magn;if(norm<1.5){size=1}else if(norm<3){size=2}else if(norm<7.5){size=5}else{size=10}size*=magn}if(size<1){size=1}}axis.tickSize=opts.tickSize||[size,unit];var tickSize=axis.tickSize[0];unit=axis.tickSize[1];var step=tickSize*timeUnitSize[unit];if(unit=="second"){d.setSeconds(floorInBase(d.getSeconds(),tickSize))}else if(unit=="minute"){d.setMinutes(floorInBase(d.getMinutes(),tickSize))}else if(unit=="hour"){d.setHours(floorInBase(d.getHours(),tickSize))}else if(unit=="month"){d.setMonth(floorInBase(d.getMonth(),tickSize))}else if(unit=="quarter"){d.setMonth(3*floorInBase(d.getMonth()/3,tickSize))}else if(unit=="year"){d.setFullYear(floorInBase(d.getFullYear(),tickSize))}d.setMilliseconds(0);if(step>=timeUnitSize.minute){d.setSeconds(0)}if(step>=timeUnitSize.hour){d.setMinutes(0)}if(step>=timeUnitSize.day){d.setHours(0)}if(step>=timeUnitSize.day*4){d.setDate(1)}if(step>=timeUnitSize.month*2){d.setMonth(floorInBase(d.getMonth(),3))}if(step>=timeUnitSize.quarter*2){d.setMonth(floorInBase(d.getMonth(),6))}if(step>=timeUnitSize.year){d.setMonth(0)}var carry=0;var v=Number.NaN;var prev;do{prev=v;v=d.getTime();ticks.push(v);if(unit=="month"||unit=="quarter"){if(tickSize<1){d.setDate(1);var start=d.getTime();d.setMonth(d.getMonth()+(unit=="quarter"?3:1));var end=d.getTime();d.setTime(v+carry*timeUnitSize.hour+(end-start)*tickSize);carry=d.getHours();d.setHours(0)}else{d.setMonth(d.getMonth()+tickSize*(unit=="quarter"?3:1))}}else if(unit=="year"){d.setFullYear(d.getFullYear()+tickSize)}else{d.setTime(v+step)}}while(v to avoid XSS via location.hash (#9521) + rquickExpr = /^(?:[^#<]*(<[\w\W]+>)[^>]*$|#([\w\-]*)$)/, + + // Match a standalone tag + rsingleTag = /^<(\w+)\s*\/?>(?:<\/\1>|)$/, + + // JSON RegExp + rvalidchars = /^[\],:{}\s]*$/, + rvalidbraces = /(?:^|:|,)(?:\s*\[)+/g, + rvalidescape = /\\(?:["\\\/bfnrt]|u[\da-fA-F]{4})/g, + rvalidtokens = /"[^"\\\r\n]*"|true|false|null|-?(?:\d\d*\.|)\d+(?:[eE][\-+]?\d+|)/g, + + // Matches dashed string for camelizing + rmsPrefix = /^-ms-/, + rdashAlpha = /-([\da-z])/gi, + + // Used by jQuery.camelCase as callback to replace() + fcamelCase = function( all, letter ) { + return ( letter + "" ).toUpperCase(); + }, + + // The ready event handler and self cleanup method + DOMContentLoaded = function() { + if ( document.addEventListener ) { + document.removeEventListener( "DOMContentLoaded", DOMContentLoaded, false ); + jQuery.ready(); + } else if ( document.readyState === "complete" ) { + // we're here because readyState === "complete" in oldIE + // which is good enough for us to call the dom ready! + document.detachEvent( "onreadystatechange", DOMContentLoaded ); + jQuery.ready(); + } + }, + + // [[Class]] -> type pairs + class2type = {}; + +jQuery.fn = jQuery.prototype = { + constructor: jQuery, + init: function( selector, context, rootjQuery ) { + var match, elem, ret, doc; + + // Handle $(""), $(null), $(undefined), $(false) + if ( !selector ) { + return this; + } + + // Handle $(DOMElement) + if ( selector.nodeType ) { + this.context = this[0] = selector; + this.length = 1; + return this; + } + + // Handle HTML strings + if ( typeof selector === "string" ) { + if ( selector.charAt(0) === "<" && selector.charAt( selector.length - 1 ) === ">" && selector.length >= 3 ) { + // Assume that strings that start and end with <> are HTML and skip the regex check + match = [ null, selector, null ]; + + } else { + match = rquickExpr.exec( selector ); + } + + // Match html or make sure no context is specified for #id + if ( match && (match[1] || !context) ) { + + // HANDLE: $(html) -> $(array) + if ( match[1] ) { + context = context instanceof jQuery ? context[0] : context; + doc = ( context && context.nodeType ? context.ownerDocument || context : document ); + + // scripts is true for back-compat + selector = jQuery.parseHTML( match[1], doc, true ); + if ( rsingleTag.test( match[1] ) && jQuery.isPlainObject( context ) ) { + this.attr.call( selector, context, true ); + } + + return jQuery.merge( this, selector ); + + // HANDLE: $(#id) + } else { + elem = document.getElementById( match[2] ); + + // Check parentNode to catch when Blackberry 4.6 returns + // nodes that are no longer in the document #6963 + if ( elem && elem.parentNode ) { + // Handle the case where IE and Opera return items + // by name instead of ID + if ( elem.id !== match[2] ) { + return rootjQuery.find( selector ); + } + + // Otherwise, we inject the element directly into the jQuery object + this.length = 1; + this[0] = elem; + } + + this.context = document; + this.selector = selector; + return this; + } + + // HANDLE: $(expr, $(...)) + } else if ( !context || context.jquery ) { + return ( context || rootjQuery ).find( selector ); + + // HANDLE: $(expr, context) + // (which is just equivalent to: $(context).find(expr) + } else { + return this.constructor( context ).find( selector ); + } + + // HANDLE: $(function) + // Shortcut for document ready + } else if ( jQuery.isFunction( selector ) ) { + return rootjQuery.ready( selector ); + } + + if ( selector.selector !== undefined ) { + this.selector = selector.selector; + this.context = selector.context; + } + + return jQuery.makeArray( selector, this ); + }, + + // Start with an empty selector + selector: "", + + // The current version of jQuery being used + jquery: "1.8.3", + + // The default length of a jQuery object is 0 + length: 0, + + // The number of elements contained in the matched element set + size: function() { + return this.length; + }, + + toArray: function() { + return core_slice.call( this ); + }, + + // Get the Nth element in the matched element set OR + // Get the whole matched element set as a clean array + get: function( num ) { + return num == null ? + + // Return a 'clean' array + this.toArray() : + + // Return just the object + ( num < 0 ? this[ this.length + num ] : this[ num ] ); + }, + + // Take an array of elements and push it onto the stack + // (returning the new matched element set) + pushStack: function( elems, name, selector ) { + + // Build a new jQuery matched element set + var ret = jQuery.merge( this.constructor(), elems ); + + // Add the old object onto the stack (as a reference) + ret.prevObject = this; + + ret.context = this.context; + + if ( name === "find" ) { + ret.selector = this.selector + ( this.selector ? " " : "" ) + selector; + } else if ( name ) { + ret.selector = this.selector + "." + name + "(" + selector + ")"; + } + + // Return the newly-formed element set + return ret; + }, + + // Execute a callback for every element in the matched set. + // (You can seed the arguments with an array of args, but this is + // only used internally.) + each: function( callback, args ) { + return jQuery.each( this, callback, args ); + }, + + ready: function( fn ) { + // Add the callback + jQuery.ready.promise().done( fn ); + + return this; + }, + + eq: function( i ) { + i = +i; + return i === -1 ? + this.slice( i ) : + this.slice( i, i + 1 ); + }, + + first: function() { + return this.eq( 0 ); + }, + + last: function() { + return this.eq( -1 ); + }, + + slice: function() { + return this.pushStack( core_slice.apply( this, arguments ), + "slice", core_slice.call(arguments).join(",") ); + }, + + map: function( callback ) { + return this.pushStack( jQuery.map(this, function( elem, i ) { + return callback.call( elem, i, elem ); + })); + }, + + end: function() { + return this.prevObject || this.constructor(null); + }, + + // For internal use only. + // Behaves like an Array's method, not like a jQuery method. + push: core_push, + sort: [].sort, + splice: [].splice +}; + +// Give the init function the jQuery prototype for later instantiation +jQuery.fn.init.prototype = jQuery.fn; + +jQuery.extend = jQuery.fn.extend = function() { + var options, name, src, copy, copyIsArray, clone, + target = arguments[0] || {}, + i = 1, + length = arguments.length, + deep = false; + + // Handle a deep copy situation + if ( typeof target === "boolean" ) { + deep = target; + target = arguments[1] || {}; + // skip the boolean and the target + i = 2; + } + + // Handle case when target is a string or something (possible in deep copy) + if ( typeof target !== "object" && !jQuery.isFunction(target) ) { + target = {}; + } + + // extend jQuery itself if only one argument is passed + if ( length === i ) { + target = this; + --i; + } + + for ( ; i < length; i++ ) { + // Only deal with non-null/undefined values + if ( (options = arguments[ i ]) != null ) { + // Extend the base object + for ( name in options ) { + src = target[ name ]; + copy = options[ name ]; + + // Prevent never-ending loop + if ( target === copy ) { + continue; + } + + // Recurse if we're merging plain objects or arrays + if ( deep && copy && ( jQuery.isPlainObject(copy) || (copyIsArray = jQuery.isArray(copy)) ) ) { + if ( copyIsArray ) { + copyIsArray = false; + clone = src && jQuery.isArray(src) ? src : []; + + } else { + clone = src && jQuery.isPlainObject(src) ? src : {}; + } + + // Never move original objects, clone them + target[ name ] = jQuery.extend( deep, clone, copy ); + + // Don't bring in undefined values + } else if ( copy !== undefined ) { + target[ name ] = copy; + } + } + } + } + + // Return the modified object + return target; +}; + +jQuery.extend({ + noConflict: function( deep ) { + if ( window.$ === jQuery ) { + window.$ = _$; + } + + if ( deep && window.jQuery === jQuery ) { + window.jQuery = _jQuery; + } + + return jQuery; + }, + + // Is the DOM ready to be used? Set to true once it occurs. + isReady: false, + + // A counter to track how many items to wait for before + // the ready event fires. See #6781 + readyWait: 1, + + // Hold (or release) the ready event + holdReady: function( hold ) { + if ( hold ) { + jQuery.readyWait++; + } else { + jQuery.ready( true ); + } + }, + + // Handle when the DOM is ready + ready: function( wait ) { + + // Abort if there are pending holds or we're already ready + if ( wait === true ? --jQuery.readyWait : jQuery.isReady ) { + return; + } + + // Make sure body exists, at least, in case IE gets a little overzealous (ticket #5443). + if ( !document.body ) { + return setTimeout( jQuery.ready, 1 ); + } + + // Remember that the DOM is ready + jQuery.isReady = true; + + // If a normal DOM Ready event fired, decrement, and wait if need be + if ( wait !== true && --jQuery.readyWait > 0 ) { + return; + } + + // If there are functions bound, to execute + readyList.resolveWith( document, [ jQuery ] ); + + // Trigger any bound ready events + if ( jQuery.fn.trigger ) { + jQuery( document ).trigger("ready").off("ready"); + } + }, + + // See test/unit/core.js for details concerning isFunction. + // Since version 1.3, DOM methods and functions like alert + // aren't supported. They return false on IE (#2968). + isFunction: function( obj ) { + return jQuery.type(obj) === "function"; + }, + + isArray: Array.isArray || function( obj ) { + return jQuery.type(obj) === "array"; + }, + + isWindow: function( obj ) { + return obj != null && obj == obj.window; + }, + + isNumeric: function( obj ) { + return !isNaN( parseFloat(obj) ) && isFinite( obj ); + }, + + type: function( obj ) { + return obj == null ? + String( obj ) : + class2type[ core_toString.call(obj) ] || "object"; + }, + + isPlainObject: function( obj ) { + // Must be an Object. + // Because of IE, we also have to check the presence of the constructor property. + // Make sure that DOM nodes and window objects don't pass through, as well + if ( !obj || jQuery.type(obj) !== "object" || obj.nodeType || jQuery.isWindow( obj ) ) { + return false; + } + + try { + // Not own constructor property must be Object + if ( obj.constructor && + !core_hasOwn.call(obj, "constructor") && + !core_hasOwn.call(obj.constructor.prototype, "isPrototypeOf") ) { + return false; + } + } catch ( e ) { + // IE8,9 Will throw exceptions on certain host objects #9897 + return false; + } + + // Own properties are enumerated firstly, so to speed up, + // if last one is own, then all properties are own. + + var key; + for ( key in obj ) {} + + return key === undefined || core_hasOwn.call( obj, key ); + }, + + isEmptyObject: function( obj ) { + var name; + for ( name in obj ) { + return false; + } + return true; + }, + + error: function( msg ) { + throw new Error( msg ); + }, + + // data: string of html + // context (optional): If specified, the fragment will be created in this context, defaults to document + // scripts (optional): If true, will include scripts passed in the html string + parseHTML: function( data, context, scripts ) { + var parsed; + if ( !data || typeof data !== "string" ) { + return null; + } + if ( typeof context === "boolean" ) { + scripts = context; + context = 0; + } + context = context || document; + + // Single tag + if ( (parsed = rsingleTag.exec( data )) ) { + return [ context.createElement( parsed[1] ) ]; + } + + parsed = jQuery.buildFragment( [ data ], context, scripts ? null : [] ); + return jQuery.merge( [], + (parsed.cacheable ? jQuery.clone( parsed.fragment ) : parsed.fragment).childNodes ); + }, + + parseJSON: function( data ) { + if ( !data || typeof data !== "string") { + return null; + } + + // Make sure leading/trailing whitespace is removed (IE can't handle it) + data = jQuery.trim( data ); + + // Attempt to parse using the native JSON parser first + if ( window.JSON && window.JSON.parse ) { + return window.JSON.parse( data ); + } + + // Make sure the incoming data is actual JSON + // Logic borrowed from http://json.org/json2.js + if ( rvalidchars.test( data.replace( rvalidescape, "@" ) + .replace( rvalidtokens, "]" ) + .replace( rvalidbraces, "")) ) { + + return ( new Function( "return " + data ) )(); + + } + jQuery.error( "Invalid JSON: " + data ); + }, + + // Cross-browser xml parsing + parseXML: function( data ) { + var xml, tmp; + if ( !data || typeof data !== "string" ) { + return null; + } + try { + if ( window.DOMParser ) { // Standard + tmp = new DOMParser(); + xml = tmp.parseFromString( data , "text/xml" ); + } else { // IE + xml = new ActiveXObject( "Microsoft.XMLDOM" ); + xml.async = "false"; + xml.loadXML( data ); + } + } catch( e ) { + xml = undefined; + } + if ( !xml || !xml.documentElement || xml.getElementsByTagName( "parsererror" ).length ) { + jQuery.error( "Invalid XML: " + data ); + } + return xml; + }, + + noop: function() {}, + + // Evaluates a script in a global context + // Workarounds based on findings by Jim Driscoll + // http://weblogs.java.net/blog/driscoll/archive/2009/09/08/eval-javascript-global-context + globalEval: function( data ) { + if ( data && core_rnotwhite.test( data ) ) { + // We use execScript on Internet Explorer + // We use an anonymous function so that context is window + // rather than jQuery in Firefox + ( window.execScript || function( data ) { + window[ "eval" ].call( window, data ); + } )( data ); + } + }, + + // Convert dashed to camelCase; used by the css and data modules + // Microsoft forgot to hump their vendor prefix (#9572) + camelCase: function( string ) { + return string.replace( rmsPrefix, "ms-" ).replace( rdashAlpha, fcamelCase ); + }, + + nodeName: function( elem, name ) { + return elem.nodeName && elem.nodeName.toLowerCase() === name.toLowerCase(); + }, + + // args is for internal usage only + each: function( obj, callback, args ) { + var name, + i = 0, + length = obj.length, + isObj = length === undefined || jQuery.isFunction( obj ); + + if ( args ) { + if ( isObj ) { + for ( name in obj ) { + if ( callback.apply( obj[ name ], args ) === false ) { + break; + } + } + } else { + for ( ; i < length; ) { + if ( callback.apply( obj[ i++ ], args ) === false ) { + break; + } + } + } + + // A special, fast, case for the most common use of each + } else { + if ( isObj ) { + for ( name in obj ) { + if ( callback.call( obj[ name ], name, obj[ name ] ) === false ) { + break; + } + } + } else { + for ( ; i < length; ) { + if ( callback.call( obj[ i ], i, obj[ i++ ] ) === false ) { + break; + } + } + } + } + + return obj; + }, + + // Use native String.trim function wherever possible + trim: core_trim && !core_trim.call("\uFEFF\xA0") ? + function( text ) { + return text == null ? + "" : + core_trim.call( text ); + } : + + // Otherwise use our own trimming functionality + function( text ) { + return text == null ? + "" : + ( text + "" ).replace( rtrim, "" ); + }, + + // results is for internal usage only + makeArray: function( arr, results ) { + var type, + ret = results || []; + + if ( arr != null ) { + // The window, strings (and functions) also have 'length' + // Tweaked logic slightly to handle Blackberry 4.7 RegExp issues #6930 + type = jQuery.type( arr ); + + if ( arr.length == null || type === "string" || type === "function" || type === "regexp" || jQuery.isWindow( arr ) ) { + core_push.call( ret, arr ); + } else { + jQuery.merge( ret, arr ); + } + } + + return ret; + }, + + inArray: function( elem, arr, i ) { + var len; + + if ( arr ) { + if ( core_indexOf ) { + return core_indexOf.call( arr, elem, i ); + } + + len = arr.length; + i = i ? i < 0 ? Math.max( 0, len + i ) : i : 0; + + for ( ; i < len; i++ ) { + // Skip accessing in sparse arrays + if ( i in arr && arr[ i ] === elem ) { + return i; + } + } + } + + return -1; + }, + + merge: function( first, second ) { + var l = second.length, + i = first.length, + j = 0; + + if ( typeof l === "number" ) { + for ( ; j < l; j++ ) { + first[ i++ ] = second[ j ]; + } + + } else { + while ( second[j] !== undefined ) { + first[ i++ ] = second[ j++ ]; + } + } + + first.length = i; + + return first; + }, + + grep: function( elems, callback, inv ) { + var retVal, + ret = [], + i = 0, + length = elems.length; + inv = !!inv; + + // Go through the array, only saving the items + // that pass the validator function + for ( ; i < length; i++ ) { + retVal = !!callback( elems[ i ], i ); + if ( inv !== retVal ) { + ret.push( elems[ i ] ); + } + } + + return ret; + }, + + // arg is for internal usage only + map: function( elems, callback, arg ) { + var value, key, + ret = [], + i = 0, + length = elems.length, + // jquery objects are treated as arrays + isArray = elems instanceof jQuery || length !== undefined && typeof length === "number" && ( ( length > 0 && elems[ 0 ] && elems[ length -1 ] ) || length === 0 || jQuery.isArray( elems ) ) ; + + // Go through the array, translating each of the items to their + if ( isArray ) { + for ( ; i < length; i++ ) { + value = callback( elems[ i ], i, arg ); + + if ( value != null ) { + ret[ ret.length ] = value; + } + } + + // Go through every key on the object, + } else { + for ( key in elems ) { + value = callback( elems[ key ], key, arg ); + + if ( value != null ) { + ret[ ret.length ] = value; + } + } + } + + // Flatten any nested arrays + return ret.concat.apply( [], ret ); + }, + + // A global GUID counter for objects + guid: 1, + + // Bind a function to a context, optionally partially applying any + // arguments. + proxy: function( fn, context ) { + var tmp, args, proxy; + + if ( typeof context === "string" ) { + tmp = fn[ context ]; + context = fn; + fn = tmp; + } + + // Quick check to determine if target is callable, in the spec + // this throws a TypeError, but we will just return undefined. + if ( !jQuery.isFunction( fn ) ) { + return undefined; + } + + // Simulated bind + args = core_slice.call( arguments, 2 ); + proxy = function() { + return fn.apply( context, args.concat( core_slice.call( arguments ) ) ); + }; + + // Set the guid of unique handler to the same of original handler, so it can be removed + proxy.guid = fn.guid = fn.guid || jQuery.guid++; + + return proxy; + }, + + // Multifunctional method to get and set values of a collection + // The value/s can optionally be executed if it's a function + access: function( elems, fn, key, value, chainable, emptyGet, pass ) { + var exec, + bulk = key == null, + i = 0, + length = elems.length; + + // Sets many values + if ( key && typeof key === "object" ) { + for ( i in key ) { + jQuery.access( elems, fn, i, key[i], 1, emptyGet, value ); + } + chainable = 1; + + // Sets one value + } else if ( value !== undefined ) { + // Optionally, function values get executed if exec is true + exec = pass === undefined && jQuery.isFunction( value ); + + if ( bulk ) { + // Bulk operations only iterate when executing function values + if ( exec ) { + exec = fn; + fn = function( elem, key, value ) { + return exec.call( jQuery( elem ), value ); + }; + + // Otherwise they run against the entire set + } else { + fn.call( elems, value ); + fn = null; + } + } + + if ( fn ) { + for (; i < length; i++ ) { + fn( elems[i], key, exec ? value.call( elems[i], i, fn( elems[i], key ) ) : value, pass ); + } + } + + chainable = 1; + } + + return chainable ? + elems : + + // Gets + bulk ? + fn.call( elems ) : + length ? fn( elems[0], key ) : emptyGet; + }, + + now: function() { + return ( new Date() ).getTime(); + } +}); + +jQuery.ready.promise = function( obj ) { + if ( !readyList ) { + + readyList = jQuery.Deferred(); + + // Catch cases where $(document).ready() is called after the browser event has already occurred. + // we once tried to use readyState "interactive" here, but it caused issues like the one + // discovered by ChrisS here: http://bugs.jquery.com/ticket/12282#comment:15 + if ( document.readyState === "complete" ) { + // Handle it asynchronously to allow scripts the opportunity to delay ready + setTimeout( jQuery.ready, 1 ); + + // Standards-based browsers support DOMContentLoaded + } else if ( document.addEventListener ) { + // Use the handy event callback + document.addEventListener( "DOMContentLoaded", DOMContentLoaded, false ); + + // A fallback to window.onload, that will always work + window.addEventListener( "load", jQuery.ready, false ); + + // If IE event model is used + } else { + // Ensure firing before onload, maybe late but safe also for iframes + document.attachEvent( "onreadystatechange", DOMContentLoaded ); + + // A fallback to window.onload, that will always work + window.attachEvent( "onload", jQuery.ready ); + + // If IE and not a frame + // continually check to see if the document is ready + var top = false; + + try { + top = window.frameElement == null && document.documentElement; + } catch(e) {} + + if ( top && top.doScroll ) { + (function doScrollCheck() { + if ( !jQuery.isReady ) { + + try { + // Use the trick by Diego Perini + // http://javascript.nwbox.com/IEContentLoaded/ + top.doScroll("left"); + } catch(e) { + return setTimeout( doScrollCheck, 50 ); + } + + // and execute any waiting functions + jQuery.ready(); + } + })(); + } + } + } + return readyList.promise( obj ); +}; + +// Populate the class2type map +jQuery.each("Boolean Number String Function Array Date RegExp Object".split(" "), function(i, name) { + class2type[ "[object " + name + "]" ] = name.toLowerCase(); +}); + +// All jQuery objects should point back to these +rootjQuery = jQuery(document); +// String to Object options format cache +var optionsCache = {}; + +// Convert String-formatted options into Object-formatted ones and store in cache +function createOptions( options ) { + var object = optionsCache[ options ] = {}; + jQuery.each( options.split( core_rspace ), function( _, flag ) { + object[ flag ] = true; + }); + return object; +} + +/* + * Create a callback list using the following parameters: + * + * options: an optional list of space-separated options that will change how + * the callback list behaves or a more traditional option object + * + * By default a callback list will act like an event callback list and can be + * "fired" multiple times. + * + * Possible options: + * + * once: will ensure the callback list can only be fired once (like a Deferred) + * + * memory: will keep track of previous values and will call any callback added + * after the list has been fired right away with the latest "memorized" + * values (like a Deferred) + * + * unique: will ensure a callback can only be added once (no duplicate in the list) + * + * stopOnFalse: interrupt callings when a callback returns false + * + */ +jQuery.Callbacks = function( options ) { + + // Convert options from String-formatted to Object-formatted if needed + // (we check in cache first) + options = typeof options === "string" ? + ( optionsCache[ options ] || createOptions( options ) ) : + jQuery.extend( {}, options ); + + var // Last fire value (for non-forgettable lists) + memory, + // Flag to know if list was already fired + fired, + // Flag to know if list is currently firing + firing, + // First callback to fire (used internally by add and fireWith) + firingStart, + // End of the loop when firing + firingLength, + // Index of currently firing callback (modified by remove if needed) + firingIndex, + // Actual callback list + list = [], + // Stack of fire calls for repeatable lists + stack = !options.once && [], + // Fire callbacks + fire = function( data ) { + memory = options.memory && data; + fired = true; + firingIndex = firingStart || 0; + firingStart = 0; + firingLength = list.length; + firing = true; + for ( ; list && firingIndex < firingLength; firingIndex++ ) { + if ( list[ firingIndex ].apply( data[ 0 ], data[ 1 ] ) === false && options.stopOnFalse ) { + memory = false; // To prevent further calls using add + break; + } + } + firing = false; + if ( list ) { + if ( stack ) { + if ( stack.length ) { + fire( stack.shift() ); + } + } else if ( memory ) { + list = []; + } else { + self.disable(); + } + } + }, + // Actual Callbacks object + self = { + // Add a callback or a collection of callbacks to the list + add: function() { + if ( list ) { + // First, we save the current length + var start = list.length; + (function add( args ) { + jQuery.each( args, function( _, arg ) { + var type = jQuery.type( arg ); + if ( type === "function" ) { + if ( !options.unique || !self.has( arg ) ) { + list.push( arg ); + } + } else if ( arg && arg.length && type !== "string" ) { + // Inspect recursively + add( arg ); + } + }); + })( arguments ); + // Do we need to add the callbacks to the + // current firing batch? + if ( firing ) { + firingLength = list.length; + // With memory, if we're not firing then + // we should call right away + } else if ( memory ) { + firingStart = start; + fire( memory ); + } + } + return this; + }, + // Remove a callback from the list + remove: function() { + if ( list ) { + jQuery.each( arguments, function( _, arg ) { + var index; + while( ( index = jQuery.inArray( arg, list, index ) ) > -1 ) { + list.splice( index, 1 ); + // Handle firing indexes + if ( firing ) { + if ( index <= firingLength ) { + firingLength--; + } + if ( index <= firingIndex ) { + firingIndex--; + } + } + } + }); + } + return this; + }, + // Control if a given callback is in the list + has: function( fn ) { + return jQuery.inArray( fn, list ) > -1; + }, + // Remove all callbacks from the list + empty: function() { + list = []; + return this; + }, + // Have the list do nothing anymore + disable: function() { + list = stack = memory = undefined; + return this; + }, + // Is it disabled? + disabled: function() { + return !list; + }, + // Lock the list in its current state + lock: function() { + stack = undefined; + if ( !memory ) { + self.disable(); + } + return this; + }, + // Is it locked? + locked: function() { + return !stack; + }, + // Call all callbacks with the given context and arguments + fireWith: function( context, args ) { + args = args || []; + args = [ context, args.slice ? args.slice() : args ]; + if ( list && ( !fired || stack ) ) { + if ( firing ) { + stack.push( args ); + } else { + fire( args ); + } + } + return this; + }, + // Call all the callbacks with the given arguments + fire: function() { + self.fireWith( this, arguments ); + return this; + }, + // To know if the callbacks have already been called at least once + fired: function() { + return !!fired; + } + }; + + return self; +}; +jQuery.extend({ + + Deferred: function( func ) { + var tuples = [ + // action, add listener, listener list, final state + [ "resolve", "done", jQuery.Callbacks("once memory"), "resolved" ], + [ "reject", "fail", jQuery.Callbacks("once memory"), "rejected" ], + [ "notify", "progress", jQuery.Callbacks("memory") ] + ], + state = "pending", + promise = { + state: function() { + return state; + }, + always: function() { + deferred.done( arguments ).fail( arguments ); + return this; + }, + then: function( /* fnDone, fnFail, fnProgress */ ) { + var fns = arguments; + return jQuery.Deferred(function( newDefer ) { + jQuery.each( tuples, function( i, tuple ) { + var action = tuple[ 0 ], + fn = fns[ i ]; + // deferred[ done | fail | progress ] for forwarding actions to newDefer + deferred[ tuple[1] ]( jQuery.isFunction( fn ) ? + function() { + var returned = fn.apply( this, arguments ); + if ( returned && jQuery.isFunction( returned.promise ) ) { + returned.promise() + .done( newDefer.resolve ) + .fail( newDefer.reject ) + .progress( newDefer.notify ); + } else { + newDefer[ action + "With" ]( this === deferred ? newDefer : this, [ returned ] ); + } + } : + newDefer[ action ] + ); + }); + fns = null; + }).promise(); + }, + // Get a promise for this deferred + // If obj is provided, the promise aspect is added to the object + promise: function( obj ) { + return obj != null ? jQuery.extend( obj, promise ) : promise; + } + }, + deferred = {}; + + // Keep pipe for back-compat + promise.pipe = promise.then; + + // Add list-specific methods + jQuery.each( tuples, function( i, tuple ) { + var list = tuple[ 2 ], + stateString = tuple[ 3 ]; + + // promise[ done | fail | progress ] = list.add + promise[ tuple[1] ] = list.add; + + // Handle state + if ( stateString ) { + list.add(function() { + // state = [ resolved | rejected ] + state = stateString; + + // [ reject_list | resolve_list ].disable; progress_list.lock + }, tuples[ i ^ 1 ][ 2 ].disable, tuples[ 2 ][ 2 ].lock ); + } + + // deferred[ resolve | reject | notify ] = list.fire + deferred[ tuple[0] ] = list.fire; + deferred[ tuple[0] + "With" ] = list.fireWith; + }); + + // Make the deferred a promise + promise.promise( deferred ); + + // Call given func if any + if ( func ) { + func.call( deferred, deferred ); + } + + // All done! + return deferred; + }, + + // Deferred helper + when: function( subordinate /* , ..., subordinateN */ ) { + var i = 0, + resolveValues = core_slice.call( arguments ), + length = resolveValues.length, + + // the count of uncompleted subordinates + remaining = length !== 1 || ( subordinate && jQuery.isFunction( subordinate.promise ) ) ? length : 0, + + // the master Deferred. If resolveValues consist of only a single Deferred, just use that. + deferred = remaining === 1 ? subordinate : jQuery.Deferred(), + + // Update function for both resolve and progress values + updateFunc = function( i, contexts, values ) { + return function( value ) { + contexts[ i ] = this; + values[ i ] = arguments.length > 1 ? core_slice.call( arguments ) : value; + if( values === progressValues ) { + deferred.notifyWith( contexts, values ); + } else if ( !( --remaining ) ) { + deferred.resolveWith( contexts, values ); + } + }; + }, + + progressValues, progressContexts, resolveContexts; + + // add listeners to Deferred subordinates; treat others as resolved + if ( length > 1 ) { + progressValues = new Array( length ); + progressContexts = new Array( length ); + resolveContexts = new Array( length ); + for ( ; i < length; i++ ) { + if ( resolveValues[ i ] && jQuery.isFunction( resolveValues[ i ].promise ) ) { + resolveValues[ i ].promise() + .done( updateFunc( i, resolveContexts, resolveValues ) ) + .fail( deferred.reject ) + .progress( updateFunc( i, progressContexts, progressValues ) ); + } else { + --remaining; + } + } + } + + // if we're not waiting on anything, resolve the master + if ( !remaining ) { + deferred.resolveWith( resolveContexts, resolveValues ); + } + + return deferred.promise(); + } +}); +jQuery.support = (function() { + + var support, + all, + a, + select, + opt, + input, + fragment, + eventName, + i, + isSupported, + clickFn, + div = document.createElement("div"); + + // Setup + div.setAttribute( "className", "t" ); + div.innerHTML = "
a"; + + // Support tests won't run in some limited or non-browser environments + all = div.getElementsByTagName("*"); + a = div.getElementsByTagName("a")[ 0 ]; + if ( !all || !a || !all.length ) { + return {}; + } + + // First batch of tests + select = document.createElement("select"); + opt = select.appendChild( document.createElement("option") ); + input = div.getElementsByTagName("input")[ 0 ]; + + a.style.cssText = "top:1px;float:left;opacity:.5"; + support = { + // IE strips leading whitespace when .innerHTML is used + leadingWhitespace: ( div.firstChild.nodeType === 3 ), + + // Make sure that tbody elements aren't automatically inserted + // IE will insert them into empty tables + tbody: !div.getElementsByTagName("tbody").length, + + // Make sure that link elements get serialized correctly by innerHTML + // This requires a wrapper element in IE + htmlSerialize: !!div.getElementsByTagName("link").length, + + // Get the style information from getAttribute + // (IE uses .cssText instead) + style: /top/.test( a.getAttribute("style") ), + + // Make sure that URLs aren't manipulated + // (IE normalizes it by default) + hrefNormalized: ( a.getAttribute("href") === "/a" ), + + // Make sure that element opacity exists + // (IE uses filter instead) + // Use a regex to work around a WebKit issue. See #5145 + opacity: /^0.5/.test( a.style.opacity ), + + // Verify style float existence + // (IE uses styleFloat instead of cssFloat) + cssFloat: !!a.style.cssFloat, + + // Make sure that if no value is specified for a checkbox + // that it defaults to "on". + // (WebKit defaults to "" instead) + checkOn: ( input.value === "on" ), + + // Make sure that a selected-by-default option has a working selected property. + // (WebKit defaults to false instead of true, IE too, if it's in an optgroup) + optSelected: opt.selected, + + // Test setAttribute on camelCase class. If it works, we need attrFixes when doing get/setAttribute (ie6/7) + getSetAttribute: div.className !== "t", + + // Tests for enctype support on a form (#6743) + enctype: !!document.createElement("form").enctype, + + // Makes sure cloning an html5 element does not cause problems + // Where outerHTML is undefined, this still works + html5Clone: document.createElement("nav").cloneNode( true ).outerHTML !== "<:nav>", + + // jQuery.support.boxModel DEPRECATED in 1.8 since we don't support Quirks Mode + boxModel: ( document.compatMode === "CSS1Compat" ), + + // Will be defined later + submitBubbles: true, + changeBubbles: true, + focusinBubbles: false, + deleteExpando: true, + noCloneEvent: true, + inlineBlockNeedsLayout: false, + shrinkWrapBlocks: false, + reliableMarginRight: true, + boxSizingReliable: true, + pixelPosition: false + }; + + // Make sure checked status is properly cloned + input.checked = true; + support.noCloneChecked = input.cloneNode( true ).checked; + + // Make sure that the options inside disabled selects aren't marked as disabled + // (WebKit marks them as disabled) + select.disabled = true; + support.optDisabled = !opt.disabled; + + // Test to see if it's possible to delete an expando from an element + // Fails in Internet Explorer + try { + delete div.test; + } catch( e ) { + support.deleteExpando = false; + } + + if ( !div.addEventListener && div.attachEvent && div.fireEvent ) { + div.attachEvent( "onclick", clickFn = function() { + // Cloning a node shouldn't copy over any + // bound event handlers (IE does this) + support.noCloneEvent = false; + }); + div.cloneNode( true ).fireEvent("onclick"); + div.detachEvent( "onclick", clickFn ); + } + + // Check if a radio maintains its value + // after being appended to the DOM + input = document.createElement("input"); + input.value = "t"; + input.setAttribute( "type", "radio" ); + support.radioValue = input.value === "t"; + + input.setAttribute( "checked", "checked" ); + + // #11217 - WebKit loses check when the name is after the checked attribute + input.setAttribute( "name", "t" ); + + div.appendChild( input ); + fragment = document.createDocumentFragment(); + fragment.appendChild( div.lastChild ); + + // WebKit doesn't clone checked state correctly in fragments + support.checkClone = fragment.cloneNode( true ).cloneNode( true ).lastChild.checked; + + // Check if a disconnected checkbox will retain its checked + // value of true after appended to the DOM (IE6/7) + support.appendChecked = input.checked; + + fragment.removeChild( input ); + fragment.appendChild( div ); + + // Technique from Juriy Zaytsev + // http://perfectionkills.com/detecting-event-support-without-browser-sniffing/ + // We only care about the case where non-standard event systems + // are used, namely in IE. Short-circuiting here helps us to + // avoid an eval call (in setAttribute) which can cause CSP + // to go haywire. See: https://developer.mozilla.org/en/Security/CSP + if ( div.attachEvent ) { + for ( i in { + submit: true, + change: true, + focusin: true + }) { + eventName = "on" + i; + isSupported = ( eventName in div ); + if ( !isSupported ) { + div.setAttribute( eventName, "return;" ); + isSupported = ( typeof div[ eventName ] === "function" ); + } + support[ i + "Bubbles" ] = isSupported; + } + } + + // Run tests that need a body at doc ready + jQuery(function() { + var container, div, tds, marginDiv, + divReset = "padding:0;margin:0;border:0;display:block;overflow:hidden;", + body = document.getElementsByTagName("body")[0]; + + if ( !body ) { + // Return for frameset docs that don't have a body + return; + } + + container = document.createElement("div"); + container.style.cssText = "visibility:hidden;border:0;width:0;height:0;position:static;top:0;margin-top:1px"; + body.insertBefore( container, body.firstChild ); + + // Construct the test element + div = document.createElement("div"); + container.appendChild( div ); + + // Check if table cells still have offsetWidth/Height when they are set + // to display:none and there are still other visible table cells in a + // table row; if so, offsetWidth/Height are not reliable for use when + // determining if an element has been hidden directly using + // display:none (it is still safe to use offsets if a parent element is + // hidden; don safety goggles and see bug #4512 for more information). + // (only IE 8 fails this test) + div.innerHTML = "
t
"; + tds = div.getElementsByTagName("td"); + tds[ 0 ].style.cssText = "padding:0;margin:0;border:0;display:none"; + isSupported = ( tds[ 0 ].offsetHeight === 0 ); + + tds[ 0 ].style.display = ""; + tds[ 1 ].style.display = "none"; + + // Check if empty table cells still have offsetWidth/Height + // (IE <= 8 fail this test) + support.reliableHiddenOffsets = isSupported && ( tds[ 0 ].offsetHeight === 0 ); + + // Check box-sizing and margin behavior + div.innerHTML = ""; + div.style.cssText = "box-sizing:border-box;-moz-box-sizing:border-box;-webkit-box-sizing:border-box;padding:1px;border:1px;display:block;width:4px;margin-top:1%;position:absolute;top:1%;"; + support.boxSizing = ( div.offsetWidth === 4 ); + support.doesNotIncludeMarginInBodyOffset = ( body.offsetTop !== 1 ); + + // NOTE: To any future maintainer, we've window.getComputedStyle + // because jsdom on node.js will break without it. + if ( window.getComputedStyle ) { + support.pixelPosition = ( window.getComputedStyle( div, null ) || {} ).top !== "1%"; + support.boxSizingReliable = ( window.getComputedStyle( div, null ) || { width: "4px" } ).width === "4px"; + + // Check if div with explicit width and no margin-right incorrectly + // gets computed margin-right based on width of container. For more + // info see bug #3333 + // Fails in WebKit before Feb 2011 nightlies + // WebKit Bug 13343 - getComputedStyle returns wrong value for margin-right + marginDiv = document.createElement("div"); + marginDiv.style.cssText = div.style.cssText = divReset; + marginDiv.style.marginRight = marginDiv.style.width = "0"; + div.style.width = "1px"; + div.appendChild( marginDiv ); + support.reliableMarginRight = + !parseFloat( ( window.getComputedStyle( marginDiv, null ) || {} ).marginRight ); + } + + if ( typeof div.style.zoom !== "undefined" ) { + // Check if natively block-level elements act like inline-block + // elements when setting their display to 'inline' and giving + // them layout + // (IE < 8 does this) + div.innerHTML = ""; + div.style.cssText = divReset + "width:1px;padding:1px;display:inline;zoom:1"; + support.inlineBlockNeedsLayout = ( div.offsetWidth === 3 ); + + // Check if elements with layout shrink-wrap their children + // (IE 6 does this) + div.style.display = "block"; + div.style.overflow = "visible"; + div.innerHTML = "
"; + div.firstChild.style.width = "5px"; + support.shrinkWrapBlocks = ( div.offsetWidth !== 3 ); + + container.style.zoom = 1; + } + + // Null elements to avoid leaks in IE + body.removeChild( container ); + container = div = tds = marginDiv = null; + }); + + // Null elements to avoid leaks in IE + fragment.removeChild( div ); + all = a = select = opt = input = fragment = div = null; + + return support; +})(); +var rbrace = /(?:\{[\s\S]*\}|\[[\s\S]*\])$/, + rmultiDash = /([A-Z])/g; + +jQuery.extend({ + cache: {}, + + deletedIds: [], + + // Remove at next major release (1.9/2.0) + uuid: 0, + + // Unique for each copy of jQuery on the page + // Non-digits removed to match rinlinejQuery + expando: "jQuery" + ( jQuery.fn.jquery + Math.random() ).replace( /\D/g, "" ), + + // The following elements throw uncatchable exceptions if you + // attempt to add expando properties to them. + noData: { + "embed": true, + // Ban all objects except for Flash (which handle expandos) + "object": "clsid:D27CDB6E-AE6D-11cf-96B8-444553540000", + "applet": true + }, + + hasData: function( elem ) { + elem = elem.nodeType ? jQuery.cache[ elem[jQuery.expando] ] : elem[ jQuery.expando ]; + return !!elem && !isEmptyDataObject( elem ); + }, + + data: function( elem, name, data, pvt /* Internal Use Only */ ) { + if ( !jQuery.acceptData( elem ) ) { + return; + } + + var thisCache, ret, + internalKey = jQuery.expando, + getByName = typeof name === "string", + + // We have to handle DOM nodes and JS objects differently because IE6-7 + // can't GC object references properly across the DOM-JS boundary + isNode = elem.nodeType, + + // Only DOM nodes need the global jQuery cache; JS object data is + // attached directly to the object so GC can occur automatically + cache = isNode ? jQuery.cache : elem, + + // Only defining an ID for JS objects if its cache already exists allows + // the code to shortcut on the same path as a DOM node with no cache + id = isNode ? elem[ internalKey ] : elem[ internalKey ] && internalKey; + + // Avoid doing any more work than we need to when trying to get data on an + // object that has no data at all + if ( (!id || !cache[id] || (!pvt && !cache[id].data)) && getByName && data === undefined ) { + return; + } + + if ( !id ) { + // Only DOM nodes need a new unique ID for each element since their data + // ends up in the global cache + if ( isNode ) { + elem[ internalKey ] = id = jQuery.deletedIds.pop() || jQuery.guid++; + } else { + id = internalKey; + } + } + + if ( !cache[ id ] ) { + cache[ id ] = {}; + + // Avoids exposing jQuery metadata on plain JS objects when the object + // is serialized using JSON.stringify + if ( !isNode ) { + cache[ id ].toJSON = jQuery.noop; + } + } + + // An object can be passed to jQuery.data instead of a key/value pair; this gets + // shallow copied over onto the existing cache + if ( typeof name === "object" || typeof name === "function" ) { + if ( pvt ) { + cache[ id ] = jQuery.extend( cache[ id ], name ); + } else { + cache[ id ].data = jQuery.extend( cache[ id ].data, name ); + } + } + + thisCache = cache[ id ]; + + // jQuery data() is stored in a separate object inside the object's internal data + // cache in order to avoid key collisions between internal data and user-defined + // data. + if ( !pvt ) { + if ( !thisCache.data ) { + thisCache.data = {}; + } + + thisCache = thisCache.data; + } + + if ( data !== undefined ) { + thisCache[ jQuery.camelCase( name ) ] = data; + } + + // Check for both converted-to-camel and non-converted data property names + // If a data property was specified + if ( getByName ) { + + // First Try to find as-is property data + ret = thisCache[ name ]; + + // Test for null|undefined property data + if ( ret == null ) { + + // Try to find the camelCased property + ret = thisCache[ jQuery.camelCase( name ) ]; + } + } else { + ret = thisCache; + } + + return ret; + }, + + removeData: function( elem, name, pvt /* Internal Use Only */ ) { + if ( !jQuery.acceptData( elem ) ) { + return; + } + + var thisCache, i, l, + + isNode = elem.nodeType, + + // See jQuery.data for more information + cache = isNode ? jQuery.cache : elem, + id = isNode ? elem[ jQuery.expando ] : jQuery.expando; + + // If there is already no cache entry for this object, there is no + // purpose in continuing + if ( !cache[ id ] ) { + return; + } + + if ( name ) { + + thisCache = pvt ? cache[ id ] : cache[ id ].data; + + if ( thisCache ) { + + // Support array or space separated string names for data keys + if ( !jQuery.isArray( name ) ) { + + // try the string as a key before any manipulation + if ( name in thisCache ) { + name = [ name ]; + } else { + + // split the camel cased version by spaces unless a key with the spaces exists + name = jQuery.camelCase( name ); + if ( name in thisCache ) { + name = [ name ]; + } else { + name = name.split(" "); + } + } + } + + for ( i = 0, l = name.length; i < l; i++ ) { + delete thisCache[ name[i] ]; + } + + // If there is no data left in the cache, we want to continue + // and let the cache object itself get destroyed + if ( !( pvt ? isEmptyDataObject : jQuery.isEmptyObject )( thisCache ) ) { + return; + } + } + } + + // See jQuery.data for more information + if ( !pvt ) { + delete cache[ id ].data; + + // Don't destroy the parent cache unless the internal data object + // had been the only thing left in it + if ( !isEmptyDataObject( cache[ id ] ) ) { + return; + } + } + + // Destroy the cache + if ( isNode ) { + jQuery.cleanData( [ elem ], true ); + + // Use delete when supported for expandos or `cache` is not a window per isWindow (#10080) + } else if ( jQuery.support.deleteExpando || cache != cache.window ) { + delete cache[ id ]; + + // When all else fails, null + } else { + cache[ id ] = null; + } + }, + + // For internal use only. + _data: function( elem, name, data ) { + return jQuery.data( elem, name, data, true ); + }, + + // A method for determining if a DOM node can handle the data expando + acceptData: function( elem ) { + var noData = elem.nodeName && jQuery.noData[ elem.nodeName.toLowerCase() ]; + + // nodes accept data unless otherwise specified; rejection can be conditional + return !noData || noData !== true && elem.getAttribute("classid") === noData; + } +}); + +jQuery.fn.extend({ + data: function( key, value ) { + var parts, part, attr, name, l, + elem = this[0], + i = 0, + data = null; + + // Gets all values + if ( key === undefined ) { + if ( this.length ) { + data = jQuery.data( elem ); + + if ( elem.nodeType === 1 && !jQuery._data( elem, "parsedAttrs" ) ) { + attr = elem.attributes; + for ( l = attr.length; i < l; i++ ) { + name = attr[i].name; + + if ( !name.indexOf( "data-" ) ) { + name = jQuery.camelCase( name.substring(5) ); + + dataAttr( elem, name, data[ name ] ); + } + } + jQuery._data( elem, "parsedAttrs", true ); + } + } + + return data; + } + + // Sets multiple values + if ( typeof key === "object" ) { + return this.each(function() { + jQuery.data( this, key ); + }); + } + + parts = key.split( ".", 2 ); + parts[1] = parts[1] ? "." + parts[1] : ""; + part = parts[1] + "!"; + + return jQuery.access( this, function( value ) { + + if ( value === undefined ) { + data = this.triggerHandler( "getData" + part, [ parts[0] ] ); + + // Try to fetch any internally stored data first + if ( data === undefined && elem ) { + data = jQuery.data( elem, key ); + data = dataAttr( elem, key, data ); + } + + return data === undefined && parts[1] ? + this.data( parts[0] ) : + data; + } + + parts[1] = value; + this.each(function() { + var self = jQuery( this ); + + self.triggerHandler( "setData" + part, parts ); + jQuery.data( this, key, value ); + self.triggerHandler( "changeData" + part, parts ); + }); + }, null, value, arguments.length > 1, null, false ); + }, + + removeData: function( key ) { + return this.each(function() { + jQuery.removeData( this, key ); + }); + } +}); + +function dataAttr( elem, key, data ) { + // If nothing was found internally, try to fetch any + // data from the HTML5 data-* attribute + if ( data === undefined && elem.nodeType === 1 ) { + + var name = "data-" + key.replace( rmultiDash, "-$1" ).toLowerCase(); + + data = elem.getAttribute( name ); + + if ( typeof data === "string" ) { + try { + data = data === "true" ? true : + data === "false" ? false : + data === "null" ? null : + // Only convert to a number if it doesn't change the string + +data + "" === data ? +data : + rbrace.test( data ) ? jQuery.parseJSON( data ) : + data; + } catch( e ) {} + + // Make sure we set the data so it isn't changed later + jQuery.data( elem, key, data ); + + } else { + data = undefined; + } + } + + return data; +} + +// checks a cache object for emptiness +function isEmptyDataObject( obj ) { + var name; + for ( name in obj ) { + + // if the public data object is empty, the private is still empty + if ( name === "data" && jQuery.isEmptyObject( obj[name] ) ) { + continue; + } + if ( name !== "toJSON" ) { + return false; + } + } + + return true; +} +jQuery.extend({ + queue: function( elem, type, data ) { + var queue; + + if ( elem ) { + type = ( type || "fx" ) + "queue"; + queue = jQuery._data( elem, type ); + + // Speed up dequeue by getting out quickly if this is just a lookup + if ( data ) { + if ( !queue || jQuery.isArray(data) ) { + queue = jQuery._data( elem, type, jQuery.makeArray(data) ); + } else { + queue.push( data ); + } + } + return queue || []; + } + }, + + dequeue: function( elem, type ) { + type = type || "fx"; + + var queue = jQuery.queue( elem, type ), + startLength = queue.length, + fn = queue.shift(), + hooks = jQuery._queueHooks( elem, type ), + next = function() { + jQuery.dequeue( elem, type ); + }; + + // If the fx queue is dequeued, always remove the progress sentinel + if ( fn === "inprogress" ) { + fn = queue.shift(); + startLength--; + } + + if ( fn ) { + + // Add a progress sentinel to prevent the fx queue from being + // automatically dequeued + if ( type === "fx" ) { + queue.unshift( "inprogress" ); + } + + // clear up the last queue stop function + delete hooks.stop; + fn.call( elem, next, hooks ); + } + + if ( !startLength && hooks ) { + hooks.empty.fire(); + } + }, + + // not intended for public consumption - generates a queueHooks object, or returns the current one + _queueHooks: function( elem, type ) { + var key = type + "queueHooks"; + return jQuery._data( elem, key ) || jQuery._data( elem, key, { + empty: jQuery.Callbacks("once memory").add(function() { + jQuery.removeData( elem, type + "queue", true ); + jQuery.removeData( elem, key, true ); + }) + }); + } +}); + +jQuery.fn.extend({ + queue: function( type, data ) { + var setter = 2; + + if ( typeof type !== "string" ) { + data = type; + type = "fx"; + setter--; + } + + if ( arguments.length < setter ) { + return jQuery.queue( this[0], type ); + } + + return data === undefined ? + this : + this.each(function() { + var queue = jQuery.queue( this, type, data ); + + // ensure a hooks for this queue + jQuery._queueHooks( this, type ); + + if ( type === "fx" && queue[0] !== "inprogress" ) { + jQuery.dequeue( this, type ); + } + }); + }, + dequeue: function( type ) { + return this.each(function() { + jQuery.dequeue( this, type ); + }); + }, + // Based off of the plugin by Clint Helfers, with permission. + // http://blindsignals.com/index.php/2009/07/jquery-delay/ + delay: function( time, type ) { + time = jQuery.fx ? jQuery.fx.speeds[ time ] || time : time; + type = type || "fx"; + + return this.queue( type, function( next, hooks ) { + var timeout = setTimeout( next, time ); + hooks.stop = function() { + clearTimeout( timeout ); + }; + }); + }, + clearQueue: function( type ) { + return this.queue( type || "fx", [] ); + }, + // Get a promise resolved when queues of a certain type + // are emptied (fx is the type by default) + promise: function( type, obj ) { + var tmp, + count = 1, + defer = jQuery.Deferred(), + elements = this, + i = this.length, + resolve = function() { + if ( !( --count ) ) { + defer.resolveWith( elements, [ elements ] ); + } + }; + + if ( typeof type !== "string" ) { + obj = type; + type = undefined; + } + type = type || "fx"; + + while( i-- ) { + tmp = jQuery._data( elements[ i ], type + "queueHooks" ); + if ( tmp && tmp.empty ) { + count++; + tmp.empty.add( resolve ); + } + } + resolve(); + return defer.promise( obj ); + } +}); +var nodeHook, boolHook, fixSpecified, + rclass = /[\t\r\n]/g, + rreturn = /\r/g, + rtype = /^(?:button|input)$/i, + rfocusable = /^(?:button|input|object|select|textarea)$/i, + rclickable = /^a(?:rea|)$/i, + rboolean = /^(?:autofocus|autoplay|async|checked|controls|defer|disabled|hidden|loop|multiple|open|readonly|required|scoped|selected)$/i, + getSetAttribute = jQuery.support.getSetAttribute; + +jQuery.fn.extend({ + attr: function( name, value ) { + return jQuery.access( this, jQuery.attr, name, value, arguments.length > 1 ); + }, + + removeAttr: function( name ) { + return this.each(function() { + jQuery.removeAttr( this, name ); + }); + }, + + prop: function( name, value ) { + return jQuery.access( this, jQuery.prop, name, value, arguments.length > 1 ); + }, + + removeProp: function( name ) { + name = jQuery.propFix[ name ] || name; + return this.each(function() { + // try/catch handles cases where IE balks (such as removing a property on window) + try { + this[ name ] = undefined; + delete this[ name ]; + } catch( e ) {} + }); + }, + + addClass: function( value ) { + var classNames, i, l, elem, + setClass, c, cl; + + if ( jQuery.isFunction( value ) ) { + return this.each(function( j ) { + jQuery( this ).addClass( value.call(this, j, this.className) ); + }); + } + + if ( value && typeof value === "string" ) { + classNames = value.split( core_rspace ); + + for ( i = 0, l = this.length; i < l; i++ ) { + elem = this[ i ]; + + if ( elem.nodeType === 1 ) { + if ( !elem.className && classNames.length === 1 ) { + elem.className = value; + + } else { + setClass = " " + elem.className + " "; + + for ( c = 0, cl = classNames.length; c < cl; c++ ) { + if ( setClass.indexOf( " " + classNames[ c ] + " " ) < 0 ) { + setClass += classNames[ c ] + " "; + } + } + elem.className = jQuery.trim( setClass ); + } + } + } + } + + return this; + }, + + removeClass: function( value ) { + var removes, className, elem, c, cl, i, l; + + if ( jQuery.isFunction( value ) ) { + return this.each(function( j ) { + jQuery( this ).removeClass( value.call(this, j, this.className) ); + }); + } + if ( (value && typeof value === "string") || value === undefined ) { + removes = ( value || "" ).split( core_rspace ); + + for ( i = 0, l = this.length; i < l; i++ ) { + elem = this[ i ]; + if ( elem.nodeType === 1 && elem.className ) { + + className = (" " + elem.className + " ").replace( rclass, " " ); + + // loop over each item in the removal list + for ( c = 0, cl = removes.length; c < cl; c++ ) { + // Remove until there is nothing to remove, + while ( className.indexOf(" " + removes[ c ] + " ") >= 0 ) { + className = className.replace( " " + removes[ c ] + " " , " " ); + } + } + elem.className = value ? jQuery.trim( className ) : ""; + } + } + } + + return this; + }, + + toggleClass: function( value, stateVal ) { + var type = typeof value, + isBool = typeof stateVal === "boolean"; + + if ( jQuery.isFunction( value ) ) { + return this.each(function( i ) { + jQuery( this ).toggleClass( value.call(this, i, this.className, stateVal), stateVal ); + }); + } + + return this.each(function() { + if ( type === "string" ) { + // toggle individual class names + var className, + i = 0, + self = jQuery( this ), + state = stateVal, + classNames = value.split( core_rspace ); + + while ( (className = classNames[ i++ ]) ) { + // check each className given, space separated list + state = isBool ? state : !self.hasClass( className ); + self[ state ? "addClass" : "removeClass" ]( className ); + } + + } else if ( type === "undefined" || type === "boolean" ) { + if ( this.className ) { + // store className if set + jQuery._data( this, "__className__", this.className ); + } + + // toggle whole className + this.className = this.className || value === false ? "" : jQuery._data( this, "__className__" ) || ""; + } + }); + }, + + hasClass: function( selector ) { + var className = " " + selector + " ", + i = 0, + l = this.length; + for ( ; i < l; i++ ) { + if ( this[i].nodeType === 1 && (" " + this[i].className + " ").replace(rclass, " ").indexOf( className ) >= 0 ) { + return true; + } + } + + return false; + }, + + val: function( value ) { + var hooks, ret, isFunction, + elem = this[0]; + + if ( !arguments.length ) { + if ( elem ) { + hooks = jQuery.valHooks[ elem.type ] || jQuery.valHooks[ elem.nodeName.toLowerCase() ]; + + if ( hooks && "get" in hooks && (ret = hooks.get( elem, "value" )) !== undefined ) { + return ret; + } + + ret = elem.value; + + return typeof ret === "string" ? + // handle most common string cases + ret.replace(rreturn, "") : + // handle cases where value is null/undef or number + ret == null ? "" : ret; + } + + return; + } + + isFunction = jQuery.isFunction( value ); + + return this.each(function( i ) { + var val, + self = jQuery(this); + + if ( this.nodeType !== 1 ) { + return; + } + + if ( isFunction ) { + val = value.call( this, i, self.val() ); + } else { + val = value; + } + + // Treat null/undefined as ""; convert numbers to string + if ( val == null ) { + val = ""; + } else if ( typeof val === "number" ) { + val += ""; + } else if ( jQuery.isArray( val ) ) { + val = jQuery.map(val, function ( value ) { + return value == null ? "" : value + ""; + }); + } + + hooks = jQuery.valHooks[ this.type ] || jQuery.valHooks[ this.nodeName.toLowerCase() ]; + + // If set returns undefined, fall back to normal setting + if ( !hooks || !("set" in hooks) || hooks.set( this, val, "value" ) === undefined ) { + this.value = val; + } + }); + } +}); + +jQuery.extend({ + valHooks: { + option: { + get: function( elem ) { + // attributes.value is undefined in Blackberry 4.7 but + // uses .value. See #6932 + var val = elem.attributes.value; + return !val || val.specified ? elem.value : elem.text; + } + }, + select: { + get: function( elem ) { + var value, option, + options = elem.options, + index = elem.selectedIndex, + one = elem.type === "select-one" || index < 0, + values = one ? null : [], + max = one ? index + 1 : options.length, + i = index < 0 ? + max : + one ? index : 0; + + // Loop through all the selected options + for ( ; i < max; i++ ) { + option = options[ i ]; + + // oldIE doesn't update selected after form reset (#2551) + if ( ( option.selected || i === index ) && + // Don't return options that are disabled or in a disabled optgroup + ( jQuery.support.optDisabled ? !option.disabled : option.getAttribute("disabled") === null ) && + ( !option.parentNode.disabled || !jQuery.nodeName( option.parentNode, "optgroup" ) ) ) { + + // Get the specific value for the option + value = jQuery( option ).val(); + + // We don't need an array for one selects + if ( one ) { + return value; + } + + // Multi-Selects return an array + values.push( value ); + } + } + + return values; + }, + + set: function( elem, value ) { + var values = jQuery.makeArray( value ); + + jQuery(elem).find("option").each(function() { + this.selected = jQuery.inArray( jQuery(this).val(), values ) >= 0; + }); + + if ( !values.length ) { + elem.selectedIndex = -1; + } + return values; + } + } + }, + + // Unused in 1.8, left in so attrFn-stabbers won't die; remove in 1.9 + attrFn: {}, + + attr: function( elem, name, value, pass ) { + var ret, hooks, notxml, + nType = elem.nodeType; + + // don't get/set attributes on text, comment and attribute nodes + if ( !elem || nType === 3 || nType === 8 || nType === 2 ) { + return; + } + + if ( pass && jQuery.isFunction( jQuery.fn[ name ] ) ) { + return jQuery( elem )[ name ]( value ); + } + + // Fallback to prop when attributes are not supported + if ( typeof elem.getAttribute === "undefined" ) { + return jQuery.prop( elem, name, value ); + } + + notxml = nType !== 1 || !jQuery.isXMLDoc( elem ); + + // All attributes are lowercase + // Grab necessary hook if one is defined + if ( notxml ) { + name = name.toLowerCase(); + hooks = jQuery.attrHooks[ name ] || ( rboolean.test( name ) ? boolHook : nodeHook ); + } + + if ( value !== undefined ) { + + if ( value === null ) { + jQuery.removeAttr( elem, name ); + return; + + } else if ( hooks && "set" in hooks && notxml && (ret = hooks.set( elem, value, name )) !== undefined ) { + return ret; + + } else { + elem.setAttribute( name, value + "" ); + return value; + } + + } else if ( hooks && "get" in hooks && notxml && (ret = hooks.get( elem, name )) !== null ) { + return ret; + + } else { + + ret = elem.getAttribute( name ); + + // Non-existent attributes return null, we normalize to undefined + return ret === null ? + undefined : + ret; + } + }, + + removeAttr: function( elem, value ) { + var propName, attrNames, name, isBool, + i = 0; + + if ( value && elem.nodeType === 1 ) { + + attrNames = value.split( core_rspace ); + + for ( ; i < attrNames.length; i++ ) { + name = attrNames[ i ]; + + if ( name ) { + propName = jQuery.propFix[ name ] || name; + isBool = rboolean.test( name ); + + // See #9699 for explanation of this approach (setting first, then removal) + // Do not do this for boolean attributes (see #10870) + if ( !isBool ) { + jQuery.attr( elem, name, "" ); + } + elem.removeAttribute( getSetAttribute ? name : propName ); + + // Set corresponding property to false for boolean attributes + if ( isBool && propName in elem ) { + elem[ propName ] = false; + } + } + } + } + }, + + attrHooks: { + type: { + set: function( elem, value ) { + // We can't allow the type property to be changed (since it causes problems in IE) + if ( rtype.test( elem.nodeName ) && elem.parentNode ) { + jQuery.error( "type property can't be changed" ); + } else if ( !jQuery.support.radioValue && value === "radio" && jQuery.nodeName(elem, "input") ) { + // Setting the type on a radio button after the value resets the value in IE6-9 + // Reset value to it's default in case type is set after value + // This is for element creation + var val = elem.value; + elem.setAttribute( "type", value ); + if ( val ) { + elem.value = val; + } + return value; + } + } + }, + // Use the value property for back compat + // Use the nodeHook for button elements in IE6/7 (#1954) + value: { + get: function( elem, name ) { + if ( nodeHook && jQuery.nodeName( elem, "button" ) ) { + return nodeHook.get( elem, name ); + } + return name in elem ? + elem.value : + null; + }, + set: function( elem, value, name ) { + if ( nodeHook && jQuery.nodeName( elem, "button" ) ) { + return nodeHook.set( elem, value, name ); + } + // Does not return so that setAttribute is also used + elem.value = value; + } + } + }, + + propFix: { + tabindex: "tabIndex", + readonly: "readOnly", + "for": "htmlFor", + "class": "className", + maxlength: "maxLength", + cellspacing: "cellSpacing", + cellpadding: "cellPadding", + rowspan: "rowSpan", + colspan: "colSpan", + usemap: "useMap", + frameborder: "frameBorder", + contenteditable: "contentEditable" + }, + + prop: function( elem, name, value ) { + var ret, hooks, notxml, + nType = elem.nodeType; + + // don't get/set properties on text, comment and attribute nodes + if ( !elem || nType === 3 || nType === 8 || nType === 2 ) { + return; + } + + notxml = nType !== 1 || !jQuery.isXMLDoc( elem ); + + if ( notxml ) { + // Fix name and attach hooks + name = jQuery.propFix[ name ] || name; + hooks = jQuery.propHooks[ name ]; + } + + if ( value !== undefined ) { + if ( hooks && "set" in hooks && (ret = hooks.set( elem, value, name )) !== undefined ) { + return ret; + + } else { + return ( elem[ name ] = value ); + } + + } else { + if ( hooks && "get" in hooks && (ret = hooks.get( elem, name )) !== null ) { + return ret; + + } else { + return elem[ name ]; + } + } + }, + + propHooks: { + tabIndex: { + get: function( elem ) { + // elem.tabIndex doesn't always return the correct value when it hasn't been explicitly set + // http://fluidproject.org/blog/2008/01/09/getting-setting-and-removing-tabindex-values-with-javascript/ + var attributeNode = elem.getAttributeNode("tabindex"); + + return attributeNode && attributeNode.specified ? + parseInt( attributeNode.value, 10 ) : + rfocusable.test( elem.nodeName ) || rclickable.test( elem.nodeName ) && elem.href ? + 0 : + undefined; + } + } + } +}); + +// Hook for boolean attributes +boolHook = { + get: function( elem, name ) { + // Align boolean attributes with corresponding properties + // Fall back to attribute presence where some booleans are not supported + var attrNode, + property = jQuery.prop( elem, name ); + return property === true || typeof property !== "boolean" && ( attrNode = elem.getAttributeNode(name) ) && attrNode.nodeValue !== false ? + name.toLowerCase() : + undefined; + }, + set: function( elem, value, name ) { + var propName; + if ( value === false ) { + // Remove boolean attributes when set to false + jQuery.removeAttr( elem, name ); + } else { + // value is true since we know at this point it's type boolean and not false + // Set boolean attributes to the same name and set the DOM property + propName = jQuery.propFix[ name ] || name; + if ( propName in elem ) { + // Only set the IDL specifically if it already exists on the element + elem[ propName ] = true; + } + + elem.setAttribute( name, name.toLowerCase() ); + } + return name; + } +}; + +// IE6/7 do not support getting/setting some attributes with get/setAttribute +if ( !getSetAttribute ) { + + fixSpecified = { + name: true, + id: true, + coords: true + }; + + // Use this for any attribute in IE6/7 + // This fixes almost every IE6/7 issue + nodeHook = jQuery.valHooks.button = { + get: function( elem, name ) { + var ret; + ret = elem.getAttributeNode( name ); + return ret && ( fixSpecified[ name ] ? ret.value !== "" : ret.specified ) ? + ret.value : + undefined; + }, + set: function( elem, value, name ) { + // Set the existing or create a new attribute node + var ret = elem.getAttributeNode( name ); + if ( !ret ) { + ret = document.createAttribute( name ); + elem.setAttributeNode( ret ); + } + return ( ret.value = value + "" ); + } + }; + + // Set width and height to auto instead of 0 on empty string( Bug #8150 ) + // This is for removals + jQuery.each([ "width", "height" ], function( i, name ) { + jQuery.attrHooks[ name ] = jQuery.extend( jQuery.attrHooks[ name ], { + set: function( elem, value ) { + if ( value === "" ) { + elem.setAttribute( name, "auto" ); + return value; + } + } + }); + }); + + // Set contenteditable to false on removals(#10429) + // Setting to empty string throws an error as an invalid value + jQuery.attrHooks.contenteditable = { + get: nodeHook.get, + set: function( elem, value, name ) { + if ( value === "" ) { + value = "false"; + } + nodeHook.set( elem, value, name ); + } + }; +} + + +// Some attributes require a special call on IE +if ( !jQuery.support.hrefNormalized ) { + jQuery.each([ "href", "src", "width", "height" ], function( i, name ) { + jQuery.attrHooks[ name ] = jQuery.extend( jQuery.attrHooks[ name ], { + get: function( elem ) { + var ret = elem.getAttribute( name, 2 ); + return ret === null ? undefined : ret; + } + }); + }); +} + +if ( !jQuery.support.style ) { + jQuery.attrHooks.style = { + get: function( elem ) { + // Return undefined in the case of empty string + // Normalize to lowercase since IE uppercases css property names + return elem.style.cssText.toLowerCase() || undefined; + }, + set: function( elem, value ) { + return ( elem.style.cssText = value + "" ); + } + }; +} + +// Safari mis-reports the default selected property of an option +// Accessing the parent's selectedIndex property fixes it +if ( !jQuery.support.optSelected ) { + jQuery.propHooks.selected = jQuery.extend( jQuery.propHooks.selected, { + get: function( elem ) { + var parent = elem.parentNode; + + if ( parent ) { + parent.selectedIndex; + + // Make sure that it also works with optgroups, see #5701 + if ( parent.parentNode ) { + parent.parentNode.selectedIndex; + } + } + return null; + } + }); +} + +// IE6/7 call enctype encoding +if ( !jQuery.support.enctype ) { + jQuery.propFix.enctype = "encoding"; +} + +// Radios and checkboxes getter/setter +if ( !jQuery.support.checkOn ) { + jQuery.each([ "radio", "checkbox" ], function() { + jQuery.valHooks[ this ] = { + get: function( elem ) { + // Handle the case where in Webkit "" is returned instead of "on" if a value isn't specified + return elem.getAttribute("value") === null ? "on" : elem.value; + } + }; + }); +} +jQuery.each([ "radio", "checkbox" ], function() { + jQuery.valHooks[ this ] = jQuery.extend( jQuery.valHooks[ this ], { + set: function( elem, value ) { + if ( jQuery.isArray( value ) ) { + return ( elem.checked = jQuery.inArray( jQuery(elem).val(), value ) >= 0 ); + } + } + }); +}); +var rformElems = /^(?:textarea|input|select)$/i, + rtypenamespace = /^([^\.]*|)(?:\.(.+)|)$/, + rhoverHack = /(?:^|\s)hover(\.\S+|)\b/, + rkeyEvent = /^key/, + rmouseEvent = /^(?:mouse|contextmenu)|click/, + rfocusMorph = /^(?:focusinfocus|focusoutblur)$/, + hoverHack = function( events ) { + return jQuery.event.special.hover ? events : events.replace( rhoverHack, "mouseenter$1 mouseleave$1" ); + }; + +/* + * Helper functions for managing events -- not part of the public interface. + * Props to Dean Edwards' addEvent library for many of the ideas. + */ +jQuery.event = { + + add: function( elem, types, handler, data, selector ) { + + var elemData, eventHandle, events, + t, tns, type, namespaces, handleObj, + handleObjIn, handlers, special; + + // Don't attach events to noData or text/comment nodes (allow plain objects tho) + if ( elem.nodeType === 3 || elem.nodeType === 8 || !types || !handler || !(elemData = jQuery._data( elem )) ) { + return; + } + + // Caller can pass in an object of custom data in lieu of the handler + if ( handler.handler ) { + handleObjIn = handler; + handler = handleObjIn.handler; + selector = handleObjIn.selector; + } + + // Make sure that the handler has a unique ID, used to find/remove it later + if ( !handler.guid ) { + handler.guid = jQuery.guid++; + } + + // Init the element's event structure and main handler, if this is the first + events = elemData.events; + if ( !events ) { + elemData.events = events = {}; + } + eventHandle = elemData.handle; + if ( !eventHandle ) { + elemData.handle = eventHandle = function( e ) { + // Discard the second event of a jQuery.event.trigger() and + // when an event is called after a page has unloaded + return typeof jQuery !== "undefined" && (!e || jQuery.event.triggered !== e.type) ? + jQuery.event.dispatch.apply( eventHandle.elem, arguments ) : + undefined; + }; + // Add elem as a property of the handle fn to prevent a memory leak with IE non-native events + eventHandle.elem = elem; + } + + // Handle multiple events separated by a space + // jQuery(...).bind("mouseover mouseout", fn); + types = jQuery.trim( hoverHack(types) ).split( " " ); + for ( t = 0; t < types.length; t++ ) { + + tns = rtypenamespace.exec( types[t] ) || []; + type = tns[1]; + namespaces = ( tns[2] || "" ).split( "." ).sort(); + + // If event changes its type, use the special event handlers for the changed type + special = jQuery.event.special[ type ] || {}; + + // If selector defined, determine special event api type, otherwise given type + type = ( selector ? special.delegateType : special.bindType ) || type; + + // Update special based on newly reset type + special = jQuery.event.special[ type ] || {}; + + // handleObj is passed to all event handlers + handleObj = jQuery.extend({ + type: type, + origType: tns[1], + data: data, + handler: handler, + guid: handler.guid, + selector: selector, + needsContext: selector && jQuery.expr.match.needsContext.test( selector ), + namespace: namespaces.join(".") + }, handleObjIn ); + + // Init the event handler queue if we're the first + handlers = events[ type ]; + if ( !handlers ) { + handlers = events[ type ] = []; + handlers.delegateCount = 0; + + // Only use addEventListener/attachEvent if the special events handler returns false + if ( !special.setup || special.setup.call( elem, data, namespaces, eventHandle ) === false ) { + // Bind the global event handler to the element + if ( elem.addEventListener ) { + elem.addEventListener( type, eventHandle, false ); + + } else if ( elem.attachEvent ) { + elem.attachEvent( "on" + type, eventHandle ); + } + } + } + + if ( special.add ) { + special.add.call( elem, handleObj ); + + if ( !handleObj.handler.guid ) { + handleObj.handler.guid = handler.guid; + } + } + + // Add to the element's handler list, delegates in front + if ( selector ) { + handlers.splice( handlers.delegateCount++, 0, handleObj ); + } else { + handlers.push( handleObj ); + } + + // Keep track of which events have ever been used, for event optimization + jQuery.event.global[ type ] = true; + } + + // Nullify elem to prevent memory leaks in IE + elem = null; + }, + + global: {}, + + // Detach an event or set of events from an element + remove: function( elem, types, handler, selector, mappedTypes ) { + + var t, tns, type, origType, namespaces, origCount, + j, events, special, eventType, handleObj, + elemData = jQuery.hasData( elem ) && jQuery._data( elem ); + + if ( !elemData || !(events = elemData.events) ) { + return; + } + + // Once for each type.namespace in types; type may be omitted + types = jQuery.trim( hoverHack( types || "" ) ).split(" "); + for ( t = 0; t < types.length; t++ ) { + tns = rtypenamespace.exec( types[t] ) || []; + type = origType = tns[1]; + namespaces = tns[2]; + + // Unbind all events (on this namespace, if provided) for the element + if ( !type ) { + for ( type in events ) { + jQuery.event.remove( elem, type + types[ t ], handler, selector, true ); + } + continue; + } + + special = jQuery.event.special[ type ] || {}; + type = ( selector? special.delegateType : special.bindType ) || type; + eventType = events[ type ] || []; + origCount = eventType.length; + namespaces = namespaces ? new RegExp("(^|\\.)" + namespaces.split(".").sort().join("\\.(?:.*\\.|)") + "(\\.|$)") : null; + + // Remove matching events + for ( j = 0; j < eventType.length; j++ ) { + handleObj = eventType[ j ]; + + if ( ( mappedTypes || origType === handleObj.origType ) && + ( !handler || handler.guid === handleObj.guid ) && + ( !namespaces || namespaces.test( handleObj.namespace ) ) && + ( !selector || selector === handleObj.selector || selector === "**" && handleObj.selector ) ) { + eventType.splice( j--, 1 ); + + if ( handleObj.selector ) { + eventType.delegateCount--; + } + if ( special.remove ) { + special.remove.call( elem, handleObj ); + } + } + } + + // Remove generic event handler if we removed something and no more handlers exist + // (avoids potential for endless recursion during removal of special event handlers) + if ( eventType.length === 0 && origCount !== eventType.length ) { + if ( !special.teardown || special.teardown.call( elem, namespaces, elemData.handle ) === false ) { + jQuery.removeEvent( elem, type, elemData.handle ); + } + + delete events[ type ]; + } + } + + // Remove the expando if it's no longer used + if ( jQuery.isEmptyObject( events ) ) { + delete elemData.handle; + + // removeData also checks for emptiness and clears the expando if empty + // so use it instead of delete + jQuery.removeData( elem, "events", true ); + } + }, + + // Events that are safe to short-circuit if no handlers are attached. + // Native DOM events should not be added, they may have inline handlers. + customEvent: { + "getData": true, + "setData": true, + "changeData": true + }, + + trigger: function( event, data, elem, onlyHandlers ) { + // Don't do events on text and comment nodes + if ( elem && (elem.nodeType === 3 || elem.nodeType === 8) ) { + return; + } + + // Event object or event type + var cache, exclusive, i, cur, old, ontype, special, handle, eventPath, bubbleType, + type = event.type || event, + namespaces = []; + + // focus/blur morphs to focusin/out; ensure we're not firing them right now + if ( rfocusMorph.test( type + jQuery.event.triggered ) ) { + return; + } + + if ( type.indexOf( "!" ) >= 0 ) { + // Exclusive events trigger only for the exact event (no namespaces) + type = type.slice(0, -1); + exclusive = true; + } + + if ( type.indexOf( "." ) >= 0 ) { + // Namespaced trigger; create a regexp to match event type in handle() + namespaces = type.split("."); + type = namespaces.shift(); + namespaces.sort(); + } + + if ( (!elem || jQuery.event.customEvent[ type ]) && !jQuery.event.global[ type ] ) { + // No jQuery handlers for this event type, and it can't have inline handlers + return; + } + + // Caller can pass in an Event, Object, or just an event type string + event = typeof event === "object" ? + // jQuery.Event object + event[ jQuery.expando ] ? event : + // Object literal + new jQuery.Event( type, event ) : + // Just the event type (string) + new jQuery.Event( type ); + + event.type = type; + event.isTrigger = true; + event.exclusive = exclusive; + event.namespace = namespaces.join( "." ); + event.namespace_re = event.namespace? new RegExp("(^|\\.)" + namespaces.join("\\.(?:.*\\.|)") + "(\\.|$)") : null; + ontype = type.indexOf( ":" ) < 0 ? "on" + type : ""; + + // Handle a global trigger + if ( !elem ) { + + // TODO: Stop taunting the data cache; remove global events and always attach to document + cache = jQuery.cache; + for ( i in cache ) { + if ( cache[ i ].events && cache[ i ].events[ type ] ) { + jQuery.event.trigger( event, data, cache[ i ].handle.elem, true ); + } + } + return; + } + + // Clean up the event in case it is being reused + event.result = undefined; + if ( !event.target ) { + event.target = elem; + } + + // Clone any incoming data and prepend the event, creating the handler arg list + data = data != null ? jQuery.makeArray( data ) : []; + data.unshift( event ); + + // Allow special events to draw outside the lines + special = jQuery.event.special[ type ] || {}; + if ( special.trigger && special.trigger.apply( elem, data ) === false ) { + return; + } + + // Determine event propagation path in advance, per W3C events spec (#9951) + // Bubble up to document, then to window; watch for a global ownerDocument var (#9724) + eventPath = [[ elem, special.bindType || type ]]; + if ( !onlyHandlers && !special.noBubble && !jQuery.isWindow( elem ) ) { + + bubbleType = special.delegateType || type; + cur = rfocusMorph.test( bubbleType + type ) ? elem : elem.parentNode; + for ( old = elem; cur; cur = cur.parentNode ) { + eventPath.push([ cur, bubbleType ]); + old = cur; + } + + // Only add window if we got to document (e.g., not plain obj or detached DOM) + if ( old === (elem.ownerDocument || document) ) { + eventPath.push([ old.defaultView || old.parentWindow || window, bubbleType ]); + } + } + + // Fire handlers on the event path + for ( i = 0; i < eventPath.length && !event.isPropagationStopped(); i++ ) { + + cur = eventPath[i][0]; + event.type = eventPath[i][1]; + + handle = ( jQuery._data( cur, "events" ) || {} )[ event.type ] && jQuery._data( cur, "handle" ); + if ( handle ) { + handle.apply( cur, data ); + } + // Note that this is a bare JS function and not a jQuery handler + handle = ontype && cur[ ontype ]; + if ( handle && jQuery.acceptData( cur ) && handle.apply && handle.apply( cur, data ) === false ) { + event.preventDefault(); + } + } + event.type = type; + + // If nobody prevented the default action, do it now + if ( !onlyHandlers && !event.isDefaultPrevented() ) { + + if ( (!special._default || special._default.apply( elem.ownerDocument, data ) === false) && + !(type === "click" && jQuery.nodeName( elem, "a" )) && jQuery.acceptData( elem ) ) { + + // Call a native DOM method on the target with the same name name as the event. + // Can't use an .isFunction() check here because IE6/7 fails that test. + // Don't do default actions on window, that's where global variables be (#6170) + // IE<9 dies on focus/blur to hidden element (#1486) + if ( ontype && elem[ type ] && ((type !== "focus" && type !== "blur") || event.target.offsetWidth !== 0) && !jQuery.isWindow( elem ) ) { + + // Don't re-trigger an onFOO event when we call its FOO() method + old = elem[ ontype ]; + + if ( old ) { + elem[ ontype ] = null; + } + + // Prevent re-triggering of the same event, since we already bubbled it above + jQuery.event.triggered = type; + elem[ type ](); + jQuery.event.triggered = undefined; + + if ( old ) { + elem[ ontype ] = old; + } + } + } + } + + return event.result; + }, + + dispatch: function( event ) { + + // Make a writable jQuery.Event from the native event object + event = jQuery.event.fix( event || window.event ); + + var i, j, cur, ret, selMatch, matched, matches, handleObj, sel, related, + handlers = ( (jQuery._data( this, "events" ) || {} )[ event.type ] || []), + delegateCount = handlers.delegateCount, + args = core_slice.call( arguments ), + run_all = !event.exclusive && !event.namespace, + special = jQuery.event.special[ event.type ] || {}, + handlerQueue = []; + + // Use the fix-ed jQuery.Event rather than the (read-only) native event + args[0] = event; + event.delegateTarget = this; + + // Call the preDispatch hook for the mapped type, and let it bail if desired + if ( special.preDispatch && special.preDispatch.call( this, event ) === false ) { + return; + } + + // Determine handlers that should run if there are delegated events + // Avoid non-left-click bubbling in Firefox (#3861) + if ( delegateCount && !(event.button && event.type === "click") ) { + + for ( cur = event.target; cur != this; cur = cur.parentNode || this ) { + + // Don't process clicks (ONLY) on disabled elements (#6911, #8165, #11382, #11764) + if ( cur.disabled !== true || event.type !== "click" ) { + selMatch = {}; + matches = []; + for ( i = 0; i < delegateCount; i++ ) { + handleObj = handlers[ i ]; + sel = handleObj.selector; + + if ( selMatch[ sel ] === undefined ) { + selMatch[ sel ] = handleObj.needsContext ? + jQuery( sel, this ).index( cur ) >= 0 : + jQuery.find( sel, this, null, [ cur ] ).length; + } + if ( selMatch[ sel ] ) { + matches.push( handleObj ); + } + } + if ( matches.length ) { + handlerQueue.push({ elem: cur, matches: matches }); + } + } + } + } + + // Add the remaining (directly-bound) handlers + if ( handlers.length > delegateCount ) { + handlerQueue.push({ elem: this, matches: handlers.slice( delegateCount ) }); + } + + // Run delegates first; they may want to stop propagation beneath us + for ( i = 0; i < handlerQueue.length && !event.isPropagationStopped(); i++ ) { + matched = handlerQueue[ i ]; + event.currentTarget = matched.elem; + + for ( j = 0; j < matched.matches.length && !event.isImmediatePropagationStopped(); j++ ) { + handleObj = matched.matches[ j ]; + + // Triggered event must either 1) be non-exclusive and have no namespace, or + // 2) have namespace(s) a subset or equal to those in the bound event (both can have no namespace). + if ( run_all || (!event.namespace && !handleObj.namespace) || event.namespace_re && event.namespace_re.test( handleObj.namespace ) ) { + + event.data = handleObj.data; + event.handleObj = handleObj; + + ret = ( (jQuery.event.special[ handleObj.origType ] || {}).handle || handleObj.handler ) + .apply( matched.elem, args ); + + if ( ret !== undefined ) { + event.result = ret; + if ( ret === false ) { + event.preventDefault(); + event.stopPropagation(); + } + } + } + } + } + + // Call the postDispatch hook for the mapped type + if ( special.postDispatch ) { + special.postDispatch.call( this, event ); + } + + return event.result; + }, + + // Includes some event props shared by KeyEvent and MouseEvent + // *** attrChange attrName relatedNode srcElement are not normalized, non-W3C, deprecated, will be removed in 1.8 *** + props: "attrChange attrName relatedNode srcElement altKey bubbles cancelable ctrlKey currentTarget eventPhase metaKey relatedTarget shiftKey target timeStamp view which".split(" "), + + fixHooks: {}, + + keyHooks: { + props: "char charCode key keyCode".split(" "), + filter: function( event, original ) { + + // Add which for key events + if ( event.which == null ) { + event.which = original.charCode != null ? original.charCode : original.keyCode; + } + + return event; + } + }, + + mouseHooks: { + props: "button buttons clientX clientY fromElement offsetX offsetY pageX pageY screenX screenY toElement".split(" "), + filter: function( event, original ) { + var eventDoc, doc, body, + button = original.button, + fromElement = original.fromElement; + + // Calculate pageX/Y if missing and clientX/Y available + if ( event.pageX == null && original.clientX != null ) { + eventDoc = event.target.ownerDocument || document; + doc = eventDoc.documentElement; + body = eventDoc.body; + + event.pageX = original.clientX + ( doc && doc.scrollLeft || body && body.scrollLeft || 0 ) - ( doc && doc.clientLeft || body && body.clientLeft || 0 ); + event.pageY = original.clientY + ( doc && doc.scrollTop || body && body.scrollTop || 0 ) - ( doc && doc.clientTop || body && body.clientTop || 0 ); + } + + // Add relatedTarget, if necessary + if ( !event.relatedTarget && fromElement ) { + event.relatedTarget = fromElement === event.target ? original.toElement : fromElement; + } + + // Add which for click: 1 === left; 2 === middle; 3 === right + // Note: button is not normalized, so don't use it + if ( !event.which && button !== undefined ) { + event.which = ( button & 1 ? 1 : ( button & 2 ? 3 : ( button & 4 ? 2 : 0 ) ) ); + } + + return event; + } + }, + + fix: function( event ) { + if ( event[ jQuery.expando ] ) { + return event; + } + + // Create a writable copy of the event object and normalize some properties + var i, prop, + originalEvent = event, + fixHook = jQuery.event.fixHooks[ event.type ] || {}, + copy = fixHook.props ? this.props.concat( fixHook.props ) : this.props; + + event = jQuery.Event( originalEvent ); + + for ( i = copy.length; i; ) { + prop = copy[ --i ]; + event[ prop ] = originalEvent[ prop ]; + } + + // Fix target property, if necessary (#1925, IE 6/7/8 & Safari2) + if ( !event.target ) { + event.target = originalEvent.srcElement || document; + } + + // Target should not be a text node (#504, Safari) + if ( event.target.nodeType === 3 ) { + event.target = event.target.parentNode; + } + + // For mouse/key events, metaKey==false if it's undefined (#3368, #11328; IE6/7/8) + event.metaKey = !!event.metaKey; + + return fixHook.filter? fixHook.filter( event, originalEvent ) : event; + }, + + special: { + load: { + // Prevent triggered image.load events from bubbling to window.load + noBubble: true + }, + + focus: { + delegateType: "focusin" + }, + blur: { + delegateType: "focusout" + }, + + beforeunload: { + setup: function( data, namespaces, eventHandle ) { + // We only want to do this special case on windows + if ( jQuery.isWindow( this ) ) { + this.onbeforeunload = eventHandle; + } + }, + + teardown: function( namespaces, eventHandle ) { + if ( this.onbeforeunload === eventHandle ) { + this.onbeforeunload = null; + } + } + } + }, + + simulate: function( type, elem, event, bubble ) { + // Piggyback on a donor event to simulate a different one. + // Fake originalEvent to avoid donor's stopPropagation, but if the + // simulated event prevents default then we do the same on the donor. + var e = jQuery.extend( + new jQuery.Event(), + event, + { type: type, + isSimulated: true, + originalEvent: {} + } + ); + if ( bubble ) { + jQuery.event.trigger( e, null, elem ); + } else { + jQuery.event.dispatch.call( elem, e ); + } + if ( e.isDefaultPrevented() ) { + event.preventDefault(); + } + } +}; + +// Some plugins are using, but it's undocumented/deprecated and will be removed. +// The 1.7 special event interface should provide all the hooks needed now. +jQuery.event.handle = jQuery.event.dispatch; + +jQuery.removeEvent = document.removeEventListener ? + function( elem, type, handle ) { + if ( elem.removeEventListener ) { + elem.removeEventListener( type, handle, false ); + } + } : + function( elem, type, handle ) { + var name = "on" + type; + + if ( elem.detachEvent ) { + + // #8545, #7054, preventing memory leaks for custom events in IE6-8 + // detachEvent needed property on element, by name of that event, to properly expose it to GC + if ( typeof elem[ name ] === "undefined" ) { + elem[ name ] = null; + } + + elem.detachEvent( name, handle ); + } + }; + +jQuery.Event = function( src, props ) { + // Allow instantiation without the 'new' keyword + if ( !(this instanceof jQuery.Event) ) { + return new jQuery.Event( src, props ); + } + + // Event object + if ( src && src.type ) { + this.originalEvent = src; + this.type = src.type; + + // Events bubbling up the document may have been marked as prevented + // by a handler lower down the tree; reflect the correct value. + this.isDefaultPrevented = ( src.defaultPrevented || src.returnValue === false || + src.getPreventDefault && src.getPreventDefault() ) ? returnTrue : returnFalse; + + // Event type + } else { + this.type = src; + } + + // Put explicitly provided properties onto the event object + if ( props ) { + jQuery.extend( this, props ); + } + + // Create a timestamp if incoming event doesn't have one + this.timeStamp = src && src.timeStamp || jQuery.now(); + + // Mark it as fixed + this[ jQuery.expando ] = true; +}; + +function returnFalse() { + return false; +} +function returnTrue() { + return true; +} + +// jQuery.Event is based on DOM3 Events as specified by the ECMAScript Language Binding +// http://www.w3.org/TR/2003/WD-DOM-Level-3-Events-20030331/ecma-script-binding.html +jQuery.Event.prototype = { + preventDefault: function() { + this.isDefaultPrevented = returnTrue; + + var e = this.originalEvent; + if ( !e ) { + return; + } + + // if preventDefault exists run it on the original event + if ( e.preventDefault ) { + e.preventDefault(); + + // otherwise set the returnValue property of the original event to false (IE) + } else { + e.returnValue = false; + } + }, + stopPropagation: function() { + this.isPropagationStopped = returnTrue; + + var e = this.originalEvent; + if ( !e ) { + return; + } + // if stopPropagation exists run it on the original event + if ( e.stopPropagation ) { + e.stopPropagation(); + } + // otherwise set the cancelBubble property of the original event to true (IE) + e.cancelBubble = true; + }, + stopImmediatePropagation: function() { + this.isImmediatePropagationStopped = returnTrue; + this.stopPropagation(); + }, + isDefaultPrevented: returnFalse, + isPropagationStopped: returnFalse, + isImmediatePropagationStopped: returnFalse +}; + +// Create mouseenter/leave events using mouseover/out and event-time checks +jQuery.each({ + mouseenter: "mouseover", + mouseleave: "mouseout" +}, function( orig, fix ) { + jQuery.event.special[ orig ] = { + delegateType: fix, + bindType: fix, + + handle: function( event ) { + var ret, + target = this, + related = event.relatedTarget, + handleObj = event.handleObj, + selector = handleObj.selector; + + // For mousenter/leave call the handler if related is outside the target. + // NB: No relatedTarget if the mouse left/entered the browser window + if ( !related || (related !== target && !jQuery.contains( target, related )) ) { + event.type = handleObj.origType; + ret = handleObj.handler.apply( this, arguments ); + event.type = fix; + } + return ret; + } + }; +}); + +// IE submit delegation +if ( !jQuery.support.submitBubbles ) { + + jQuery.event.special.submit = { + setup: function() { + // Only need this for delegated form submit events + if ( jQuery.nodeName( this, "form" ) ) { + return false; + } + + // Lazy-add a submit handler when a descendant form may potentially be submitted + jQuery.event.add( this, "click._submit keypress._submit", function( e ) { + // Node name check avoids a VML-related crash in IE (#9807) + var elem = e.target, + form = jQuery.nodeName( elem, "input" ) || jQuery.nodeName( elem, "button" ) ? elem.form : undefined; + if ( form && !jQuery._data( form, "_submit_attached" ) ) { + jQuery.event.add( form, "submit._submit", function( event ) { + event._submit_bubble = true; + }); + jQuery._data( form, "_submit_attached", true ); + } + }); + // return undefined since we don't need an event listener + }, + + postDispatch: function( event ) { + // If form was submitted by the user, bubble the event up the tree + if ( event._submit_bubble ) { + delete event._submit_bubble; + if ( this.parentNode && !event.isTrigger ) { + jQuery.event.simulate( "submit", this.parentNode, event, true ); + } + } + }, + + teardown: function() { + // Only need this for delegated form submit events + if ( jQuery.nodeName( this, "form" ) ) { + return false; + } + + // Remove delegated handlers; cleanData eventually reaps submit handlers attached above + jQuery.event.remove( this, "._submit" ); + } + }; +} + +// IE change delegation and checkbox/radio fix +if ( !jQuery.support.changeBubbles ) { + + jQuery.event.special.change = { + + setup: function() { + + if ( rformElems.test( this.nodeName ) ) { + // IE doesn't fire change on a check/radio until blur; trigger it on click + // after a propertychange. Eat the blur-change in special.change.handle. + // This still fires onchange a second time for check/radio after blur. + if ( this.type === "checkbox" || this.type === "radio" ) { + jQuery.event.add( this, "propertychange._change", function( event ) { + if ( event.originalEvent.propertyName === "checked" ) { + this._just_changed = true; + } + }); + jQuery.event.add( this, "click._change", function( event ) { + if ( this._just_changed && !event.isTrigger ) { + this._just_changed = false; + } + // Allow triggered, simulated change events (#11500) + jQuery.event.simulate( "change", this, event, true ); + }); + } + return false; + } + // Delegated event; lazy-add a change handler on descendant inputs + jQuery.event.add( this, "beforeactivate._change", function( e ) { + var elem = e.target; + + if ( rformElems.test( elem.nodeName ) && !jQuery._data( elem, "_change_attached" ) ) { + jQuery.event.add( elem, "change._change", function( event ) { + if ( this.parentNode && !event.isSimulated && !event.isTrigger ) { + jQuery.event.simulate( "change", this.parentNode, event, true ); + } + }); + jQuery._data( elem, "_change_attached", true ); + } + }); + }, + + handle: function( event ) { + var elem = event.target; + + // Swallow native change events from checkbox/radio, we already triggered them above + if ( this !== elem || event.isSimulated || event.isTrigger || (elem.type !== "radio" && elem.type !== "checkbox") ) { + return event.handleObj.handler.apply( this, arguments ); + } + }, + + teardown: function() { + jQuery.event.remove( this, "._change" ); + + return !rformElems.test( this.nodeName ); + } + }; +} + +// Create "bubbling" focus and blur events +if ( !jQuery.support.focusinBubbles ) { + jQuery.each({ focus: "focusin", blur: "focusout" }, function( orig, fix ) { + + // Attach a single capturing handler while someone wants focusin/focusout + var attaches = 0, + handler = function( event ) { + jQuery.event.simulate( fix, event.target, jQuery.event.fix( event ), true ); + }; + + jQuery.event.special[ fix ] = { + setup: function() { + if ( attaches++ === 0 ) { + document.addEventListener( orig, handler, true ); + } + }, + teardown: function() { + if ( --attaches === 0 ) { + document.removeEventListener( orig, handler, true ); + } + } + }; + }); +} + +jQuery.fn.extend({ + + on: function( types, selector, data, fn, /*INTERNAL*/ one ) { + var origFn, type; + + // Types can be a map of types/handlers + if ( typeof types === "object" ) { + // ( types-Object, selector, data ) + if ( typeof selector !== "string" ) { // && selector != null + // ( types-Object, data ) + data = data || selector; + selector = undefined; + } + for ( type in types ) { + this.on( type, selector, data, types[ type ], one ); + } + return this; + } + + if ( data == null && fn == null ) { + // ( types, fn ) + fn = selector; + data = selector = undefined; + } else if ( fn == null ) { + if ( typeof selector === "string" ) { + // ( types, selector, fn ) + fn = data; + data = undefined; + } else { + // ( types, data, fn ) + fn = data; + data = selector; + selector = undefined; + } + } + if ( fn === false ) { + fn = returnFalse; + } else if ( !fn ) { + return this; + } + + if ( one === 1 ) { + origFn = fn; + fn = function( event ) { + // Can use an empty set, since event contains the info + jQuery().off( event ); + return origFn.apply( this, arguments ); + }; + // Use same guid so caller can remove using origFn + fn.guid = origFn.guid || ( origFn.guid = jQuery.guid++ ); + } + return this.each( function() { + jQuery.event.add( this, types, fn, data, selector ); + }); + }, + one: function( types, selector, data, fn ) { + return this.on( types, selector, data, fn, 1 ); + }, + off: function( types, selector, fn ) { + var handleObj, type; + if ( types && types.preventDefault && types.handleObj ) { + // ( event ) dispatched jQuery.Event + handleObj = types.handleObj; + jQuery( types.delegateTarget ).off( + handleObj.namespace ? handleObj.origType + "." + handleObj.namespace : handleObj.origType, + handleObj.selector, + handleObj.handler + ); + return this; + } + if ( typeof types === "object" ) { + // ( types-object [, selector] ) + for ( type in types ) { + this.off( type, selector, types[ type ] ); + } + return this; + } + if ( selector === false || typeof selector === "function" ) { + // ( types [, fn] ) + fn = selector; + selector = undefined; + } + if ( fn === false ) { + fn = returnFalse; + } + return this.each(function() { + jQuery.event.remove( this, types, fn, selector ); + }); + }, + + bind: function( types, data, fn ) { + return this.on( types, null, data, fn ); + }, + unbind: function( types, fn ) { + return this.off( types, null, fn ); + }, + + live: function( types, data, fn ) { + jQuery( this.context ).on( types, this.selector, data, fn ); + return this; + }, + die: function( types, fn ) { + jQuery( this.context ).off( types, this.selector || "**", fn ); + return this; + }, + + delegate: function( selector, types, data, fn ) { + return this.on( types, selector, data, fn ); + }, + undelegate: function( selector, types, fn ) { + // ( namespace ) or ( selector, types [, fn] ) + return arguments.length === 1 ? this.off( selector, "**" ) : this.off( types, selector || "**", fn ); + }, + + trigger: function( type, data ) { + return this.each(function() { + jQuery.event.trigger( type, data, this ); + }); + }, + triggerHandler: function( type, data ) { + if ( this[0] ) { + return jQuery.event.trigger( type, data, this[0], true ); + } + }, + + toggle: function( fn ) { + // Save reference to arguments for access in closure + var args = arguments, + guid = fn.guid || jQuery.guid++, + i = 0, + toggler = function( event ) { + // Figure out which function to execute + var lastToggle = ( jQuery._data( this, "lastToggle" + fn.guid ) || 0 ) % i; + jQuery._data( this, "lastToggle" + fn.guid, lastToggle + 1 ); + + // Make sure that clicks stop + event.preventDefault(); + + // and execute the function + return args[ lastToggle ].apply( this, arguments ) || false; + }; + + // link all the functions, so any of them can unbind this click handler + toggler.guid = guid; + while ( i < args.length ) { + args[ i++ ].guid = guid; + } + + return this.click( toggler ); + }, + + hover: function( fnOver, fnOut ) { + return this.mouseenter( fnOver ).mouseleave( fnOut || fnOver ); + } +}); + +jQuery.each( ("blur focus focusin focusout load resize scroll unload click dblclick " + + "mousedown mouseup mousemove mouseover mouseout mouseenter mouseleave " + + "change select submit keydown keypress keyup error contextmenu").split(" "), function( i, name ) { + + // Handle event binding + jQuery.fn[ name ] = function( data, fn ) { + if ( fn == null ) { + fn = data; + data = null; + } + + return arguments.length > 0 ? + this.on( name, null, data, fn ) : + this.trigger( name ); + }; + + if ( rkeyEvent.test( name ) ) { + jQuery.event.fixHooks[ name ] = jQuery.event.keyHooks; + } + + if ( rmouseEvent.test( name ) ) { + jQuery.event.fixHooks[ name ] = jQuery.event.mouseHooks; + } +}); +/*! + * Sizzle CSS Selector Engine + * Copyright 2012 jQuery Foundation and other contributors + * Released under the MIT license + * http://sizzlejs.com/ + */ +(function( window, undefined ) { + +var cachedruns, + assertGetIdNotName, + Expr, + getText, + isXML, + contains, + compile, + sortOrder, + hasDuplicate, + outermostContext, + + baseHasDuplicate = true, + strundefined = "undefined", + + expando = ( "sizcache" + Math.random() ).replace( ".", "" ), + + Token = String, + document = window.document, + docElem = document.documentElement, + dirruns = 0, + done = 0, + pop = [].pop, + push = [].push, + slice = [].slice, + // Use a stripped-down indexOf if a native one is unavailable + indexOf = [].indexOf || function( elem ) { + var i = 0, + len = this.length; + for ( ; i < len; i++ ) { + if ( this[i] === elem ) { + return i; + } + } + return -1; + }, + + // Augment a function for special use by Sizzle + markFunction = function( fn, value ) { + fn[ expando ] = value == null || value; + return fn; + }, + + createCache = function() { + var cache = {}, + keys = []; + + return markFunction(function( key, value ) { + // Only keep the most recent entries + if ( keys.push( key ) > Expr.cacheLength ) { + delete cache[ keys.shift() ]; + } + + // Retrieve with (key + " ") to avoid collision with native Object.prototype properties (see Issue #157) + return (cache[ key + " " ] = value); + }, cache ); + }, + + classCache = createCache(), + tokenCache = createCache(), + compilerCache = createCache(), + + // Regex + + // Whitespace characters http://www.w3.org/TR/css3-selectors/#whitespace + whitespace = "[\\x20\\t\\r\\n\\f]", + // http://www.w3.org/TR/css3-syntax/#characters + characterEncoding = "(?:\\\\.|[-\\w]|[^\\x00-\\xa0])+", + + // Loosely modeled on CSS identifier characters + // An unquoted value should be a CSS identifier (http://www.w3.org/TR/css3-selectors/#attribute-selectors) + // Proper syntax: http://www.w3.org/TR/CSS21/syndata.html#value-def-identifier + identifier = characterEncoding.replace( "w", "w#" ), + + // Acceptable operators http://www.w3.org/TR/selectors/#attribute-selectors + operators = "([*^$|!~]?=)", + attributes = "\\[" + whitespace + "*(" + characterEncoding + ")" + whitespace + + "*(?:" + operators + whitespace + "*(?:(['\"])((?:\\\\.|[^\\\\])*?)\\3|(" + identifier + ")|)|)" + whitespace + "*\\]", + + // Prefer arguments not in parens/brackets, + // then attribute selectors and non-pseudos (denoted by :), + // then anything else + // These preferences are here to reduce the number of selectors + // needing tokenize in the PSEUDO preFilter + pseudos = ":(" + characterEncoding + ")(?:\\((?:(['\"])((?:\\\\.|[^\\\\])*?)\\2|([^()[\\]]*|(?:(?:" + attributes + ")|[^:]|\\\\.)*|.*))\\)|)", + + // For matchExpr.POS and matchExpr.needsContext + pos = ":(even|odd|eq|gt|lt|nth|first|last)(?:\\(" + whitespace + + "*((?:-\\d)?\\d*)" + whitespace + "*\\)|)(?=[^-]|$)", + + // Leading and non-escaped trailing whitespace, capturing some non-whitespace characters preceding the latter + rtrim = new RegExp( "^" + whitespace + "+|((?:^|[^\\\\])(?:\\\\.)*)" + whitespace + "+$", "g" ), + + rcomma = new RegExp( "^" + whitespace + "*," + whitespace + "*" ), + rcombinators = new RegExp( "^" + whitespace + "*([\\x20\\t\\r\\n\\f>+~])" + whitespace + "*" ), + rpseudo = new RegExp( pseudos ), + + // Easily-parseable/retrievable ID or TAG or CLASS selectors + rquickExpr = /^(?:#([\w\-]+)|(\w+)|\.([\w\-]+))$/, + + rnot = /^:not/, + rsibling = /[\x20\t\r\n\f]*[+~]/, + rendsWithNot = /:not\($/, + + rheader = /h\d/i, + rinputs = /input|select|textarea|button/i, + + rbackslash = /\\(?!\\)/g, + + matchExpr = { + "ID": new RegExp( "^#(" + characterEncoding + ")" ), + "CLASS": new RegExp( "^\\.(" + characterEncoding + ")" ), + "NAME": new RegExp( "^\\[name=['\"]?(" + characterEncoding + ")['\"]?\\]" ), + "TAG": new RegExp( "^(" + characterEncoding.replace( "w", "w*" ) + ")" ), + "ATTR": new RegExp( "^" + attributes ), + "PSEUDO": new RegExp( "^" + pseudos ), + "POS": new RegExp( pos, "i" ), + "CHILD": new RegExp( "^:(only|nth|first|last)-child(?:\\(" + whitespace + + "*(even|odd|(([+-]|)(\\d*)n|)" + whitespace + "*(?:([+-]|)" + whitespace + + "*(\\d+)|))" + whitespace + "*\\)|)", "i" ), + // For use in libraries implementing .is() + "needsContext": new RegExp( "^" + whitespace + "*[>+~]|" + pos, "i" ) + }, + + // Support + + // Used for testing something on an element + assert = function( fn ) { + var div = document.createElement("div"); + + try { + return fn( div ); + } catch (e) { + return false; + } finally { + // release memory in IE + div = null; + } + }, + + // Check if getElementsByTagName("*") returns only elements + assertTagNameNoComments = assert(function( div ) { + div.appendChild( document.createComment("") ); + return !div.getElementsByTagName("*").length; + }), + + // Check if getAttribute returns normalized href attributes + assertHrefNotNormalized = assert(function( div ) { + div.innerHTML = ""; + return div.firstChild && typeof div.firstChild.getAttribute !== strundefined && + div.firstChild.getAttribute("href") === "#"; + }), + + // Check if attributes should be retrieved by attribute nodes + assertAttributes = assert(function( div ) { + div.innerHTML = ""; + var type = typeof div.lastChild.getAttribute("multiple"); + // IE8 returns a string for some attributes even when not present + return type !== "boolean" && type !== "string"; + }), + + // Check if getElementsByClassName can be trusted + assertUsableClassName = assert(function( div ) { + // Opera can't find a second classname (in 9.6) + div.innerHTML = ""; + if ( !div.getElementsByClassName || !div.getElementsByClassName("e").length ) { + return false; + } + + // Safari 3.2 caches class attributes and doesn't catch changes + div.lastChild.className = "e"; + return div.getElementsByClassName("e").length === 2; + }), + + // Check if getElementById returns elements by name + // Check if getElementsByName privileges form controls or returns elements by ID + assertUsableName = assert(function( div ) { + // Inject content + div.id = expando + 0; + div.innerHTML = "
"; + docElem.insertBefore( div, docElem.firstChild ); + + // Test + var pass = document.getElementsByName && + // buggy browsers will return fewer than the correct 2 + document.getElementsByName( expando ).length === 2 + + // buggy browsers will return more than the correct 0 + document.getElementsByName( expando + 0 ).length; + assertGetIdNotName = !document.getElementById( expando ); + + // Cleanup + docElem.removeChild( div ); + + return pass; + }); + +// If slice is not available, provide a backup +try { + slice.call( docElem.childNodes, 0 )[0].nodeType; +} catch ( e ) { + slice = function( i ) { + var elem, + results = []; + for ( ; (elem = this[i]); i++ ) { + results.push( elem ); + } + return results; + }; +} + +function Sizzle( selector, context, results, seed ) { + results = results || []; + context = context || document; + var match, elem, xml, m, + nodeType = context.nodeType; + + if ( !selector || typeof selector !== "string" ) { + return results; + } + + if ( nodeType !== 1 && nodeType !== 9 ) { + return []; + } + + xml = isXML( context ); + + if ( !xml && !seed ) { + if ( (match = rquickExpr.exec( selector )) ) { + // Speed-up: Sizzle("#ID") + if ( (m = match[1]) ) { + if ( nodeType === 9 ) { + elem = context.getElementById( m ); + // Check parentNode to catch when Blackberry 4.6 returns + // nodes that are no longer in the document #6963 + if ( elem && elem.parentNode ) { + // Handle the case where IE, Opera, and Webkit return items + // by name instead of ID + if ( elem.id === m ) { + results.push( elem ); + return results; + } + } else { + return results; + } + } else { + // Context is not a document + if ( context.ownerDocument && (elem = context.ownerDocument.getElementById( m )) && + contains( context, elem ) && elem.id === m ) { + results.push( elem ); + return results; + } + } + + // Speed-up: Sizzle("TAG") + } else if ( match[2] ) { + push.apply( results, slice.call(context.getElementsByTagName( selector ), 0) ); + return results; + + // Speed-up: Sizzle(".CLASS") + } else if ( (m = match[3]) && assertUsableClassName && context.getElementsByClassName ) { + push.apply( results, slice.call(context.getElementsByClassName( m ), 0) ); + return results; + } + } + } + + // All others + return select( selector.replace( rtrim, "$1" ), context, results, seed, xml ); +} + +Sizzle.matches = function( expr, elements ) { + return Sizzle( expr, null, null, elements ); +}; + +Sizzle.matchesSelector = function( elem, expr ) { + return Sizzle( expr, null, null, [ elem ] ).length > 0; +}; + +// Returns a function to use in pseudos for input types +function createInputPseudo( type ) { + return function( elem ) { + var name = elem.nodeName.toLowerCase(); + return name === "input" && elem.type === type; + }; +} + +// Returns a function to use in pseudos for buttons +function createButtonPseudo( type ) { + return function( elem ) { + var name = elem.nodeName.toLowerCase(); + return (name === "input" || name === "button") && elem.type === type; + }; +} + +// Returns a function to use in pseudos for positionals +function createPositionalPseudo( fn ) { + return markFunction(function( argument ) { + argument = +argument; + return markFunction(function( seed, matches ) { + var j, + matchIndexes = fn( [], seed.length, argument ), + i = matchIndexes.length; + + // Match elements found at the specified indexes + while ( i-- ) { + if ( seed[ (j = matchIndexes[i]) ] ) { + seed[j] = !(matches[j] = seed[j]); + } + } + }); + }); +} + +/** + * Utility function for retrieving the text value of an array of DOM nodes + * @param {Array|Element} elem + */ +getText = Sizzle.getText = function( elem ) { + var node, + ret = "", + i = 0, + nodeType = elem.nodeType; + + if ( nodeType ) { + if ( nodeType === 1 || nodeType === 9 || nodeType === 11 ) { + // Use textContent for elements + // innerText usage removed for consistency of new lines (see #11153) + if ( typeof elem.textContent === "string" ) { + return elem.textContent; + } else { + // Traverse its children + for ( elem = elem.firstChild; elem; elem = elem.nextSibling ) { + ret += getText( elem ); + } + } + } else if ( nodeType === 3 || nodeType === 4 ) { + return elem.nodeValue; + } + // Do not include comment or processing instruction nodes + } else { + + // If no nodeType, this is expected to be an array + for ( ; (node = elem[i]); i++ ) { + // Do not traverse comment nodes + ret += getText( node ); + } + } + return ret; +}; + +isXML = Sizzle.isXML = function( elem ) { + // documentElement is verified for cases where it doesn't yet exist + // (such as loading iframes in IE - #4833) + var documentElement = elem && (elem.ownerDocument || elem).documentElement; + return documentElement ? documentElement.nodeName !== "HTML" : false; +}; + +// Element contains another +contains = Sizzle.contains = docElem.contains ? + function( a, b ) { + var adown = a.nodeType === 9 ? a.documentElement : a, + bup = b && b.parentNode; + return a === bup || !!( bup && bup.nodeType === 1 && adown.contains && adown.contains(bup) ); + } : + docElem.compareDocumentPosition ? + function( a, b ) { + return b && !!( a.compareDocumentPosition( b ) & 16 ); + } : + function( a, b ) { + while ( (b = b.parentNode) ) { + if ( b === a ) { + return true; + } + } + return false; + }; + +Sizzle.attr = function( elem, name ) { + var val, + xml = isXML( elem ); + + if ( !xml ) { + name = name.toLowerCase(); + } + if ( (val = Expr.attrHandle[ name ]) ) { + return val( elem ); + } + if ( xml || assertAttributes ) { + return elem.getAttribute( name ); + } + val = elem.getAttributeNode( name ); + return val ? + typeof elem[ name ] === "boolean" ? + elem[ name ] ? name : null : + val.specified ? val.value : null : + null; +}; + +Expr = Sizzle.selectors = { + + // Can be adjusted by the user + cacheLength: 50, + + createPseudo: markFunction, + + match: matchExpr, + + // IE6/7 return a modified href + attrHandle: assertHrefNotNormalized ? + {} : + { + "href": function( elem ) { + return elem.getAttribute( "href", 2 ); + }, + "type": function( elem ) { + return elem.getAttribute("type"); + } + }, + + find: { + "ID": assertGetIdNotName ? + function( id, context, xml ) { + if ( typeof context.getElementById !== strundefined && !xml ) { + var m = context.getElementById( id ); + // Check parentNode to catch when Blackberry 4.6 returns + // nodes that are no longer in the document #6963 + return m && m.parentNode ? [m] : []; + } + } : + function( id, context, xml ) { + if ( typeof context.getElementById !== strundefined && !xml ) { + var m = context.getElementById( id ); + + return m ? + m.id === id || typeof m.getAttributeNode !== strundefined && m.getAttributeNode("id").value === id ? + [m] : + undefined : + []; + } + }, + + "TAG": assertTagNameNoComments ? + function( tag, context ) { + if ( typeof context.getElementsByTagName !== strundefined ) { + return context.getElementsByTagName( tag ); + } + } : + function( tag, context ) { + var results = context.getElementsByTagName( tag ); + + // Filter out possible comments + if ( tag === "*" ) { + var elem, + tmp = [], + i = 0; + + for ( ; (elem = results[i]); i++ ) { + if ( elem.nodeType === 1 ) { + tmp.push( elem ); + } + } + + return tmp; + } + return results; + }, + + "NAME": assertUsableName && function( tag, context ) { + if ( typeof context.getElementsByName !== strundefined ) { + return context.getElementsByName( name ); + } + }, + + "CLASS": assertUsableClassName && function( className, context, xml ) { + if ( typeof context.getElementsByClassName !== strundefined && !xml ) { + return context.getElementsByClassName( className ); + } + } + }, + + relative: { + ">": { dir: "parentNode", first: true }, + " ": { dir: "parentNode" }, + "+": { dir: "previousSibling", first: true }, + "~": { dir: "previousSibling" } + }, + + preFilter: { + "ATTR": function( match ) { + match[1] = match[1].replace( rbackslash, "" ); + + // Move the given value to match[3] whether quoted or unquoted + match[3] = ( match[4] || match[5] || "" ).replace( rbackslash, "" ); + + if ( match[2] === "~=" ) { + match[3] = " " + match[3] + " "; + } + + return match.slice( 0, 4 ); + }, + + "CHILD": function( match ) { + /* matches from matchExpr["CHILD"] + 1 type (only|nth|...) + 2 argument (even|odd|\d*|\d*n([+-]\d+)?|...) + 3 xn-component of xn+y argument ([+-]?\d*n|) + 4 sign of xn-component + 5 x of xn-component + 6 sign of y-component + 7 y of y-component + */ + match[1] = match[1].toLowerCase(); + + if ( match[1] === "nth" ) { + // nth-child requires argument + if ( !match[2] ) { + Sizzle.error( match[0] ); + } + + // numeric x and y parameters for Expr.filter.CHILD + // remember that false/true cast respectively to 0/1 + match[3] = +( match[3] ? match[4] + (match[5] || 1) : 2 * ( match[2] === "even" || match[2] === "odd" ) ); + match[4] = +( ( match[6] + match[7] ) || match[2] === "odd" ); + + // other types prohibit arguments + } else if ( match[2] ) { + Sizzle.error( match[0] ); + } + + return match; + }, + + "PSEUDO": function( match ) { + var unquoted, excess; + if ( matchExpr["CHILD"].test( match[0] ) ) { + return null; + } + + if ( match[3] ) { + match[2] = match[3]; + } else if ( (unquoted = match[4]) ) { + // Only check arguments that contain a pseudo + if ( rpseudo.test(unquoted) && + // Get excess from tokenize (recursively) + (excess = tokenize( unquoted, true )) && + // advance to the next closing parenthesis + (excess = unquoted.indexOf( ")", unquoted.length - excess ) - unquoted.length) ) { + + // excess is a negative index + unquoted = unquoted.slice( 0, excess ); + match[0] = match[0].slice( 0, excess ); + } + match[2] = unquoted; + } + + // Return only captures needed by the pseudo filter method (type and argument) + return match.slice( 0, 3 ); + } + }, + + filter: { + "ID": assertGetIdNotName ? + function( id ) { + id = id.replace( rbackslash, "" ); + return function( elem ) { + return elem.getAttribute("id") === id; + }; + } : + function( id ) { + id = id.replace( rbackslash, "" ); + return function( elem ) { + var node = typeof elem.getAttributeNode !== strundefined && elem.getAttributeNode("id"); + return node && node.value === id; + }; + }, + + "TAG": function( nodeName ) { + if ( nodeName === "*" ) { + return function() { return true; }; + } + nodeName = nodeName.replace( rbackslash, "" ).toLowerCase(); + + return function( elem ) { + return elem.nodeName && elem.nodeName.toLowerCase() === nodeName; + }; + }, + + "CLASS": function( className ) { + var pattern = classCache[ expando ][ className + " " ]; + + return pattern || + (pattern = new RegExp( "(^|" + whitespace + ")" + className + "(" + whitespace + "|$)" )) && + classCache( className, function( elem ) { + return pattern.test( elem.className || (typeof elem.getAttribute !== strundefined && elem.getAttribute("class")) || "" ); + }); + }, + + "ATTR": function( name, operator, check ) { + return function( elem, context ) { + var result = Sizzle.attr( elem, name ); + + if ( result == null ) { + return operator === "!="; + } + if ( !operator ) { + return true; + } + + result += ""; + + return operator === "=" ? result === check : + operator === "!=" ? result !== check : + operator === "^=" ? check && result.indexOf( check ) === 0 : + operator === "*=" ? check && result.indexOf( check ) > -1 : + operator === "$=" ? check && result.substr( result.length - check.length ) === check : + operator === "~=" ? ( " " + result + " " ).indexOf( check ) > -1 : + operator === "|=" ? result === check || result.substr( 0, check.length + 1 ) === check + "-" : + false; + }; + }, + + "CHILD": function( type, argument, first, last ) { + + if ( type === "nth" ) { + return function( elem ) { + var node, diff, + parent = elem.parentNode; + + if ( first === 1 && last === 0 ) { + return true; + } + + if ( parent ) { + diff = 0; + for ( node = parent.firstChild; node; node = node.nextSibling ) { + if ( node.nodeType === 1 ) { + diff++; + if ( elem === node ) { + break; + } + } + } + } + + // Incorporate the offset (or cast to NaN), then check against cycle size + diff -= last; + return diff === first || ( diff % first === 0 && diff / first >= 0 ); + }; + } + + return function( elem ) { + var node = elem; + + switch ( type ) { + case "only": + case "first": + while ( (node = node.previousSibling) ) { + if ( node.nodeType === 1 ) { + return false; + } + } + + if ( type === "first" ) { + return true; + } + + node = elem; + + /* falls through */ + case "last": + while ( (node = node.nextSibling) ) { + if ( node.nodeType === 1 ) { + return false; + } + } + + return true; + } + }; + }, + + "PSEUDO": function( pseudo, argument ) { + // pseudo-class names are case-insensitive + // http://www.w3.org/TR/selectors/#pseudo-classes + // Prioritize by case sensitivity in case custom pseudos are added with uppercase letters + // Remember that setFilters inherits from pseudos + var args, + fn = Expr.pseudos[ pseudo ] || Expr.setFilters[ pseudo.toLowerCase() ] || + Sizzle.error( "unsupported pseudo: " + pseudo ); + + // The user may use createPseudo to indicate that + // arguments are needed to create the filter function + // just as Sizzle does + if ( fn[ expando ] ) { + return fn( argument ); + } + + // But maintain support for old signatures + if ( fn.length > 1 ) { + args = [ pseudo, pseudo, "", argument ]; + return Expr.setFilters.hasOwnProperty( pseudo.toLowerCase() ) ? + markFunction(function( seed, matches ) { + var idx, + matched = fn( seed, argument ), + i = matched.length; + while ( i-- ) { + idx = indexOf.call( seed, matched[i] ); + seed[ idx ] = !( matches[ idx ] = matched[i] ); + } + }) : + function( elem ) { + return fn( elem, 0, args ); + }; + } + + return fn; + } + }, + + pseudos: { + "not": markFunction(function( selector ) { + // Trim the selector passed to compile + // to avoid treating leading and trailing + // spaces as combinators + var input = [], + results = [], + matcher = compile( selector.replace( rtrim, "$1" ) ); + + return matcher[ expando ] ? + markFunction(function( seed, matches, context, xml ) { + var elem, + unmatched = matcher( seed, null, xml, [] ), + i = seed.length; + + // Match elements unmatched by `matcher` + while ( i-- ) { + if ( (elem = unmatched[i]) ) { + seed[i] = !(matches[i] = elem); + } + } + }) : + function( elem, context, xml ) { + input[0] = elem; + matcher( input, null, xml, results ); + return !results.pop(); + }; + }), + + "has": markFunction(function( selector ) { + return function( elem ) { + return Sizzle( selector, elem ).length > 0; + }; + }), + + "contains": markFunction(function( text ) { + return function( elem ) { + return ( elem.textContent || elem.innerText || getText( elem ) ).indexOf( text ) > -1; + }; + }), + + "enabled": function( elem ) { + return elem.disabled === false; + }, + + "disabled": function( elem ) { + return elem.disabled === true; + }, + + "checked": function( elem ) { + // In CSS3, :checked should return both checked and selected elements + // http://www.w3.org/TR/2011/REC-css3-selectors-20110929/#checked + var nodeName = elem.nodeName.toLowerCase(); + return (nodeName === "input" && !!elem.checked) || (nodeName === "option" && !!elem.selected); + }, + + "selected": function( elem ) { + // Accessing this property makes selected-by-default + // options in Safari work properly + if ( elem.parentNode ) { + elem.parentNode.selectedIndex; + } + + return elem.selected === true; + }, + + "parent": function( elem ) { + return !Expr.pseudos["empty"]( elem ); + }, + + "empty": function( elem ) { + // http://www.w3.org/TR/selectors/#empty-pseudo + // :empty is only affected by element nodes and content nodes(including text(3), cdata(4)), + // not comment, processing instructions, or others + // Thanks to Diego Perini for the nodeName shortcut + // Greater than "@" means alpha characters (specifically not starting with "#" or "?") + var nodeType; + elem = elem.firstChild; + while ( elem ) { + if ( elem.nodeName > "@" || (nodeType = elem.nodeType) === 3 || nodeType === 4 ) { + return false; + } + elem = elem.nextSibling; + } + return true; + }, + + "header": function( elem ) { + return rheader.test( elem.nodeName ); + }, + + "text": function( elem ) { + var type, attr; + // IE6 and 7 will map elem.type to 'text' for new HTML5 types (search, etc) + // use getAttribute instead to test this case + return elem.nodeName.toLowerCase() === "input" && + (type = elem.type) === "text" && + ( (attr = elem.getAttribute("type")) == null || attr.toLowerCase() === type ); + }, + + // Input types + "radio": createInputPseudo("radio"), + "checkbox": createInputPseudo("checkbox"), + "file": createInputPseudo("file"), + "password": createInputPseudo("password"), + "image": createInputPseudo("image"), + + "submit": createButtonPseudo("submit"), + "reset": createButtonPseudo("reset"), + + "button": function( elem ) { + var name = elem.nodeName.toLowerCase(); + return name === "input" && elem.type === "button" || name === "button"; + }, + + "input": function( elem ) { + return rinputs.test( elem.nodeName ); + }, + + "focus": function( elem ) { + var doc = elem.ownerDocument; + return elem === doc.activeElement && (!doc.hasFocus || doc.hasFocus()) && !!(elem.type || elem.href || ~elem.tabIndex); + }, + + "active": function( elem ) { + return elem === elem.ownerDocument.activeElement; + }, + + // Positional types + "first": createPositionalPseudo(function() { + return [ 0 ]; + }), + + "last": createPositionalPseudo(function( matchIndexes, length ) { + return [ length - 1 ]; + }), + + "eq": createPositionalPseudo(function( matchIndexes, length, argument ) { + return [ argument < 0 ? argument + length : argument ]; + }), + + "even": createPositionalPseudo(function( matchIndexes, length ) { + for ( var i = 0; i < length; i += 2 ) { + matchIndexes.push( i ); + } + return matchIndexes; + }), + + "odd": createPositionalPseudo(function( matchIndexes, length ) { + for ( var i = 1; i < length; i += 2 ) { + matchIndexes.push( i ); + } + return matchIndexes; + }), + + "lt": createPositionalPseudo(function( matchIndexes, length, argument ) { + for ( var i = argument < 0 ? argument + length : argument; --i >= 0; ) { + matchIndexes.push( i ); + } + return matchIndexes; + }), + + "gt": createPositionalPseudo(function( matchIndexes, length, argument ) { + for ( var i = argument < 0 ? argument + length : argument; ++i < length; ) { + matchIndexes.push( i ); + } + return matchIndexes; + }) + } +}; + +function siblingCheck( a, b, ret ) { + if ( a === b ) { + return ret; + } + + var cur = a.nextSibling; + + while ( cur ) { + if ( cur === b ) { + return -1; + } + + cur = cur.nextSibling; + } + + return 1; +} + +sortOrder = docElem.compareDocumentPosition ? + function( a, b ) { + if ( a === b ) { + hasDuplicate = true; + return 0; + } + + return ( !a.compareDocumentPosition || !b.compareDocumentPosition ? + a.compareDocumentPosition : + a.compareDocumentPosition(b) & 4 + ) ? -1 : 1; + } : + function( a, b ) { + // The nodes are identical, we can exit early + if ( a === b ) { + hasDuplicate = true; + return 0; + + // Fallback to using sourceIndex (in IE) if it's available on both nodes + } else if ( a.sourceIndex && b.sourceIndex ) { + return a.sourceIndex - b.sourceIndex; + } + + var al, bl, + ap = [], + bp = [], + aup = a.parentNode, + bup = b.parentNode, + cur = aup; + + // If the nodes are siblings (or identical) we can do a quick check + if ( aup === bup ) { + return siblingCheck( a, b ); + + // If no parents were found then the nodes are disconnected + } else if ( !aup ) { + return -1; + + } else if ( !bup ) { + return 1; + } + + // Otherwise they're somewhere else in the tree so we need + // to build up a full list of the parentNodes for comparison + while ( cur ) { + ap.unshift( cur ); + cur = cur.parentNode; + } + + cur = bup; + + while ( cur ) { + bp.unshift( cur ); + cur = cur.parentNode; + } + + al = ap.length; + bl = bp.length; + + // Start walking down the tree looking for a discrepancy + for ( var i = 0; i < al && i < bl; i++ ) { + if ( ap[i] !== bp[i] ) { + return siblingCheck( ap[i], bp[i] ); + } + } + + // We ended someplace up the tree so do a sibling check + return i === al ? + siblingCheck( a, bp[i], -1 ) : + siblingCheck( ap[i], b, 1 ); + }; + +// Always assume the presence of duplicates if sort doesn't +// pass them to our comparison function (as in Google Chrome). +[0, 0].sort( sortOrder ); +baseHasDuplicate = !hasDuplicate; + +// Document sorting and removing duplicates +Sizzle.uniqueSort = function( results ) { + var elem, + duplicates = [], + i = 1, + j = 0; + + hasDuplicate = baseHasDuplicate; + results.sort( sortOrder ); + + if ( hasDuplicate ) { + for ( ; (elem = results[i]); i++ ) { + if ( elem === results[ i - 1 ] ) { + j = duplicates.push( i ); + } + } + while ( j-- ) { + results.splice( duplicates[ j ], 1 ); + } + } + + return results; +}; + +Sizzle.error = function( msg ) { + throw new Error( "Syntax error, unrecognized expression: " + msg ); +}; + +function tokenize( selector, parseOnly ) { + var matched, match, tokens, type, + soFar, groups, preFilters, + cached = tokenCache[ expando ][ selector + " " ]; + + if ( cached ) { + return parseOnly ? 0 : cached.slice( 0 ); + } + + soFar = selector; + groups = []; + preFilters = Expr.preFilter; + + while ( soFar ) { + + // Comma and first run + if ( !matched || (match = rcomma.exec( soFar )) ) { + if ( match ) { + // Don't consume trailing commas as valid + soFar = soFar.slice( match[0].length ) || soFar; + } + groups.push( tokens = [] ); + } + + matched = false; + + // Combinators + if ( (match = rcombinators.exec( soFar )) ) { + tokens.push( matched = new Token( match.shift() ) ); + soFar = soFar.slice( matched.length ); + + // Cast descendant combinators to space + matched.type = match[0].replace( rtrim, " " ); + } + + // Filters + for ( type in Expr.filter ) { + if ( (match = matchExpr[ type ].exec( soFar )) && (!preFilters[ type ] || + (match = preFilters[ type ]( match ))) ) { + + tokens.push( matched = new Token( match.shift() ) ); + soFar = soFar.slice( matched.length ); + matched.type = type; + matched.matches = match; + } + } + + if ( !matched ) { + break; + } + } + + // Return the length of the invalid excess + // if we're just parsing + // Otherwise, throw an error or return tokens + return parseOnly ? + soFar.length : + soFar ? + Sizzle.error( selector ) : + // Cache the tokens + tokenCache( selector, groups ).slice( 0 ); +} + +function addCombinator( matcher, combinator, base ) { + var dir = combinator.dir, + checkNonElements = base && combinator.dir === "parentNode", + doneName = done++; + + return combinator.first ? + // Check against closest ancestor/preceding element + function( elem, context, xml ) { + while ( (elem = elem[ dir ]) ) { + if ( checkNonElements || elem.nodeType === 1 ) { + return matcher( elem, context, xml ); + } + } + } : + + // Check against all ancestor/preceding elements + function( elem, context, xml ) { + // We can't set arbitrary data on XML nodes, so they don't benefit from dir caching + if ( !xml ) { + var cache, + dirkey = dirruns + " " + doneName + " ", + cachedkey = dirkey + cachedruns; + while ( (elem = elem[ dir ]) ) { + if ( checkNonElements || elem.nodeType === 1 ) { + if ( (cache = elem[ expando ]) === cachedkey ) { + return elem.sizset; + } else if ( typeof cache === "string" && cache.indexOf(dirkey) === 0 ) { + if ( elem.sizset ) { + return elem; + } + } else { + elem[ expando ] = cachedkey; + if ( matcher( elem, context, xml ) ) { + elem.sizset = true; + return elem; + } + elem.sizset = false; + } + } + } + } else { + while ( (elem = elem[ dir ]) ) { + if ( checkNonElements || elem.nodeType === 1 ) { + if ( matcher( elem, context, xml ) ) { + return elem; + } + } + } + } + }; +} + +function elementMatcher( matchers ) { + return matchers.length > 1 ? + function( elem, context, xml ) { + var i = matchers.length; + while ( i-- ) { + if ( !matchers[i]( elem, context, xml ) ) { + return false; + } + } + return true; + } : + matchers[0]; +} + +function condense( unmatched, map, filter, context, xml ) { + var elem, + newUnmatched = [], + i = 0, + len = unmatched.length, + mapped = map != null; + + for ( ; i < len; i++ ) { + if ( (elem = unmatched[i]) ) { + if ( !filter || filter( elem, context, xml ) ) { + newUnmatched.push( elem ); + if ( mapped ) { + map.push( i ); + } + } + } + } + + return newUnmatched; +} + +function setMatcher( preFilter, selector, matcher, postFilter, postFinder, postSelector ) { + if ( postFilter && !postFilter[ expando ] ) { + postFilter = setMatcher( postFilter ); + } + if ( postFinder && !postFinder[ expando ] ) { + postFinder = setMatcher( postFinder, postSelector ); + } + return markFunction(function( seed, results, context, xml ) { + var temp, i, elem, + preMap = [], + postMap = [], + preexisting = results.length, + + // Get initial elements from seed or context + elems = seed || multipleContexts( selector || "*", context.nodeType ? [ context ] : context, [] ), + + // Prefilter to get matcher input, preserving a map for seed-results synchronization + matcherIn = preFilter && ( seed || !selector ) ? + condense( elems, preMap, preFilter, context, xml ) : + elems, + + matcherOut = matcher ? + // If we have a postFinder, or filtered seed, or non-seed postFilter or preexisting results, + postFinder || ( seed ? preFilter : preexisting || postFilter ) ? + + // ...intermediate processing is necessary + [] : + + // ...otherwise use results directly + results : + matcherIn; + + // Find primary matches + if ( matcher ) { + matcher( matcherIn, matcherOut, context, xml ); + } + + // Apply postFilter + if ( postFilter ) { + temp = condense( matcherOut, postMap ); + postFilter( temp, [], context, xml ); + + // Un-match failing elements by moving them back to matcherIn + i = temp.length; + while ( i-- ) { + if ( (elem = temp[i]) ) { + matcherOut[ postMap[i] ] = !(matcherIn[ postMap[i] ] = elem); + } + } + } + + if ( seed ) { + if ( postFinder || preFilter ) { + if ( postFinder ) { + // Get the final matcherOut by condensing this intermediate into postFinder contexts + temp = []; + i = matcherOut.length; + while ( i-- ) { + if ( (elem = matcherOut[i]) ) { + // Restore matcherIn since elem is not yet a final match + temp.push( (matcherIn[i] = elem) ); + } + } + postFinder( null, (matcherOut = []), temp, xml ); + } + + // Move matched elements from seed to results to keep them synchronized + i = matcherOut.length; + while ( i-- ) { + if ( (elem = matcherOut[i]) && + (temp = postFinder ? indexOf.call( seed, elem ) : preMap[i]) > -1 ) { + + seed[temp] = !(results[temp] = elem); + } + } + } + + // Add elements to results, through postFinder if defined + } else { + matcherOut = condense( + matcherOut === results ? + matcherOut.splice( preexisting, matcherOut.length ) : + matcherOut + ); + if ( postFinder ) { + postFinder( null, results, matcherOut, xml ); + } else { + push.apply( results, matcherOut ); + } + } + }); +} + +function matcherFromTokens( tokens ) { + var checkContext, matcher, j, + len = tokens.length, + leadingRelative = Expr.relative[ tokens[0].type ], + implicitRelative = leadingRelative || Expr.relative[" "], + i = leadingRelative ? 1 : 0, + + // The foundational matcher ensures that elements are reachable from top-level context(s) + matchContext = addCombinator( function( elem ) { + return elem === checkContext; + }, implicitRelative, true ), + matchAnyContext = addCombinator( function( elem ) { + return indexOf.call( checkContext, elem ) > -1; + }, implicitRelative, true ), + matchers = [ function( elem, context, xml ) { + return ( !leadingRelative && ( xml || context !== outermostContext ) ) || ( + (checkContext = context).nodeType ? + matchContext( elem, context, xml ) : + matchAnyContext( elem, context, xml ) ); + } ]; + + for ( ; i < len; i++ ) { + if ( (matcher = Expr.relative[ tokens[i].type ]) ) { + matchers = [ addCombinator( elementMatcher( matchers ), matcher ) ]; + } else { + matcher = Expr.filter[ tokens[i].type ].apply( null, tokens[i].matches ); + + // Return special upon seeing a positional matcher + if ( matcher[ expando ] ) { + // Find the next relative operator (if any) for proper handling + j = ++i; + for ( ; j < len; j++ ) { + if ( Expr.relative[ tokens[j].type ] ) { + break; + } + } + return setMatcher( + i > 1 && elementMatcher( matchers ), + i > 1 && tokens.slice( 0, i - 1 ).join("").replace( rtrim, "$1" ), + matcher, + i < j && matcherFromTokens( tokens.slice( i, j ) ), + j < len && matcherFromTokens( (tokens = tokens.slice( j )) ), + j < len && tokens.join("") + ); + } + matchers.push( matcher ); + } + } + + return elementMatcher( matchers ); +} + +function matcherFromGroupMatchers( elementMatchers, setMatchers ) { + var bySet = setMatchers.length > 0, + byElement = elementMatchers.length > 0, + superMatcher = function( seed, context, xml, results, expandContext ) { + var elem, j, matcher, + setMatched = [], + matchedCount = 0, + i = "0", + unmatched = seed && [], + outermost = expandContext != null, + contextBackup = outermostContext, + // We must always have either seed elements or context + elems = seed || byElement && Expr.find["TAG"]( "*", expandContext && context.parentNode || context ), + // Nested matchers should use non-integer dirruns + dirrunsUnique = (dirruns += contextBackup == null ? 1 : Math.E); + + if ( outermost ) { + outermostContext = context !== document && context; + cachedruns = superMatcher.el; + } + + // Add elements passing elementMatchers directly to results + for ( ; (elem = elems[i]) != null; i++ ) { + if ( byElement && elem ) { + for ( j = 0; (matcher = elementMatchers[j]); j++ ) { + if ( matcher( elem, context, xml ) ) { + results.push( elem ); + break; + } + } + if ( outermost ) { + dirruns = dirrunsUnique; + cachedruns = ++superMatcher.el; + } + } + + // Track unmatched elements for set filters + if ( bySet ) { + // They will have gone through all possible matchers + if ( (elem = !matcher && elem) ) { + matchedCount--; + } + + // Lengthen the array for every element, matched or not + if ( seed ) { + unmatched.push( elem ); + } + } + } + + // Apply set filters to unmatched elements + matchedCount += i; + if ( bySet && i !== matchedCount ) { + for ( j = 0; (matcher = setMatchers[j]); j++ ) { + matcher( unmatched, setMatched, context, xml ); + } + + if ( seed ) { + // Reintegrate element matches to eliminate the need for sorting + if ( matchedCount > 0 ) { + while ( i-- ) { + if ( !(unmatched[i] || setMatched[i]) ) { + setMatched[i] = pop.call( results ); + } + } + } + + // Discard index placeholder values to get only actual matches + setMatched = condense( setMatched ); + } + + // Add matches to results + push.apply( results, setMatched ); + + // Seedless set matches succeeding multiple successful matchers stipulate sorting + if ( outermost && !seed && setMatched.length > 0 && + ( matchedCount + setMatchers.length ) > 1 ) { + + Sizzle.uniqueSort( results ); + } + } + + // Override manipulation of globals by nested matchers + if ( outermost ) { + dirruns = dirrunsUnique; + outermostContext = contextBackup; + } + + return unmatched; + }; + + superMatcher.el = 0; + return bySet ? + markFunction( superMatcher ) : + superMatcher; +} + +compile = Sizzle.compile = function( selector, group /* Internal Use Only */ ) { + var i, + setMatchers = [], + elementMatchers = [], + cached = compilerCache[ expando ][ selector + " " ]; + + if ( !cached ) { + // Generate a function of recursive functions that can be used to check each element + if ( !group ) { + group = tokenize( selector ); + } + i = group.length; + while ( i-- ) { + cached = matcherFromTokens( group[i] ); + if ( cached[ expando ] ) { + setMatchers.push( cached ); + } else { + elementMatchers.push( cached ); + } + } + + // Cache the compiled function + cached = compilerCache( selector, matcherFromGroupMatchers( elementMatchers, setMatchers ) ); + } + return cached; +}; + +function multipleContexts( selector, contexts, results ) { + var i = 0, + len = contexts.length; + for ( ; i < len; i++ ) { + Sizzle( selector, contexts[i], results ); + } + return results; +} + +function select( selector, context, results, seed, xml ) { + var i, tokens, token, type, find, + match = tokenize( selector ), + j = match.length; + + if ( !seed ) { + // Try to minimize operations if there is only one group + if ( match.length === 1 ) { + + // Take a shortcut and set the context if the root selector is an ID + tokens = match[0] = match[0].slice( 0 ); + if ( tokens.length > 2 && (token = tokens[0]).type === "ID" && + context.nodeType === 9 && !xml && + Expr.relative[ tokens[1].type ] ) { + + context = Expr.find["ID"]( token.matches[0].replace( rbackslash, "" ), context, xml )[0]; + if ( !context ) { + return results; + } + + selector = selector.slice( tokens.shift().length ); + } + + // Fetch a seed set for right-to-left matching + for ( i = matchExpr["POS"].test( selector ) ? -1 : tokens.length - 1; i >= 0; i-- ) { + token = tokens[i]; + + // Abort if we hit a combinator + if ( Expr.relative[ (type = token.type) ] ) { + break; + } + if ( (find = Expr.find[ type ]) ) { + // Search, expanding context for leading sibling combinators + if ( (seed = find( + token.matches[0].replace( rbackslash, "" ), + rsibling.test( tokens[0].type ) && context.parentNode || context, + xml + )) ) { + + // If seed is empty or no tokens remain, we can return early + tokens.splice( i, 1 ); + selector = seed.length && tokens.join(""); + if ( !selector ) { + push.apply( results, slice.call( seed, 0 ) ); + return results; + } + + break; + } + } + } + } + } + + // Compile and execute a filtering function + // Provide `match` to avoid retokenization if we modified the selector above + compile( selector, match )( + seed, + context, + xml, + results, + rsibling.test( selector ) + ); + return results; +} + +if ( document.querySelectorAll ) { + (function() { + var disconnectedMatch, + oldSelect = select, + rescape = /'|\\/g, + rattributeQuotes = /\=[\x20\t\r\n\f]*([^'"\]]*)[\x20\t\r\n\f]*\]/g, + + // qSa(:focus) reports false when true (Chrome 21), no need to also add to buggyMatches since matches checks buggyQSA + // A support test would require too much code (would include document ready) + rbuggyQSA = [ ":focus" ], + + // matchesSelector(:active) reports false when true (IE9/Opera 11.5) + // A support test would require too much code (would include document ready) + // just skip matchesSelector for :active + rbuggyMatches = [ ":active" ], + matches = docElem.matchesSelector || + docElem.mozMatchesSelector || + docElem.webkitMatchesSelector || + docElem.oMatchesSelector || + docElem.msMatchesSelector; + + // Build QSA regex + // Regex strategy adopted from Diego Perini + assert(function( div ) { + // Select is set to empty string on purpose + // This is to test IE's treatment of not explictly + // setting a boolean content attribute, + // since its presence should be enough + // http://bugs.jquery.com/ticket/12359 + div.innerHTML = ""; + + // IE8 - Some boolean attributes are not treated correctly + if ( !div.querySelectorAll("[selected]").length ) { + rbuggyQSA.push( "\\[" + whitespace + "*(?:checked|disabled|ismap|multiple|readonly|selected|value)" ); + } + + // Webkit/Opera - :checked should return selected option elements + // http://www.w3.org/TR/2011/REC-css3-selectors-20110929/#checked + // IE8 throws error here (do not put tests after this one) + if ( !div.querySelectorAll(":checked").length ) { + rbuggyQSA.push(":checked"); + } + }); + + assert(function( div ) { + + // Opera 10-12/IE9 - ^= $= *= and empty values + // Should not select anything + div.innerHTML = "

"; + if ( div.querySelectorAll("[test^='']").length ) { + rbuggyQSA.push( "[*^$]=" + whitespace + "*(?:\"\"|'')" ); + } + + // FF 3.5 - :enabled/:disabled and hidden elements (hidden elements are still enabled) + // IE8 throws error here (do not put tests after this one) + div.innerHTML = ""; + if ( !div.querySelectorAll(":enabled").length ) { + rbuggyQSA.push(":enabled", ":disabled"); + } + }); + + // rbuggyQSA always contains :focus, so no need for a length check + rbuggyQSA = /* rbuggyQSA.length && */ new RegExp( rbuggyQSA.join("|") ); + + select = function( selector, context, results, seed, xml ) { + // Only use querySelectorAll when not filtering, + // when this is not xml, + // and when no QSA bugs apply + if ( !seed && !xml && !rbuggyQSA.test( selector ) ) { + var groups, i, + old = true, + nid = expando, + newContext = context, + newSelector = context.nodeType === 9 && selector; + + // qSA works strangely on Element-rooted queries + // We can work around this by specifying an extra ID on the root + // and working up from there (Thanks to Andrew Dupont for the technique) + // IE 8 doesn't work on object elements + if ( context.nodeType === 1 && context.nodeName.toLowerCase() !== "object" ) { + groups = tokenize( selector ); + + if ( (old = context.getAttribute("id")) ) { + nid = old.replace( rescape, "\\$&" ); + } else { + context.setAttribute( "id", nid ); + } + nid = "[id='" + nid + "'] "; + + i = groups.length; + while ( i-- ) { + groups[i] = nid + groups[i].join(""); + } + newContext = rsibling.test( selector ) && context.parentNode || context; + newSelector = groups.join(","); + } + + if ( newSelector ) { + try { + push.apply( results, slice.call( newContext.querySelectorAll( + newSelector + ), 0 ) ); + return results; + } catch(qsaError) { + } finally { + if ( !old ) { + context.removeAttribute("id"); + } + } + } + } + + return oldSelect( selector, context, results, seed, xml ); + }; + + if ( matches ) { + assert(function( div ) { + // Check to see if it's possible to do matchesSelector + // on a disconnected node (IE 9) + disconnectedMatch = matches.call( div, "div" ); + + // This should fail with an exception + // Gecko does not error, returns false instead + try { + matches.call( div, "[test!='']:sizzle" ); + rbuggyMatches.push( "!=", pseudos ); + } catch ( e ) {} + }); + + // rbuggyMatches always contains :active and :focus, so no need for a length check + rbuggyMatches = /* rbuggyMatches.length && */ new RegExp( rbuggyMatches.join("|") ); + + Sizzle.matchesSelector = function( elem, expr ) { + // Make sure that attribute selectors are quoted + expr = expr.replace( rattributeQuotes, "='$1']" ); + + // rbuggyMatches always contains :active, so no need for an existence check + if ( !isXML( elem ) && !rbuggyMatches.test( expr ) && !rbuggyQSA.test( expr ) ) { + try { + var ret = matches.call( elem, expr ); + + // IE 9's matchesSelector returns false on disconnected nodes + if ( ret || disconnectedMatch || + // As well, disconnected nodes are said to be in a document + // fragment in IE 9 + elem.document && elem.document.nodeType !== 11 ) { + return ret; + } + } catch(e) {} + } + + return Sizzle( expr, null, null, [ elem ] ).length > 0; + }; + } + })(); +} + +// Deprecated +Expr.pseudos["nth"] = Expr.pseudos["eq"]; + +// Back-compat +function setFilters() {} +Expr.filters = setFilters.prototype = Expr.pseudos; +Expr.setFilters = new setFilters(); + +// Override sizzle attribute retrieval +Sizzle.attr = jQuery.attr; +jQuery.find = Sizzle; +jQuery.expr = Sizzle.selectors; +jQuery.expr[":"] = jQuery.expr.pseudos; +jQuery.unique = Sizzle.uniqueSort; +jQuery.text = Sizzle.getText; +jQuery.isXMLDoc = Sizzle.isXML; +jQuery.contains = Sizzle.contains; + + +})( window ); +var runtil = /Until$/, + rparentsprev = /^(?:parents|prev(?:Until|All))/, + isSimple = /^.[^:#\[\.,]*$/, + rneedsContext = jQuery.expr.match.needsContext, + // methods guaranteed to produce a unique set when starting from a unique set + guaranteedUnique = { + children: true, + contents: true, + next: true, + prev: true + }; + +jQuery.fn.extend({ + find: function( selector ) { + var i, l, length, n, r, ret, + self = this; + + if ( typeof selector !== "string" ) { + return jQuery( selector ).filter(function() { + for ( i = 0, l = self.length; i < l; i++ ) { + if ( jQuery.contains( self[ i ], this ) ) { + return true; + } + } + }); + } + + ret = this.pushStack( "", "find", selector ); + + for ( i = 0, l = this.length; i < l; i++ ) { + length = ret.length; + jQuery.find( selector, this[i], ret ); + + if ( i > 0 ) { + // Make sure that the results are unique + for ( n = length; n < ret.length; n++ ) { + for ( r = 0; r < length; r++ ) { + if ( ret[r] === ret[n] ) { + ret.splice(n--, 1); + break; + } + } + } + } + } + + return ret; + }, + + has: function( target ) { + var i, + targets = jQuery( target, this ), + len = targets.length; + + return this.filter(function() { + for ( i = 0; i < len; i++ ) { + if ( jQuery.contains( this, targets[i] ) ) { + return true; + } + } + }); + }, + + not: function( selector ) { + return this.pushStack( winnow(this, selector, false), "not", selector); + }, + + filter: function( selector ) { + return this.pushStack( winnow(this, selector, true), "filter", selector ); + }, + + is: function( selector ) { + return !!selector && ( + typeof selector === "string" ? + // If this is a positional/relative selector, check membership in the returned set + // so $("p:first").is("p:last") won't return true for a doc with two "p". + rneedsContext.test( selector ) ? + jQuery( selector, this.context ).index( this[0] ) >= 0 : + jQuery.filter( selector, this ).length > 0 : + this.filter( selector ).length > 0 ); + }, + + closest: function( selectors, context ) { + var cur, + i = 0, + l = this.length, + ret = [], + pos = rneedsContext.test( selectors ) || typeof selectors !== "string" ? + jQuery( selectors, context || this.context ) : + 0; + + for ( ; i < l; i++ ) { + cur = this[i]; + + while ( cur && cur.ownerDocument && cur !== context && cur.nodeType !== 11 ) { + if ( pos ? pos.index(cur) > -1 : jQuery.find.matchesSelector(cur, selectors) ) { + ret.push( cur ); + break; + } + cur = cur.parentNode; + } + } + + ret = ret.length > 1 ? jQuery.unique( ret ) : ret; + + return this.pushStack( ret, "closest", selectors ); + }, + + // Determine the position of an element within + // the matched set of elements + index: function( elem ) { + + // No argument, return index in parent + if ( !elem ) { + return ( this[0] && this[0].parentNode ) ? this.prevAll().length : -1; + } + + // index in selector + if ( typeof elem === "string" ) { + return jQuery.inArray( this[0], jQuery( elem ) ); + } + + // Locate the position of the desired element + return jQuery.inArray( + // If it receives a jQuery object, the first element is used + elem.jquery ? elem[0] : elem, this ); + }, + + add: function( selector, context ) { + var set = typeof selector === "string" ? + jQuery( selector, context ) : + jQuery.makeArray( selector && selector.nodeType ? [ selector ] : selector ), + all = jQuery.merge( this.get(), set ); + + return this.pushStack( isDisconnected( set[0] ) || isDisconnected( all[0] ) ? + all : + jQuery.unique( all ) ); + }, + + addBack: function( selector ) { + return this.add( selector == null ? + this.prevObject : this.prevObject.filter(selector) + ); + } +}); + +jQuery.fn.andSelf = jQuery.fn.addBack; + +// A painfully simple check to see if an element is disconnected +// from a document (should be improved, where feasible). +function isDisconnected( node ) { + return !node || !node.parentNode || node.parentNode.nodeType === 11; +} + +function sibling( cur, dir ) { + do { + cur = cur[ dir ]; + } while ( cur && cur.nodeType !== 1 ); + + return cur; +} + +jQuery.each({ + parent: function( elem ) { + var parent = elem.parentNode; + return parent && parent.nodeType !== 11 ? parent : null; + }, + parents: function( elem ) { + return jQuery.dir( elem, "parentNode" ); + }, + parentsUntil: function( elem, i, until ) { + return jQuery.dir( elem, "parentNode", until ); + }, + next: function( elem ) { + return sibling( elem, "nextSibling" ); + }, + prev: function( elem ) { + return sibling( elem, "previousSibling" ); + }, + nextAll: function( elem ) { + return jQuery.dir( elem, "nextSibling" ); + }, + prevAll: function( elem ) { + return jQuery.dir( elem, "previousSibling" ); + }, + nextUntil: function( elem, i, until ) { + return jQuery.dir( elem, "nextSibling", until ); + }, + prevUntil: function( elem, i, until ) { + return jQuery.dir( elem, "previousSibling", until ); + }, + siblings: function( elem ) { + return jQuery.sibling( ( elem.parentNode || {} ).firstChild, elem ); + }, + children: function( elem ) { + return jQuery.sibling( elem.firstChild ); + }, + contents: function( elem ) { + return jQuery.nodeName( elem, "iframe" ) ? + elem.contentDocument || elem.contentWindow.document : + jQuery.merge( [], elem.childNodes ); + } +}, function( name, fn ) { + jQuery.fn[ name ] = function( until, selector ) { + var ret = jQuery.map( this, fn, until ); + + if ( !runtil.test( name ) ) { + selector = until; + } + + if ( selector && typeof selector === "string" ) { + ret = jQuery.filter( selector, ret ); + } + + ret = this.length > 1 && !guaranteedUnique[ name ] ? jQuery.unique( ret ) : ret; + + if ( this.length > 1 && rparentsprev.test( name ) ) { + ret = ret.reverse(); + } + + return this.pushStack( ret, name, core_slice.call( arguments ).join(",") ); + }; +}); + +jQuery.extend({ + filter: function( expr, elems, not ) { + if ( not ) { + expr = ":not(" + expr + ")"; + } + + return elems.length === 1 ? + jQuery.find.matchesSelector(elems[0], expr) ? [ elems[0] ] : [] : + jQuery.find.matches(expr, elems); + }, + + dir: function( elem, dir, until ) { + var matched = [], + cur = elem[ dir ]; + + while ( cur && cur.nodeType !== 9 && (until === undefined || cur.nodeType !== 1 || !jQuery( cur ).is( until )) ) { + if ( cur.nodeType === 1 ) { + matched.push( cur ); + } + cur = cur[dir]; + } + return matched; + }, + + sibling: function( n, elem ) { + var r = []; + + for ( ; n; n = n.nextSibling ) { + if ( n.nodeType === 1 && n !== elem ) { + r.push( n ); + } + } + + return r; + } +}); + +// Implement the identical functionality for filter and not +function winnow( elements, qualifier, keep ) { + + // Can't pass null or undefined to indexOf in Firefox 4 + // Set to 0 to skip string check + qualifier = qualifier || 0; + + if ( jQuery.isFunction( qualifier ) ) { + return jQuery.grep(elements, function( elem, i ) { + var retVal = !!qualifier.call( elem, i, elem ); + return retVal === keep; + }); + + } else if ( qualifier.nodeType ) { + return jQuery.grep(elements, function( elem, i ) { + return ( elem === qualifier ) === keep; + }); + + } else if ( typeof qualifier === "string" ) { + var filtered = jQuery.grep(elements, function( elem ) { + return elem.nodeType === 1; + }); + + if ( isSimple.test( qualifier ) ) { + return jQuery.filter(qualifier, filtered, !keep); + } else { + qualifier = jQuery.filter( qualifier, filtered ); + } + } + + return jQuery.grep(elements, function( elem, i ) { + return ( jQuery.inArray( elem, qualifier ) >= 0 ) === keep; + }); +} +function createSafeFragment( document ) { + var list = nodeNames.split( "|" ), + safeFrag = document.createDocumentFragment(); + + if ( safeFrag.createElement ) { + while ( list.length ) { + safeFrag.createElement( + list.pop() + ); + } + } + return safeFrag; +} + +var nodeNames = "abbr|article|aside|audio|bdi|canvas|data|datalist|details|figcaption|figure|footer|" + + "header|hgroup|mark|meter|nav|output|progress|section|summary|time|video", + rinlinejQuery = / jQuery\d+="(?:null|\d+)"/g, + rleadingWhitespace = /^\s+/, + rxhtmlTag = /<(?!area|br|col|embed|hr|img|input|link|meta|param)(([\w:]+)[^>]*)\/>/gi, + rtagName = /<([\w:]+)/, + rtbody = /]", "i"), + rcheckableType = /^(?:checkbox|radio)$/, + // checked="checked" or checked + rchecked = /checked\s*(?:[^=]|=\s*.checked.)/i, + rscriptType = /\/(java|ecma)script/i, + rcleanScript = /^\s*\s*$/g, + wrapMap = { + option: [ 1, "" ], + legend: [ 1, "
", "
" ], + thead: [ 1, "", "
" ], + tr: [ 2, "", "
" ], + td: [ 3, "", "
" ], + col: [ 2, "", "
" ], + area: [ 1, "", "" ], + _default: [ 0, "", "" ] + }, + safeFragment = createSafeFragment( document ), + fragmentDiv = safeFragment.appendChild( document.createElement("div") ); + +wrapMap.optgroup = wrapMap.option; +wrapMap.tbody = wrapMap.tfoot = wrapMap.colgroup = wrapMap.caption = wrapMap.thead; +wrapMap.th = wrapMap.td; + +// IE6-8 can't serialize link, script, style, or any html5 (NoScope) tags, +// unless wrapped in a div with non-breaking characters in front of it. +if ( !jQuery.support.htmlSerialize ) { + wrapMap._default = [ 1, "X
", "
" ]; +} + +jQuery.fn.extend({ + text: function( value ) { + return jQuery.access( this, function( value ) { + return value === undefined ? + jQuery.text( this ) : + this.empty().append( ( this[0] && this[0].ownerDocument || document ).createTextNode( value ) ); + }, null, value, arguments.length ); + }, + + wrapAll: function( html ) { + if ( jQuery.isFunction( html ) ) { + return this.each(function(i) { + jQuery(this).wrapAll( html.call(this, i) ); + }); + } + + if ( this[0] ) { + // The elements to wrap the target around + var wrap = jQuery( html, this[0].ownerDocument ).eq(0).clone(true); + + if ( this[0].parentNode ) { + wrap.insertBefore( this[0] ); + } + + wrap.map(function() { + var elem = this; + + while ( elem.firstChild && elem.firstChild.nodeType === 1 ) { + elem = elem.firstChild; + } + + return elem; + }).append( this ); + } + + return this; + }, + + wrapInner: function( html ) { + if ( jQuery.isFunction( html ) ) { + return this.each(function(i) { + jQuery(this).wrapInner( html.call(this, i) ); + }); + } + + return this.each(function() { + var self = jQuery( this ), + contents = self.contents(); + + if ( contents.length ) { + contents.wrapAll( html ); + + } else { + self.append( html ); + } + }); + }, + + wrap: function( html ) { + var isFunction = jQuery.isFunction( html ); + + return this.each(function(i) { + jQuery( this ).wrapAll( isFunction ? html.call(this, i) : html ); + }); + }, + + unwrap: function() { + return this.parent().each(function() { + if ( !jQuery.nodeName( this, "body" ) ) { + jQuery( this ).replaceWith( this.childNodes ); + } + }).end(); + }, + + append: function() { + return this.domManip(arguments, true, function( elem ) { + if ( this.nodeType === 1 || this.nodeType === 11 ) { + this.appendChild( elem ); + } + }); + }, + + prepend: function() { + return this.domManip(arguments, true, function( elem ) { + if ( this.nodeType === 1 || this.nodeType === 11 ) { + this.insertBefore( elem, this.firstChild ); + } + }); + }, + + before: function() { + if ( !isDisconnected( this[0] ) ) { + return this.domManip(arguments, false, function( elem ) { + this.parentNode.insertBefore( elem, this ); + }); + } + + if ( arguments.length ) { + var set = jQuery.clean( arguments ); + return this.pushStack( jQuery.merge( set, this ), "before", this.selector ); + } + }, + + after: function() { + if ( !isDisconnected( this[0] ) ) { + return this.domManip(arguments, false, function( elem ) { + this.parentNode.insertBefore( elem, this.nextSibling ); + }); + } + + if ( arguments.length ) { + var set = jQuery.clean( arguments ); + return this.pushStack( jQuery.merge( this, set ), "after", this.selector ); + } + }, + + // keepData is for internal use only--do not document + remove: function( selector, keepData ) { + var elem, + i = 0; + + for ( ; (elem = this[i]) != null; i++ ) { + if ( !selector || jQuery.filter( selector, [ elem ] ).length ) { + if ( !keepData && elem.nodeType === 1 ) { + jQuery.cleanData( elem.getElementsByTagName("*") ); + jQuery.cleanData( [ elem ] ); + } + + if ( elem.parentNode ) { + elem.parentNode.removeChild( elem ); + } + } + } + + return this; + }, + + empty: function() { + var elem, + i = 0; + + for ( ; (elem = this[i]) != null; i++ ) { + // Remove element nodes and prevent memory leaks + if ( elem.nodeType === 1 ) { + jQuery.cleanData( elem.getElementsByTagName("*") ); + } + + // Remove any remaining nodes + while ( elem.firstChild ) { + elem.removeChild( elem.firstChild ); + } + } + + return this; + }, + + clone: function( dataAndEvents, deepDataAndEvents ) { + dataAndEvents = dataAndEvents == null ? false : dataAndEvents; + deepDataAndEvents = deepDataAndEvents == null ? dataAndEvents : deepDataAndEvents; + + return this.map( function () { + return jQuery.clone( this, dataAndEvents, deepDataAndEvents ); + }); + }, + + html: function( value ) { + return jQuery.access( this, function( value ) { + var elem = this[0] || {}, + i = 0, + l = this.length; + + if ( value === undefined ) { + return elem.nodeType === 1 ? + elem.innerHTML.replace( rinlinejQuery, "" ) : + undefined; + } + + // See if we can take a shortcut and just use innerHTML + if ( typeof value === "string" && !rnoInnerhtml.test( value ) && + ( jQuery.support.htmlSerialize || !rnoshimcache.test( value ) ) && + ( jQuery.support.leadingWhitespace || !rleadingWhitespace.test( value ) ) && + !wrapMap[ ( rtagName.exec( value ) || ["", ""] )[1].toLowerCase() ] ) { + + value = value.replace( rxhtmlTag, "<$1>" ); + + try { + for (; i < l; i++ ) { + // Remove element nodes and prevent memory leaks + elem = this[i] || {}; + if ( elem.nodeType === 1 ) { + jQuery.cleanData( elem.getElementsByTagName( "*" ) ); + elem.innerHTML = value; + } + } + + elem = 0; + + // If using innerHTML throws an exception, use the fallback method + } catch(e) {} + } + + if ( elem ) { + this.empty().append( value ); + } + }, null, value, arguments.length ); + }, + + replaceWith: function( value ) { + if ( !isDisconnected( this[0] ) ) { + // Make sure that the elements are removed from the DOM before they are inserted + // this can help fix replacing a parent with child elements + if ( jQuery.isFunction( value ) ) { + return this.each(function(i) { + var self = jQuery(this), old = self.html(); + self.replaceWith( value.call( this, i, old ) ); + }); + } + + if ( typeof value !== "string" ) { + value = jQuery( value ).detach(); + } + + return this.each(function() { + var next = this.nextSibling, + parent = this.parentNode; + + jQuery( this ).remove(); + + if ( next ) { + jQuery(next).before( value ); + } else { + jQuery(parent).append( value ); + } + }); + } + + return this.length ? + this.pushStack( jQuery(jQuery.isFunction(value) ? value() : value), "replaceWith", value ) : + this; + }, + + detach: function( selector ) { + return this.remove( selector, true ); + }, + + domManip: function( args, table, callback ) { + + // Flatten any nested arrays + args = [].concat.apply( [], args ); + + var results, first, fragment, iNoClone, + i = 0, + value = args[0], + scripts = [], + l = this.length; + + // We can't cloneNode fragments that contain checked, in WebKit + if ( !jQuery.support.checkClone && l > 1 && typeof value === "string" && rchecked.test( value ) ) { + return this.each(function() { + jQuery(this).domManip( args, table, callback ); + }); + } + + if ( jQuery.isFunction(value) ) { + return this.each(function(i) { + var self = jQuery(this); + args[0] = value.call( this, i, table ? self.html() : undefined ); + self.domManip( args, table, callback ); + }); + } + + if ( this[0] ) { + results = jQuery.buildFragment( args, this, scripts ); + fragment = results.fragment; + first = fragment.firstChild; + + if ( fragment.childNodes.length === 1 ) { + fragment = first; + } + + if ( first ) { + table = table && jQuery.nodeName( first, "tr" ); + + // Use the original fragment for the last item instead of the first because it can end up + // being emptied incorrectly in certain situations (#8070). + // Fragments from the fragment cache must always be cloned and never used in place. + for ( iNoClone = results.cacheable || l - 1; i < l; i++ ) { + callback.call( + table && jQuery.nodeName( this[i], "table" ) ? + findOrAppend( this[i], "tbody" ) : + this[i], + i === iNoClone ? + fragment : + jQuery.clone( fragment, true, true ) + ); + } + } + + // Fix #11809: Avoid leaking memory + fragment = first = null; + + if ( scripts.length ) { + jQuery.each( scripts, function( i, elem ) { + if ( elem.src ) { + if ( jQuery.ajax ) { + jQuery.ajax({ + url: elem.src, + type: "GET", + dataType: "script", + async: false, + global: false, + "throws": true + }); + } else { + jQuery.error("no ajax"); + } + } else { + jQuery.globalEval( ( elem.text || elem.textContent || elem.innerHTML || "" ).replace( rcleanScript, "" ) ); + } + + if ( elem.parentNode ) { + elem.parentNode.removeChild( elem ); + } + }); + } + } + + return this; + } +}); + +function findOrAppend( elem, tag ) { + return elem.getElementsByTagName( tag )[0] || elem.appendChild( elem.ownerDocument.createElement( tag ) ); +} + +function cloneCopyEvent( src, dest ) { + + if ( dest.nodeType !== 1 || !jQuery.hasData( src ) ) { + return; + } + + var type, i, l, + oldData = jQuery._data( src ), + curData = jQuery._data( dest, oldData ), + events = oldData.events; + + if ( events ) { + delete curData.handle; + curData.events = {}; + + for ( type in events ) { + for ( i = 0, l = events[ type ].length; i < l; i++ ) { + jQuery.event.add( dest, type, events[ type ][ i ] ); + } + } + } + + // make the cloned public data object a copy from the original + if ( curData.data ) { + curData.data = jQuery.extend( {}, curData.data ); + } +} + +function cloneFixAttributes( src, dest ) { + var nodeName; + + // We do not need to do anything for non-Elements + if ( dest.nodeType !== 1 ) { + return; + } + + // clearAttributes removes the attributes, which we don't want, + // but also removes the attachEvent events, which we *do* want + if ( dest.clearAttributes ) { + dest.clearAttributes(); + } + + // mergeAttributes, in contrast, only merges back on the + // original attributes, not the events + if ( dest.mergeAttributes ) { + dest.mergeAttributes( src ); + } + + nodeName = dest.nodeName.toLowerCase(); + + if ( nodeName === "object" ) { + // IE6-10 improperly clones children of object elements using classid. + // IE10 throws NoModificationAllowedError if parent is null, #12132. + if ( dest.parentNode ) { + dest.outerHTML = src.outerHTML; + } + + // This path appears unavoidable for IE9. When cloning an object + // element in IE9, the outerHTML strategy above is not sufficient. + // If the src has innerHTML and the destination does not, + // copy the src.innerHTML into the dest.innerHTML. #10324 + if ( jQuery.support.html5Clone && (src.innerHTML && !jQuery.trim(dest.innerHTML)) ) { + dest.innerHTML = src.innerHTML; + } + + } else if ( nodeName === "input" && rcheckableType.test( src.type ) ) { + // IE6-8 fails to persist the checked state of a cloned checkbox + // or radio button. Worse, IE6-7 fail to give the cloned element + // a checked appearance if the defaultChecked value isn't also set + + dest.defaultChecked = dest.checked = src.checked; + + // IE6-7 get confused and end up setting the value of a cloned + // checkbox/radio button to an empty string instead of "on" + if ( dest.value !== src.value ) { + dest.value = src.value; + } + + // IE6-8 fails to return the selected option to the default selected + // state when cloning options + } else if ( nodeName === "option" ) { + dest.selected = src.defaultSelected; + + // IE6-8 fails to set the defaultValue to the correct value when + // cloning other types of input fields + } else if ( nodeName === "input" || nodeName === "textarea" ) { + dest.defaultValue = src.defaultValue; + + // IE blanks contents when cloning scripts + } else if ( nodeName === "script" && dest.text !== src.text ) { + dest.text = src.text; + } + + // Event data gets referenced instead of copied if the expando + // gets copied too + dest.removeAttribute( jQuery.expando ); +} + +jQuery.buildFragment = function( args, context, scripts ) { + var fragment, cacheable, cachehit, + first = args[ 0 ]; + + // Set context from what may come in as undefined or a jQuery collection or a node + // Updated to fix #12266 where accessing context[0] could throw an exception in IE9/10 & + // also doubles as fix for #8950 where plain objects caused createDocumentFragment exception + context = context || document; + context = !context.nodeType && context[0] || context; + context = context.ownerDocument || context; + + // Only cache "small" (1/2 KB) HTML strings that are associated with the main document + // Cloning options loses the selected state, so don't cache them + // IE 6 doesn't like it when you put or elements in a fragment + // Also, WebKit does not clone 'checked' attributes on cloneNode, so don't cache + // Lastly, IE6,7,8 will not correctly reuse cached fragments that were created from unknown elems #10501 + if ( args.length === 1 && typeof first === "string" && first.length < 512 && context === document && + first.charAt(0) === "<" && !rnocache.test( first ) && + (jQuery.support.checkClone || !rchecked.test( first )) && + (jQuery.support.html5Clone || !rnoshimcache.test( first )) ) { + + // Mark cacheable and look for a hit + cacheable = true; + fragment = jQuery.fragments[ first ]; + cachehit = fragment !== undefined; + } + + if ( !fragment ) { + fragment = context.createDocumentFragment(); + jQuery.clean( args, context, fragment, scripts ); + + // Update the cache, but only store false + // unless this is a second parsing of the same content + if ( cacheable ) { + jQuery.fragments[ first ] = cachehit && fragment; + } + } + + return { fragment: fragment, cacheable: cacheable }; +}; + +jQuery.fragments = {}; + +jQuery.each({ + appendTo: "append", + prependTo: "prepend", + insertBefore: "before", + insertAfter: "after", + replaceAll: "replaceWith" +}, function( name, original ) { + jQuery.fn[ name ] = function( selector ) { + var elems, + i = 0, + ret = [], + insert = jQuery( selector ), + l = insert.length, + parent = this.length === 1 && this[0].parentNode; + + if ( (parent == null || parent && parent.nodeType === 11 && parent.childNodes.length === 1) && l === 1 ) { + insert[ original ]( this[0] ); + return this; + } else { + for ( ; i < l; i++ ) { + elems = ( i > 0 ? this.clone(true) : this ).get(); + jQuery( insert[i] )[ original ]( elems ); + ret = ret.concat( elems ); + } + + return this.pushStack( ret, name, insert.selector ); + } + }; +}); + +function getAll( elem ) { + if ( typeof elem.getElementsByTagName !== "undefined" ) { + return elem.getElementsByTagName( "*" ); + + } else if ( typeof elem.querySelectorAll !== "undefined" ) { + return elem.querySelectorAll( "*" ); + + } else { + return []; + } +} + +// Used in clean, fixes the defaultChecked property +function fixDefaultChecked( elem ) { + if ( rcheckableType.test( elem.type ) ) { + elem.defaultChecked = elem.checked; + } +} + +jQuery.extend({ + clone: function( elem, dataAndEvents, deepDataAndEvents ) { + var srcElements, + destElements, + i, + clone; + + if ( jQuery.support.html5Clone || jQuery.isXMLDoc(elem) || !rnoshimcache.test( "<" + elem.nodeName + ">" ) ) { + clone = elem.cloneNode( true ); + + // IE<=8 does not properly clone detached, unknown element nodes + } else { + fragmentDiv.innerHTML = elem.outerHTML; + fragmentDiv.removeChild( clone = fragmentDiv.firstChild ); + } + + if ( (!jQuery.support.noCloneEvent || !jQuery.support.noCloneChecked) && + (elem.nodeType === 1 || elem.nodeType === 11) && !jQuery.isXMLDoc(elem) ) { + // IE copies events bound via attachEvent when using cloneNode. + // Calling detachEvent on the clone will also remove the events + // from the original. In order to get around this, we use some + // proprietary methods to clear the events. Thanks to MooTools + // guys for this hotness. + + cloneFixAttributes( elem, clone ); + + // Using Sizzle here is crazy slow, so we use getElementsByTagName instead + srcElements = getAll( elem ); + destElements = getAll( clone ); + + // Weird iteration because IE will replace the length property + // with an element if you are cloning the body and one of the + // elements on the page has a name or id of "length" + for ( i = 0; srcElements[i]; ++i ) { + // Ensure that the destination node is not null; Fixes #9587 + if ( destElements[i] ) { + cloneFixAttributes( srcElements[i], destElements[i] ); + } + } + } + + // Copy the events from the original to the clone + if ( dataAndEvents ) { + cloneCopyEvent( elem, clone ); + + if ( deepDataAndEvents ) { + srcElements = getAll( elem ); + destElements = getAll( clone ); + + for ( i = 0; srcElements[i]; ++i ) { + cloneCopyEvent( srcElements[i], destElements[i] ); + } + } + } + + srcElements = destElements = null; + + // Return the cloned set + return clone; + }, + + clean: function( elems, context, fragment, scripts ) { + var i, j, elem, tag, wrap, depth, div, hasBody, tbody, len, handleScript, jsTags, + safe = context === document && safeFragment, + ret = []; + + // Ensure that context is a document + if ( !context || typeof context.createDocumentFragment === "undefined" ) { + context = document; + } + + // Use the already-created safe fragment if context permits + for ( i = 0; (elem = elems[i]) != null; i++ ) { + if ( typeof elem === "number" ) { + elem += ""; + } + + if ( !elem ) { + continue; + } + + // Convert html string into DOM nodes + if ( typeof elem === "string" ) { + if ( !rhtml.test( elem ) ) { + elem = context.createTextNode( elem ); + } else { + // Ensure a safe container in which to render the html + safe = safe || createSafeFragment( context ); + div = context.createElement("div"); + safe.appendChild( div ); + + // Fix "XHTML"-style tags in all browsers + elem = elem.replace(rxhtmlTag, "<$1>"); + + // Go to html and back, then peel off extra wrappers + tag = ( rtagName.exec( elem ) || ["", ""] )[1].toLowerCase(); + wrap = wrapMap[ tag ] || wrapMap._default; + depth = wrap[0]; + div.innerHTML = wrap[1] + elem + wrap[2]; + + // Move to the right depth + while ( depth-- ) { + div = div.lastChild; + } + + // Remove IE's autoinserted from table fragments + if ( !jQuery.support.tbody ) { + + // String was a , *may* have spurious + hasBody = rtbody.test(elem); + tbody = tag === "table" && !hasBody ? + div.firstChild && div.firstChild.childNodes : + + // String was a bare or + wrap[1] === "
" && !hasBody ? + div.childNodes : + []; + + for ( j = tbody.length - 1; j >= 0 ; --j ) { + if ( jQuery.nodeName( tbody[ j ], "tbody" ) && !tbody[ j ].childNodes.length ) { + tbody[ j ].parentNode.removeChild( tbody[ j ] ); + } + } + } + + // IE completely kills leading whitespace when innerHTML is used + if ( !jQuery.support.leadingWhitespace && rleadingWhitespace.test( elem ) ) { + div.insertBefore( context.createTextNode( rleadingWhitespace.exec(elem)[0] ), div.firstChild ); + } + + elem = div.childNodes; + + // Take out of fragment container (we need a fresh div each time) + div.parentNode.removeChild( div ); + } + } + + if ( elem.nodeType ) { + ret.push( elem ); + } else { + jQuery.merge( ret, elem ); + } + } + + // Fix #11356: Clear elements from safeFragment + if ( div ) { + elem = div = safe = null; + } + + // Reset defaultChecked for any radios and checkboxes + // about to be appended to the DOM in IE 6/7 (#8060) + if ( !jQuery.support.appendChecked ) { + for ( i = 0; (elem = ret[i]) != null; i++ ) { + if ( jQuery.nodeName( elem, "input" ) ) { + fixDefaultChecked( elem ); + } else if ( typeof elem.getElementsByTagName !== "undefined" ) { + jQuery.grep( elem.getElementsByTagName("input"), fixDefaultChecked ); + } + } + } + + // Append elements to a provided document fragment + if ( fragment ) { + // Special handling of each script element + handleScript = function( elem ) { + // Check if we consider it executable + if ( !elem.type || rscriptType.test( elem.type ) ) { + // Detach the script and store it in the scripts array (if provided) or the fragment + // Return truthy to indicate that it has been handled + return scripts ? + scripts.push( elem.parentNode ? elem.parentNode.removeChild( elem ) : elem ) : + fragment.appendChild( elem ); + } + }; + + for ( i = 0; (elem = ret[i]) != null; i++ ) { + // Check if we're done after handling an executable script + if ( !( jQuery.nodeName( elem, "script" ) && handleScript( elem ) ) ) { + // Append to fragment and handle embedded scripts + fragment.appendChild( elem ); + if ( typeof elem.getElementsByTagName !== "undefined" ) { + // handleScript alters the DOM, so use jQuery.merge to ensure snapshot iteration + jsTags = jQuery.grep( jQuery.merge( [], elem.getElementsByTagName("script") ), handleScript ); + + // Splice the scripts into ret after their former ancestor and advance our index beyond them + ret.splice.apply( ret, [i + 1, 0].concat( jsTags ) ); + i += jsTags.length; + } + } + } + } + + return ret; + }, + + cleanData: function( elems, /* internal */ acceptData ) { + var data, id, elem, type, + i = 0, + internalKey = jQuery.expando, + cache = jQuery.cache, + deleteExpando = jQuery.support.deleteExpando, + special = jQuery.event.special; + + for ( ; (elem = elems[i]) != null; i++ ) { + + if ( acceptData || jQuery.acceptData( elem ) ) { + + id = elem[ internalKey ]; + data = id && cache[ id ]; + + if ( data ) { + if ( data.events ) { + for ( type in data.events ) { + if ( special[ type ] ) { + jQuery.event.remove( elem, type ); + + // This is a shortcut to avoid jQuery.event.remove's overhead + } else { + jQuery.removeEvent( elem, type, data.handle ); + } + } + } + + // Remove cache only if it was not already removed by jQuery.event.remove + if ( cache[ id ] ) { + + delete cache[ id ]; + + // IE does not allow us to delete expando properties from nodes, + // nor does it have a removeAttribute function on Document nodes; + // we must handle all of these cases + if ( deleteExpando ) { + delete elem[ internalKey ]; + + } else if ( elem.removeAttribute ) { + elem.removeAttribute( internalKey ); + + } else { + elem[ internalKey ] = null; + } + + jQuery.deletedIds.push( id ); + } + } + } + } + } +}); +// Limit scope pollution from any deprecated API +(function() { + +var matched, browser; + +// Use of jQuery.browser is frowned upon. +// More details: http://api.jquery.com/jQuery.browser +// jQuery.uaMatch maintained for back-compat +jQuery.uaMatch = function( ua ) { + ua = ua.toLowerCase(); + + var match = /(chrome)[ \/]([\w.]+)/.exec( ua ) || + /(webkit)[ \/]([\w.]+)/.exec( ua ) || + /(opera)(?:.*version|)[ \/]([\w.]+)/.exec( ua ) || + /(msie) ([\w.]+)/.exec( ua ) || + ua.indexOf("compatible") < 0 && /(mozilla)(?:.*? rv:([\w.]+)|)/.exec( ua ) || + []; + + return { + browser: match[ 1 ] || "", + version: match[ 2 ] || "0" + }; +}; + +matched = jQuery.uaMatch( navigator.userAgent ); +browser = {}; + +if ( matched.browser ) { + browser[ matched.browser ] = true; + browser.version = matched.version; +} + +// Chrome is Webkit, but Webkit is also Safari. +if ( browser.chrome ) { + browser.webkit = true; +} else if ( browser.webkit ) { + browser.safari = true; +} + +jQuery.browser = browser; + +jQuery.sub = function() { + function jQuerySub( selector, context ) { + return new jQuerySub.fn.init( selector, context ); + } + jQuery.extend( true, jQuerySub, this ); + jQuerySub.superclass = this; + jQuerySub.fn = jQuerySub.prototype = this(); + jQuerySub.fn.constructor = jQuerySub; + jQuerySub.sub = this.sub; + jQuerySub.fn.init = function init( selector, context ) { + if ( context && context instanceof jQuery && !(context instanceof jQuerySub) ) { + context = jQuerySub( context ); + } + + return jQuery.fn.init.call( this, selector, context, rootjQuerySub ); + }; + jQuerySub.fn.init.prototype = jQuerySub.fn; + var rootjQuerySub = jQuerySub(document); + return jQuerySub; +}; + +})(); +var curCSS, iframe, iframeDoc, + ralpha = /alpha\([^)]*\)/i, + ropacity = /opacity=([^)]*)/, + rposition = /^(top|right|bottom|left)$/, + // swappable if display is none or starts with table except "table", "table-cell", or "table-caption" + // see here for display values: https://developer.mozilla.org/en-US/docs/CSS/display + rdisplayswap = /^(none|table(?!-c[ea]).+)/, + rmargin = /^margin/, + rnumsplit = new RegExp( "^(" + core_pnum + ")(.*)$", "i" ), + rnumnonpx = new RegExp( "^(" + core_pnum + ")(?!px)[a-z%]+$", "i" ), + rrelNum = new RegExp( "^([-+])=(" + core_pnum + ")", "i" ), + elemdisplay = { BODY: "block" }, + + cssShow = { position: "absolute", visibility: "hidden", display: "block" }, + cssNormalTransform = { + letterSpacing: 0, + fontWeight: 400 + }, + + cssExpand = [ "Top", "Right", "Bottom", "Left" ], + cssPrefixes = [ "Webkit", "O", "Moz", "ms" ], + + eventsToggle = jQuery.fn.toggle; + +// return a css property mapped to a potentially vendor prefixed property +function vendorPropName( style, name ) { + + // shortcut for names that are not vendor prefixed + if ( name in style ) { + return name; + } + + // check for vendor prefixed names + var capName = name.charAt(0).toUpperCase() + name.slice(1), + origName = name, + i = cssPrefixes.length; + + while ( i-- ) { + name = cssPrefixes[ i ] + capName; + if ( name in style ) { + return name; + } + } + + return origName; +} + +function isHidden( elem, el ) { + elem = el || elem; + return jQuery.css( elem, "display" ) === "none" || !jQuery.contains( elem.ownerDocument, elem ); +} + +function showHide( elements, show ) { + var elem, display, + values = [], + index = 0, + length = elements.length; + + for ( ; index < length; index++ ) { + elem = elements[ index ]; + if ( !elem.style ) { + continue; + } + values[ index ] = jQuery._data( elem, "olddisplay" ); + if ( show ) { + // Reset the inline display of this element to learn if it is + // being hidden by cascaded rules or not + if ( !values[ index ] && elem.style.display === "none" ) { + elem.style.display = ""; + } + + // Set elements which have been overridden with display: none + // in a stylesheet to whatever the default browser style is + // for such an element + if ( elem.style.display === "" && isHidden( elem ) ) { + values[ index ] = jQuery._data( elem, "olddisplay", css_defaultDisplay(elem.nodeName) ); + } + } else { + display = curCSS( elem, "display" ); + + if ( !values[ index ] && display !== "none" ) { + jQuery._data( elem, "olddisplay", display ); + } + } + } + + // Set the display of most of the elements in a second loop + // to avoid the constant reflow + for ( index = 0; index < length; index++ ) { + elem = elements[ index ]; + if ( !elem.style ) { + continue; + } + if ( !show || elem.style.display === "none" || elem.style.display === "" ) { + elem.style.display = show ? values[ index ] || "" : "none"; + } + } + + return elements; +} + +jQuery.fn.extend({ + css: function( name, value ) { + return jQuery.access( this, function( elem, name, value ) { + return value !== undefined ? + jQuery.style( elem, name, value ) : + jQuery.css( elem, name ); + }, name, value, arguments.length > 1 ); + }, + show: function() { + return showHide( this, true ); + }, + hide: function() { + return showHide( this ); + }, + toggle: function( state, fn2 ) { + var bool = typeof state === "boolean"; + + if ( jQuery.isFunction( state ) && jQuery.isFunction( fn2 ) ) { + return eventsToggle.apply( this, arguments ); + } + + return this.each(function() { + if ( bool ? state : isHidden( this ) ) { + jQuery( this ).show(); + } else { + jQuery( this ).hide(); + } + }); + } +}); + +jQuery.extend({ + // Add in style property hooks for overriding the default + // behavior of getting and setting a style property + cssHooks: { + opacity: { + get: function( elem, computed ) { + if ( computed ) { + // We should always get a number back from opacity + var ret = curCSS( elem, "opacity" ); + return ret === "" ? "1" : ret; + + } + } + } + }, + + // Exclude the following css properties to add px + cssNumber: { + "fillOpacity": true, + "fontWeight": true, + "lineHeight": true, + "opacity": true, + "orphans": true, + "widows": true, + "zIndex": true, + "zoom": true + }, + + // Add in properties whose names you wish to fix before + // setting or getting the value + cssProps: { + // normalize float css property + "float": jQuery.support.cssFloat ? "cssFloat" : "styleFloat" + }, + + // Get and set the style property on a DOM Node + style: function( elem, name, value, extra ) { + // Don't set styles on text and comment nodes + if ( !elem || elem.nodeType === 3 || elem.nodeType === 8 || !elem.style ) { + return; + } + + // Make sure that we're working with the right name + var ret, type, hooks, + origName = jQuery.camelCase( name ), + style = elem.style; + + name = jQuery.cssProps[ origName ] || ( jQuery.cssProps[ origName ] = vendorPropName( style, origName ) ); + + // gets hook for the prefixed version + // followed by the unprefixed version + hooks = jQuery.cssHooks[ name ] || jQuery.cssHooks[ origName ]; + + // Check if we're setting a value + if ( value !== undefined ) { + type = typeof value; + + // convert relative number strings (+= or -=) to relative numbers. #7345 + if ( type === "string" && (ret = rrelNum.exec( value )) ) { + value = ( ret[1] + 1 ) * ret[2] + parseFloat( jQuery.css( elem, name ) ); + // Fixes bug #9237 + type = "number"; + } + + // Make sure that NaN and null values aren't set. See: #7116 + if ( value == null || type === "number" && isNaN( value ) ) { + return; + } + + // If a number was passed in, add 'px' to the (except for certain CSS properties) + if ( type === "number" && !jQuery.cssNumber[ origName ] ) { + value += "px"; + } + + // If a hook was provided, use that value, otherwise just set the specified value + if ( !hooks || !("set" in hooks) || (value = hooks.set( elem, value, extra )) !== undefined ) { + // Wrapped to prevent IE from throwing errors when 'invalid' values are provided + // Fixes bug #5509 + try { + style[ name ] = value; + } catch(e) {} + } + + } else { + // If a hook was provided get the non-computed value from there + if ( hooks && "get" in hooks && (ret = hooks.get( elem, false, extra )) !== undefined ) { + return ret; + } + + // Otherwise just get the value from the style object + return style[ name ]; + } + }, + + css: function( elem, name, numeric, extra ) { + var val, num, hooks, + origName = jQuery.camelCase( name ); + + // Make sure that we're working with the right name + name = jQuery.cssProps[ origName ] || ( jQuery.cssProps[ origName ] = vendorPropName( elem.style, origName ) ); + + // gets hook for the prefixed version + // followed by the unprefixed version + hooks = jQuery.cssHooks[ name ] || jQuery.cssHooks[ origName ]; + + // If a hook was provided get the computed value from there + if ( hooks && "get" in hooks ) { + val = hooks.get( elem, true, extra ); + } + + // Otherwise, if a way to get the computed value exists, use that + if ( val === undefined ) { + val = curCSS( elem, name ); + } + + //convert "normal" to computed value + if ( val === "normal" && name in cssNormalTransform ) { + val = cssNormalTransform[ name ]; + } + + // Return, converting to number if forced or a qualifier was provided and val looks numeric + if ( numeric || extra !== undefined ) { + num = parseFloat( val ); + return numeric || jQuery.isNumeric( num ) ? num || 0 : val; + } + return val; + }, + + // A method for quickly swapping in/out CSS properties to get correct calculations + swap: function( elem, options, callback ) { + var ret, name, + old = {}; + + // Remember the old values, and insert the new ones + for ( name in options ) { + old[ name ] = elem.style[ name ]; + elem.style[ name ] = options[ name ]; + } + + ret = callback.call( elem ); + + // Revert the old values + for ( name in options ) { + elem.style[ name ] = old[ name ]; + } + + return ret; + } +}); + +// NOTE: To any future maintainer, we've window.getComputedStyle +// because jsdom on node.js will break without it. +if ( window.getComputedStyle ) { + curCSS = function( elem, name ) { + var ret, width, minWidth, maxWidth, + computed = window.getComputedStyle( elem, null ), + style = elem.style; + + if ( computed ) { + + // getPropertyValue is only needed for .css('filter') in IE9, see #12537 + ret = computed.getPropertyValue( name ) || computed[ name ]; + + if ( ret === "" && !jQuery.contains( elem.ownerDocument, elem ) ) { + ret = jQuery.style( elem, name ); + } + + // A tribute to the "awesome hack by Dean Edwards" + // Chrome < 17 and Safari 5.0 uses "computed value" instead of "used value" for margin-right + // Safari 5.1.7 (at least) returns percentage for a larger set of values, but width seems to be reliably pixels + // this is against the CSSOM draft spec: http://dev.w3.org/csswg/cssom/#resolved-values + if ( rnumnonpx.test( ret ) && rmargin.test( name ) ) { + width = style.width; + minWidth = style.minWidth; + maxWidth = style.maxWidth; + + style.minWidth = style.maxWidth = style.width = ret; + ret = computed.width; + + style.width = width; + style.minWidth = minWidth; + style.maxWidth = maxWidth; + } + } + + return ret; + }; +} else if ( document.documentElement.currentStyle ) { + curCSS = function( elem, name ) { + var left, rsLeft, + ret = elem.currentStyle && elem.currentStyle[ name ], + style = elem.style; + + // Avoid setting ret to empty string here + // so we don't default to auto + if ( ret == null && style && style[ name ] ) { + ret = style[ name ]; + } + + // From the awesome hack by Dean Edwards + // http://erik.eae.net/archives/2007/07/27/18.54.15/#comment-102291 + + // If we're not dealing with a regular pixel number + // but a number that has a weird ending, we need to convert it to pixels + // but not position css attributes, as those are proportional to the parent element instead + // and we can't measure the parent instead because it might trigger a "stacking dolls" problem + if ( rnumnonpx.test( ret ) && !rposition.test( name ) ) { + + // Remember the original values + left = style.left; + rsLeft = elem.runtimeStyle && elem.runtimeStyle.left; + + // Put in the new values to get a computed value out + if ( rsLeft ) { + elem.runtimeStyle.left = elem.currentStyle.left; + } + style.left = name === "fontSize" ? "1em" : ret; + ret = style.pixelLeft + "px"; + + // Revert the changed values + style.left = left; + if ( rsLeft ) { + elem.runtimeStyle.left = rsLeft; + } + } + + return ret === "" ? "auto" : ret; + }; +} + +function setPositiveNumber( elem, value, subtract ) { + var matches = rnumsplit.exec( value ); + return matches ? + Math.max( 0, matches[ 1 ] - ( subtract || 0 ) ) + ( matches[ 2 ] || "px" ) : + value; +} + +function augmentWidthOrHeight( elem, name, extra, isBorderBox ) { + var i = extra === ( isBorderBox ? "border" : "content" ) ? + // If we already have the right measurement, avoid augmentation + 4 : + // Otherwise initialize for horizontal or vertical properties + name === "width" ? 1 : 0, + + val = 0; + + for ( ; i < 4; i += 2 ) { + // both box models exclude margin, so add it if we want it + if ( extra === "margin" ) { + // we use jQuery.css instead of curCSS here + // because of the reliableMarginRight CSS hook! + val += jQuery.css( elem, extra + cssExpand[ i ], true ); + } + + // From this point on we use curCSS for maximum performance (relevant in animations) + if ( isBorderBox ) { + // border-box includes padding, so remove it if we want content + if ( extra === "content" ) { + val -= parseFloat( curCSS( elem, "padding" + cssExpand[ i ] ) ) || 0; + } + + // at this point, extra isn't border nor margin, so remove border + if ( extra !== "margin" ) { + val -= parseFloat( curCSS( elem, "border" + cssExpand[ i ] + "Width" ) ) || 0; + } + } else { + // at this point, extra isn't content, so add padding + val += parseFloat( curCSS( elem, "padding" + cssExpand[ i ] ) ) || 0; + + // at this point, extra isn't content nor padding, so add border + if ( extra !== "padding" ) { + val += parseFloat( curCSS( elem, "border" + cssExpand[ i ] + "Width" ) ) || 0; + } + } + } + + return val; +} + +function getWidthOrHeight( elem, name, extra ) { + + // Start with offset property, which is equivalent to the border-box value + var val = name === "width" ? elem.offsetWidth : elem.offsetHeight, + valueIsBorderBox = true, + isBorderBox = jQuery.support.boxSizing && jQuery.css( elem, "boxSizing" ) === "border-box"; + + // some non-html elements return undefined for offsetWidth, so check for null/undefined + // svg - https://bugzilla.mozilla.org/show_bug.cgi?id=649285 + // MathML - https://bugzilla.mozilla.org/show_bug.cgi?id=491668 + if ( val <= 0 || val == null ) { + // Fall back to computed then uncomputed css if necessary + val = curCSS( elem, name ); + if ( val < 0 || val == null ) { + val = elem.style[ name ]; + } + + // Computed unit is not pixels. Stop here and return. + if ( rnumnonpx.test(val) ) { + return val; + } + + // we need the check for style in case a browser which returns unreliable values + // for getComputedStyle silently falls back to the reliable elem.style + valueIsBorderBox = isBorderBox && ( jQuery.support.boxSizingReliable || val === elem.style[ name ] ); + + // Normalize "", auto, and prepare for extra + val = parseFloat( val ) || 0; + } + + // use the active box-sizing model to add/subtract irrelevant styles + return ( val + + augmentWidthOrHeight( + elem, + name, + extra || ( isBorderBox ? "border" : "content" ), + valueIsBorderBox + ) + ) + "px"; +} + + +// Try to determine the default display value of an element +function css_defaultDisplay( nodeName ) { + if ( elemdisplay[ nodeName ] ) { + return elemdisplay[ nodeName ]; + } + + var elem = jQuery( "<" + nodeName + ">" ).appendTo( document.body ), + display = elem.css("display"); + elem.remove(); + + // If the simple way fails, + // get element's real default display by attaching it to a temp iframe + if ( display === "none" || display === "" ) { + // Use the already-created iframe if possible + iframe = document.body.appendChild( + iframe || jQuery.extend( document.createElement("iframe"), { + frameBorder: 0, + width: 0, + height: 0 + }) + ); + + // Create a cacheable copy of the iframe document on first call. + // IE and Opera will allow us to reuse the iframeDoc without re-writing the fake HTML + // document to it; WebKit & Firefox won't allow reusing the iframe document. + if ( !iframeDoc || !iframe.createElement ) { + iframeDoc = ( iframe.contentWindow || iframe.contentDocument ).document; + iframeDoc.write(""); + iframeDoc.close(); + } + + elem = iframeDoc.body.appendChild( iframeDoc.createElement(nodeName) ); + + display = curCSS( elem, "display" ); + document.body.removeChild( iframe ); + } + + // Store the correct default display + elemdisplay[ nodeName ] = display; + + return display; +} + +jQuery.each([ "height", "width" ], function( i, name ) { + jQuery.cssHooks[ name ] = { + get: function( elem, computed, extra ) { + if ( computed ) { + // certain elements can have dimension info if we invisibly show them + // however, it must have a current display style that would benefit from this + if ( elem.offsetWidth === 0 && rdisplayswap.test( curCSS( elem, "display" ) ) ) { + return jQuery.swap( elem, cssShow, function() { + return getWidthOrHeight( elem, name, extra ); + }); + } else { + return getWidthOrHeight( elem, name, extra ); + } + } + }, + + set: function( elem, value, extra ) { + return setPositiveNumber( elem, value, extra ? + augmentWidthOrHeight( + elem, + name, + extra, + jQuery.support.boxSizing && jQuery.css( elem, "boxSizing" ) === "border-box" + ) : 0 + ); + } + }; +}); + +if ( !jQuery.support.opacity ) { + jQuery.cssHooks.opacity = { + get: function( elem, computed ) { + // IE uses filters for opacity + return ropacity.test( (computed && elem.currentStyle ? elem.currentStyle.filter : elem.style.filter) || "" ) ? + ( 0.01 * parseFloat( RegExp.$1 ) ) + "" : + computed ? "1" : ""; + }, + + set: function( elem, value ) { + var style = elem.style, + currentStyle = elem.currentStyle, + opacity = jQuery.isNumeric( value ) ? "alpha(opacity=" + value * 100 + ")" : "", + filter = currentStyle && currentStyle.filter || style.filter || ""; + + // IE has trouble with opacity if it does not have layout + // Force it by setting the zoom level + style.zoom = 1; + + // if setting opacity to 1, and no other filters exist - attempt to remove filter attribute #6652 + if ( value >= 1 && jQuery.trim( filter.replace( ralpha, "" ) ) === "" && + style.removeAttribute ) { + + // Setting style.filter to null, "" & " " still leave "filter:" in the cssText + // if "filter:" is present at all, clearType is disabled, we want to avoid this + // style.removeAttribute is IE Only, but so apparently is this code path... + style.removeAttribute( "filter" ); + + // if there there is no filter style applied in a css rule, we are done + if ( currentStyle && !currentStyle.filter ) { + return; + } + } + + // otherwise, set new filter values + style.filter = ralpha.test( filter ) ? + filter.replace( ralpha, opacity ) : + filter + " " + opacity; + } + }; +} + +// These hooks cannot be added until DOM ready because the support test +// for it is not run until after DOM ready +jQuery(function() { + if ( !jQuery.support.reliableMarginRight ) { + jQuery.cssHooks.marginRight = { + get: function( elem, computed ) { + // WebKit Bug 13343 - getComputedStyle returns wrong value for margin-right + // Work around by temporarily setting element display to inline-block + return jQuery.swap( elem, { "display": "inline-block" }, function() { + if ( computed ) { + return curCSS( elem, "marginRight" ); + } + }); + } + }; + } + + // Webkit bug: https://bugs.webkit.org/show_bug.cgi?id=29084 + // getComputedStyle returns percent when specified for top/left/bottom/right + // rather than make the css module depend on the offset module, we just check for it here + if ( !jQuery.support.pixelPosition && jQuery.fn.position ) { + jQuery.each( [ "top", "left" ], function( i, prop ) { + jQuery.cssHooks[ prop ] = { + get: function( elem, computed ) { + if ( computed ) { + var ret = curCSS( elem, prop ); + // if curCSS returns percentage, fallback to offset + return rnumnonpx.test( ret ) ? jQuery( elem ).position()[ prop ] + "px" : ret; + } + } + }; + }); + } + +}); + +if ( jQuery.expr && jQuery.expr.filters ) { + jQuery.expr.filters.hidden = function( elem ) { + return ( elem.offsetWidth === 0 && elem.offsetHeight === 0 ) || (!jQuery.support.reliableHiddenOffsets && ((elem.style && elem.style.display) || curCSS( elem, "display" )) === "none"); + }; + + jQuery.expr.filters.visible = function( elem ) { + return !jQuery.expr.filters.hidden( elem ); + }; +} + +// These hooks are used by animate to expand properties +jQuery.each({ + margin: "", + padding: "", + border: "Width" +}, function( prefix, suffix ) { + jQuery.cssHooks[ prefix + suffix ] = { + expand: function( value ) { + var i, + + // assumes a single number if not a string + parts = typeof value === "string" ? value.split(" ") : [ value ], + expanded = {}; + + for ( i = 0; i < 4; i++ ) { + expanded[ prefix + cssExpand[ i ] + suffix ] = + parts[ i ] || parts[ i - 2 ] || parts[ 0 ]; + } + + return expanded; + } + }; + + if ( !rmargin.test( prefix ) ) { + jQuery.cssHooks[ prefix + suffix ].set = setPositiveNumber; + } +}); +var r20 = /%20/g, + rbracket = /\[\]$/, + rCRLF = /\r?\n/g, + rinput = /^(?:color|date|datetime|datetime-local|email|hidden|month|number|password|range|search|tel|text|time|url|week)$/i, + rselectTextarea = /^(?:select|textarea)/i; + +jQuery.fn.extend({ + serialize: function() { + return jQuery.param( this.serializeArray() ); + }, + serializeArray: function() { + return this.map(function(){ + return this.elements ? jQuery.makeArray( this.elements ) : this; + }) + .filter(function(){ + return this.name && !this.disabled && + ( this.checked || rselectTextarea.test( this.nodeName ) || + rinput.test( this.type ) ); + }) + .map(function( i, elem ){ + var val = jQuery( this ).val(); + + return val == null ? + null : + jQuery.isArray( val ) ? + jQuery.map( val, function( val, i ){ + return { name: elem.name, value: val.replace( rCRLF, "\r\n" ) }; + }) : + { name: elem.name, value: val.replace( rCRLF, "\r\n" ) }; + }).get(); + } +}); + +//Serialize an array of form elements or a set of +//key/values into a query string +jQuery.param = function( a, traditional ) { + var prefix, + s = [], + add = function( key, value ) { + // If value is a function, invoke it and return its value + value = jQuery.isFunction( value ) ? value() : ( value == null ? "" : value ); + s[ s.length ] = encodeURIComponent( key ) + "=" + encodeURIComponent( value ); + }; + + // Set traditional to true for jQuery <= 1.3.2 behavior. + if ( traditional === undefined ) { + traditional = jQuery.ajaxSettings && jQuery.ajaxSettings.traditional; + } + + // If an array was passed in, assume that it is an array of form elements. + if ( jQuery.isArray( a ) || ( a.jquery && !jQuery.isPlainObject( a ) ) ) { + // Serialize the form elements + jQuery.each( a, function() { + add( this.name, this.value ); + }); + + } else { + // If traditional, encode the "old" way (the way 1.3.2 or older + // did it), otherwise encode params recursively. + for ( prefix in a ) { + buildParams( prefix, a[ prefix ], traditional, add ); + } + } + + // Return the resulting serialization + return s.join( "&" ).replace( r20, "+" ); +}; + +function buildParams( prefix, obj, traditional, add ) { + var name; + + if ( jQuery.isArray( obj ) ) { + // Serialize array item. + jQuery.each( obj, function( i, v ) { + if ( traditional || rbracket.test( prefix ) ) { + // Treat each array item as a scalar. + add( prefix, v ); + + } else { + // If array item is non-scalar (array or object), encode its + // numeric index to resolve deserialization ambiguity issues. + // Note that rack (as of 1.0.0) can't currently deserialize + // nested arrays properly, and attempting to do so may cause + // a server error. Possible fixes are to modify rack's + // deserialization algorithm or to provide an option or flag + // to force array serialization to be shallow. + buildParams( prefix + "[" + ( typeof v === "object" ? i : "" ) + "]", v, traditional, add ); + } + }); + + } else if ( !traditional && jQuery.type( obj ) === "object" ) { + // Serialize object item. + for ( name in obj ) { + buildParams( prefix + "[" + name + "]", obj[ name ], traditional, add ); + } + + } else { + // Serialize scalar item. + add( prefix, obj ); + } +} +var + // Document location + ajaxLocParts, + ajaxLocation, + + rhash = /#.*$/, + rheaders = /^(.*?):[ \t]*([^\r\n]*)\r?$/mg, // IE leaves an \r character at EOL + // #7653, #8125, #8152: local protocol detection + rlocalProtocol = /^(?:about|app|app\-storage|.+\-extension|file|res|widget):$/, + rnoContent = /^(?:GET|HEAD)$/, + rprotocol = /^\/\//, + rquery = /\?/, + rscript = /)<[^<]*)*<\/script>/gi, + rts = /([?&])_=[^&]*/, + rurl = /^([\w\+\.\-]+:)(?:\/\/([^\/?#:]*)(?::(\d+)|)|)/, + + // Keep a copy of the old load method + _load = jQuery.fn.load, + + /* Prefilters + * 1) They are useful to introduce custom dataTypes (see ajax/jsonp.js for an example) + * 2) These are called: + * - BEFORE asking for a transport + * - AFTER param serialization (s.data is a string if s.processData is true) + * 3) key is the dataType + * 4) the catchall symbol "*" can be used + * 5) execution will start with transport dataType and THEN continue down to "*" if needed + */ + prefilters = {}, + + /* Transports bindings + * 1) key is the dataType + * 2) the catchall symbol "*" can be used + * 3) selection will start with transport dataType and THEN go to "*" if needed + */ + transports = {}, + + // Avoid comment-prolog char sequence (#10098); must appease lint and evade compression + allTypes = ["*/"] + ["*"]; + +// #8138, IE may throw an exception when accessing +// a field from window.location if document.domain has been set +try { + ajaxLocation = location.href; +} catch( e ) { + // Use the href attribute of an A element + // since IE will modify it given document.location + ajaxLocation = document.createElement( "a" ); + ajaxLocation.href = ""; + ajaxLocation = ajaxLocation.href; +} + +// Segment location into parts +ajaxLocParts = rurl.exec( ajaxLocation.toLowerCase() ) || []; + +// Base "constructor" for jQuery.ajaxPrefilter and jQuery.ajaxTransport +function addToPrefiltersOrTransports( structure ) { + + // dataTypeExpression is optional and defaults to "*" + return function( dataTypeExpression, func ) { + + if ( typeof dataTypeExpression !== "string" ) { + func = dataTypeExpression; + dataTypeExpression = "*"; + } + + var dataType, list, placeBefore, + dataTypes = dataTypeExpression.toLowerCase().split( core_rspace ), + i = 0, + length = dataTypes.length; + + if ( jQuery.isFunction( func ) ) { + // For each dataType in the dataTypeExpression + for ( ; i < length; i++ ) { + dataType = dataTypes[ i ]; + // We control if we're asked to add before + // any existing element + placeBefore = /^\+/.test( dataType ); + if ( placeBefore ) { + dataType = dataType.substr( 1 ) || "*"; + } + list = structure[ dataType ] = structure[ dataType ] || []; + // then we add to the structure accordingly + list[ placeBefore ? "unshift" : "push" ]( func ); + } + } + }; +} + +// Base inspection function for prefilters and transports +function inspectPrefiltersOrTransports( structure, options, originalOptions, jqXHR, + dataType /* internal */, inspected /* internal */ ) { + + dataType = dataType || options.dataTypes[ 0 ]; + inspected = inspected || {}; + + inspected[ dataType ] = true; + + var selection, + list = structure[ dataType ], + i = 0, + length = list ? list.length : 0, + executeOnly = ( structure === prefilters ); + + for ( ; i < length && ( executeOnly || !selection ); i++ ) { + selection = list[ i ]( options, originalOptions, jqXHR ); + // If we got redirected to another dataType + // we try there if executing only and not done already + if ( typeof selection === "string" ) { + if ( !executeOnly || inspected[ selection ] ) { + selection = undefined; + } else { + options.dataTypes.unshift( selection ); + selection = inspectPrefiltersOrTransports( + structure, options, originalOptions, jqXHR, selection, inspected ); + } + } + } + // If we're only executing or nothing was selected + // we try the catchall dataType if not done already + if ( ( executeOnly || !selection ) && !inspected[ "*" ] ) { + selection = inspectPrefiltersOrTransports( + structure, options, originalOptions, jqXHR, "*", inspected ); + } + // unnecessary when only executing (prefilters) + // but it'll be ignored by the caller in that case + return selection; +} + +// A special extend for ajax options +// that takes "flat" options (not to be deep extended) +// Fixes #9887 +function ajaxExtend( target, src ) { + var key, deep, + flatOptions = jQuery.ajaxSettings.flatOptions || {}; + for ( key in src ) { + if ( src[ key ] !== undefined ) { + ( flatOptions[ key ] ? target : ( deep || ( deep = {} ) ) )[ key ] = src[ key ]; + } + } + if ( deep ) { + jQuery.extend( true, target, deep ); + } +} + +jQuery.fn.load = function( url, params, callback ) { + if ( typeof url !== "string" && _load ) { + return _load.apply( this, arguments ); + } + + // Don't do a request if no elements are being requested + if ( !this.length ) { + return this; + } + + var selector, type, response, + self = this, + off = url.indexOf(" "); + + if ( off >= 0 ) { + selector = url.slice( off, url.length ); + url = url.slice( 0, off ); + } + + // If it's a function + if ( jQuery.isFunction( params ) ) { + + // We assume that it's the callback + callback = params; + params = undefined; + + // Otherwise, build a param string + } else if ( params && typeof params === "object" ) { + type = "POST"; + } + + // Request the remote document + jQuery.ajax({ + url: url, + + // if "type" variable is undefined, then "GET" method will be used + type: type, + dataType: "html", + data: params, + complete: function( jqXHR, status ) { + if ( callback ) { + self.each( callback, response || [ jqXHR.responseText, status, jqXHR ] ); + } + } + }).done(function( responseText ) { + + // Save response for use in complete callback + response = arguments; + + // See if a selector was specified + self.html( selector ? + + // Create a dummy div to hold the results + jQuery("
") + + // inject the contents of the document in, removing the scripts + // to avoid any 'Permission Denied' errors in IE + .append( responseText.replace( rscript, "" ) ) + + // Locate the specified elements + .find( selector ) : + + // If not, just inject the full result + responseText ); + + }); + + return this; +}; + +// Attach a bunch of functions for handling common AJAX events +jQuery.each( "ajaxStart ajaxStop ajaxComplete ajaxError ajaxSuccess ajaxSend".split( " " ), function( i, o ){ + jQuery.fn[ o ] = function( f ){ + return this.on( o, f ); + }; +}); + +jQuery.each( [ "get", "post" ], function( i, method ) { + jQuery[ method ] = function( url, data, callback, type ) { + // shift arguments if data argument was omitted + if ( jQuery.isFunction( data ) ) { + type = type || callback; + callback = data; + data = undefined; + } + + return jQuery.ajax({ + type: method, + url: url, + data: data, + success: callback, + dataType: type + }); + }; +}); + +jQuery.extend({ + + getScript: function( url, callback ) { + return jQuery.get( url, undefined, callback, "script" ); + }, + + getJSON: function( url, data, callback ) { + return jQuery.get( url, data, callback, "json" ); + }, + + // Creates a full fledged settings object into target + // with both ajaxSettings and settings fields. + // If target is omitted, writes into ajaxSettings. + ajaxSetup: function( target, settings ) { + if ( settings ) { + // Building a settings object + ajaxExtend( target, jQuery.ajaxSettings ); + } else { + // Extending ajaxSettings + settings = target; + target = jQuery.ajaxSettings; + } + ajaxExtend( target, settings ); + return target; + }, + + ajaxSettings: { + url: ajaxLocation, + isLocal: rlocalProtocol.test( ajaxLocParts[ 1 ] ), + global: true, + type: "GET", + contentType: "application/x-www-form-urlencoded; charset=UTF-8", + processData: true, + async: true, + /* + timeout: 0, + data: null, + dataType: null, + username: null, + password: null, + cache: null, + throws: false, + traditional: false, + headers: {}, + */ + + accepts: { + xml: "application/xml, text/xml", + html: "text/html", + text: "text/plain", + json: "application/json, text/javascript", + "*": allTypes + }, + + contents: { + xml: /xml/, + html: /html/, + json: /json/ + }, + + responseFields: { + xml: "responseXML", + text: "responseText" + }, + + // List of data converters + // 1) key format is "source_type destination_type" (a single space in-between) + // 2) the catchall symbol "*" can be used for source_type + converters: { + + // Convert anything to text + "* text": window.String, + + // Text to html (true = no transformation) + "text html": true, + + // Evaluate text as a json expression + "text json": jQuery.parseJSON, + + // Parse text as xml + "text xml": jQuery.parseXML + }, + + // For options that shouldn't be deep extended: + // you can add your own custom options here if + // and when you create one that shouldn't be + // deep extended (see ajaxExtend) + flatOptions: { + context: true, + url: true + } + }, + + ajaxPrefilter: addToPrefiltersOrTransports( prefilters ), + ajaxTransport: addToPrefiltersOrTransports( transports ), + + // Main method + ajax: function( url, options ) { + + // If url is an object, simulate pre-1.5 signature + if ( typeof url === "object" ) { + options = url; + url = undefined; + } + + // Force options to be an object + options = options || {}; + + var // ifModified key + ifModifiedKey, + // Response headers + responseHeadersString, + responseHeaders, + // transport + transport, + // timeout handle + timeoutTimer, + // Cross-domain detection vars + parts, + // To know if global events are to be dispatched + fireGlobals, + // Loop variable + i, + // Create the final options object + s = jQuery.ajaxSetup( {}, options ), + // Callbacks context + callbackContext = s.context || s, + // Context for global events + // It's the callbackContext if one was provided in the options + // and if it's a DOM node or a jQuery collection + globalEventContext = callbackContext !== s && + ( callbackContext.nodeType || callbackContext instanceof jQuery ) ? + jQuery( callbackContext ) : jQuery.event, + // Deferreds + deferred = jQuery.Deferred(), + completeDeferred = jQuery.Callbacks( "once memory" ), + // Status-dependent callbacks + statusCode = s.statusCode || {}, + // Headers (they are sent all at once) + requestHeaders = {}, + requestHeadersNames = {}, + // The jqXHR state + state = 0, + // Default abort message + strAbort = "canceled", + // Fake xhr + jqXHR = { + + readyState: 0, + + // Caches the header + setRequestHeader: function( name, value ) { + if ( !state ) { + var lname = name.toLowerCase(); + name = requestHeadersNames[ lname ] = requestHeadersNames[ lname ] || name; + requestHeaders[ name ] = value; + } + return this; + }, + + // Raw string + getAllResponseHeaders: function() { + return state === 2 ? responseHeadersString : null; + }, + + // Builds headers hashtable if needed + getResponseHeader: function( key ) { + var match; + if ( state === 2 ) { + if ( !responseHeaders ) { + responseHeaders = {}; + while( ( match = rheaders.exec( responseHeadersString ) ) ) { + responseHeaders[ match[1].toLowerCase() ] = match[ 2 ]; + } + } + match = responseHeaders[ key.toLowerCase() ]; + } + return match === undefined ? null : match; + }, + + // Overrides response content-type header + overrideMimeType: function( type ) { + if ( !state ) { + s.mimeType = type; + } + return this; + }, + + // Cancel the request + abort: function( statusText ) { + statusText = statusText || strAbort; + if ( transport ) { + transport.abort( statusText ); + } + done( 0, statusText ); + return this; + } + }; + + // Callback for when everything is done + // It is defined here because jslint complains if it is declared + // at the end of the function (which would be more logical and readable) + function done( status, nativeStatusText, responses, headers ) { + var isSuccess, success, error, response, modified, + statusText = nativeStatusText; + + // Called once + if ( state === 2 ) { + return; + } + + // State is "done" now + state = 2; + + // Clear timeout if it exists + if ( timeoutTimer ) { + clearTimeout( timeoutTimer ); + } + + // Dereference transport for early garbage collection + // (no matter how long the jqXHR object will be used) + transport = undefined; + + // Cache response headers + responseHeadersString = headers || ""; + + // Set readyState + jqXHR.readyState = status > 0 ? 4 : 0; + + // Get response data + if ( responses ) { + response = ajaxHandleResponses( s, jqXHR, responses ); + } + + // If successful, handle type chaining + if ( status >= 200 && status < 300 || status === 304 ) { + + // Set the If-Modified-Since and/or If-None-Match header, if in ifModified mode. + if ( s.ifModified ) { + + modified = jqXHR.getResponseHeader("Last-Modified"); + if ( modified ) { + jQuery.lastModified[ ifModifiedKey ] = modified; + } + modified = jqXHR.getResponseHeader("Etag"); + if ( modified ) { + jQuery.etag[ ifModifiedKey ] = modified; + } + } + + // If not modified + if ( status === 304 ) { + + statusText = "notmodified"; + isSuccess = true; + + // If we have data + } else { + + isSuccess = ajaxConvert( s, response ); + statusText = isSuccess.state; + success = isSuccess.data; + error = isSuccess.error; + isSuccess = !error; + } + } else { + // We extract error from statusText + // then normalize statusText and status for non-aborts + error = statusText; + if ( !statusText || status ) { + statusText = "error"; + if ( status < 0 ) { + status = 0; + } + } + } + + // Set data for the fake xhr object + jqXHR.status = status; + jqXHR.statusText = ( nativeStatusText || statusText ) + ""; + + // Success/Error + if ( isSuccess ) { + deferred.resolveWith( callbackContext, [ success, statusText, jqXHR ] ); + } else { + deferred.rejectWith( callbackContext, [ jqXHR, statusText, error ] ); + } + + // Status-dependent callbacks + jqXHR.statusCode( statusCode ); + statusCode = undefined; + + if ( fireGlobals ) { + globalEventContext.trigger( "ajax" + ( isSuccess ? "Success" : "Error" ), + [ jqXHR, s, isSuccess ? success : error ] ); + } + + // Complete + completeDeferred.fireWith( callbackContext, [ jqXHR, statusText ] ); + + if ( fireGlobals ) { + globalEventContext.trigger( "ajaxComplete", [ jqXHR, s ] ); + // Handle the global AJAX counter + if ( !( --jQuery.active ) ) { + jQuery.event.trigger( "ajaxStop" ); + } + } + } + + // Attach deferreds + deferred.promise( jqXHR ); + jqXHR.success = jqXHR.done; + jqXHR.error = jqXHR.fail; + jqXHR.complete = completeDeferred.add; + + // Status-dependent callbacks + jqXHR.statusCode = function( map ) { + if ( map ) { + var tmp; + if ( state < 2 ) { + for ( tmp in map ) { + statusCode[ tmp ] = [ statusCode[tmp], map[tmp] ]; + } + } else { + tmp = map[ jqXHR.status ]; + jqXHR.always( tmp ); + } + } + return this; + }; + + // Remove hash character (#7531: and string promotion) + // Add protocol if not provided (#5866: IE7 issue with protocol-less urls) + // We also use the url parameter if available + s.url = ( ( url || s.url ) + "" ).replace( rhash, "" ).replace( rprotocol, ajaxLocParts[ 1 ] + "//" ); + + // Extract dataTypes list + s.dataTypes = jQuery.trim( s.dataType || "*" ).toLowerCase().split( core_rspace ); + + // A cross-domain request is in order when we have a protocol:host:port mismatch + if ( s.crossDomain == null ) { + parts = rurl.exec( s.url.toLowerCase() ); + s.crossDomain = !!( parts && + ( parts[ 1 ] !== ajaxLocParts[ 1 ] || parts[ 2 ] !== ajaxLocParts[ 2 ] || + ( parts[ 3 ] || ( parts[ 1 ] === "http:" ? 80 : 443 ) ) != + ( ajaxLocParts[ 3 ] || ( ajaxLocParts[ 1 ] === "http:" ? 80 : 443 ) ) ) + ); + } + + // Convert data if not already a string + if ( s.data && s.processData && typeof s.data !== "string" ) { + s.data = jQuery.param( s.data, s.traditional ); + } + + // Apply prefilters + inspectPrefiltersOrTransports( prefilters, s, options, jqXHR ); + + // If request was aborted inside a prefilter, stop there + if ( state === 2 ) { + return jqXHR; + } + + // We can fire global events as of now if asked to + fireGlobals = s.global; + + // Uppercase the type + s.type = s.type.toUpperCase(); + + // Determine if request has content + s.hasContent = !rnoContent.test( s.type ); + + // Watch for a new set of requests + if ( fireGlobals && jQuery.active++ === 0 ) { + jQuery.event.trigger( "ajaxStart" ); + } + + // More options handling for requests with no content + if ( !s.hasContent ) { + + // If data is available, append data to url + if ( s.data ) { + s.url += ( rquery.test( s.url ) ? "&" : "?" ) + s.data; + // #9682: remove data so that it's not used in an eventual retry + delete s.data; + } + + // Get ifModifiedKey before adding the anti-cache parameter + ifModifiedKey = s.url; + + // Add anti-cache in url if needed + if ( s.cache === false ) { + + var ts = jQuery.now(), + // try replacing _= if it is there + ret = s.url.replace( rts, "$1_=" + ts ); + + // if nothing was replaced, add timestamp to the end + s.url = ret + ( ( ret === s.url ) ? ( rquery.test( s.url ) ? "&" : "?" ) + "_=" + ts : "" ); + } + } + + // Set the correct header, if data is being sent + if ( s.data && s.hasContent && s.contentType !== false || options.contentType ) { + jqXHR.setRequestHeader( "Content-Type", s.contentType ); + } + + // Set the If-Modified-Since and/or If-None-Match header, if in ifModified mode. + if ( s.ifModified ) { + ifModifiedKey = ifModifiedKey || s.url; + if ( jQuery.lastModified[ ifModifiedKey ] ) { + jqXHR.setRequestHeader( "If-Modified-Since", jQuery.lastModified[ ifModifiedKey ] ); + } + if ( jQuery.etag[ ifModifiedKey ] ) { + jqXHR.setRequestHeader( "If-None-Match", jQuery.etag[ ifModifiedKey ] ); + } + } + + // Set the Accepts header for the server, depending on the dataType + jqXHR.setRequestHeader( + "Accept", + s.dataTypes[ 0 ] && s.accepts[ s.dataTypes[0] ] ? + s.accepts[ s.dataTypes[0] ] + ( s.dataTypes[ 0 ] !== "*" ? ", " + allTypes + "; q=0.01" : "" ) : + s.accepts[ "*" ] + ); + + // Check for headers option + for ( i in s.headers ) { + jqXHR.setRequestHeader( i, s.headers[ i ] ); + } + + // Allow custom headers/mimetypes and early abort + if ( s.beforeSend && ( s.beforeSend.call( callbackContext, jqXHR, s ) === false || state === 2 ) ) { + // Abort if not done already and return + return jqXHR.abort(); + + } + + // aborting is no longer a cancellation + strAbort = "abort"; + + // Install callbacks on deferreds + for ( i in { success: 1, error: 1, complete: 1 } ) { + jqXHR[ i ]( s[ i ] ); + } + + // Get transport + transport = inspectPrefiltersOrTransports( transports, s, options, jqXHR ); + + // If no transport, we auto-abort + if ( !transport ) { + done( -1, "No Transport" ); + } else { + jqXHR.readyState = 1; + // Send global event + if ( fireGlobals ) { + globalEventContext.trigger( "ajaxSend", [ jqXHR, s ] ); + } + // Timeout + if ( s.async && s.timeout > 0 ) { + timeoutTimer = setTimeout( function(){ + jqXHR.abort( "timeout" ); + }, s.timeout ); + } + + try { + state = 1; + transport.send( requestHeaders, done ); + } catch (e) { + // Propagate exception as error if not done + if ( state < 2 ) { + done( -1, e ); + // Simply rethrow otherwise + } else { + throw e; + } + } + } + + return jqXHR; + }, + + // Counter for holding the number of active queries + active: 0, + + // Last-Modified header cache for next request + lastModified: {}, + etag: {} + +}); + +/* Handles responses to an ajax request: + * - sets all responseXXX fields accordingly + * - finds the right dataType (mediates between content-type and expected dataType) + * - returns the corresponding response + */ +function ajaxHandleResponses( s, jqXHR, responses ) { + + var ct, type, finalDataType, firstDataType, + contents = s.contents, + dataTypes = s.dataTypes, + responseFields = s.responseFields; + + // Fill responseXXX fields + for ( type in responseFields ) { + if ( type in responses ) { + jqXHR[ responseFields[type] ] = responses[ type ]; + } + } + + // Remove auto dataType and get content-type in the process + while( dataTypes[ 0 ] === "*" ) { + dataTypes.shift(); + if ( ct === undefined ) { + ct = s.mimeType || jqXHR.getResponseHeader( "content-type" ); + } + } + + // Check if we're dealing with a known content-type + if ( ct ) { + for ( type in contents ) { + if ( contents[ type ] && contents[ type ].test( ct ) ) { + dataTypes.unshift( type ); + break; + } + } + } + + // Check to see if we have a response for the expected dataType + if ( dataTypes[ 0 ] in responses ) { + finalDataType = dataTypes[ 0 ]; + } else { + // Try convertible dataTypes + for ( type in responses ) { + if ( !dataTypes[ 0 ] || s.converters[ type + " " + dataTypes[0] ] ) { + finalDataType = type; + break; + } + if ( !firstDataType ) { + firstDataType = type; + } + } + // Or just use first one + finalDataType = finalDataType || firstDataType; + } + + // If we found a dataType + // We add the dataType to the list if needed + // and return the corresponding response + if ( finalDataType ) { + if ( finalDataType !== dataTypes[ 0 ] ) { + dataTypes.unshift( finalDataType ); + } + return responses[ finalDataType ]; + } +} + +// Chain conversions given the request and the original response +function ajaxConvert( s, response ) { + + var conv, conv2, current, tmp, + // Work with a copy of dataTypes in case we need to modify it for conversion + dataTypes = s.dataTypes.slice(), + prev = dataTypes[ 0 ], + converters = {}, + i = 0; + + // Apply the dataFilter if provided + if ( s.dataFilter ) { + response = s.dataFilter( response, s.dataType ); + } + + // Create converters map with lowercased keys + if ( dataTypes[ 1 ] ) { + for ( conv in s.converters ) { + converters[ conv.toLowerCase() ] = s.converters[ conv ]; + } + } + + // Convert to each sequential dataType, tolerating list modification + for ( ; (current = dataTypes[++i]); ) { + + // There's only work to do if current dataType is non-auto + if ( current !== "*" ) { + + // Convert response if prev dataType is non-auto and differs from current + if ( prev !== "*" && prev !== current ) { + + // Seek a direct converter + conv = converters[ prev + " " + current ] || converters[ "* " + current ]; + + // If none found, seek a pair + if ( !conv ) { + for ( conv2 in converters ) { + + // If conv2 outputs current + tmp = conv2.split(" "); + if ( tmp[ 1 ] === current ) { + + // If prev can be converted to accepted input + conv = converters[ prev + " " + tmp[ 0 ] ] || + converters[ "* " + tmp[ 0 ] ]; + if ( conv ) { + // Condense equivalence converters + if ( conv === true ) { + conv = converters[ conv2 ]; + + // Otherwise, insert the intermediate dataType + } else if ( converters[ conv2 ] !== true ) { + current = tmp[ 0 ]; + dataTypes.splice( i--, 0, current ); + } + + break; + } + } + } + } + + // Apply converter (if not an equivalence) + if ( conv !== true ) { + + // Unless errors are allowed to bubble, catch and return them + if ( conv && s["throws"] ) { + response = conv( response ); + } else { + try { + response = conv( response ); + } catch ( e ) { + return { state: "parsererror", error: conv ? e : "No conversion from " + prev + " to " + current }; + } + } + } + } + + // Update prev for next iteration + prev = current; + } + } + + return { state: "success", data: response }; +} +var oldCallbacks = [], + rquestion = /\?/, + rjsonp = /(=)\?(?=&|$)|\?\?/, + nonce = jQuery.now(); + +// Default jsonp settings +jQuery.ajaxSetup({ + jsonp: "callback", + jsonpCallback: function() { + var callback = oldCallbacks.pop() || ( jQuery.expando + "_" + ( nonce++ ) ); + this[ callback ] = true; + return callback; + } +}); + +// Detect, normalize options and install callbacks for jsonp requests +jQuery.ajaxPrefilter( "json jsonp", function( s, originalSettings, jqXHR ) { + + var callbackName, overwritten, responseContainer, + data = s.data, + url = s.url, + hasCallback = s.jsonp !== false, + replaceInUrl = hasCallback && rjsonp.test( url ), + replaceInData = hasCallback && !replaceInUrl && typeof data === "string" && + !( s.contentType || "" ).indexOf("application/x-www-form-urlencoded") && + rjsonp.test( data ); + + // Handle iff the expected data type is "jsonp" or we have a parameter to set + if ( s.dataTypes[ 0 ] === "jsonp" || replaceInUrl || replaceInData ) { + + // Get callback name, remembering preexisting value associated with it + callbackName = s.jsonpCallback = jQuery.isFunction( s.jsonpCallback ) ? + s.jsonpCallback() : + s.jsonpCallback; + overwritten = window[ callbackName ]; + + // Insert callback into url or form data + if ( replaceInUrl ) { + s.url = url.replace( rjsonp, "$1" + callbackName ); + } else if ( replaceInData ) { + s.data = data.replace( rjsonp, "$1" + callbackName ); + } else if ( hasCallback ) { + s.url += ( rquestion.test( url ) ? "&" : "?" ) + s.jsonp + "=" + callbackName; + } + + // Use data converter to retrieve json after script execution + s.converters["script json"] = function() { + if ( !responseContainer ) { + jQuery.error( callbackName + " was not called" ); + } + return responseContainer[ 0 ]; + }; + + // force json dataType + s.dataTypes[ 0 ] = "json"; + + // Install callback + window[ callbackName ] = function() { + responseContainer = arguments; + }; + + // Clean-up function (fires after converters) + jqXHR.always(function() { + // Restore preexisting value + window[ callbackName ] = overwritten; + + // Save back as free + if ( s[ callbackName ] ) { + // make sure that re-using the options doesn't screw things around + s.jsonpCallback = originalSettings.jsonpCallback; + + // save the callback name for future use + oldCallbacks.push( callbackName ); + } + + // Call if it was a function and we have a response + if ( responseContainer && jQuery.isFunction( overwritten ) ) { + overwritten( responseContainer[ 0 ] ); + } + + responseContainer = overwritten = undefined; + }); + + // Delegate to script + return "script"; + } +}); +// Install script dataType +jQuery.ajaxSetup({ + accepts: { + script: "text/javascript, application/javascript, application/ecmascript, application/x-ecmascript" + }, + contents: { + script: /javascript|ecmascript/ + }, + converters: { + "text script": function( text ) { + jQuery.globalEval( text ); + return text; + } + } +}); + +// Handle cache's special case and global +jQuery.ajaxPrefilter( "script", function( s ) { + if ( s.cache === undefined ) { + s.cache = false; + } + if ( s.crossDomain ) { + s.type = "GET"; + s.global = false; + } +}); + +// Bind script tag hack transport +jQuery.ajaxTransport( "script", function(s) { + + // This transport only deals with cross domain requests + if ( s.crossDomain ) { + + var script, + head = document.head || document.getElementsByTagName( "head" )[0] || document.documentElement; + + return { + + send: function( _, callback ) { + + script = document.createElement( "script" ); + + script.async = "async"; + + if ( s.scriptCharset ) { + script.charset = s.scriptCharset; + } + + script.src = s.url; + + // Attach handlers for all browsers + script.onload = script.onreadystatechange = function( _, isAbort ) { + + if ( isAbort || !script.readyState || /loaded|complete/.test( script.readyState ) ) { + + // Handle memory leak in IE + script.onload = script.onreadystatechange = null; + + // Remove the script + if ( head && script.parentNode ) { + head.removeChild( script ); + } + + // Dereference the script + script = undefined; + + // Callback if not abort + if ( !isAbort ) { + callback( 200, "success" ); + } + } + }; + // Use insertBefore instead of appendChild to circumvent an IE6 bug. + // This arises when a base node is used (#2709 and #4378). + head.insertBefore( script, head.firstChild ); + }, + + abort: function() { + if ( script ) { + script.onload( 0, 1 ); + } + } + }; + } +}); +var xhrCallbacks, + // #5280: Internet Explorer will keep connections alive if we don't abort on unload + xhrOnUnloadAbort = window.ActiveXObject ? function() { + // Abort all pending requests + for ( var key in xhrCallbacks ) { + xhrCallbacks[ key ]( 0, 1 ); + } + } : false, + xhrId = 0; + +// Functions to create xhrs +function createStandardXHR() { + try { + return new window.XMLHttpRequest(); + } catch( e ) {} +} + +function createActiveXHR() { + try { + return new window.ActiveXObject( "Microsoft.XMLHTTP" ); + } catch( e ) {} +} + +// Create the request object +// (This is still attached to ajaxSettings for backward compatibility) +jQuery.ajaxSettings.xhr = window.ActiveXObject ? + /* Microsoft failed to properly + * implement the XMLHttpRequest in IE7 (can't request local files), + * so we use the ActiveXObject when it is available + * Additionally XMLHttpRequest can be disabled in IE7/IE8 so + * we need a fallback. + */ + function() { + return !this.isLocal && createStandardXHR() || createActiveXHR(); + } : + // For all other browsers, use the standard XMLHttpRequest object + createStandardXHR; + +// Determine support properties +(function( xhr ) { + jQuery.extend( jQuery.support, { + ajax: !!xhr, + cors: !!xhr && ( "withCredentials" in xhr ) + }); +})( jQuery.ajaxSettings.xhr() ); + +// Create transport if the browser can provide an xhr +if ( jQuery.support.ajax ) { + + jQuery.ajaxTransport(function( s ) { + // Cross domain only allowed if supported through XMLHttpRequest + if ( !s.crossDomain || jQuery.support.cors ) { + + var callback; + + return { + send: function( headers, complete ) { + + // Get a new xhr + var handle, i, + xhr = s.xhr(); + + // Open the socket + // Passing null username, generates a login popup on Opera (#2865) + if ( s.username ) { + xhr.open( s.type, s.url, s.async, s.username, s.password ); + } else { + xhr.open( s.type, s.url, s.async ); + } + + // Apply custom fields if provided + if ( s.xhrFields ) { + for ( i in s.xhrFields ) { + xhr[ i ] = s.xhrFields[ i ]; + } + } + + // Override mime type if needed + if ( s.mimeType && xhr.overrideMimeType ) { + xhr.overrideMimeType( s.mimeType ); + } + + // X-Requested-With header + // For cross-domain requests, seeing as conditions for a preflight are + // akin to a jigsaw puzzle, we simply never set it to be sure. + // (it can always be set on a per-request basis or even using ajaxSetup) + // For same-domain requests, won't change header if already provided. + if ( !s.crossDomain && !headers["X-Requested-With"] ) { + headers[ "X-Requested-With" ] = "XMLHttpRequest"; + } + + // Need an extra try/catch for cross domain requests in Firefox 3 + try { + for ( i in headers ) { + xhr.setRequestHeader( i, headers[ i ] ); + } + } catch( _ ) {} + + // Do send the request + // This may raise an exception which is actually + // handled in jQuery.ajax (so no try/catch here) + xhr.send( ( s.hasContent && s.data ) || null ); + + // Listener + callback = function( _, isAbort ) { + + var status, + statusText, + responseHeaders, + responses, + xml; + + // Firefox throws exceptions when accessing properties + // of an xhr when a network error occurred + // http://helpful.knobs-dials.com/index.php/Component_returned_failure_code:_0x80040111_(NS_ERROR_NOT_AVAILABLE) + try { + + // Was never called and is aborted or complete + if ( callback && ( isAbort || xhr.readyState === 4 ) ) { + + // Only called once + callback = undefined; + + // Do not keep as active anymore + if ( handle ) { + xhr.onreadystatechange = jQuery.noop; + if ( xhrOnUnloadAbort ) { + delete xhrCallbacks[ handle ]; + } + } + + // If it's an abort + if ( isAbort ) { + // Abort it manually if needed + if ( xhr.readyState !== 4 ) { + xhr.abort(); + } + } else { + status = xhr.status; + responseHeaders = xhr.getAllResponseHeaders(); + responses = {}; + xml = xhr.responseXML; + + // Construct response list + if ( xml && xml.documentElement /* #4958 */ ) { + responses.xml = xml; + } + + // When requesting binary data, IE6-9 will throw an exception + // on any attempt to access responseText (#11426) + try { + responses.text = xhr.responseText; + } catch( e ) { + } + + // Firefox throws an exception when accessing + // statusText for faulty cross-domain requests + try { + statusText = xhr.statusText; + } catch( e ) { + // We normalize with Webkit giving an empty statusText + statusText = ""; + } + + // Filter status for non standard behaviors + + // If the request is local and we have data: assume a success + // (success with no data won't get notified, that's the best we + // can do given current implementations) + if ( !status && s.isLocal && !s.crossDomain ) { + status = responses.text ? 200 : 404; + // IE - #1450: sometimes returns 1223 when it should be 204 + } else if ( status === 1223 ) { + status = 204; + } + } + } + } catch( firefoxAccessException ) { + if ( !isAbort ) { + complete( -1, firefoxAccessException ); + } + } + + // Call complete if needed + if ( responses ) { + complete( status, statusText, responses, responseHeaders ); + } + }; + + if ( !s.async ) { + // if we're in sync mode we fire the callback + callback(); + } else if ( xhr.readyState === 4 ) { + // (IE6 & IE7) if it's in cache and has been + // retrieved directly we need to fire the callback + setTimeout( callback, 0 ); + } else { + handle = ++xhrId; + if ( xhrOnUnloadAbort ) { + // Create the active xhrs callbacks list if needed + // and attach the unload handler + if ( !xhrCallbacks ) { + xhrCallbacks = {}; + jQuery( window ).unload( xhrOnUnloadAbort ); + } + // Add to list of active xhrs callbacks + xhrCallbacks[ handle ] = callback; + } + xhr.onreadystatechange = callback; + } + }, + + abort: function() { + if ( callback ) { + callback(0,1); + } + } + }; + } + }); +} +var fxNow, timerId, + rfxtypes = /^(?:toggle|show|hide)$/, + rfxnum = new RegExp( "^(?:([-+])=|)(" + core_pnum + ")([a-z%]*)$", "i" ), + rrun = /queueHooks$/, + animationPrefilters = [ defaultPrefilter ], + tweeners = { + "*": [function( prop, value ) { + var end, unit, + tween = this.createTween( prop, value ), + parts = rfxnum.exec( value ), + target = tween.cur(), + start = +target || 0, + scale = 1, + maxIterations = 20; + + if ( parts ) { + end = +parts[2]; + unit = parts[3] || ( jQuery.cssNumber[ prop ] ? "" : "px" ); + + // We need to compute starting value + if ( unit !== "px" && start ) { + // Iteratively approximate from a nonzero starting point + // Prefer the current property, because this process will be trivial if it uses the same units + // Fallback to end or a simple constant + start = jQuery.css( tween.elem, prop, true ) || end || 1; + + do { + // If previous iteration zeroed out, double until we get *something* + // Use a string for doubling factor so we don't accidentally see scale as unchanged below + scale = scale || ".5"; + + // Adjust and apply + start = start / scale; + jQuery.style( tween.elem, prop, start + unit ); + + // Update scale, tolerating zero or NaN from tween.cur() + // And breaking the loop if scale is unchanged or perfect, or if we've just had enough + } while ( scale !== (scale = tween.cur() / target) && scale !== 1 && --maxIterations ); + } + + tween.unit = unit; + tween.start = start; + // If a +=/-= token was provided, we're doing a relative animation + tween.end = parts[1] ? start + ( parts[1] + 1 ) * end : end; + } + return tween; + }] + }; + +// Animations created synchronously will run synchronously +function createFxNow() { + setTimeout(function() { + fxNow = undefined; + }, 0 ); + return ( fxNow = jQuery.now() ); +} + +function createTweens( animation, props ) { + jQuery.each( props, function( prop, value ) { + var collection = ( tweeners[ prop ] || [] ).concat( tweeners[ "*" ] ), + index = 0, + length = collection.length; + for ( ; index < length; index++ ) { + if ( collection[ index ].call( animation, prop, value ) ) { + + // we're done with this property + return; + } + } + }); +} + +function Animation( elem, properties, options ) { + var result, + index = 0, + tweenerIndex = 0, + length = animationPrefilters.length, + deferred = jQuery.Deferred().always( function() { + // don't match elem in the :animated selector + delete tick.elem; + }), + tick = function() { + var currentTime = fxNow || createFxNow(), + remaining = Math.max( 0, animation.startTime + animation.duration - currentTime ), + // archaic crash bug won't allow us to use 1 - ( 0.5 || 0 ) (#12497) + temp = remaining / animation.duration || 0, + percent = 1 - temp, + index = 0, + length = animation.tweens.length; + + for ( ; index < length ; index++ ) { + animation.tweens[ index ].run( percent ); + } + + deferred.notifyWith( elem, [ animation, percent, remaining ]); + + if ( percent < 1 && length ) { + return remaining; + } else { + deferred.resolveWith( elem, [ animation ] ); + return false; + } + }, + animation = deferred.promise({ + elem: elem, + props: jQuery.extend( {}, properties ), + opts: jQuery.extend( true, { specialEasing: {} }, options ), + originalProperties: properties, + originalOptions: options, + startTime: fxNow || createFxNow(), + duration: options.duration, + tweens: [], + createTween: function( prop, end, easing ) { + var tween = jQuery.Tween( elem, animation.opts, prop, end, + animation.opts.specialEasing[ prop ] || animation.opts.easing ); + animation.tweens.push( tween ); + return tween; + }, + stop: function( gotoEnd ) { + var index = 0, + // if we are going to the end, we want to run all the tweens + // otherwise we skip this part + length = gotoEnd ? animation.tweens.length : 0; + + for ( ; index < length ; index++ ) { + animation.tweens[ index ].run( 1 ); + } + + // resolve when we played the last frame + // otherwise, reject + if ( gotoEnd ) { + deferred.resolveWith( elem, [ animation, gotoEnd ] ); + } else { + deferred.rejectWith( elem, [ animation, gotoEnd ] ); + } + return this; + } + }), + props = animation.props; + + propFilter( props, animation.opts.specialEasing ); + + for ( ; index < length ; index++ ) { + result = animationPrefilters[ index ].call( animation, elem, props, animation.opts ); + if ( result ) { + return result; + } + } + + createTweens( animation, props ); + + if ( jQuery.isFunction( animation.opts.start ) ) { + animation.opts.start.call( elem, animation ); + } + + jQuery.fx.timer( + jQuery.extend( tick, { + anim: animation, + queue: animation.opts.queue, + elem: elem + }) + ); + + // attach callbacks from options + return animation.progress( animation.opts.progress ) + .done( animation.opts.done, animation.opts.complete ) + .fail( animation.opts.fail ) + .always( animation.opts.always ); +} + +function propFilter( props, specialEasing ) { + var index, name, easing, value, hooks; + + // camelCase, specialEasing and expand cssHook pass + for ( index in props ) { + name = jQuery.camelCase( index ); + easing = specialEasing[ name ]; + value = props[ index ]; + if ( jQuery.isArray( value ) ) { + easing = value[ 1 ]; + value = props[ index ] = value[ 0 ]; + } + + if ( index !== name ) { + props[ name ] = value; + delete props[ index ]; + } + + hooks = jQuery.cssHooks[ name ]; + if ( hooks && "expand" in hooks ) { + value = hooks.expand( value ); + delete props[ name ]; + + // not quite $.extend, this wont overwrite keys already present. + // also - reusing 'index' from above because we have the correct "name" + for ( index in value ) { + if ( !( index in props ) ) { + props[ index ] = value[ index ]; + specialEasing[ index ] = easing; + } + } + } else { + specialEasing[ name ] = easing; + } + } +} + +jQuery.Animation = jQuery.extend( Animation, { + + tweener: function( props, callback ) { + if ( jQuery.isFunction( props ) ) { + callback = props; + props = [ "*" ]; + } else { + props = props.split(" "); + } + + var prop, + index = 0, + length = props.length; + + for ( ; index < length ; index++ ) { + prop = props[ index ]; + tweeners[ prop ] = tweeners[ prop ] || []; + tweeners[ prop ].unshift( callback ); + } + }, + + prefilter: function( callback, prepend ) { + if ( prepend ) { + animationPrefilters.unshift( callback ); + } else { + animationPrefilters.push( callback ); + } + } +}); + +function defaultPrefilter( elem, props, opts ) { + var index, prop, value, length, dataShow, toggle, tween, hooks, oldfire, + anim = this, + style = elem.style, + orig = {}, + handled = [], + hidden = elem.nodeType && isHidden( elem ); + + // handle queue: false promises + if ( !opts.queue ) { + hooks = jQuery._queueHooks( elem, "fx" ); + if ( hooks.unqueued == null ) { + hooks.unqueued = 0; + oldfire = hooks.empty.fire; + hooks.empty.fire = function() { + if ( !hooks.unqueued ) { + oldfire(); + } + }; + } + hooks.unqueued++; + + anim.always(function() { + // doing this makes sure that the complete handler will be called + // before this completes + anim.always(function() { + hooks.unqueued--; + if ( !jQuery.queue( elem, "fx" ).length ) { + hooks.empty.fire(); + } + }); + }); + } + + // height/width overflow pass + if ( elem.nodeType === 1 && ( "height" in props || "width" in props ) ) { + // Make sure that nothing sneaks out + // Record all 3 overflow attributes because IE does not + // change the overflow attribute when overflowX and + // overflowY are set to the same value + opts.overflow = [ style.overflow, style.overflowX, style.overflowY ]; + + // Set display property to inline-block for height/width + // animations on inline elements that are having width/height animated + if ( jQuery.css( elem, "display" ) === "inline" && + jQuery.css( elem, "float" ) === "none" ) { + + // inline-level elements accept inline-block; + // block-level elements need to be inline with layout + if ( !jQuery.support.inlineBlockNeedsLayout || css_defaultDisplay( elem.nodeName ) === "inline" ) { + style.display = "inline-block"; + + } else { + style.zoom = 1; + } + } + } + + if ( opts.overflow ) { + style.overflow = "hidden"; + if ( !jQuery.support.shrinkWrapBlocks ) { + anim.done(function() { + style.overflow = opts.overflow[ 0 ]; + style.overflowX = opts.overflow[ 1 ]; + style.overflowY = opts.overflow[ 2 ]; + }); + } + } + + + // show/hide pass + for ( index in props ) { + value = props[ index ]; + if ( rfxtypes.exec( value ) ) { + delete props[ index ]; + toggle = toggle || value === "toggle"; + if ( value === ( hidden ? "hide" : "show" ) ) { + continue; + } + handled.push( index ); + } + } + + length = handled.length; + if ( length ) { + dataShow = jQuery._data( elem, "fxshow" ) || jQuery._data( elem, "fxshow", {} ); + if ( "hidden" in dataShow ) { + hidden = dataShow.hidden; + } + + // store state if its toggle - enables .stop().toggle() to "reverse" + if ( toggle ) { + dataShow.hidden = !hidden; + } + if ( hidden ) { + jQuery( elem ).show(); + } else { + anim.done(function() { + jQuery( elem ).hide(); + }); + } + anim.done(function() { + var prop; + jQuery.removeData( elem, "fxshow", true ); + for ( prop in orig ) { + jQuery.style( elem, prop, orig[ prop ] ); + } + }); + for ( index = 0 ; index < length ; index++ ) { + prop = handled[ index ]; + tween = anim.createTween( prop, hidden ? dataShow[ prop ] : 0 ); + orig[ prop ] = dataShow[ prop ] || jQuery.style( elem, prop ); + + if ( !( prop in dataShow ) ) { + dataShow[ prop ] = tween.start; + if ( hidden ) { + tween.end = tween.start; + tween.start = prop === "width" || prop === "height" ? 1 : 0; + } + } + } + } +} + +function Tween( elem, options, prop, end, easing ) { + return new Tween.prototype.init( elem, options, prop, end, easing ); +} +jQuery.Tween = Tween; + +Tween.prototype = { + constructor: Tween, + init: function( elem, options, prop, end, easing, unit ) { + this.elem = elem; + this.prop = prop; + this.easing = easing || "swing"; + this.options = options; + this.start = this.now = this.cur(); + this.end = end; + this.unit = unit || ( jQuery.cssNumber[ prop ] ? "" : "px" ); + }, + cur: function() { + var hooks = Tween.propHooks[ this.prop ]; + + return hooks && hooks.get ? + hooks.get( this ) : + Tween.propHooks._default.get( this ); + }, + run: function( percent ) { + var eased, + hooks = Tween.propHooks[ this.prop ]; + + if ( this.options.duration ) { + this.pos = eased = jQuery.easing[ this.easing ]( + percent, this.options.duration * percent, 0, 1, this.options.duration + ); + } else { + this.pos = eased = percent; + } + this.now = ( this.end - this.start ) * eased + this.start; + + if ( this.options.step ) { + this.options.step.call( this.elem, this.now, this ); + } + + if ( hooks && hooks.set ) { + hooks.set( this ); + } else { + Tween.propHooks._default.set( this ); + } + return this; + } +}; + +Tween.prototype.init.prototype = Tween.prototype; + +Tween.propHooks = { + _default: { + get: function( tween ) { + var result; + + if ( tween.elem[ tween.prop ] != null && + (!tween.elem.style || tween.elem.style[ tween.prop ] == null) ) { + return tween.elem[ tween.prop ]; + } + + // passing any value as a 4th parameter to .css will automatically + // attempt a parseFloat and fallback to a string if the parse fails + // so, simple values such as "10px" are parsed to Float. + // complex values such as "rotate(1rad)" are returned as is. + result = jQuery.css( tween.elem, tween.prop, false, "" ); + // Empty strings, null, undefined and "auto" are converted to 0. + return !result || result === "auto" ? 0 : result; + }, + set: function( tween ) { + // use step hook for back compat - use cssHook if its there - use .style if its + // available and use plain properties where available + if ( jQuery.fx.step[ tween.prop ] ) { + jQuery.fx.step[ tween.prop ]( tween ); + } else if ( tween.elem.style && ( tween.elem.style[ jQuery.cssProps[ tween.prop ] ] != null || jQuery.cssHooks[ tween.prop ] ) ) { + jQuery.style( tween.elem, tween.prop, tween.now + tween.unit ); + } else { + tween.elem[ tween.prop ] = tween.now; + } + } + } +}; + +// Remove in 2.0 - this supports IE8's panic based approach +// to setting things on disconnected nodes + +Tween.propHooks.scrollTop = Tween.propHooks.scrollLeft = { + set: function( tween ) { + if ( tween.elem.nodeType && tween.elem.parentNode ) { + tween.elem[ tween.prop ] = tween.now; + } + } +}; + +jQuery.each([ "toggle", "show", "hide" ], function( i, name ) { + var cssFn = jQuery.fn[ name ]; + jQuery.fn[ name ] = function( speed, easing, callback ) { + return speed == null || typeof speed === "boolean" || + // special check for .toggle( handler, handler, ... ) + ( !i && jQuery.isFunction( speed ) && jQuery.isFunction( easing ) ) ? + cssFn.apply( this, arguments ) : + this.animate( genFx( name, true ), speed, easing, callback ); + }; +}); + +jQuery.fn.extend({ + fadeTo: function( speed, to, easing, callback ) { + + // show any hidden elements after setting opacity to 0 + return this.filter( isHidden ).css( "opacity", 0 ).show() + + // animate to the value specified + .end().animate({ opacity: to }, speed, easing, callback ); + }, + animate: function( prop, speed, easing, callback ) { + var empty = jQuery.isEmptyObject( prop ), + optall = jQuery.speed( speed, easing, callback ), + doAnimation = function() { + // Operate on a copy of prop so per-property easing won't be lost + var anim = Animation( this, jQuery.extend( {}, prop ), optall ); + + // Empty animations resolve immediately + if ( empty ) { + anim.stop( true ); + } + }; + + return empty || optall.queue === false ? + this.each( doAnimation ) : + this.queue( optall.queue, doAnimation ); + }, + stop: function( type, clearQueue, gotoEnd ) { + var stopQueue = function( hooks ) { + var stop = hooks.stop; + delete hooks.stop; + stop( gotoEnd ); + }; + + if ( typeof type !== "string" ) { + gotoEnd = clearQueue; + clearQueue = type; + type = undefined; + } + if ( clearQueue && type !== false ) { + this.queue( type || "fx", [] ); + } + + return this.each(function() { + var dequeue = true, + index = type != null && type + "queueHooks", + timers = jQuery.timers, + data = jQuery._data( this ); + + if ( index ) { + if ( data[ index ] && data[ index ].stop ) { + stopQueue( data[ index ] ); + } + } else { + for ( index in data ) { + if ( data[ index ] && data[ index ].stop && rrun.test( index ) ) { + stopQueue( data[ index ] ); + } + } + } + + for ( index = timers.length; index--; ) { + if ( timers[ index ].elem === this && (type == null || timers[ index ].queue === type) ) { + timers[ index ].anim.stop( gotoEnd ); + dequeue = false; + timers.splice( index, 1 ); + } + } + + // start the next in the queue if the last step wasn't forced + // timers currently will call their complete callbacks, which will dequeue + // but only if they were gotoEnd + if ( dequeue || !gotoEnd ) { + jQuery.dequeue( this, type ); + } + }); + } +}); + +// Generate parameters to create a standard animation +function genFx( type, includeWidth ) { + var which, + attrs = { height: type }, + i = 0; + + // if we include width, step value is 1 to do all cssExpand values, + // if we don't include width, step value is 2 to skip over Left and Right + includeWidth = includeWidth? 1 : 0; + for( ; i < 4 ; i += 2 - includeWidth ) { + which = cssExpand[ i ]; + attrs[ "margin" + which ] = attrs[ "padding" + which ] = type; + } + + if ( includeWidth ) { + attrs.opacity = attrs.width = type; + } + + return attrs; +} + +// Generate shortcuts for custom animations +jQuery.each({ + slideDown: genFx("show"), + slideUp: genFx("hide"), + slideToggle: genFx("toggle"), + fadeIn: { opacity: "show" }, + fadeOut: { opacity: "hide" }, + fadeToggle: { opacity: "toggle" } +}, function( name, props ) { + jQuery.fn[ name ] = function( speed, easing, callback ) { + return this.animate( props, speed, easing, callback ); + }; +}); + +jQuery.speed = function( speed, easing, fn ) { + var opt = speed && typeof speed === "object" ? jQuery.extend( {}, speed ) : { + complete: fn || !fn && easing || + jQuery.isFunction( speed ) && speed, + duration: speed, + easing: fn && easing || easing && !jQuery.isFunction( easing ) && easing + }; + + opt.duration = jQuery.fx.off ? 0 : typeof opt.duration === "number" ? opt.duration : + opt.duration in jQuery.fx.speeds ? jQuery.fx.speeds[ opt.duration ] : jQuery.fx.speeds._default; + + // normalize opt.queue - true/undefined/null -> "fx" + if ( opt.queue == null || opt.queue === true ) { + opt.queue = "fx"; + } + + // Queueing + opt.old = opt.complete; + + opt.complete = function() { + if ( jQuery.isFunction( opt.old ) ) { + opt.old.call( this ); + } + + if ( opt.queue ) { + jQuery.dequeue( this, opt.queue ); + } + }; + + return opt; +}; + +jQuery.easing = { + linear: function( p ) { + return p; + }, + swing: function( p ) { + return 0.5 - Math.cos( p*Math.PI ) / 2; + } +}; + +jQuery.timers = []; +jQuery.fx = Tween.prototype.init; +jQuery.fx.tick = function() { + var timer, + timers = jQuery.timers, + i = 0; + + fxNow = jQuery.now(); + + for ( ; i < timers.length; i++ ) { + timer = timers[ i ]; + // Checks the timer has not already been removed + if ( !timer() && timers[ i ] === timer ) { + timers.splice( i--, 1 ); + } + } + + if ( !timers.length ) { + jQuery.fx.stop(); + } + fxNow = undefined; +}; + +jQuery.fx.timer = function( timer ) { + if ( timer() && jQuery.timers.push( timer ) && !timerId ) { + timerId = setInterval( jQuery.fx.tick, jQuery.fx.interval ); + } +}; + +jQuery.fx.interval = 13; + +jQuery.fx.stop = function() { + clearInterval( timerId ); + timerId = null; +}; + +jQuery.fx.speeds = { + slow: 600, + fast: 200, + // Default speed + _default: 400 +}; + +// Back Compat <1.8 extension point +jQuery.fx.step = {}; + +if ( jQuery.expr && jQuery.expr.filters ) { + jQuery.expr.filters.animated = function( elem ) { + return jQuery.grep(jQuery.timers, function( fn ) { + return elem === fn.elem; + }).length; + }; +} +var rroot = /^(?:body|html)$/i; + +jQuery.fn.offset = function( options ) { + if ( arguments.length ) { + return options === undefined ? + this : + this.each(function( i ) { + jQuery.offset.setOffset( this, options, i ); + }); + } + + var docElem, body, win, clientTop, clientLeft, scrollTop, scrollLeft, + box = { top: 0, left: 0 }, + elem = this[ 0 ], + doc = elem && elem.ownerDocument; + + if ( !doc ) { + return; + } + + if ( (body = doc.body) === elem ) { + return jQuery.offset.bodyOffset( elem ); + } + + docElem = doc.documentElement; + + // Make sure it's not a disconnected DOM node + if ( !jQuery.contains( docElem, elem ) ) { + return box; + } + + // If we don't have gBCR, just use 0,0 rather than error + // BlackBerry 5, iOS 3 (original iPhone) + if ( typeof elem.getBoundingClientRect !== "undefined" ) { + box = elem.getBoundingClientRect(); + } + win = getWindow( doc ); + clientTop = docElem.clientTop || body.clientTop || 0; + clientLeft = docElem.clientLeft || body.clientLeft || 0; + scrollTop = win.pageYOffset || docElem.scrollTop; + scrollLeft = win.pageXOffset || docElem.scrollLeft; + return { + top: box.top + scrollTop - clientTop, + left: box.left + scrollLeft - clientLeft + }; +}; + +jQuery.offset = { + + bodyOffset: function( body ) { + var top = body.offsetTop, + left = body.offsetLeft; + + if ( jQuery.support.doesNotIncludeMarginInBodyOffset ) { + top += parseFloat( jQuery.css(body, "marginTop") ) || 0; + left += parseFloat( jQuery.css(body, "marginLeft") ) || 0; + } + + return { top: top, left: left }; + }, + + setOffset: function( elem, options, i ) { + var position = jQuery.css( elem, "position" ); + + // set position first, in-case top/left are set even on static elem + if ( position === "static" ) { + elem.style.position = "relative"; + } + + var curElem = jQuery( elem ), + curOffset = curElem.offset(), + curCSSTop = jQuery.css( elem, "top" ), + curCSSLeft = jQuery.css( elem, "left" ), + calculatePosition = ( position === "absolute" || position === "fixed" ) && jQuery.inArray("auto", [curCSSTop, curCSSLeft]) > -1, + props = {}, curPosition = {}, curTop, curLeft; + + // need to be able to calculate position if either top or left is auto and position is either absolute or fixed + if ( calculatePosition ) { + curPosition = curElem.position(); + curTop = curPosition.top; + curLeft = curPosition.left; + } else { + curTop = parseFloat( curCSSTop ) || 0; + curLeft = parseFloat( curCSSLeft ) || 0; + } + + if ( jQuery.isFunction( options ) ) { + options = options.call( elem, i, curOffset ); + } + + if ( options.top != null ) { + props.top = ( options.top - curOffset.top ) + curTop; + } + if ( options.left != null ) { + props.left = ( options.left - curOffset.left ) + curLeft; + } + + if ( "using" in options ) { + options.using.call( elem, props ); + } else { + curElem.css( props ); + } + } +}; + + +jQuery.fn.extend({ + + position: function() { + if ( !this[0] ) { + return; + } + + var elem = this[0], + + // Get *real* offsetParent + offsetParent = this.offsetParent(), + + // Get correct offsets + offset = this.offset(), + parentOffset = rroot.test(offsetParent[0].nodeName) ? { top: 0, left: 0 } : offsetParent.offset(); + + // Subtract element margins + // note: when an element has margin: auto the offsetLeft and marginLeft + // are the same in Safari causing offset.left to incorrectly be 0 + offset.top -= parseFloat( jQuery.css(elem, "marginTop") ) || 0; + offset.left -= parseFloat( jQuery.css(elem, "marginLeft") ) || 0; + + // Add offsetParent borders + parentOffset.top += parseFloat( jQuery.css(offsetParent[0], "borderTopWidth") ) || 0; + parentOffset.left += parseFloat( jQuery.css(offsetParent[0], "borderLeftWidth") ) || 0; + + // Subtract the two offsets + return { + top: offset.top - parentOffset.top, + left: offset.left - parentOffset.left + }; + }, + + offsetParent: function() { + return this.map(function() { + var offsetParent = this.offsetParent || document.body; + while ( offsetParent && (!rroot.test(offsetParent.nodeName) && jQuery.css(offsetParent, "position") === "static") ) { + offsetParent = offsetParent.offsetParent; + } + return offsetParent || document.body; + }); + } +}); + + +// Create scrollLeft and scrollTop methods +jQuery.each( {scrollLeft: "pageXOffset", scrollTop: "pageYOffset"}, function( method, prop ) { + var top = /Y/.test( prop ); + + jQuery.fn[ method ] = function( val ) { + return jQuery.access( this, function( elem, method, val ) { + var win = getWindow( elem ); + + if ( val === undefined ) { + return win ? (prop in win) ? win[ prop ] : + win.document.documentElement[ method ] : + elem[ method ]; + } + + if ( win ) { + win.scrollTo( + !top ? val : jQuery( win ).scrollLeft(), + top ? val : jQuery( win ).scrollTop() + ); + + } else { + elem[ method ] = val; + } + }, method, val, arguments.length, null ); + }; +}); + +function getWindow( elem ) { + return jQuery.isWindow( elem ) ? + elem : + elem.nodeType === 9 ? + elem.defaultView || elem.parentWindow : + false; +} +// Create innerHeight, innerWidth, height, width, outerHeight and outerWidth methods +jQuery.each( { Height: "height", Width: "width" }, function( name, type ) { + jQuery.each( { padding: "inner" + name, content: type, "": "outer" + name }, function( defaultExtra, funcName ) { + // margin is only for outerHeight, outerWidth + jQuery.fn[ funcName ] = function( margin, value ) { + var chainable = arguments.length && ( defaultExtra || typeof margin !== "boolean" ), + extra = defaultExtra || ( margin === true || value === true ? "margin" : "border" ); + + return jQuery.access( this, function( elem, type, value ) { + var doc; + + if ( jQuery.isWindow( elem ) ) { + // As of 5/8/2012 this will yield incorrect results for Mobile Safari, but there + // isn't a whole lot we can do. See pull request at this URL for discussion: + // https://github.com/jquery/jquery/pull/764 + return elem.document.documentElement[ "client" + name ]; + } + + // Get document width or height + if ( elem.nodeType === 9 ) { + doc = elem.documentElement; + + // Either scroll[Width/Height] or offset[Width/Height] or client[Width/Height], whichever is greatest + // unfortunately, this causes bug #3838 in IE6/8 only, but there is currently no good, small way to fix it. + return Math.max( + elem.body[ "scroll" + name ], doc[ "scroll" + name ], + elem.body[ "offset" + name ], doc[ "offset" + name ], + doc[ "client" + name ] + ); + } + + return value === undefined ? + // Get width or height on the element, requesting but not forcing parseFloat + jQuery.css( elem, type, value, extra ) : + + // Set width or height on the element + jQuery.style( elem, type, value, extra ); + }, type, chainable ? margin : undefined, chainable, null ); + }; + }); +}); +// Expose jQuery to the global object +window.jQuery = window.$ = jQuery; + +// Expose jQuery as an AMD module, but only for AMD loaders that +// understand the issues with loading multiple versions of jQuery +// in a page that all might call define(). The loader will indicate +// they have special allowances for multiple jQuery versions by +// specifying define.amd.jQuery = true. Register as a named module, +// since jQuery can be concatenated with other files that may use define, +// but not use a proper concatenation script that understands anonymous +// AMD modules. A named AMD is safest and most robust way to register. +// Lowercase jquery is used because AMD module names are derived from +// file names, and jQuery is normally delivered in a lowercase file name. +// Do this after creating the global so that if an AMD module wants to call +// noConflict to hide this version of jQuery, it will work. +if ( typeof define === "function" && define.amd && define.amd.jQuery ) { + define( "jquery", [], function () { return jQuery; } ); +} + +})( window ); diff --git a/pandora_console/include/graphs/flot/jquery.min.js b/pandora_console/include/graphs/flot/jquery.min.js new file mode 100644 index 0000000000..ee48790811 --- /dev/null +++ b/pandora_console/include/graphs/flot/jquery.min.js @@ -0,0 +1,5 @@ +(function(window,undefined){var rootjQuery,readyList,document=window.document,location=window.location,navigator=window.navigator,_jQuery=window.jQuery,_$=window.$,core_push=Array.prototype.push,core_slice=Array.prototype.slice,core_indexOf=Array.prototype.indexOf,core_toString=Object.prototype.toString,core_hasOwn=Object.prototype.hasOwnProperty,core_trim=String.prototype.trim,jQuery=function(selector,context){return new jQuery.fn.init(selector,context,rootjQuery)},core_pnum=/[\-+]?(?:\d*\.|)\d+(?:[eE][\-+]?\d+|)/.source,core_rnotwhite=/\S/,core_rspace=/\s+/,rtrim=/^[\s\uFEFF\xA0]+|[\s\uFEFF\xA0]+$/g,rquickExpr=/^(?:[^#<]*(<[\w\W]+>)[^>]*$|#([\w\-]*)$)/,rsingleTag=/^<(\w+)\s*\/?>(?:<\/\1>|)$/,rvalidchars=/^[\],:{}\s]*$/,rvalidbraces=/(?:^|:|,)(?:\s*\[)+/g,rvalidescape=/\\(?:["\\\/bfnrt]|u[\da-fA-F]{4})/g,rvalidtokens=/"[^"\\\r\n]*"|true|false|null|-?(?:\d\d*\.|)\d+(?:[eE][\-+]?\d+|)/g,rmsPrefix=/^-ms-/,rdashAlpha=/-([\da-z])/gi,fcamelCase=function(all,letter){return(letter+"").toUpperCase()},DOMContentLoaded=function(){if(document.addEventListener){document.removeEventListener("DOMContentLoaded",DOMContentLoaded,false);jQuery.ready()}else if(document.readyState==="complete"){document.detachEvent("onreadystatechange",DOMContentLoaded);jQuery.ready()}},class2type={};jQuery.fn=jQuery.prototype={constructor:jQuery,init:function(selector,context,rootjQuery){var match,elem,ret,doc;if(!selector){return this}if(selector.nodeType){this.context=this[0]=selector;this.length=1;return this}if(typeof selector==="string"){if(selector.charAt(0)==="<"&&selector.charAt(selector.length-1)===">"&&selector.length>=3){match=[null,selector,null]}else{match=rquickExpr.exec(selector)}if(match&&(match[1]||!context)){if(match[1]){context=context instanceof jQuery?context[0]:context;doc=context&&context.nodeType?context.ownerDocument||context:document;selector=jQuery.parseHTML(match[1],doc,true);if(rsingleTag.test(match[1])&&jQuery.isPlainObject(context)){this.attr.call(selector,context,true)}return jQuery.merge(this,selector)}else{elem=document.getElementById(match[2]);if(elem&&elem.parentNode){if(elem.id!==match[2]){return rootjQuery.find(selector)}this.length=1;this[0]=elem}this.context=document;this.selector=selector;return this}}else if(!context||context.jquery){return(context||rootjQuery).find(selector)}else{return this.constructor(context).find(selector)}}else if(jQuery.isFunction(selector)){return rootjQuery.ready(selector)}if(selector.selector!==undefined){this.selector=selector.selector;this.context=selector.context}return jQuery.makeArray(selector,this)},selector:"",jquery:"1.8.3",length:0,size:function(){return this.length},toArray:function(){return core_slice.call(this)},get:function(num){return num==null?this.toArray():num<0?this[this.length+num]:this[num]},pushStack:function(elems,name,selector){var ret=jQuery.merge(this.constructor(),elems);ret.prevObject=this;ret.context=this.context;if(name==="find"){ret.selector=this.selector+(this.selector?" ":"")+selector}else if(name){ret.selector=this.selector+"."+name+"("+selector+")"}return ret},each:function(callback,args){return jQuery.each(this,callback,args)},ready:function(fn){jQuery.ready.promise().done(fn);return this},eq:function(i){i=+i;return i===-1?this.slice(i):this.slice(i,i+1)},first:function(){return this.eq(0)},last:function(){return this.eq(-1)},slice:function(){return this.pushStack(core_slice.apply(this,arguments),"slice",core_slice.call(arguments).join(","))},map:function(callback){return this.pushStack(jQuery.map(this,function(elem,i){return callback.call(elem,i,elem)}))},end:function(){return this.prevObject||this.constructor(null)},push:core_push,sort:[].sort,splice:[].splice};jQuery.fn.init.prototype=jQuery.fn;jQuery.extend=jQuery.fn.extend=function(){var options,name,src,copy,copyIsArray,clone,target=arguments[0]||{},i=1,length=arguments.length,deep=false;if(typeof target==="boolean"){deep=target;target=arguments[1]||{};i=2}if(typeof target!=="object"&&!jQuery.isFunction(target)){target={}}if(length===i){target=this;--i}for(;i0){return}readyList.resolveWith(document,[jQuery]);if(jQuery.fn.trigger){jQuery(document).trigger("ready").off("ready")}},isFunction:function(obj){return jQuery.type(obj)==="function"},isArray:Array.isArray||function(obj){return jQuery.type(obj)==="array"},isWindow:function(obj){return obj!=null&&obj==obj.window},isNumeric:function(obj){return!isNaN(parseFloat(obj))&&isFinite(obj)},type:function(obj){return obj==null?String(obj):class2type[core_toString.call(obj)]||"object"},isPlainObject:function(obj){if(!obj||jQuery.type(obj)!=="object"||obj.nodeType||jQuery.isWindow(obj)){return false}try{if(obj.constructor&&!core_hasOwn.call(obj,"constructor")&&!core_hasOwn.call(obj.constructor.prototype,"isPrototypeOf")){return false}}catch(e){return false}var key;for(key in obj){}return key===undefined||core_hasOwn.call(obj,key)},isEmptyObject:function(obj){var name;for(name in obj){return false}return true},error:function(msg){throw new Error(msg)},parseHTML:function(data,context,scripts){var parsed;if(!data||typeof data!=="string"){return null}if(typeof context==="boolean"){scripts=context;context=0}context=context||document;if(parsed=rsingleTag.exec(data)){return[context.createElement(parsed[1])]}parsed=jQuery.buildFragment([data],context,scripts?null:[]);return jQuery.merge([],(parsed.cacheable?jQuery.clone(parsed.fragment):parsed.fragment).childNodes)},parseJSON:function(data){if(!data||typeof data!=="string"){return null}data=jQuery.trim(data);if(window.JSON&&window.JSON.parse){return window.JSON.parse(data)}if(rvalidchars.test(data.replace(rvalidescape,"@").replace(rvalidtokens,"]").replace(rvalidbraces,""))){return new Function("return "+data)()}jQuery.error("Invalid JSON: "+data)},parseXML:function(data){var xml,tmp;if(!data||typeof data!=="string"){return null}try{if(window.DOMParser){tmp=new DOMParser;xml=tmp.parseFromString(data,"text/xml")}else{xml=new ActiveXObject("Microsoft.XMLDOM");xml.async="false";xml.loadXML(data)}}catch(e){xml=undefined}if(!xml||!xml.documentElement||xml.getElementsByTagName("parsererror").length){jQuery.error("Invalid XML: "+data)}return xml},noop:function(){},globalEval:function(data){if(data&&core_rnotwhite.test(data)){(window.execScript||function(data){window["eval"].call(window,data)})(data)}},camelCase:function(string){return string.replace(rmsPrefix,"ms-").replace(rdashAlpha,fcamelCase)},nodeName:function(elem,name){return elem.nodeName&&elem.nodeName.toLowerCase()===name.toLowerCase()},each:function(obj,callback,args){var name,i=0,length=obj.length,isObj=length===undefined||jQuery.isFunction(obj);if(args){if(isObj){for(name in obj){if(callback.apply(obj[name],args)===false){break}}}else{for(;i0&&elems[0]&&elems[length-1]||length===0||jQuery.isArray(elems));if(isArray){for(;i-1){list.splice(index,1);if(firing){if(index<=firingLength){firingLength--}if(index<=firingIndex){firingIndex--}}}})}return this},has:function(fn){return jQuery.inArray(fn,list)>-1},empty:function(){list=[];return this},disable:function(){list=stack=memory=undefined;return this},disabled:function(){return!list},lock:function(){stack=undefined;if(!memory){self.disable()}return this},locked:function(){return!stack},fireWith:function(context,args){args=args||[];args=[context,args.slice?args.slice():args];if(list&&(!fired||stack)){if(firing){stack.push(args)}else{fire(args)}}return this},fire:function(){self.fireWith(this,arguments);return this},fired:function(){return!!fired}};return self};jQuery.extend({Deferred:function(func){var tuples=[["resolve","done",jQuery.Callbacks("once memory"),"resolved"],["reject","fail",jQuery.Callbacks("once memory"),"rejected"],["notify","progress",jQuery.Callbacks("memory")]],state="pending",promise={state:function(){return state},always:function(){deferred.done(arguments).fail(arguments);return this},then:function(){var fns=arguments;return jQuery.Deferred(function(newDefer){jQuery.each(tuples,function(i,tuple){var action=tuple[0],fn=fns[i];deferred[tuple[1]](jQuery.isFunction(fn)?function(){var returned=fn.apply(this,arguments);if(returned&&jQuery.isFunction(returned.promise)){returned.promise().done(newDefer.resolve).fail(newDefer.reject).progress(newDefer.notify)}else{newDefer[action+"With"](this===deferred?newDefer:this,[returned])}}:newDefer[action])});fns=null}).promise()},promise:function(obj){return obj!=null?jQuery.extend(obj,promise):promise}},deferred={};promise.pipe=promise.then;jQuery.each(tuples,function(i,tuple){var list=tuple[2],stateString=tuple[3];promise[tuple[1]]=list.add;if(stateString){list.add(function(){state=stateString},tuples[i^1][2].disable,tuples[2][2].lock)}deferred[tuple[0]]=list.fire;deferred[tuple[0]+"With"]=list.fireWith});promise.promise(deferred);if(func){func.call(deferred,deferred)}return deferred},when:function(subordinate){var i=0,resolveValues=core_slice.call(arguments),length=resolveValues.length,remaining=length!==1||subordinate&&jQuery.isFunction(subordinate.promise)?length:0,deferred=remaining===1?subordinate:jQuery.Deferred(),updateFunc=function(i,contexts,values){return function(value){contexts[i]=this;values[i]=arguments.length>1?core_slice.call(arguments):value;if(values===progressValues){deferred.notifyWith(contexts,values)}else if(!--remaining){deferred.resolveWith(contexts,values)}}},progressValues,progressContexts,resolveContexts;if(length>1){progressValues=new Array(length);progressContexts=new Array(length);resolveContexts=new Array(length);for(;i
a";all=div.getElementsByTagName("*");a=div.getElementsByTagName("a")[0];if(!all||!a||!all.length){return{}}select=document.createElement("select");opt=select.appendChild(document.createElement("option"));input=div.getElementsByTagName("input")[0];a.style.cssText="top:1px;float:left;opacity:.5";support={leadingWhitespace:div.firstChild.nodeType===3,tbody:!div.getElementsByTagName("tbody").length,htmlSerialize:!!div.getElementsByTagName("link").length,style:/top/.test(a.getAttribute("style")),hrefNormalized:a.getAttribute("href")==="/a",opacity:/^0.5/.test(a.style.opacity),cssFloat:!!a.style.cssFloat,checkOn:input.value==="on",optSelected:opt.selected,getSetAttribute:div.className!=="t",enctype:!!document.createElement("form").enctype,html5Clone:document.createElement("nav").cloneNode(true).outerHTML!=="<:nav>",boxModel:document.compatMode==="CSS1Compat",submitBubbles:true,changeBubbles:true,focusinBubbles:false,deleteExpando:true,noCloneEvent:true,inlineBlockNeedsLayout:false,shrinkWrapBlocks:false,reliableMarginRight:true,boxSizingReliable:true,pixelPosition:false};input.checked=true;support.noCloneChecked=input.cloneNode(true).checked;select.disabled=true;support.optDisabled=!opt.disabled;try{delete div.test}catch(e){support.deleteExpando=false}if(!div.addEventListener&&div.attachEvent&&div.fireEvent){div.attachEvent("onclick",clickFn=function(){support.noCloneEvent=false});div.cloneNode(true).fireEvent("onclick");div.detachEvent("onclick",clickFn)}input=document.createElement("input");input.value="t";input.setAttribute("type","radio");support.radioValue=input.value==="t";input.setAttribute("checked","checked");input.setAttribute("name","t");div.appendChild(input);fragment=document.createDocumentFragment();fragment.appendChild(div.lastChild);support.checkClone=fragment.cloneNode(true).cloneNode(true).lastChild.checked;support.appendChecked=input.checked;fragment.removeChild(input);fragment.appendChild(div);if(div.attachEvent){for(i in{submit:true,change:true,focusin:true}){eventName="on"+i;isSupported=eventName in div;if(!isSupported){div.setAttribute(eventName,"return;");isSupported=typeof div[eventName]==="function"}support[i+"Bubbles"]=isSupported}}jQuery(function(){var container,div,tds,marginDiv,divReset="padding:0;margin:0;border:0;display:block;overflow:hidden;",body=document.getElementsByTagName("body")[0];if(!body){return}container=document.createElement("div");container.style.cssText="visibility:hidden;border:0;width:0;height:0;position:static;top:0;margin-top:1px";body.insertBefore(container,body.firstChild);div=document.createElement("div");container.appendChild(div);div.innerHTML="
t
";tds=div.getElementsByTagName("td");tds[0].style.cssText="padding:0;margin:0;border:0;display:none";isSupported=tds[0].offsetHeight===0;tds[0].style.display="";tds[1].style.display="none";support.reliableHiddenOffsets=isSupported&&tds[0].offsetHeight===0;div.innerHTML="";div.style.cssText="box-sizing:border-box;-moz-box-sizing:border-box;-webkit-box-sizing:border-box;padding:1px;border:1px;display:block;width:4px;margin-top:1%;position:absolute;top:1%;";support.boxSizing=div.offsetWidth===4;support.doesNotIncludeMarginInBodyOffset=body.offsetTop!==1;if(window.getComputedStyle){support.pixelPosition=(window.getComputedStyle(div,null)||{}).top!=="1%";support.boxSizingReliable=(window.getComputedStyle(div,null)||{width:"4px"}).width==="4px";marginDiv=document.createElement("div");marginDiv.style.cssText=div.style.cssText=divReset;marginDiv.style.marginRight=marginDiv.style.width="0";div.style.width="1px";div.appendChild(marginDiv);support.reliableMarginRight=!parseFloat((window.getComputedStyle(marginDiv,null)||{}).marginRight)}if(typeof div.style.zoom!=="undefined"){div.innerHTML="";div.style.cssText=divReset+"width:1px;padding:1px;display:inline;zoom:1";support.inlineBlockNeedsLayout=div.offsetWidth===3;div.style.display="block";div.style.overflow="visible";div.innerHTML="
";div.firstChild.style.width="5px";support.shrinkWrapBlocks=div.offsetWidth!==3;container.style.zoom=1}body.removeChild(container);container=div=tds=marginDiv=null});fragment.removeChild(div);all=a=select=opt=input=fragment=div=null;return support}();var rbrace=/(?:\{[\s\S]*\}|\[[\s\S]*\])$/,rmultiDash=/([A-Z])/g;jQuery.extend({cache:{},deletedIds:[],uuid:0,expando:"jQuery"+(jQuery.fn.jquery+Math.random()).replace(/\D/g,""),noData:{embed:true,object:"clsid:D27CDB6E-AE6D-11cf-96B8-444553540000",applet:true},hasData:function(elem){elem=elem.nodeType?jQuery.cache[elem[jQuery.expando]]:elem[jQuery.expando];return!!elem&&!isEmptyDataObject(elem)},data:function(elem,name,data,pvt){if(!jQuery.acceptData(elem)){return}var thisCache,ret,internalKey=jQuery.expando,getByName=typeof name==="string",isNode=elem.nodeType,cache=isNode?jQuery.cache:elem,id=isNode?elem[internalKey]:elem[internalKey]&&internalKey;if((!id||!cache[id]||!pvt&&!cache[id].data)&&getByName&&data===undefined){return}if(!id){if(isNode){elem[internalKey]=id=jQuery.deletedIds.pop()||jQuery.guid++}else{id=internalKey}}if(!cache[id]){cache[id]={};if(!isNode){cache[id].toJSON=jQuery.noop}}if(typeof name==="object"||typeof name==="function"){if(pvt){cache[id]=jQuery.extend(cache[id],name)}else{cache[id].data=jQuery.extend(cache[id].data,name)}}thisCache=cache[id];if(!pvt){if(!thisCache.data){thisCache.data={}}thisCache=thisCache.data}if(data!==undefined){thisCache[jQuery.camelCase(name)]=data}if(getByName){ret=thisCache[name];if(ret==null){ret=thisCache[jQuery.camelCase(name)]}}else{ret=thisCache}return ret},removeData:function(elem,name,pvt){if(!jQuery.acceptData(elem)){return}var thisCache,i,l,isNode=elem.nodeType,cache=isNode?jQuery.cache:elem,id=isNode?elem[jQuery.expando]:jQuery.expando;if(!cache[id]){return}if(name){thisCache=pvt?cache[id]:cache[id].data;if(thisCache){if(!jQuery.isArray(name)){if(name in thisCache){name=[name]}else{name=jQuery.camelCase(name);if(name in thisCache){name=[name]}else{name=name.split(" ")}}}for(i=0,l=name.length;i1,null,false)},removeData:function(key){return this.each(function(){jQuery.removeData(this,key)})}});function dataAttr(elem,key,data){if(data===undefined&&elem.nodeType===1){var name="data-"+key.replace(rmultiDash,"-$1").toLowerCase();data=elem.getAttribute(name);if(typeof data==="string"){try{data=data==="true"?true:data==="false"?false:data==="null"?null:+data+""===data?+data:rbrace.test(data)?jQuery.parseJSON(data):data}catch(e){}jQuery.data(elem,key,data)}else{data=undefined}}return data}function isEmptyDataObject(obj){var name;for(name in obj){if(name==="data"&&jQuery.isEmptyObject(obj[name])){continue}if(name!=="toJSON"){return false}}return true}jQuery.extend({queue:function(elem,type,data){var queue;if(elem){type=(type||"fx")+"queue";queue=jQuery._data(elem,type);if(data){if(!queue||jQuery.isArray(data)){queue=jQuery._data(elem,type,jQuery.makeArray(data))}else{queue.push(data)}}return queue||[]}},dequeue:function(elem,type){type=type||"fx";var queue=jQuery.queue(elem,type),startLength=queue.length,fn=queue.shift(),hooks=jQuery._queueHooks(elem,type),next=function(){jQuery.dequeue(elem,type)};if(fn==="inprogress"){fn=queue.shift();startLength--}if(fn){if(type==="fx"){queue.unshift("inprogress")}delete hooks.stop;fn.call(elem,next,hooks)}if(!startLength&&hooks){hooks.empty.fire()}},_queueHooks:function(elem,type){var key=type+"queueHooks";return jQuery._data(elem,key)||jQuery._data(elem,key,{empty:jQuery.Callbacks("once memory").add(function(){jQuery.removeData(elem,type+"queue",true);jQuery.removeData(elem,key,true)})})}});jQuery.fn.extend({queue:function(type,data){var setter=2;if(typeof type!=="string"){data=type;type="fx";setter--}if(arguments.length1)},removeAttr:function(name){return this.each(function(){jQuery.removeAttr(this,name)})},prop:function(name,value){return jQuery.access(this,jQuery.prop,name,value,arguments.length>1)},removeProp:function(name){name=jQuery.propFix[name]||name;return this.each(function(){try{this[name]=undefined;delete this[name]}catch(e){}})},addClass:function(value){var classNames,i,l,elem,setClass,c,cl;if(jQuery.isFunction(value)){return this.each(function(j){jQuery(this).addClass(value.call(this,j,this.className))})}if(value&&typeof value==="string"){classNames=value.split(core_rspace);for(i=0,l=this.length;i=0){className=className.replace(" "+removes[c]+" "," ")}}elem.className=value?jQuery.trim(className):""}}}return this},toggleClass:function(value,stateVal){var type=typeof value,isBool=typeof stateVal==="boolean";if(jQuery.isFunction(value)){return this.each(function(i){jQuery(this).toggleClass(value.call(this,i,this.className,stateVal),stateVal)})}return this.each(function(){if(type==="string"){var className,i=0,self=jQuery(this),state=stateVal,classNames=value.split(core_rspace);while(className=classNames[i++]){state=isBool?state:!self.hasClass(className);self[state?"addClass":"removeClass"](className)}}else if(type==="undefined"||type==="boolean"){if(this.className){jQuery._data(this,"__className__",this.className)}this.className=this.className||value===false?"":jQuery._data(this,"__className__")||""}})},hasClass:function(selector){var className=" "+selector+" ",i=0,l=this.length;for(;i=0){return true}}return false},val:function(value){var hooks,ret,isFunction,elem=this[0];if(!arguments.length){if(elem){hooks=jQuery.valHooks[elem.type]||jQuery.valHooks[elem.nodeName.toLowerCase()];if(hooks&&"get"in hooks&&(ret=hooks.get(elem,"value"))!==undefined){return ret}ret=elem.value;return typeof ret==="string"?ret.replace(rreturn,""):ret==null?"":ret}return}isFunction=jQuery.isFunction(value);return this.each(function(i){var val,self=jQuery(this);if(this.nodeType!==1){return}if(isFunction){val=value.call(this,i,self.val())}else{val=value}if(val==null){val=""}else if(typeof val==="number"){val+=""}else if(jQuery.isArray(val)){val=jQuery.map(val,function(value){return value==null?"":value+""})}hooks=jQuery.valHooks[this.type]||jQuery.valHooks[this.nodeName.toLowerCase()];if(!hooks||!("set"in hooks)||hooks.set(this,val,"value")===undefined){this.value=val}})}});jQuery.extend({valHooks:{option:{get:function(elem){var val=elem.attributes.value;return!val||val.specified?elem.value:elem.text}},select:{get:function(elem){var value,option,options=elem.options,index=elem.selectedIndex,one=elem.type==="select-one"||index<0,values=one?null:[],max=one?index+1:options.length,i=index<0?max:one?index:0;for(;i=0});if(!values.length){elem.selectedIndex=-1}return values}}},attrFn:{},attr:function(elem,name,value,pass){var ret,hooks,notxml,nType=elem.nodeType;if(!elem||nType===3||nType===8||nType===2){return}if(pass&&jQuery.isFunction(jQuery.fn[name])){return jQuery(elem)[name](value)}if(typeof elem.getAttribute==="undefined"){return jQuery.prop(elem,name,value)}notxml=nType!==1||!jQuery.isXMLDoc(elem);if(notxml){name=name.toLowerCase();hooks=jQuery.attrHooks[name]||(rboolean.test(name)?boolHook:nodeHook)}if(value!==undefined){if(value===null){jQuery.removeAttr(elem,name);return}else if(hooks&&"set"in hooks&¬xml&&(ret=hooks.set(elem,value,name))!==undefined){return ret}else{elem.setAttribute(name,value+"");return value}}else if(hooks&&"get"in hooks&¬xml&&(ret=hooks.get(elem,name))!==null){return ret}else{ret=elem.getAttribute(name);return ret===null?undefined:ret}},removeAttr:function(elem,value){var propName,attrNames,name,isBool,i=0;if(value&&elem.nodeType===1){attrNames=value.split(core_rspace);for(;i=0}}})});var rformElems=/^(?:textarea|input|select)$/i,rtypenamespace=/^([^\.]*|)(?:\.(.+)|)$/,rhoverHack=/(?:^|\s)hover(\.\S+|)\b/,rkeyEvent=/^key/,rmouseEvent=/^(?:mouse|contextmenu)|click/,rfocusMorph=/^(?:focusinfocus|focusoutblur)$/,hoverHack=function(events){return jQuery.event.special.hover?events:events.replace(rhoverHack,"mouseenter$1 mouseleave$1")};jQuery.event={add:function(elem,types,handler,data,selector){var elemData,eventHandle,events,t,tns,type,namespaces,handleObj,handleObjIn,handlers,special;if(elem.nodeType===3||elem.nodeType===8||!types||!handler||!(elemData=jQuery._data(elem))){return}if(handler.handler){handleObjIn=handler;handler=handleObjIn.handler;selector=handleObjIn.selector}if(!handler.guid){handler.guid=jQuery.guid++}events=elemData.events;if(!events){elemData.events=events={}}eventHandle=elemData.handle;if(!eventHandle){elemData.handle=eventHandle=function(e){return typeof jQuery!=="undefined"&&(!e||jQuery.event.triggered!==e.type)?jQuery.event.dispatch.apply(eventHandle.elem,arguments):undefined};eventHandle.elem=elem}types=jQuery.trim(hoverHack(types)).split(" ");for(t=0;t=0){type=type.slice(0,-1);exclusive=true}if(type.indexOf(".")>=0){namespaces=type.split(".");type=namespaces.shift();namespaces.sort()}if((!elem||jQuery.event.customEvent[type])&&!jQuery.event.global[type]){return}event=typeof event==="object"?event[jQuery.expando]?event:new jQuery.Event(type,event):new jQuery.Event(type);event.type=type;event.isTrigger=true;event.exclusive=exclusive;event.namespace=namespaces.join(".");event.namespace_re=event.namespace?new RegExp("(^|\\.)"+namespaces.join("\\.(?:.*\\.|)")+"(\\.|$)"):null;ontype=type.indexOf(":")<0?"on"+type:"";if(!elem){cache=jQuery.cache;for(i in cache){if(cache[i].events&&cache[i].events[type]){jQuery.event.trigger(event,data,cache[i].handle.elem,true)}}return}event.result=undefined;if(!event.target){event.target=elem}data=data!=null?jQuery.makeArray(data):[];data.unshift(event);special=jQuery.event.special[type]||{};if(special.trigger&&special.trigger.apply(elem,data)===false){return}eventPath=[[elem,special.bindType||type]];if(!onlyHandlers&&!special.noBubble&&!jQuery.isWindow(elem)){bubbleType=special.delegateType||type;cur=rfocusMorph.test(bubbleType+type)?elem:elem.parentNode;for(old=elem;cur;cur=cur.parentNode){eventPath.push([cur,bubbleType]);old=cur}if(old===(elem.ownerDocument||document)){eventPath.push([old.defaultView||old.parentWindow||window,bubbleType])}}for(i=0;i=0:jQuery.find(sel,this,null,[cur]).length}if(selMatch[sel]){matches.push(handleObj)}}if(matches.length){handlerQueue.push({elem:cur,matches:matches})}}}}if(handlers.length>delegateCount){handlerQueue.push({elem:this,matches:handlers.slice(delegateCount)})}for(i=0;i0?this.on(name,null,data,fn):this.trigger(name)};if(rkeyEvent.test(name)){jQuery.event.fixHooks[name]=jQuery.event.keyHooks}if(rmouseEvent.test(name)){jQuery.event.fixHooks[name]=jQuery.event.mouseHooks}});(function(window,undefined){var cachedruns,assertGetIdNotName,Expr,getText,isXML,contains,compile,sortOrder,hasDuplicate,outermostContext,baseHasDuplicate=true,strundefined="undefined",expando=("sizcache"+Math.random()).replace(".",""),Token=String,document=window.document,docElem=document.documentElement,dirruns=0,done=0,pop=[].pop,push=[].push,slice=[].slice,indexOf=[].indexOf||function(elem){var i=0,len=this.length;for(;iExpr.cacheLength){delete cache[keys.shift()]}return cache[key+" "]=value},cache)},classCache=createCache(),tokenCache=createCache(),compilerCache=createCache(),whitespace="[\\x20\\t\\r\\n\\f]",characterEncoding="(?:\\\\.|[-\\w]|[^\\x00-\\xa0])+",identifier=characterEncoding.replace("w","w#"),operators="([*^$|!~]?=)",attributes="\\["+whitespace+"*("+characterEncoding+")"+whitespace+"*(?:"+operators+whitespace+"*(?:(['\"])((?:\\\\.|[^\\\\])*?)\\3|("+identifier+")|)|)"+whitespace+"*\\]",pseudos=":("+characterEncoding+")(?:\\((?:(['\"])((?:\\\\.|[^\\\\])*?)\\2|([^()[\\]]*|(?:(?:"+attributes+")|[^:]|\\\\.)*|.*))\\)|)",pos=":(even|odd|eq|gt|lt|nth|first|last)(?:\\("+whitespace+"*((?:-\\d)?\\d*)"+whitespace+"*\\)|)(?=[^-]|$)",rtrim=new RegExp("^"+whitespace+"+|((?:^|[^\\\\])(?:\\\\.)*)"+whitespace+"+$","g"),rcomma=new RegExp("^"+whitespace+"*,"+whitespace+"*"),rcombinators=new RegExp("^"+whitespace+"*([\\x20\\t\\r\\n\\f>+~])"+whitespace+"*"),rpseudo=new RegExp(pseudos),rquickExpr=/^(?:#([\w\-]+)|(\w+)|\.([\w\-]+))$/,rnot=/^:not/,rsibling=/[\x20\t\r\n\f]*[+~]/,rendsWithNot=/:not\($/,rheader=/h\d/i,rinputs=/input|select|textarea|button/i,rbackslash=/\\(?!\\)/g,matchExpr={ID:new RegExp("^#("+characterEncoding+")"),CLASS:new RegExp("^\\.("+characterEncoding+")"),NAME:new RegExp("^\\[name=['\"]?("+characterEncoding+")['\"]?\\]"),TAG:new RegExp("^("+characterEncoding.replace("w","w*")+")"),ATTR:new RegExp("^"+attributes),PSEUDO:new RegExp("^"+pseudos),POS:new RegExp(pos,"i"),CHILD:new RegExp("^:(only|nth|first|last)-child(?:\\("+whitespace+"*(even|odd|(([+-]|)(\\d*)n|)"+whitespace+"*(?:([+-]|)"+whitespace+"*(\\d+)|))"+whitespace+"*\\)|)","i"),needsContext:new RegExp("^"+whitespace+"*[>+~]|"+pos,"i")},assert=function(fn){var div=document.createElement("div");try{return fn(div)}catch(e){return false}finally{div=null}},assertTagNameNoComments=assert(function(div){div.appendChild(document.createComment(""));return!div.getElementsByTagName("*").length}),assertHrefNotNormalized=assert(function(div){div.innerHTML="";return div.firstChild&&typeof div.firstChild.getAttribute!==strundefined&&div.firstChild.getAttribute("href")==="#"}),assertAttributes=assert(function(div){div.innerHTML="";var type=typeof div.lastChild.getAttribute("multiple");return type!=="boolean"&&type!=="string"}),assertUsableClassName=assert(function(div){div.innerHTML="";if(!div.getElementsByClassName||!div.getElementsByClassName("e").length){return false}div.lastChild.className="e";return div.getElementsByClassName("e").length===2}),assertUsableName=assert(function(div){div.id=expando+0;div.innerHTML="
";docElem.insertBefore(div,docElem.firstChild);var pass=document.getElementsByName&&document.getElementsByName(expando).length===2+document.getElementsByName(expando+0).length;assertGetIdNotName=!document.getElementById(expando);docElem.removeChild(div);return pass});try{slice.call(docElem.childNodes,0)[0].nodeType}catch(e){slice=function(i){var elem,results=[];for(;elem=this[i];i++){results.push(elem)}return results}}function Sizzle(selector,context,results,seed){results=results||[];context=context||document;var match,elem,xml,m,nodeType=context.nodeType;if(!selector||typeof selector!=="string"){return results}if(nodeType!==1&&nodeType!==9){return[]}xml=isXML(context);if(!xml&&!seed){if(match=rquickExpr.exec(selector)){if(m=match[1]){if(nodeType===9){elem=context.getElementById(m);if(elem&&elem.parentNode){if(elem.id===m){results.push(elem);return results}}else{return results}}else{if(context.ownerDocument&&(elem=context.ownerDocument.getElementById(m))&&contains(context,elem)&&elem.id===m){results.push(elem);return results}}}else if(match[2]){push.apply(results,slice.call(context.getElementsByTagName(selector),0));return results}else if((m=match[3])&&assertUsableClassName&&context.getElementsByClassName){push.apply(results,slice.call(context.getElementsByClassName(m),0));return results}}}return select(selector.replace(rtrim,"$1"),context,results,seed,xml)}Sizzle.matches=function(expr,elements){return Sizzle(expr,null,null,elements)};Sizzle.matchesSelector=function(elem,expr){return Sizzle(expr,null,null,[elem]).length>0};function createInputPseudo(type){return function(elem){var name=elem.nodeName.toLowerCase();return name==="input"&&elem.type===type}}function createButtonPseudo(type){return function(elem){var name=elem.nodeName.toLowerCase();return(name==="input"||name==="button")&&elem.type===type}}function createPositionalPseudo(fn){return markFunction(function(argument){argument=+argument;return markFunction(function(seed,matches){var j,matchIndexes=fn([],seed.length,argument),i=matchIndexes.length;while(i--){if(seed[j=matchIndexes[i]]){seed[j]=!(matches[j]=seed[j])}}})})}getText=Sizzle.getText=function(elem){var node,ret="",i=0,nodeType=elem.nodeType;if(nodeType){if(nodeType===1||nodeType===9||nodeType===11){if(typeof elem.textContent==="string"){return elem.textContent}else{for(elem=elem.firstChild;elem;elem=elem.nextSibling){ret+=getText(elem)}}}else if(nodeType===3||nodeType===4){return elem.nodeValue}}else{for(;node=elem[i];i++){ret+=getText(node)}}return ret};isXML=Sizzle.isXML=function(elem){var documentElement=elem&&(elem.ownerDocument||elem).documentElement;return documentElement?documentElement.nodeName!=="HTML":false};contains=Sizzle.contains=docElem.contains?function(a,b){var adown=a.nodeType===9?a.documentElement:a,bup=b&&b.parentNode;return a===bup||!!(bup&&bup.nodeType===1&&adown.contains&&adown.contains(bup))}:docElem.compareDocumentPosition?function(a,b){return b&&!!(a.compareDocumentPosition(b)&16)}:function(a,b){while(b=b.parentNode){if(b===a){return true}}return false};Sizzle.attr=function(elem,name){var val,xml=isXML(elem);if(!xml){name=name.toLowerCase()}if(val=Expr.attrHandle[name]){return val(elem)}if(xml||assertAttributes){return elem.getAttribute(name)}val=elem.getAttributeNode(name);return val?typeof elem[name]==="boolean"?elem[name]?name:null:val.specified?val.value:null:null};Expr=Sizzle.selectors={cacheLength:50,createPseudo:markFunction,match:matchExpr,attrHandle:assertHrefNotNormalized?{}:{href:function(elem){return elem.getAttribute("href",2)},type:function(elem){return elem.getAttribute("type")}},find:{ID:assertGetIdNotName?function(id,context,xml){if(typeof context.getElementById!==strundefined&&!xml){var m=context.getElementById(id);return m&&m.parentNode?[m]:[]}}:function(id,context,xml){if(typeof context.getElementById!==strundefined&&!xml){var m=context.getElementById(id);return m?m.id===id||typeof m.getAttributeNode!==strundefined&&m.getAttributeNode("id").value===id?[m]:undefined:[]}},TAG:assertTagNameNoComments?function(tag,context){if(typeof context.getElementsByTagName!==strundefined){return context.getElementsByTagName(tag)}}:function(tag,context){var results=context.getElementsByTagName(tag);if(tag==="*"){var elem,tmp=[],i=0;for(;elem=results[i];i++){if(elem.nodeType===1){tmp.push(elem)}}return tmp}return results},NAME:assertUsableName&&function(tag,context){if(typeof context.getElementsByName!==strundefined){return context.getElementsByName(name)}},CLASS:assertUsableClassName&&function(className,context,xml){if(typeof context.getElementsByClassName!==strundefined&&!xml){return context.getElementsByClassName(className)}}},relative:{">":{dir:"parentNode",first:true}," ":{dir:"parentNode"},"+":{dir:"previousSibling",first:true},"~":{dir:"previousSibling"}},preFilter:{ATTR:function(match){match[1]=match[1].replace(rbackslash,"");match[3]=(match[4]||match[5]||"").replace(rbackslash,"");if(match[2]==="~="){match[3]=" "+match[3]+" "}return match.slice(0,4)},CHILD:function(match){match[1]=match[1].toLowerCase();if(match[1]==="nth"){if(!match[2]){Sizzle.error(match[0])}match[3]=+(match[3]?match[4]+(match[5]||1):2*(match[2]==="even"||match[2]==="odd"));match[4]=+(match[6]+match[7]||match[2]==="odd")}else if(match[2]){Sizzle.error(match[0])}return match},PSEUDO:function(match){var unquoted,excess;if(matchExpr["CHILD"].test(match[0])){return null}if(match[3]){match[2]=match[3]}else if(unquoted=match[4]){if(rpseudo.test(unquoted)&&(excess=tokenize(unquoted,true))&&(excess=unquoted.indexOf(")",unquoted.length-excess)-unquoted.length)){unquoted=unquoted.slice(0,excess);match[0]=match[0].slice(0,excess)}match[2]=unquoted}return match.slice(0,3)}},filter:{ID:assertGetIdNotName?function(id){id=id.replace(rbackslash,"");return function(elem){return elem.getAttribute("id")===id}}:function(id){id=id.replace(rbackslash,"");return function(elem){var node=typeof elem.getAttributeNode!==strundefined&&elem.getAttributeNode("id");return node&&node.value===id}},TAG:function(nodeName){if(nodeName==="*"){return function(){return true}}nodeName=nodeName.replace(rbackslash,"").toLowerCase();return function(elem){return elem.nodeName&&elem.nodeName.toLowerCase()===nodeName}},CLASS:function(className){var pattern=classCache[expando][className+" "];return pattern||(pattern=new RegExp("(^|"+whitespace+")"+className+"("+whitespace+"|$)"))&&classCache(className,function(elem){return pattern.test(elem.className||typeof elem.getAttribute!==strundefined&&elem.getAttribute("class")||"")})},ATTR:function(name,operator,check){return function(elem,context){var result=Sizzle.attr(elem,name);if(result==null){return operator==="!="}if(!operator){return true}result+="";return operator==="="?result===check:operator==="!="?result!==check:operator==="^="?check&&result.indexOf(check)===0:operator==="*="?check&&result.indexOf(check)>-1:operator==="$="?check&&result.substr(result.length-check.length)===check:operator==="~="?(" "+result+" ").indexOf(check)>-1:operator==="|="?result===check||result.substr(0,check.length+1)===check+"-":false}},CHILD:function(type,argument,first,last){if(type==="nth"){return function(elem){var node,diff,parent=elem.parentNode;if(first===1&&last===0){return true}if(parent){diff=0;for(node=parent.firstChild;node;node=node.nextSibling){if(node.nodeType===1){diff++; +if(elem===node){break}}}}diff-=last;return diff===first||diff%first===0&&diff/first>=0}}return function(elem){var node=elem;switch(type){case"only":case"first":while(node=node.previousSibling){if(node.nodeType===1){return false}}if(type==="first"){return true}node=elem;case"last":while(node=node.nextSibling){if(node.nodeType===1){return false}}return true}}},PSEUDO:function(pseudo,argument){var args,fn=Expr.pseudos[pseudo]||Expr.setFilters[pseudo.toLowerCase()]||Sizzle.error("unsupported pseudo: "+pseudo);if(fn[expando]){return fn(argument)}if(fn.length>1){args=[pseudo,pseudo,"",argument];return Expr.setFilters.hasOwnProperty(pseudo.toLowerCase())?markFunction(function(seed,matches){var idx,matched=fn(seed,argument),i=matched.length;while(i--){idx=indexOf.call(seed,matched[i]);seed[idx]=!(matches[idx]=matched[i])}}):function(elem){return fn(elem,0,args)}}return fn}},pseudos:{not:markFunction(function(selector){var input=[],results=[],matcher=compile(selector.replace(rtrim,"$1"));return matcher[expando]?markFunction(function(seed,matches,context,xml){var elem,unmatched=matcher(seed,null,xml,[]),i=seed.length;while(i--){if(elem=unmatched[i]){seed[i]=!(matches[i]=elem)}}}):function(elem,context,xml){input[0]=elem;matcher(input,null,xml,results);return!results.pop()}}),has:markFunction(function(selector){return function(elem){return Sizzle(selector,elem).length>0}}),contains:markFunction(function(text){return function(elem){return(elem.textContent||elem.innerText||getText(elem)).indexOf(text)>-1}}),enabled:function(elem){return elem.disabled===false},disabled:function(elem){return elem.disabled===true},checked:function(elem){var nodeName=elem.nodeName.toLowerCase();return nodeName==="input"&&!!elem.checked||nodeName==="option"&&!!elem.selected},selected:function(elem){if(elem.parentNode){elem.parentNode.selectedIndex}return elem.selected===true},parent:function(elem){return!Expr.pseudos["empty"](elem)},empty:function(elem){var nodeType;elem=elem.firstChild;while(elem){if(elem.nodeName>"@"||(nodeType=elem.nodeType)===3||nodeType===4){return false}elem=elem.nextSibling}return true},header:function(elem){return rheader.test(elem.nodeName)},text:function(elem){var type,attr;return elem.nodeName.toLowerCase()==="input"&&(type=elem.type)==="text"&&((attr=elem.getAttribute("type"))==null||attr.toLowerCase()===type)},radio:createInputPseudo("radio"),checkbox:createInputPseudo("checkbox"),file:createInputPseudo("file"),password:createInputPseudo("password"),image:createInputPseudo("image"),submit:createButtonPseudo("submit"),reset:createButtonPseudo("reset"),button:function(elem){var name=elem.nodeName.toLowerCase();return name==="input"&&elem.type==="button"||name==="button"},input:function(elem){return rinputs.test(elem.nodeName)},focus:function(elem){var doc=elem.ownerDocument;return elem===doc.activeElement&&(!doc.hasFocus||doc.hasFocus())&&!!(elem.type||elem.href||~elem.tabIndex)},active:function(elem){return elem===elem.ownerDocument.activeElement},first:createPositionalPseudo(function(){return[0]}),last:createPositionalPseudo(function(matchIndexes,length){return[length-1]}),eq:createPositionalPseudo(function(matchIndexes,length,argument){return[argument<0?argument+length:argument]}),even:createPositionalPseudo(function(matchIndexes,length){for(var i=0;i=0;){matchIndexes.push(i)}return matchIndexes}),gt:createPositionalPseudo(function(matchIndexes,length,argument){for(var i=argument<0?argument+length:argument;++i1?function(elem,context,xml){var i=matchers.length;while(i--){if(!matchers[i](elem,context,xml)){return false}}return true}:matchers[0]}function condense(unmatched,map,filter,context,xml){var elem,newUnmatched=[],i=0,len=unmatched.length,mapped=map!=null;for(;i-1){seed[temp]=!(results[temp]=elem)}}}}else{matcherOut=condense(matcherOut===results?matcherOut.splice(preexisting,matcherOut.length):matcherOut);if(postFinder){postFinder(null,results,matcherOut,xml)}else{push.apply(results,matcherOut)}}})}function matcherFromTokens(tokens){var checkContext,matcher,j,len=tokens.length,leadingRelative=Expr.relative[tokens[0].type],implicitRelative=leadingRelative||Expr.relative[" "],i=leadingRelative?1:0,matchContext=addCombinator(function(elem){return elem===checkContext},implicitRelative,true),matchAnyContext=addCombinator(function(elem){return indexOf.call(checkContext,elem)>-1},implicitRelative,true),matchers=[function(elem,context,xml){return!leadingRelative&&(xml||context!==outermostContext)||((checkContext=context).nodeType?matchContext(elem,context,xml):matchAnyContext(elem,context,xml))}];for(;i1&&elementMatcher(matchers),i>1&&tokens.slice(0,i-1).join("").replace(rtrim,"$1"),matcher,i0,byElement=elementMatchers.length>0,superMatcher=function(seed,context,xml,results,expandContext){var elem,j,matcher,setMatched=[],matchedCount=0,i="0",unmatched=seed&&[],outermost=expandContext!=null,contextBackup=outermostContext,elems=seed||byElement&&Expr.find["TAG"]("*",expandContext&&context.parentNode||context),dirrunsUnique=dirruns+=contextBackup==null?1:Math.E;if(outermost){outermostContext=context!==document&&context;cachedruns=superMatcher.el}for(;(elem=elems[i])!=null;i++){if(byElement&&elem){for(j=0;matcher=elementMatchers[j];j++){if(matcher(elem,context,xml)){results.push(elem);break}}if(outermost){dirruns=dirrunsUnique;cachedruns=++superMatcher.el}}if(bySet){if(elem=!matcher&&elem){matchedCount--}if(seed){unmatched.push(elem)}}}matchedCount+=i;if(bySet&&i!==matchedCount){for(j=0;matcher=setMatchers[j];j++){matcher(unmatched,setMatched,context,xml)}if(seed){if(matchedCount>0){while(i--){if(!(unmatched[i]||setMatched[i])){setMatched[i]=pop.call(results)}}}setMatched=condense(setMatched)}push.apply(results,setMatched);if(outermost&&!seed&&setMatched.length>0&&matchedCount+setMatchers.length>1){Sizzle.uniqueSort(results)}}if(outermost){dirruns=dirrunsUnique;outermostContext=contextBackup}return unmatched};superMatcher.el=0;return bySet?markFunction(superMatcher):superMatcher}compile=Sizzle.compile=function(selector,group){var i,setMatchers=[],elementMatchers=[],cached=compilerCache[expando][selector+" "];if(!cached){if(!group){group=tokenize(selector)}i=group.length;while(i--){cached=matcherFromTokens(group[i]);if(cached[expando]){setMatchers.push(cached)}else{elementMatchers.push(cached)}}cached=compilerCache(selector,matcherFromGroupMatchers(elementMatchers,setMatchers))}return cached};function multipleContexts(selector,contexts,results){var i=0,len=contexts.length;for(;i2&&(token=tokens[0]).type==="ID"&&context.nodeType===9&&!xml&&Expr.relative[tokens[1].type]){context=Expr.find["ID"](token.matches[0].replace(rbackslash,""),context,xml)[0];if(!context){return results}selector=selector.slice(tokens.shift().length)}for(i=matchExpr["POS"].test(selector)?-1:tokens.length-1;i>=0;i--){token=tokens[i];if(Expr.relative[type=token.type]){break}if(find=Expr.find[type]){if(seed=find(token.matches[0].replace(rbackslash,""),rsibling.test(tokens[0].type)&&context.parentNode||context,xml)){tokens.splice(i,1);selector=seed.length&&tokens.join("");if(!selector){push.apply(results,slice.call(seed,0));return results}break}}}}}compile(selector,match)(seed,context,xml,results,rsibling.test(selector));return results}if(document.querySelectorAll){(function(){var disconnectedMatch,oldSelect=select,rescape=/'|\\/g,rattributeQuotes=/\=[\x20\t\r\n\f]*([^'"\]]*)[\x20\t\r\n\f]*\]/g,rbuggyQSA=[":focus"],rbuggyMatches=[":active"],matches=docElem.matchesSelector||docElem.mozMatchesSelector||docElem.webkitMatchesSelector||docElem.oMatchesSelector||docElem.msMatchesSelector;assert(function(div){div.innerHTML="";if(!div.querySelectorAll("[selected]").length){rbuggyQSA.push("\\["+whitespace+"*(?:checked|disabled|ismap|multiple|readonly|selected|value)")}if(!div.querySelectorAll(":checked").length){rbuggyQSA.push(":checked")}});assert(function(div){div.innerHTML="

";if(div.querySelectorAll("[test^='']").length){rbuggyQSA.push("[*^$]="+whitespace+"*(?:\"\"|'')")}div.innerHTML="";if(!div.querySelectorAll(":enabled").length){rbuggyQSA.push(":enabled",":disabled")}});rbuggyQSA=new RegExp(rbuggyQSA.join("|"));select=function(selector,context,results,seed,xml){if(!seed&&!xml&&!rbuggyQSA.test(selector)){var groups,i,old=true,nid=expando,newContext=context,newSelector=context.nodeType===9&&selector;if(context.nodeType===1&&context.nodeName.toLowerCase()!=="object"){groups=tokenize(selector);if(old=context.getAttribute("id")){nid=old.replace(rescape,"\\$&")}else{context.setAttribute("id",nid)}nid="[id='"+nid+"'] ";i=groups.length;while(i--){groups[i]=nid+groups[i].join("")}newContext=rsibling.test(selector)&&context.parentNode||context;newSelector=groups.join(",")}if(newSelector){try{push.apply(results,slice.call(newContext.querySelectorAll(newSelector),0));return results}catch(qsaError){}finally{if(!old){context.removeAttribute("id")}}}}return oldSelect(selector,context,results,seed,xml)};if(matches){assert(function(div){disconnectedMatch=matches.call(div,"div");try{matches.call(div,"[test!='']:sizzle");rbuggyMatches.push("!=",pseudos)}catch(e){}});rbuggyMatches=new RegExp(rbuggyMatches.join("|"));Sizzle.matchesSelector=function(elem,expr){expr=expr.replace(rattributeQuotes,"='$1']");if(!isXML(elem)&&!rbuggyMatches.test(expr)&&!rbuggyQSA.test(expr)){try{var ret=matches.call(elem,expr);if(ret||disconnectedMatch||elem.document&&elem.document.nodeType!==11){return ret}}catch(e){}}return Sizzle(expr,null,null,[elem]).length>0}}})()}Expr.pseudos["nth"]=Expr.pseudos["eq"];function setFilters(){}Expr.filters=setFilters.prototype=Expr.pseudos;Expr.setFilters=new setFilters;Sizzle.attr=jQuery.attr;jQuery.find=Sizzle;jQuery.expr=Sizzle.selectors;jQuery.expr[":"]=jQuery.expr.pseudos;jQuery.unique=Sizzle.uniqueSort;jQuery.text=Sizzle.getText;jQuery.isXMLDoc=Sizzle.isXML;jQuery.contains=Sizzle.contains})(window);var runtil=/Until$/,rparentsprev=/^(?:parents|prev(?:Until|All))/,isSimple=/^.[^:#\[\.,]*$/,rneedsContext=jQuery.expr.match.needsContext,guaranteedUnique={children:true,contents:true,next:true,prev:true};jQuery.fn.extend({find:function(selector){var i,l,length,n,r,ret,self=this;if(typeof selector!=="string"){return jQuery(selector).filter(function(){for(i=0,l=self.length;i0){for(n=length;n=0:jQuery.filter(selector,this).length>0:this.filter(selector).length>0)},closest:function(selectors,context){var cur,i=0,l=this.length,ret=[],pos=rneedsContext.test(selectors)||typeof selectors!=="string"?jQuery(selectors,context||this.context):0;for(;i-1:jQuery.find.matchesSelector(cur,selectors)){ret.push(cur);break}cur=cur.parentNode}}ret=ret.length>1?jQuery.unique(ret):ret;return this.pushStack(ret,"closest",selectors)},index:function(elem){if(!elem){return this[0]&&this[0].parentNode?this.prevAll().length:-1}if(typeof elem==="string"){return jQuery.inArray(this[0],jQuery(elem))}return jQuery.inArray(elem.jquery?elem[0]:elem,this)},add:function(selector,context){var set=typeof selector==="string"?jQuery(selector,context):jQuery.makeArray(selector&&selector.nodeType?[selector]:selector),all=jQuery.merge(this.get(),set);return this.pushStack(isDisconnected(set[0])||isDisconnected(all[0])?all:jQuery.unique(all))},addBack:function(selector){return this.add(selector==null?this.prevObject:this.prevObject.filter(selector))}});jQuery.fn.andSelf=jQuery.fn.addBack;function isDisconnected(node){return!node||!node.parentNode||node.parentNode.nodeType===11}function sibling(cur,dir){do{cur=cur[dir]}while(cur&&cur.nodeType!==1);return cur}jQuery.each({parent:function(elem){var parent=elem.parentNode;return parent&&parent.nodeType!==11?parent:null},parents:function(elem){return jQuery.dir(elem,"parentNode")},parentsUntil:function(elem,i,until){return jQuery.dir(elem,"parentNode",until)},next:function(elem){return sibling(elem,"nextSibling")},prev:function(elem){return sibling(elem,"previousSibling")},nextAll:function(elem){return jQuery.dir(elem,"nextSibling")},prevAll:function(elem){return jQuery.dir(elem,"previousSibling")},nextUntil:function(elem,i,until){return jQuery.dir(elem,"nextSibling",until)},prevUntil:function(elem,i,until){return jQuery.dir(elem,"previousSibling",until)},siblings:function(elem){return jQuery.sibling((elem.parentNode||{}).firstChild,elem)},children:function(elem){return jQuery.sibling(elem.firstChild)},contents:function(elem){return jQuery.nodeName(elem,"iframe")?elem.contentDocument||elem.contentWindow.document:jQuery.merge([],elem.childNodes)}},function(name,fn){jQuery.fn[name]=function(until,selector){var ret=jQuery.map(this,fn,until);if(!runtil.test(name)){selector=until}if(selector&&typeof selector==="string"){ret=jQuery.filter(selector,ret)}ret=this.length>1&&!guaranteedUnique[name]?jQuery.unique(ret):ret;if(this.length>1&&rparentsprev.test(name)){ret=ret.reverse()}return this.pushStack(ret,name,core_slice.call(arguments).join(","))}});jQuery.extend({filter:function(expr,elems,not){if(not){expr=":not("+expr+")"}return elems.length===1?jQuery.find.matchesSelector(elems[0],expr)?[elems[0]]:[]:jQuery.find.matches(expr,elems)},dir:function(elem,dir,until){var matched=[],cur=elem[dir];while(cur&&cur.nodeType!==9&&(until===undefined||cur.nodeType!==1||!jQuery(cur).is(until))){if(cur.nodeType===1){matched.push(cur)}cur=cur[dir]}return matched},sibling:function(n,elem){var r=[];for(;n;n=n.nextSibling){if(n.nodeType===1&&n!==elem){r.push(n)}}return r}});function winnow(elements,qualifier,keep){qualifier=qualifier||0;if(jQuery.isFunction(qualifier)){return jQuery.grep(elements,function(elem,i){var retVal=!!qualifier.call(elem,i,elem);return retVal===keep})}else if(qualifier.nodeType){return jQuery.grep(elements,function(elem,i){return elem===qualifier===keep})}else if(typeof qualifier==="string"){var filtered=jQuery.grep(elements,function(elem){return elem.nodeType===1});if(isSimple.test(qualifier)){return jQuery.filter(qualifier,filtered,!keep)}else{qualifier=jQuery.filter(qualifier,filtered)}}return jQuery.grep(elements,function(elem,i){return jQuery.inArray(elem,qualifier)>=0===keep})}function createSafeFragment(document){var list=nodeNames.split("|"),safeFrag=document.createDocumentFragment();if(safeFrag.createElement){while(list.length){safeFrag.createElement(list.pop())}}return safeFrag}var nodeNames="abbr|article|aside|audio|bdi|canvas|data|datalist|details|figcaption|figure|footer|"+"header|hgroup|mark|meter|nav|output|progress|section|summary|time|video",rinlinejQuery=/ jQuery\d+="(?:null|\d+)"/g,rleadingWhitespace=/^\s+/,rxhtmlTag=/<(?!area|br|col|embed|hr|img|input|link|meta|param)(([\w:]+)[^>]*)\/>/gi,rtagName=/<([\w:]+)/,rtbody=/]","i"),rcheckableType=/^(?:checkbox|radio)$/,rchecked=/checked\s*(?:[^=]|=\s*.checked.)/i,rscriptType=/\/(java|ecma)script/i,rcleanScript=/^\s*\s*$/g,wrapMap={option:[1,""],legend:[1,"
","
"],thead:[1,"","
"],tr:[2,"","
"],td:[3,"","
"],col:[2,"","
"],area:[1,"",""],_default:[0,"",""]},safeFragment=createSafeFragment(document),fragmentDiv=safeFragment.appendChild(document.createElement("div"));wrapMap.optgroup=wrapMap.option;wrapMap.tbody=wrapMap.tfoot=wrapMap.colgroup=wrapMap.caption=wrapMap.thead;wrapMap.th=wrapMap.td;if(!jQuery.support.htmlSerialize){wrapMap._default=[1,"X
","
"]}jQuery.fn.extend({text:function(value){return jQuery.access(this,function(value){return value===undefined?jQuery.text(this):this.empty().append((this[0]&&this[0].ownerDocument||document).createTextNode(value))},null,value,arguments.length)},wrapAll:function(html){if(jQuery.isFunction(html)){return this.each(function(i){jQuery(this).wrapAll(html.call(this,i))})}if(this[0]){var wrap=jQuery(html,this[0].ownerDocument).eq(0).clone(true);if(this[0].parentNode){wrap.insertBefore(this[0])}wrap.map(function(){var elem=this;while(elem.firstChild&&elem.firstChild.nodeType===1){elem=elem.firstChild}return elem}).append(this)}return this},wrapInner:function(html){if(jQuery.isFunction(html)){return this.each(function(i){jQuery(this).wrapInner(html.call(this,i))})}return this.each(function(){var self=jQuery(this),contents=self.contents();if(contents.length){contents.wrapAll(html)}else{self.append(html)}})},wrap:function(html){var isFunction=jQuery.isFunction(html);return this.each(function(i){jQuery(this).wrapAll(isFunction?html.call(this,i):html)})},unwrap:function(){return this.parent().each(function(){if(!jQuery.nodeName(this,"body")){jQuery(this).replaceWith(this.childNodes)}}).end()},append:function(){return this.domManip(arguments,true,function(elem){if(this.nodeType===1||this.nodeType===11){this.appendChild(elem)}})},prepend:function(){return this.domManip(arguments,true,function(elem){if(this.nodeType===1||this.nodeType===11){this.insertBefore(elem,this.firstChild)}})},before:function(){if(!isDisconnected(this[0])){return this.domManip(arguments,false,function(elem){this.parentNode.insertBefore(elem,this)})}if(arguments.length){var set=jQuery.clean(arguments);return this.pushStack(jQuery.merge(set,this),"before",this.selector)}},after:function(){if(!isDisconnected(this[0])){return this.domManip(arguments,false,function(elem){this.parentNode.insertBefore(elem,this.nextSibling)})}if(arguments.length){var set=jQuery.clean(arguments);return this.pushStack(jQuery.merge(this,set),"after",this.selector)}},remove:function(selector,keepData){var elem,i=0;for(;(elem=this[i])!=null;i++){if(!selector||jQuery.filter(selector,[elem]).length){if(!keepData&&elem.nodeType===1){jQuery.cleanData(elem.getElementsByTagName("*"));jQuery.cleanData([elem])}if(elem.parentNode){elem.parentNode.removeChild(elem)}}}return this},empty:function(){var elem,i=0;for(;(elem=this[i])!=null;i++){if(elem.nodeType===1){jQuery.cleanData(elem.getElementsByTagName("*"))}while(elem.firstChild){elem.removeChild(elem.firstChild)}}return this},clone:function(dataAndEvents,deepDataAndEvents){dataAndEvents=dataAndEvents==null?false:dataAndEvents;deepDataAndEvents=deepDataAndEvents==null?dataAndEvents:deepDataAndEvents;return this.map(function(){return jQuery.clone(this,dataAndEvents,deepDataAndEvents)})},html:function(value){return jQuery.access(this,function(value){var elem=this[0]||{},i=0,l=this.length;if(value===undefined){return elem.nodeType===1?elem.innerHTML.replace(rinlinejQuery,""):undefined}if(typeof value==="string"&&!rnoInnerhtml.test(value)&&(jQuery.support.htmlSerialize||!rnoshimcache.test(value))&&(jQuery.support.leadingWhitespace||!rleadingWhitespace.test(value))&&!wrapMap[(rtagName.exec(value)||["",""])[1].toLowerCase()]){value=value.replace(rxhtmlTag,"<$1>");try{for(;i1&&typeof value==="string"&&rchecked.test(value)){return this.each(function(){jQuery(this).domManip(args,table,callback)})}if(jQuery.isFunction(value)){return this.each(function(i){var self=jQuery(this);args[0]=value.call(this,i,table?self.html():undefined);self.domManip(args,table,callback)})}if(this[0]){results=jQuery.buildFragment(args,this,scripts);fragment=results.fragment;first=fragment.firstChild;if(fragment.childNodes.length===1){fragment=first}if(first){table=table&&jQuery.nodeName(first,"tr");for(iNoClone=results.cacheable||l-1;i0?this.clone(true):this).get();jQuery(insert[i])[original](elems);ret=ret.concat(elems)}return this.pushStack(ret,name,insert.selector)}}});function getAll(elem){if(typeof elem.getElementsByTagName!=="undefined"){return elem.getElementsByTagName("*")}else if(typeof elem.querySelectorAll!=="undefined"){return elem.querySelectorAll("*")}else{return[]}}function fixDefaultChecked(elem){if(rcheckableType.test(elem.type)){elem.defaultChecked=elem.checked}}jQuery.extend({clone:function(elem,dataAndEvents,deepDataAndEvents){var srcElements,destElements,i,clone;if(jQuery.support.html5Clone||jQuery.isXMLDoc(elem)||!rnoshimcache.test("<"+elem.nodeName+">")){clone=elem.cloneNode(true)}else{fragmentDiv.innerHTML=elem.outerHTML;fragmentDiv.removeChild(clone=fragmentDiv.firstChild)}if((!jQuery.support.noCloneEvent||!jQuery.support.noCloneChecked)&&(elem.nodeType===1||elem.nodeType===11)&&!jQuery.isXMLDoc(elem)){cloneFixAttributes(elem,clone);srcElements=getAll(elem);destElements=getAll(clone);for(i=0;srcElements[i];++i){if(destElements[i]){cloneFixAttributes(srcElements[i],destElements[i])}}}if(dataAndEvents){cloneCopyEvent(elem,clone);if(deepDataAndEvents){srcElements=getAll(elem);destElements=getAll(clone);for(i=0;srcElements[i];++i){cloneCopyEvent(srcElements[i],destElements[i])}}}srcElements=destElements=null;return clone},clean:function(elems,context,fragment,scripts){var i,j,elem,tag,wrap,depth,div,hasBody,tbody,len,handleScript,jsTags,safe=context===document&&safeFragment,ret=[];if(!context||typeof context.createDocumentFragment==="undefined"){context=document}for(i=0;(elem=elems[i])!=null;i++){if(typeof elem==="number"){elem+=""}if(!elem){continue}if(typeof elem==="string"){if(!rhtml.test(elem)){elem=context.createTextNode(elem)}else{safe=safe||createSafeFragment(context);div=context.createElement("div");safe.appendChild(div);elem=elem.replace(rxhtmlTag,"<$1>");tag=(rtagName.exec(elem)||["",""])[1].toLowerCase();wrap=wrapMap[tag]||wrapMap._default;depth=wrap[0];div.innerHTML=wrap[1]+elem+wrap[2];while(depth--){div=div.lastChild +}if(!jQuery.support.tbody){hasBody=rtbody.test(elem);tbody=tag==="table"&&!hasBody?div.firstChild&&div.firstChild.childNodes:wrap[1]===""&&!hasBody?div.childNodes:[];for(j=tbody.length-1;j>=0;--j){if(jQuery.nodeName(tbody[j],"tbody")&&!tbody[j].childNodes.length){tbody[j].parentNode.removeChild(tbody[j])}}}if(!jQuery.support.leadingWhitespace&&rleadingWhitespace.test(elem)){div.insertBefore(context.createTextNode(rleadingWhitespace.exec(elem)[0]),div.firstChild)}elem=div.childNodes;div.parentNode.removeChild(div)}}if(elem.nodeType){ret.push(elem)}else{jQuery.merge(ret,elem)}}if(div){elem=div=safe=null}if(!jQuery.support.appendChecked){for(i=0;(elem=ret[i])!=null;i++){if(jQuery.nodeName(elem,"input")){fixDefaultChecked(elem)}else if(typeof elem.getElementsByTagName!=="undefined"){jQuery.grep(elem.getElementsByTagName("input"),fixDefaultChecked)}}}if(fragment){handleScript=function(elem){if(!elem.type||rscriptType.test(elem.type)){return scripts?scripts.push(elem.parentNode?elem.parentNode.removeChild(elem):elem):fragment.appendChild(elem)}};for(i=0;(elem=ret[i])!=null;i++){if(!(jQuery.nodeName(elem,"script")&&handleScript(elem))){fragment.appendChild(elem);if(typeof elem.getElementsByTagName!=="undefined"){jsTags=jQuery.grep(jQuery.merge([],elem.getElementsByTagName("script")),handleScript);ret.splice.apply(ret,[i+1,0].concat(jsTags));i+=jsTags.length}}}}return ret},cleanData:function(elems,acceptData){var data,id,elem,type,i=0,internalKey=jQuery.expando,cache=jQuery.cache,deleteExpando=jQuery.support.deleteExpando,special=jQuery.event.special;for(;(elem=elems[i])!=null;i++){if(acceptData||jQuery.acceptData(elem)){id=elem[internalKey];data=id&&cache[id];if(data){if(data.events){for(type in data.events){if(special[type]){jQuery.event.remove(elem,type)}else{jQuery.removeEvent(elem,type,data.handle)}}}if(cache[id]){delete cache[id];if(deleteExpando){delete elem[internalKey]}else if(elem.removeAttribute){elem.removeAttribute(internalKey)}else{elem[internalKey]=null}jQuery.deletedIds.push(id)}}}}}});(function(){var matched,browser;jQuery.uaMatch=function(ua){ua=ua.toLowerCase();var match=/(chrome)[ \/]([\w.]+)/.exec(ua)||/(webkit)[ \/]([\w.]+)/.exec(ua)||/(opera)(?:.*version|)[ \/]([\w.]+)/.exec(ua)||/(msie) ([\w.]+)/.exec(ua)||ua.indexOf("compatible")<0&&/(mozilla)(?:.*? rv:([\w.]+)|)/.exec(ua)||[];return{browser:match[1]||"",version:match[2]||"0"}};matched=jQuery.uaMatch(navigator.userAgent);browser={};if(matched.browser){browser[matched.browser]=true;browser.version=matched.version}if(browser.chrome){browser.webkit=true}else if(browser.webkit){browser.safari=true}jQuery.browser=browser;jQuery.sub=function(){function jQuerySub(selector,context){return new jQuerySub.fn.init(selector,context)}jQuery.extend(true,jQuerySub,this);jQuerySub.superclass=this;jQuerySub.fn=jQuerySub.prototype=this();jQuerySub.fn.constructor=jQuerySub;jQuerySub.sub=this.sub;jQuerySub.fn.init=function init(selector,context){if(context&&context instanceof jQuery&&!(context instanceof jQuerySub)){context=jQuerySub(context)}return jQuery.fn.init.call(this,selector,context,rootjQuerySub)};jQuerySub.fn.init.prototype=jQuerySub.fn;var rootjQuerySub=jQuerySub(document);return jQuerySub}})();var curCSS,iframe,iframeDoc,ralpha=/alpha\([^)]*\)/i,ropacity=/opacity=([^)]*)/,rposition=/^(top|right|bottom|left)$/,rdisplayswap=/^(none|table(?!-c[ea]).+)/,rmargin=/^margin/,rnumsplit=new RegExp("^("+core_pnum+")(.*)$","i"),rnumnonpx=new RegExp("^("+core_pnum+")(?!px)[a-z%]+$","i"),rrelNum=new RegExp("^([-+])=("+core_pnum+")","i"),elemdisplay={BODY:"block"},cssShow={position:"absolute",visibility:"hidden",display:"block"},cssNormalTransform={letterSpacing:0,fontWeight:400},cssExpand=["Top","Right","Bottom","Left"],cssPrefixes=["Webkit","O","Moz","ms"],eventsToggle=jQuery.fn.toggle;function vendorPropName(style,name){if(name in style){return name}var capName=name.charAt(0).toUpperCase()+name.slice(1),origName=name,i=cssPrefixes.length;while(i--){name=cssPrefixes[i]+capName;if(name in style){return name}}return origName}function isHidden(elem,el){elem=el||elem;return jQuery.css(elem,"display")==="none"||!jQuery.contains(elem.ownerDocument,elem)}function showHide(elements,show){var elem,display,values=[],index=0,length=elements.length;for(;index1)},show:function(){return showHide(this,true)},hide:function(){return showHide(this)},toggle:function(state,fn2){var bool=typeof state==="boolean";if(jQuery.isFunction(state)&&jQuery.isFunction(fn2)){return eventsToggle.apply(this,arguments)}return this.each(function(){if(bool?state:isHidden(this)){jQuery(this).show()}else{jQuery(this).hide()}})}});jQuery.extend({cssHooks:{opacity:{get:function(elem,computed){if(computed){var ret=curCSS(elem,"opacity");return ret===""?"1":ret}}}},cssNumber:{fillOpacity:true,fontWeight:true,lineHeight:true,opacity:true,orphans:true,widows:true,zIndex:true,zoom:true},cssProps:{"float":jQuery.support.cssFloat?"cssFloat":"styleFloat"},style:function(elem,name,value,extra){if(!elem||elem.nodeType===3||elem.nodeType===8||!elem.style){return}var ret,type,hooks,origName=jQuery.camelCase(name),style=elem.style;name=jQuery.cssProps[origName]||(jQuery.cssProps[origName]=vendorPropName(style,origName));hooks=jQuery.cssHooks[name]||jQuery.cssHooks[origName];if(value!==undefined){type=typeof value;if(type==="string"&&(ret=rrelNum.exec(value))){value=(ret[1]+1)*ret[2]+parseFloat(jQuery.css(elem,name));type="number"}if(value==null||type==="number"&&isNaN(value)){return}if(type==="number"&&!jQuery.cssNumber[origName]){value+="px"}if(!hooks||!("set"in hooks)||(value=hooks.set(elem,value,extra))!==undefined){try{style[name]=value}catch(e){}}}else{if(hooks&&"get"in hooks&&(ret=hooks.get(elem,false,extra))!==undefined){return ret}return style[name]}},css:function(elem,name,numeric,extra){var val,num,hooks,origName=jQuery.camelCase(name);name=jQuery.cssProps[origName]||(jQuery.cssProps[origName]=vendorPropName(elem.style,origName));hooks=jQuery.cssHooks[name]||jQuery.cssHooks[origName];if(hooks&&"get"in hooks){val=hooks.get(elem,true,extra)}if(val===undefined){val=curCSS(elem,name)}if(val==="normal"&&name in cssNormalTransform){val=cssNormalTransform[name]}if(numeric||extra!==undefined){num=parseFloat(val);return numeric||jQuery.isNumeric(num)?num||0:val}return val},swap:function(elem,options,callback){var ret,name,old={};for(name in options){old[name]=elem.style[name];elem.style[name]=options[name]}ret=callback.call(elem);for(name in options){elem.style[name]=old[name]}return ret}});if(window.getComputedStyle){curCSS=function(elem,name){var ret,width,minWidth,maxWidth,computed=window.getComputedStyle(elem,null),style=elem.style;if(computed){ret=computed.getPropertyValue(name)||computed[name];if(ret===""&&!jQuery.contains(elem.ownerDocument,elem)){ret=jQuery.style(elem,name)}if(rnumnonpx.test(ret)&&rmargin.test(name)){width=style.width;minWidth=style.minWidth;maxWidth=style.maxWidth;style.minWidth=style.maxWidth=style.width=ret;ret=computed.width;style.width=width;style.minWidth=minWidth;style.maxWidth=maxWidth}}return ret}}else if(document.documentElement.currentStyle){curCSS=function(elem,name){var left,rsLeft,ret=elem.currentStyle&&elem.currentStyle[name],style=elem.style;if(ret==null&&style&&style[name]){ret=style[name]}if(rnumnonpx.test(ret)&&!rposition.test(name)){left=style.left;rsLeft=elem.runtimeStyle&&elem.runtimeStyle.left;if(rsLeft){elem.runtimeStyle.left=elem.currentStyle.left}style.left=name==="fontSize"?"1em":ret;ret=style.pixelLeft+"px";style.left=left;if(rsLeft){elem.runtimeStyle.left=rsLeft}}return ret===""?"auto":ret}}function setPositiveNumber(elem,value,subtract){var matches=rnumsplit.exec(value);return matches?Math.max(0,matches[1]-(subtract||0))+(matches[2]||"px"):value}function augmentWidthOrHeight(elem,name,extra,isBorderBox){var i=extra===(isBorderBox?"border":"content")?4:name==="width"?1:0,val=0;for(;i<4;i+=2){if(extra==="margin"){val+=jQuery.css(elem,extra+cssExpand[i],true)}if(isBorderBox){if(extra==="content"){val-=parseFloat(curCSS(elem,"padding"+cssExpand[i]))||0}if(extra!=="margin"){val-=parseFloat(curCSS(elem,"border"+cssExpand[i]+"Width"))||0}}else{val+=parseFloat(curCSS(elem,"padding"+cssExpand[i]))||0;if(extra!=="padding"){val+=parseFloat(curCSS(elem,"border"+cssExpand[i]+"Width"))||0}}}return val}function getWidthOrHeight(elem,name,extra){var val=name==="width"?elem.offsetWidth:elem.offsetHeight,valueIsBorderBox=true,isBorderBox=jQuery.support.boxSizing&&jQuery.css(elem,"boxSizing")==="border-box";if(val<=0||val==null){val=curCSS(elem,name);if(val<0||val==null){val=elem.style[name]}if(rnumnonpx.test(val)){return val}valueIsBorderBox=isBorderBox&&(jQuery.support.boxSizingReliable||val===elem.style[name]);val=parseFloat(val)||0}return val+augmentWidthOrHeight(elem,name,extra||(isBorderBox?"border":"content"),valueIsBorderBox)+"px"}function css_defaultDisplay(nodeName){if(elemdisplay[nodeName]){return elemdisplay[nodeName]}var elem=jQuery("<"+nodeName+">").appendTo(document.body),display=elem.css("display");elem.remove();if(display==="none"||display===""){iframe=document.body.appendChild(iframe||jQuery.extend(document.createElement("iframe"),{frameBorder:0,width:0,height:0}));if(!iframeDoc||!iframe.createElement){iframeDoc=(iframe.contentWindow||iframe.contentDocument).document;iframeDoc.write("");iframeDoc.close()}elem=iframeDoc.body.appendChild(iframeDoc.createElement(nodeName));display=curCSS(elem,"display");document.body.removeChild(iframe)}elemdisplay[nodeName]=display;return display}jQuery.each(["height","width"],function(i,name){jQuery.cssHooks[name]={get:function(elem,computed,extra){if(computed){if(elem.offsetWidth===0&&rdisplayswap.test(curCSS(elem,"display"))){return jQuery.swap(elem,cssShow,function(){return getWidthOrHeight(elem,name,extra)})}else{return getWidthOrHeight(elem,name,extra)}}},set:function(elem,value,extra){return setPositiveNumber(elem,value,extra?augmentWidthOrHeight(elem,name,extra,jQuery.support.boxSizing&&jQuery.css(elem,"boxSizing")==="border-box"):0)}}});if(!jQuery.support.opacity){jQuery.cssHooks.opacity={get:function(elem,computed){return ropacity.test((computed&&elem.currentStyle?elem.currentStyle.filter:elem.style.filter)||"")?.01*parseFloat(RegExp.$1)+"":computed?"1":""},set:function(elem,value){var style=elem.style,currentStyle=elem.currentStyle,opacity=jQuery.isNumeric(value)?"alpha(opacity="+value*100+")":"",filter=currentStyle&¤tStyle.filter||style.filter||"";style.zoom=1;if(value>=1&&jQuery.trim(filter.replace(ralpha,""))===""&&style.removeAttribute){style.removeAttribute("filter");if(currentStyle&&!currentStyle.filter){return}}style.filter=ralpha.test(filter)?filter.replace(ralpha,opacity):filter+" "+opacity}}}jQuery(function(){if(!jQuery.support.reliableMarginRight){jQuery.cssHooks.marginRight={get:function(elem,computed){return jQuery.swap(elem,{display:"inline-block"},function(){if(computed){return curCSS(elem,"marginRight")}})}}}if(!jQuery.support.pixelPosition&&jQuery.fn.position){jQuery.each(["top","left"],function(i,prop){jQuery.cssHooks[prop]={get:function(elem,computed){if(computed){var ret=curCSS(elem,prop);return rnumnonpx.test(ret)?jQuery(elem).position()[prop]+"px":ret}}}})}});if(jQuery.expr&&jQuery.expr.filters){jQuery.expr.filters.hidden=function(elem){return elem.offsetWidth===0&&elem.offsetHeight===0||!jQuery.support.reliableHiddenOffsets&&(elem.style&&elem.style.display||curCSS(elem,"display"))==="none"};jQuery.expr.filters.visible=function(elem){return!jQuery.expr.filters.hidden(elem)}}jQuery.each({margin:"",padding:"",border:"Width"},function(prefix,suffix){jQuery.cssHooks[prefix+suffix]={expand:function(value){var i,parts=typeof value==="string"?value.split(" "):[value],expanded={};for(i=0;i<4;i++){expanded[prefix+cssExpand[i]+suffix]=parts[i]||parts[i-2]||parts[0]}return expanded}};if(!rmargin.test(prefix)){jQuery.cssHooks[prefix+suffix].set=setPositiveNumber}});var r20=/%20/g,rbracket=/\[\]$/,rCRLF=/\r?\n/g,rinput=/^(?:color|date|datetime|datetime-local|email|hidden|month|number|password|range|search|tel|text|time|url|week)$/i,rselectTextarea=/^(?:select|textarea)/i;jQuery.fn.extend({serialize:function(){return jQuery.param(this.serializeArray())},serializeArray:function(){return this.map(function(){return this.elements?jQuery.makeArray(this.elements):this}).filter(function(){return this.name&&!this.disabled&&(this.checked||rselectTextarea.test(this.nodeName)||rinput.test(this.type))}).map(function(i,elem){var val=jQuery(this).val();return val==null?null:jQuery.isArray(val)?jQuery.map(val,function(val,i){return{name:elem.name,value:val.replace(rCRLF,"\r\n")}}):{name:elem.name,value:val.replace(rCRLF,"\r\n")}}).get()}});jQuery.param=function(a,traditional){var prefix,s=[],add=function(key,value){value=jQuery.isFunction(value)?value():value==null?"":value;s[s.length]=encodeURIComponent(key)+"="+encodeURIComponent(value)};if(traditional===undefined){traditional=jQuery.ajaxSettings&&jQuery.ajaxSettings.traditional}if(jQuery.isArray(a)||a.jquery&&!jQuery.isPlainObject(a)){jQuery.each(a,function(){add(this.name,this.value)})}else{for(prefix in a){buildParams(prefix,a[prefix],traditional,add)}}return s.join("&").replace(r20,"+")};function buildParams(prefix,obj,traditional,add){var name;if(jQuery.isArray(obj)){jQuery.each(obj,function(i,v){if(traditional||rbracket.test(prefix)){add(prefix,v)}else{buildParams(prefix+"["+(typeof v==="object"?i:"")+"]",v,traditional,add)}})}else if(!traditional&&jQuery.type(obj)==="object"){for(name in obj){buildParams(prefix+"["+name+"]",obj[name],traditional,add)}}else{add(prefix,obj)}}var ajaxLocParts,ajaxLocation,rhash=/#.*$/,rheaders=/^(.*?):[ \t]*([^\r\n]*)\r?$/gm,rlocalProtocol=/^(?:about|app|app\-storage|.+\-extension|file|res|widget):$/,rnoContent=/^(?:GET|HEAD)$/,rprotocol=/^\/\//,rquery=/\?/,rscript=/)<[^<]*)*<\/script>/gi,rts=/([?&])_=[^&]*/,rurl=/^([\w\+\.\-]+:)(?:\/\/([^\/?#:]*)(?::(\d+)|)|)/,_load=jQuery.fn.load,prefilters={},transports={},allTypes=["*/"]+["*"];try{ajaxLocation=location.href}catch(e){ajaxLocation=document.createElement("a");ajaxLocation.href="";ajaxLocation=ajaxLocation.href}ajaxLocParts=rurl.exec(ajaxLocation.toLowerCase())||[];function addToPrefiltersOrTransports(structure){return function(dataTypeExpression,func){if(typeof dataTypeExpression!=="string"){func=dataTypeExpression;dataTypeExpression="*"}var dataType,list,placeBefore,dataTypes=dataTypeExpression.toLowerCase().split(core_rspace),i=0,length=dataTypes.length;if(jQuery.isFunction(func)){for(;i=0){selector=url.slice(off,url.length);url=url.slice(0,off)}if(jQuery.isFunction(params)){callback=params;params=undefined}else if(params&&typeof params==="object"){type="POST"}jQuery.ajax({url:url,type:type,dataType:"html",data:params,complete:function(jqXHR,status){if(callback){self.each(callback,response||[jqXHR.responseText,status,jqXHR])}}}).done(function(responseText){response=arguments;self.html(selector?jQuery("
").append(responseText.replace(rscript,"")).find(selector):responseText)});return this};jQuery.each("ajaxStart ajaxStop ajaxComplete ajaxError ajaxSuccess ajaxSend".split(" "),function(i,o){jQuery.fn[o]=function(f){return this.on(o,f)}});jQuery.each(["get","post"],function(i,method){jQuery[method]=function(url,data,callback,type){if(jQuery.isFunction(data)){type=type||callback;callback=data;data=undefined}return jQuery.ajax({type:method,url:url,data:data,success:callback,dataType:type})}});jQuery.extend({getScript:function(url,callback){return jQuery.get(url,undefined,callback,"script")},getJSON:function(url,data,callback){return jQuery.get(url,data,callback,"json")},ajaxSetup:function(target,settings){if(settings){ajaxExtend(target,jQuery.ajaxSettings)}else{settings=target;target=jQuery.ajaxSettings}ajaxExtend(target,settings);return target},ajaxSettings:{url:ajaxLocation,isLocal:rlocalProtocol.test(ajaxLocParts[1]),global:true,type:"GET",contentType:"application/x-www-form-urlencoded; charset=UTF-8",processData:true,async:true,accepts:{xml:"application/xml, text/xml",html:"text/html",text:"text/plain",json:"application/json, text/javascript","*":allTypes},contents:{xml:/xml/,html:/html/,json:/json/},responseFields:{xml:"responseXML",text:"responseText"},converters:{"* text":window.String,"text html":true,"text json":jQuery.parseJSON,"text xml":jQuery.parseXML},flatOptions:{context:true,url:true}},ajaxPrefilter:addToPrefiltersOrTransports(prefilters),ajaxTransport:addToPrefiltersOrTransports(transports),ajax:function(url,options){if(typeof url==="object"){options=url;url=undefined}options=options||{};var ifModifiedKey,responseHeadersString,responseHeaders,transport,timeoutTimer,parts,fireGlobals,i,s=jQuery.ajaxSetup({},options),callbackContext=s.context||s,globalEventContext=callbackContext!==s&&(callbackContext.nodeType||callbackContext instanceof jQuery)?jQuery(callbackContext):jQuery.event,deferred=jQuery.Deferred(),completeDeferred=jQuery.Callbacks("once memory"),statusCode=s.statusCode||{},requestHeaders={},requestHeadersNames={},state=0,strAbort="canceled",jqXHR={readyState:0,setRequestHeader:function(name,value){if(!state){var lname=name.toLowerCase();name=requestHeadersNames[lname]=requestHeadersNames[lname]||name;requestHeaders[name]=value}return this},getAllResponseHeaders:function(){return state===2?responseHeadersString:null},getResponseHeader:function(key){var match;if(state===2){if(!responseHeaders){responseHeaders={};while(match=rheaders.exec(responseHeadersString)){responseHeaders[match[1].toLowerCase()]=match[2]}}match=responseHeaders[key.toLowerCase()]}return match===undefined?null:match},overrideMimeType:function(type){if(!state){s.mimeType=type}return this},abort:function(statusText){statusText=statusText||strAbort;if(transport){transport.abort(statusText)}done(0,statusText);return this}};function done(status,nativeStatusText,responses,headers){var isSuccess,success,error,response,modified,statusText=nativeStatusText;if(state===2){return}state=2;if(timeoutTimer){clearTimeout(timeoutTimer)}transport=undefined;responseHeadersString=headers||"";jqXHR.readyState=status>0?4:0;if(responses){response=ajaxHandleResponses(s,jqXHR,responses)}if(status>=200&&status<300||status===304){if(s.ifModified){modified=jqXHR.getResponseHeader("Last-Modified");if(modified){jQuery.lastModified[ifModifiedKey]=modified}modified=jqXHR.getResponseHeader("Etag");if(modified){jQuery.etag[ifModifiedKey]=modified}}if(status===304){statusText="notmodified";isSuccess=true}else{isSuccess=ajaxConvert(s,response);statusText=isSuccess.state;success=isSuccess.data;error=isSuccess.error;isSuccess=!error}}else{error=statusText;if(!statusText||status){statusText="error";if(status<0){status=0}}}jqXHR.status=status;jqXHR.statusText=(nativeStatusText||statusText)+"";if(isSuccess){deferred.resolveWith(callbackContext,[success,statusText,jqXHR])}else{deferred.rejectWith(callbackContext,[jqXHR,statusText,error])}jqXHR.statusCode(statusCode);statusCode=undefined;if(fireGlobals){globalEventContext.trigger("ajax"+(isSuccess?"Success":"Error"),[jqXHR,s,isSuccess?success:error])}completeDeferred.fireWith(callbackContext,[jqXHR,statusText]);if(fireGlobals){globalEventContext.trigger("ajaxComplete",[jqXHR,s]);if(!--jQuery.active){jQuery.event.trigger("ajaxStop")}}}deferred.promise(jqXHR);jqXHR.success=jqXHR.done;jqXHR.error=jqXHR.fail;jqXHR.complete=completeDeferred.add;jqXHR.statusCode=function(map){if(map){var tmp;if(state<2){for(tmp in map){statusCode[tmp]=[statusCode[tmp],map[tmp]]}}else{tmp=map[jqXHR.status];jqXHR.always(tmp)}}return this};s.url=((url||s.url)+"").replace(rhash,"").replace(rprotocol,ajaxLocParts[1]+"//");s.dataTypes=jQuery.trim(s.dataType||"*").toLowerCase().split(core_rspace);if(s.crossDomain==null){parts=rurl.exec(s.url.toLowerCase());s.crossDomain=!!(parts&&(parts[1]!==ajaxLocParts[1]||parts[2]!==ajaxLocParts[2]||(parts[3]||(parts[1]==="http:"?80:443))!=(ajaxLocParts[3]||(ajaxLocParts[1]==="http:"?80:443))))}if(s.data&&s.processData&&typeof s.data!=="string"){s.data=jQuery.param(s.data,s.traditional)}inspectPrefiltersOrTransports(prefilters,s,options,jqXHR);if(state===2){return jqXHR}fireGlobals=s.global;s.type=s.type.toUpperCase();s.hasContent=!rnoContent.test(s.type);if(fireGlobals&&jQuery.active++===0){jQuery.event.trigger("ajaxStart")}if(!s.hasContent){if(s.data){s.url+=(rquery.test(s.url)?"&":"?")+s.data;delete s.data}ifModifiedKey=s.url;if(s.cache===false){var ts=jQuery.now(),ret=s.url.replace(rts,"$1_="+ts);s.url=ret+(ret===s.url?(rquery.test(s.url)?"&":"?")+"_="+ts:"")}}if(s.data&&s.hasContent&&s.contentType!==false||options.contentType){jqXHR.setRequestHeader("Content-Type",s.contentType)}if(s.ifModified){ifModifiedKey=ifModifiedKey||s.url;if(jQuery.lastModified[ifModifiedKey]){jqXHR.setRequestHeader("If-Modified-Since",jQuery.lastModified[ifModifiedKey])}if(jQuery.etag[ifModifiedKey]){jqXHR.setRequestHeader("If-None-Match",jQuery.etag[ifModifiedKey])}}jqXHR.setRequestHeader("Accept",s.dataTypes[0]&&s.accepts[s.dataTypes[0]]?s.accepts[s.dataTypes[0]]+(s.dataTypes[0]!=="*"?", "+allTypes+"; q=0.01":""):s.accepts["*"]);for(i in s.headers){jqXHR.setRequestHeader(i,s.headers[i])}if(s.beforeSend&&(s.beforeSend.call(callbackContext,jqXHR,s)===false||state===2)){return jqXHR.abort()}strAbort="abort";for(i in{success:1,error:1,complete:1}){jqXHR[i](s[i])}transport=inspectPrefiltersOrTransports(transports,s,options,jqXHR);if(!transport){done(-1,"No Transport")}else{jqXHR.readyState=1;if(fireGlobals){globalEventContext.trigger("ajaxSend",[jqXHR,s])}if(s.async&&s.timeout>0){timeoutTimer=setTimeout(function(){jqXHR.abort("timeout")},s.timeout)}try{state=1;transport.send(requestHeaders,done)}catch(e){if(state<2){done(-1,e)}else{throw e}}}return jqXHR},active:0,lastModified:{},etag:{}});function ajaxHandleResponses(s,jqXHR,responses){var ct,type,finalDataType,firstDataType,contents=s.contents,dataTypes=s.dataTypes,responseFields=s.responseFields;for(type in responseFields){if(type in responses){jqXHR[responseFields[type]]=responses[type]}}while(dataTypes[0]==="*"){dataTypes.shift();if(ct===undefined){ct=s.mimeType||jqXHR.getResponseHeader("content-type")}}if(ct){for(type in contents){if(contents[type]&&contents[type].test(ct)){dataTypes.unshift(type);break}}}if(dataTypes[0]in responses){finalDataType=dataTypes[0]}else{for(type in responses){if(!dataTypes[0]||s.converters[type+" "+dataTypes[0]]){finalDataType=type;break}if(!firstDataType){firstDataType=type}}finalDataType=finalDataType||firstDataType}if(finalDataType){if(finalDataType!==dataTypes[0]){dataTypes.unshift(finalDataType)}return responses[finalDataType]}}function ajaxConvert(s,response){var conv,conv2,current,tmp,dataTypes=s.dataTypes.slice(),prev=dataTypes[0],converters={},i=0;if(s.dataFilter){response=s.dataFilter(response,s.dataType)}if(dataTypes[1]){for(conv in s.converters){converters[conv.toLowerCase()]=s.converters[conv]}}for(;current=dataTypes[++i];){if(current!=="*"){if(prev!=="*"&&prev!==current){conv=converters[prev+" "+current]||converters["* "+current];if(!conv){for(conv2 in converters){tmp=conv2.split(" ");if(tmp[1]===current){conv=converters[prev+" "+tmp[0]]||converters["* "+tmp[0]];if(conv){if(conv===true){conv=converters[conv2]}else if(converters[conv2]!==true){current=tmp[0];dataTypes.splice(i--,0,current)}break}}}}if(conv!==true){if(conv&&s["throws"]){response=conv(response)}else{try{response=conv(response)}catch(e){return{state:"parsererror",error:conv?e:"No conversion from "+prev+" to "+current}}}}}prev=current}}return{state:"success",data:response}}var oldCallbacks=[],rquestion=/\?/,rjsonp=/(=)\?(?=&|$)|\?\?/,nonce=jQuery.now();jQuery.ajaxSetup({jsonp:"callback",jsonpCallback:function(){var callback=oldCallbacks.pop()||jQuery.expando+"_"+nonce++;this[callback]=true;return callback}});jQuery.ajaxPrefilter("json jsonp",function(s,originalSettings,jqXHR){var callbackName,overwritten,responseContainer,data=s.data,url=s.url,hasCallback=s.jsonp!==false,replaceInUrl=hasCallback&&rjsonp.test(url),replaceInData=hasCallback&&!replaceInUrl&&typeof data==="string"&&!(s.contentType||"").indexOf("application/x-www-form-urlencoded")&&rjsonp.test(data);if(s.dataTypes[0]==="jsonp"||replaceInUrl||replaceInData){callbackName=s.jsonpCallback=jQuery.isFunction(s.jsonpCallback)?s.jsonpCallback():s.jsonpCallback;overwritten=window[callbackName];if(replaceInUrl){s.url=url.replace(rjsonp,"$1"+callbackName)}else if(replaceInData){s.data=data.replace(rjsonp,"$1"+callbackName)}else if(hasCallback){s.url+=(rquestion.test(url)?"&":"?")+s.jsonp+"="+callbackName}s.converters["script json"]=function(){if(!responseContainer){jQuery.error(callbackName+" was not called")}return responseContainer[0]};s.dataTypes[0]="json";window[callbackName]=function(){responseContainer=arguments};jqXHR.always(function(){window[callbackName]=overwritten;if(s[callbackName]){s.jsonpCallback=originalSettings.jsonpCallback;oldCallbacks.push(callbackName)}if(responseContainer&&jQuery.isFunction(overwritten)){overwritten(responseContainer[0])}responseContainer=overwritten=undefined});return"script"}});jQuery.ajaxSetup({accepts:{script:"text/javascript, application/javascript, application/ecmascript, application/x-ecmascript"},contents:{script:/javascript|ecmascript/},converters:{"text script":function(text){jQuery.globalEval(text);return text}}});jQuery.ajaxPrefilter("script",function(s){if(s.cache===undefined){s.cache=false}if(s.crossDomain){s.type="GET";s.global=false}});jQuery.ajaxTransport("script",function(s){if(s.crossDomain){var script,head=document.head||document.getElementsByTagName("head")[0]||document.documentElement;return{send:function(_,callback){script=document.createElement("script");script.async="async";if(s.scriptCharset){script.charset=s.scriptCharset}script.src=s.url;script.onload=script.onreadystatechange=function(_,isAbort){if(isAbort||!script.readyState||/loaded|complete/.test(script.readyState)){script.onload=script.onreadystatechange=null;if(head&&script.parentNode){head.removeChild(script)}script=undefined;if(!isAbort){callback(200,"success")}}};head.insertBefore(script,head.firstChild)},abort:function(){if(script){script.onload(0,1)}}}}});var xhrCallbacks,xhrOnUnloadAbort=window.ActiveXObject?function(){for(var key in xhrCallbacks){xhrCallbacks[key](0,1)}}:false,xhrId=0;function createStandardXHR(){try{return new window.XMLHttpRequest}catch(e){}}function createActiveXHR(){try{return new window.ActiveXObject("Microsoft.XMLHTTP")}catch(e){}}jQuery.ajaxSettings.xhr=window.ActiveXObject?function(){return!this.isLocal&&createStandardXHR()||createActiveXHR()}:createStandardXHR;(function(xhr){jQuery.extend(jQuery.support,{ajax:!!xhr,cors:!!xhr&&"withCredentials"in xhr})})(jQuery.ajaxSettings.xhr());if(jQuery.support.ajax){jQuery.ajaxTransport(function(s){if(!s.crossDomain||jQuery.support.cors){var callback;return{send:function(headers,complete){var handle,i,xhr=s.xhr();if(s.username){xhr.open(s.type,s.url,s.async,s.username,s.password)}else{xhr.open(s.type,s.url,s.async)}if(s.xhrFields){for(i in s.xhrFields){xhr[i]=s.xhrFields[i]}}if(s.mimeType&&xhr.overrideMimeType){xhr.overrideMimeType(s.mimeType)}if(!s.crossDomain&&!headers["X-Requested-With"]){headers["X-Requested-With"]="XMLHttpRequest"}try{for(i in headers){xhr.setRequestHeader(i,headers[i])}}catch(_){}xhr.send(s.hasContent&&s.data||null);callback=function(_,isAbort){var status,statusText,responseHeaders,responses,xml;try{if(callback&&(isAbort||xhr.readyState===4)){callback=undefined;if(handle){xhr.onreadystatechange=jQuery.noop;if(xhrOnUnloadAbort){delete xhrCallbacks[handle]}}if(isAbort){if(xhr.readyState!==4){xhr.abort()}}else{status=xhr.status;responseHeaders=xhr.getAllResponseHeaders();responses={};xml=xhr.responseXML;if(xml&&xml.documentElement){responses.xml=xml}try{responses.text=xhr.responseText}catch(e){}try{statusText=xhr.statusText}catch(e){statusText=""}if(!status&&s.isLocal&&!s.crossDomain){status=responses.text?200:404}else if(status===1223){status=204}}}}catch(firefoxAccessException){if(!isAbort){complete(-1,firefoxAccessException)}}if(responses){complete(status,statusText,responses,responseHeaders)}};if(!s.async){callback()}else if(xhr.readyState===4){setTimeout(callback,0)}else{handle=++xhrId;if(xhrOnUnloadAbort){if(!xhrCallbacks){xhrCallbacks={};jQuery(window).unload(xhrOnUnloadAbort)}xhrCallbacks[handle]=callback}xhr.onreadystatechange=callback}},abort:function(){if(callback){callback(0,1)}}}}})}var fxNow,timerId,rfxtypes=/^(?:toggle|show|hide)$/,rfxnum=new RegExp("^(?:([-+])=|)("+core_pnum+")([a-z%]*)$","i"),rrun=/queueHooks$/,animationPrefilters=[defaultPrefilter],tweeners={"*":[function(prop,value){var end,unit,tween=this.createTween(prop,value),parts=rfxnum.exec(value),target=tween.cur(),start=+target||0,scale=1,maxIterations=20;if(parts){end=+parts[2];unit=parts[3]||(jQuery.cssNumber[prop]?"":"px");if(unit!=="px"&&start){start=jQuery.css(tween.elem,prop,true)||end||1;do{scale=scale||".5";start=start/scale;jQuery.style(tween.elem,prop,start+unit)}while(scale!==(scale=tween.cur()/target)&&scale!==1&&--maxIterations)}tween.unit=unit;tween.start=start;tween.end=parts[1]?start+(parts[1]+1)*end:end}return tween}]};function createFxNow(){setTimeout(function(){fxNow=undefined},0);return fxNow=jQuery.now()}function createTweens(animation,props){jQuery.each(props,function(prop,value){var collection=(tweeners[prop]||[]).concat(tweeners["*"]),index=0,length=collection.length;for(;index-1,props={},curPosition={},curTop,curLeft;if(calculatePosition){curPosition=curElem.position();curTop=curPosition.top;curLeft=curPosition.left}else{curTop=parseFloat(curCSSTop)||0;curLeft=parseFloat(curCSSLeft)||0}if(jQuery.isFunction(options)){options=options.call(elem,i,curOffset)}if(options.top!=null){props.top=options.top-curOffset.top+curTop}if(options.left!=null){props.left=options.left-curOffset.left+curLeft}if("using"in options){options.using.call(elem,props)}else{curElem.css(props)}}};jQuery.fn.extend({position:function(){if(!this[0]){return}var elem=this[0],offsetParent=this.offsetParent(),offset=this.offset(),parentOffset=rroot.test(offsetParent[0].nodeName)?{top:0,left:0}:offsetParent.offset();offset.top-=parseFloat(jQuery.css(elem,"marginTop"))||0;offset.left-=parseFloat(jQuery.css(elem,"marginLeft"))||0;parentOffset.top+=parseFloat(jQuery.css(offsetParent[0],"borderTopWidth"))||0;parentOffset.left+=parseFloat(jQuery.css(offsetParent[0],"borderLeftWidth"))||0;return{top:offset.top-parentOffset.top,left:offset.left-parentOffset.left}},offsetParent:function(){return this.map(function(){var offsetParent=this.offsetParent||document.body;while(offsetParent&&(!rroot.test(offsetParent.nodeName)&&jQuery.css(offsetParent,"position")==="static")){offsetParent=offsetParent.offsetParent}return offsetParent||document.body})}});jQuery.each({scrollLeft:"pageXOffset",scrollTop:"pageYOffset"},function(method,prop){var top=/Y/.test(prop);jQuery.fn[method]=function(val){return jQuery.access(this,function(elem,method,val){var win=getWindow(elem);if(val===undefined){return win?prop in win?win[prop]:win.document.documentElement[method]:elem[method]}if(win){win.scrollTo(!top?val:jQuery(win).scrollLeft(),top?val:jQuery(win).scrollTop())}else{elem[method]=val}},method,val,arguments.length,null)}});function getWindow(elem){return jQuery.isWindow(elem)?elem:elem.nodeType===9?elem.defaultView||elem.parentWindow:false}jQuery.each({Height:"height",Width:"width"},function(name,type){jQuery.each({padding:"inner"+name,content:type,"":"outer"+name},function(defaultExtra,funcName){jQuery.fn[funcName]=function(margin,value){var chainable=arguments.length&&(defaultExtra||typeof margin!=="boolean"),extra=defaultExtra||(margin===true||value===true?"margin":"border");return jQuery.access(this,function(elem,type,value){var doc;if(jQuery.isWindow(elem)){return elem.document.documentElement["client"+name]}if(elem.nodeType===9){doc=elem.documentElement;return Math.max(elem.body["scroll"+name],doc["scroll"+name],elem.body["offset"+name],doc["offset"+name],doc["client"+name])}return value===undefined?jQuery.css(elem,type,value,extra):jQuery.style(elem,type,value,extra)},type,chainable?margin:undefined,chainable,null)}})});window.jQuery=window.$=jQuery;if(typeof define==="function"&&define.amd&&define.amd.jQuery){define("jquery",[],function(){return jQuery})}})(window); \ No newline at end of file diff --git a/pandora_console/include/graphs/flot/pandora.flot.js b/pandora_console/include/graphs/flot/pandora.flot.js index 0ab870c20c..40d1f6cf6f 100644 --- a/pandora_console/include/graphs/flot/pandora.flot.js +++ b/pandora_console/include/graphs/flot/pandora.flot.js @@ -165,9 +165,9 @@ function pandoraFlotPieCustom(graph_id, values, labels, width, color: '' } }; - + } - + var conf_pie = { series: { pie: { @@ -302,15 +302,14 @@ function pandoraFlotPieCustom(graph_id, values, labels, width, $(this).text(legend[j]); j++; }); - + if ($('input[name="custom_graph"]').val()) { $('.legend>div').css('right',($('.legend>div').height()*-1)); $('.legend>table').css('right',($('.legend>div').height()*-1)); } //$('.legend>table').css('border',"1px solid #E2E2E2"); $('.legend>table').css('background-color',"transparent"); - - + var pielegends = $('#'+graph_id+' .pieLabelBackground'); pielegends.each(function () { $(this).css('transform', "rotate(-35deg)").css('color', 'black'); @@ -324,7 +323,8 @@ function pandoraFlotPieCustom(graph_id, values, labels, width, } function pandoraFlotHBars(graph_id, values, labels, water_mark, - maxvalue, water_mark, separator, separator2, font, font_size, background_color, tick_color, min, max) { + maxvalue, water_mark, separator, separator2, font, font_size, + background_color, tick_color, min, max) { var colors_data = ['#FC4444','#FFA631','#FAD403','#5BB6E5','#F2919D','#80BA27']; values = values.split(separator2); @@ -416,6 +416,7 @@ function pandoraFlotHBars(graph_id, values, labels, water_mark, function yFormatter(v, axis) { format = new Array(); + for (i = 0; i < labels_total.length; i++) { var label = labels_total[i][1]; // var shortLabel = reduceText(label, 25); @@ -692,7 +693,7 @@ function pandoraFlotSlicebar(graph_id, values, datacolor, labels, legend, acumul if (full_legend != false) { full_legend = full_legend.split(separator); } - + // Check possible adapt_keys on classes check_adaptions(graph_id); @@ -805,7 +806,7 @@ function pandoraFlotSlicebar(graph_id, values, datacolor, labels, legend, acumul var to = legend[item.seriesIndex+1]; //current date var dateObj = new Date(); - + if (full_legend != "") { newdate = full_legend[item.seriesIndex]; newdate2 = full_legend[item.seriesIndex+1]; @@ -816,11 +817,11 @@ function pandoraFlotSlicebar(graph_id, values, datacolor, labels, legend, acumul var year = dateObj.getUTCFullYear(); newdate = year + "/" + month + "/" + day; } - + if(!to){ to= '23:59'; } - + if (full_legend != "") { if (newdate2 == undefined) { window.location='index.php?sec=eventos&sec2=operation/events/events&id_agent='+id_agent+'&date_from='+newdate+'&time_from='+from+'&status=-1'; @@ -854,206 +855,113 @@ function pandoraFlotSlicebar(graph_id, values, datacolor, labels, legend, acumul } } -function pandoraFlotArea(graph_id, values, labels, labels_long, legend, - colors, type, serie_types, water_mark, width, max_x, homeurl, unit, - font_size, font, menu, events, event_ids, legend_events, alerts, - alert_ids, legend_alerts, yellow_threshold, red_threshold, - force_integer, separator, separator2, - yellow_up, red_up, yellow_inverse, red_inverse, - series_suffix_str, dashboard, vconsole, xaxisname,background_color,legend_color, - short_data) { - - var threshold = true; - var thresholded = false; - font = font.split("/").pop().split(".").shift(); +function pandoraFlotArea( + graph_id, values, legend, agent_module_id, + series_type, color, water_mark, date_array, + data_module_graph, params, + force_integer, background_color, + legend_color, short_data, events_array +) { - values = values.split(separator2); - serie_types = serie_types.split(separator); - labels_long = labels_long.split(separator); - labels = labels.split(separator); - legend = legend.split(separator); - events = events.split(separator); - event_ids = event_ids.split(separator); - if (alerts.length != 0) - alerts = alerts.split(separator); - else - alerts = []; - alert_ids = alert_ids.split(separator); - colors = colors.split(separator); + //diferents vars + var unit = params.unit ? params.unit : ''; + var homeurl = params.homeurl; + var font_size = params.font_size; + var font = params.font; + var width = params.width; + var height = params.height; + var vconsole = params.vconsole; + var dashboard = params.dashboard; + var menu = params.menu; + var min_x = date_array['start_date'] *1000; + var max_x = date_array['final_date'] *1000; + var type = params.stacked; + var show_legend = params.show_legend; + var image_treshold = params.image_treshold; - var eventsz = new Array(); - $.each(events,function(i,v) { - eventsz[event_ids[i]] = v; - }); - - var alertsz = new Array(); - $.each(alerts,function(i,v) { - alertsz[alert_ids[i]] = v; - }); - - switch (type) { - case 'line_simple': - stacked = null; - filled = false; - break; - case 'line_stacked': - stacked = 'stack'; - filled = false; - break; - case 'area_simple': - stacked = null; - filled = true; - break; - case 'area_stacked': - stacked = 'stack'; - filled = true; - break; + if(typeof type === 'undefined' || type == ''){ + type = params.type_graph; } - var datas = new Array(); - var data_base = new Array(); + //for threshold + var threshold = true; + var thresholded = false; + var yellow_threshold = parseFloat (data_module_graph.w_min); + var red_threshold = parseFloat (data_module_graph.c_min); + var yellow_up = parseFloat (data_module_graph.w_max); + var red_up = parseFloat (data_module_graph.c_max); + var yellow_inverse = parseInt (data_module_graph.w_inv); + var red_inverse = parseInt (data_module_graph.c_inv); - // Prepared to turn series with a checkbox - // var showed = new Array(); + //XXXXX + var markins_graph = true; - var min_check = 0; - for (i = 0; i < values.length; i++) { - var serie = values[i].split(separator); - var aux = new Array(); - $.each(serie, function(i, v) { - if(v < 0){ - if(min_check > parseFloat(v)){ - min_check = v; - } - } - - aux.push([i, v]); - }); - - switch (serie_types[i]) { - case 'area': - line_show = true; - points_show = false; - filled = true; - steps_chart = false; - break; - case 'line': - default: - line_show = true; - points_show = false; - filled = false; - steps_chart = false; - break; - case 'points': - line_show = false; - points_show = true; - filled = false; - steps_chart = false - break; - case 'unknown': - case 'boolean': - line_show = true; - points_show = false; - filled = true; - steps_chart = true; - break; - } - - var serie_color; - if (colors[i] != '') { - serie_color = colors[i]; - } - else { - serie_color = '#8c2'; - } - - var normalw = '#efe'; - var warningw = '#ffe'; - var criticalw = '#fee'; - var normal = '#0f0'; - var warning = '#ff0'; - var critical = '#f00'; - - // setup background areas - //vnormal_max = vwarning_min - 1; - - var markings = null; - - // Fill the grid background with weak threshold colors - //~ markings = [ - //~ { color: normalw, yaxis: { from: -1,to: vnormal_max } }, - //~ { color: warningw, yaxis: { from: vwarning_min, to: vwarning_max } }, - //~ { color: criticalw, yaxis: { from: vcritical_min } }, - //~ { color: criticalw, yaxis: { to: -1 } } - //~ ]; - - var lineWidth = $('#hidden-line_width_graph').val() || 1; - - // Data - data_base.push({ - id: 'serie_' + i, - data: aux, - label: legend[i], - color: serie_color, - //threshold: [{ below: 80, color: "rgb(200, 20, 30)" } , { below: 65, color: "rgb(30, 200, 30)" }, { below: 50, color: "rgb(30, 200, 30)" }], - lines: { - show: line_show, - fill: filled, - fillColor: { - colors: [ { opacity: 0.5 }, { opacity: 1 } ] - }, - lineWidth: lineWidth, - steps: steps_chart - }, - points: { show: points_show } - }); - - // Prepared to turn series with a checkbox - // showed[i] = true; - } - - if(min_check != 0){ - min_check = min_check -5; - } + var legend_events = null; + var legend_alerts = null; // If threshold and up are the same, that critical or warning is disabled - if (yellow_threshold == yellow_up) yellow_inverse = false; - if (red_threshold == red_up) red_inverse = false; + if (yellow_threshold == yellow_up){ + yellow_inverse = false; + } + + if (red_threshold == red_up){ + red_inverse = false; + } //Array with points to be painted var threshold_data = new Array(); //Array with some interesting points var extremes = new Array (); - - yellow_threshold = parseFloat (yellow_threshold); - yellow_up = parseFloat (yellow_up); - red_threshold = parseFloat (red_threshold); - red_up = parseFloat (red_up); + var yellow_only_min = ((yellow_up == 0) && (yellow_threshold != 0)); - red_only_min = ((red_up == 0) && (red_threshold != 0)); - + var red_only_min = ((red_up == 0) && (red_threshold != 0)); + + //color + var normalw = '#efe'; + var warningw = '#ffe'; + var criticalw = '#fee'; + var normal = '#0f0'; + var warning = 'yellow'; + var critical = 'red'; + if (threshold) { // Warning interval. Change extremes depends on critical interval if (yellow_inverse && red_inverse) { if (red_only_min && yellow_only_min) { // C: |-------- | // W: |ยทยทยทยทยทยทยทยท==== | - if (yellow_threshold > red_threshold) { threshold_data.push({ id: 'warning_normal_fdown', data: [[max_x, red_threshold]], label: null, - color: warning, - bars: {show: true, align: "left", barWidth: yellow_threshold - red_threshold, lineWidth: 0, horizontal: true} + color: warning, + bars: { + show: true, + align: "left", + barWidth: yellow_threshold - red_threshold, + lineWidth: 0, + horizontal: true, + fillColor: { + colors: [ + { + opacity: 0.1 + }, + { + opacity: 0.1 + } + ] + } + }, + highlightColor: 'rgba(254, 255, 198, 0)' }); + extremes['warning_normal_fdown_1'] = red_threshold; extremes['warning_normal_fdown_2'] = yellow_threshold; } } else if (!red_only_min && yellow_only_min) { // C: |-------- ------| // W: |ยทยทยทยทยทยทยทยท===ยท | - if (yellow_threshold > red_up) { yellow_threshold = red_up; } @@ -1062,8 +970,9 @@ function pandoraFlotArea(graph_id, values, labels, labels_long, legend, id: 'warning_normal_fdown', data: [[max_x, red_threshold]], label: null, - color: warning, - bars: {show: true, align: "left", barWidth: yellow_threshold - red_threshold, lineWidth: 0, horizontal: true} + color: warning, + bars: {show: true, align: "left", barWidth: yellow_threshold - red_threshold, lineWidth: 0, horizontal: true, fillColor: { colors: [ { opacity: 0.1 }, { opacity: 0.1 } ] }}, + highlightColor: 'rgba(254, 255, 198, 0)' }); extremes['warning_normal_fdown_1'] = red_threshold; extremes['warning_normal_fdown_2'] = yellow_threshold; @@ -1076,13 +985,14 @@ function pandoraFlotArea(graph_id, values, labels, labels_long, legend, id: 'warning_normal_fdown', data: [[max_x, red_threshold]], label: null, - color: warning, - bars: {show: true, align: "left", barWidth: yellow_threshold - red_threshold, lineWidth: 0, horizontal: true} + color: warning, + bars: {show: true, align: "left", barWidth: yellow_threshold - red_threshold, lineWidth: 0, horizontal: true, fillColor: { colors: [ { opacity: 0.1 }, { opacity: 0.1 } ] }}, + highlightColor: 'rgba(254, 255, 198, 0)' }); extremes['warning_normal_fdown_1'] = red_threshold; extremes['warning_normal_fdown_2'] = yellow_threshold; } - + if (yellow_up < red_threshold) { yellow_up = red_threshold; } @@ -1090,11 +1000,11 @@ function pandoraFlotArea(graph_id, values, labels, labels_long, legend, id: 'warning_up', data: [[max_x, yellow_up]], label: null, - color: warning, - bars: {show: true, align: "left", barWidth: 1, lineWidth: 0, horizontal: true} + color: warning, + bars: {show: true, align: "left", barWidth: 1, lineWidth: 0, horizontal: true, fillColor: { colors: [ { opacity: 0.1 }, { opacity: 0.1 } ] }}, + highlightColor: 'rgba(254, 255, 198, 0)' }); extremes['warning_up'] = yellow_up; - } else { if (yellow_threshold > red_threshold) { // C: |-------- ------| @@ -1106,8 +1016,9 @@ function pandoraFlotArea(graph_id, values, labels, labels_long, legend, id: 'warning_normal_fdown', data: [[max_x, red_threshold]], label: null, - color: warning, - bars: {show: true, align: "left", barWidth: yellow_threshold - red_threshold, lineWidth: 0, horizontal: true} + color: warning, + bars: {show: true, align: "left", barWidth: yellow_threshold - red_threshold, lineWidth: 0, horizontal: true, fillColor: { colors: [ { opacity: 0.1 }, { opacity: 0.1 } ] }}, + highlightColor: 'rgba(254, 255, 198, 0)' }); extremes['warning_normal_fdown_1'] = red_threshold; extremes['warning_normal_fdown_2'] = yellow_threshold; @@ -1118,12 +1029,13 @@ function pandoraFlotArea(graph_id, values, labels, labels_long, legend, if (yellow_up < red_threshold) { yellow_up = red_up; } - threshold_data.push({ + threshold_data.push({ id: 'warning_normal_fup', data: [[max_x, yellow_up]], label: null, - color: warning, - bars: {show: true, align: "left", barWidth: red_up - yellow_up, lineWidth: 0, horizontal: true} + color: warning, + bars: {show: true, align: "left", barWidth: red_up - yellow_up, lineWidth: 0, horizontal: true, fillColor: { colors: [ { opacity: 0.1 }, { opacity: 0.1 } ] }}, + highlightColor: 'rgba(254, 255, 198, 0)' }); extremes['warning_normal_fup_1'] = red_up; extremes['warning_normal_fup_2'] = yellow_up; @@ -1143,27 +1055,29 @@ function pandoraFlotArea(graph_id, values, labels, labels_long, legend, id: 'warning_down', data: [[max_x, yellow_threshold]], label: null, - color: warning, - bars: {show: true, align: "left", barWidth: 1, lineWidth: 0, horizontal: true} + color: warning, + bars: {show: true, align: "left", barWidth: 1, lineWidth: 0, horizontal: true, fillColor: { colors: [ { opacity: 0.1 }, { opacity: 0.1 } ] }}, + highlightColor: 'rgba(254, 255, 198, 0)' }); extremes['warning_down'] = yellow_threshold; - + } else if (!red_only_min && yellow_only_min) { // C: | ---- | // W: |======ยทยทยทยท=== | - + if (yellow_threshold > red_up) { threshold_data.push({ id: 'warning_normal_fdown', data: [[max_x, red_up]], label: null, - color: warning, - bars: {show: true, align: "left", barWidth: yellow_threshold - red_up, lineWidth: 0, horizontal: true} + color: warning, + bars: {show: true, align: "left", barWidth: yellow_threshold - red_up, lineWidth: 0, horizontal: true, fillColor: { colors: [ { opacity: 0.1 }, { opacity: 0.1 } ] }}, + highlightColor: 'rgba(254, 255, 198, 0)' }); extremes['warning_normal_fdown_1'] = red_up; extremes['warning_normal_fdown_2'] = yellow_threshold; } - + if (yellow_threshold > red_threshold) { yellow_threshold = red_threshold; } @@ -1171,11 +1085,12 @@ function pandoraFlotArea(graph_id, values, labels, labels_long, legend, id: 'warning_down', data: [[max_x, yellow_threshold]], label: null, - color: warning, - bars: {show: true, align: "left", barWidth: 1, lineWidth: 0, horizontal: true} + color: warning, + bars: {show: true, align: "left", barWidth: 1, lineWidth: 0, horizontal: true, fillColor: { colors: [ { opacity: 0.1 }, { opacity: 0.1 } ] }}, + highlightColor: 'rgba(254, 255, 198, 0)' }); extremes['warning_down'] = yellow_threshold; - + } else if (red_only_min && !yellow_only_min) { if (yellow_threshold < red_threshold) { // C: | -----| @@ -1184,18 +1099,20 @@ function pandoraFlotArea(graph_id, values, labels, labels_long, legend, id: 'warning_down', data: [[max_x, yellow_threshold]], label: null, - color: warning, - bars: {show: true, align: "left", barWidth: 1, lineWidth: 0, horizontal: true} + color: warning, + bars: {show: true, align: "left", barWidth: 1, lineWidth: 0, horizontal: true, fillColor: { colors: [ { opacity: 0.1 }, { opacity: 0.1 } ] }}, + highlightColor: 'rgba(254, 255, 198, 0)' }); extremes['warning_down'] = yellow_threshold; - + if (red_threshold > yellow_up) { threshold_data.push({ id: 'warning_normal_fup', data: [[max_x, yellow_up]], label: null, - color: warning, - bars: {show: true, align: "left", barWidth: red_threshold - yellow_up, lineWidth: 0, horizontal: true} + color: warning, + bars: {show: true, align: "left", barWidth: red_threshold - yellow_up, lineWidth: 0, horizontal: true, fillColor: { colors: [ { opacity: 0.1 }, { opacity: 0.1 } ] }}, + highlightColor: 'rgba(254, 255, 198, 0)' }); extremes['warning_normal_fup_1'] = yellow_up; extremes['warning_normal_fup_2'] = red_threshold; @@ -1207,8 +1124,9 @@ function pandoraFlotArea(graph_id, values, labels, labels_long, legend, id: 'warning_down', data: [[max_x, red_threshold]], label: null, - color: warning, - bars: {show: true, align: "left", barWidth: 1, lineWidth: 0, horizontal: true} + color: warning, + bars: {show: true, align: "left", barWidth: 1, lineWidth: 0, horizontal: true, fillColor: { colors: [ { opacity: 0.1 }, { opacity: 0.1 } ] }}, + highlightColor: 'rgba(254, 255, 198, 0)' }); extremes['warning_down'] = red_threshold; } @@ -1220,27 +1138,30 @@ function pandoraFlotArea(graph_id, values, labels, labels_long, legend, id: 'warning_down', data: [[max_x, red_threshold]], label: null, - color: warning, - bars: {show: true, align: "left", barWidth: 1, lineWidth: 0, horizontal: true} + color: warning, + bars: {show: true, align: "left", barWidth: 1, lineWidth: 0, horizontal: true, fillColor: { colors: [ { opacity: 0.1 }, { opacity: 0.1 } ] }}, + highlightColor: 'rgba(254, 255, 198, 0)' }); extremes['warning_down'] = red_threshold; - + threshold_data.push({ id: 'warning_normal_fdown', data: [[max_x, red_up]], label: null, - color: warning, - bars: {show: true, align: "left", barWidth: yellow_threshold - red_up, lineWidth: 0, horizontal: true} + color: warning, + bars: {show: true, align: "left", barWidth: yellow_threshold - red_up, lineWidth: 0, horizontal: true, fillColor: { colors: [ { opacity: 0.1 }, { opacity: 0.1 } ] }}, + highlightColor: 'rgba(254, 255, 198, 0)' }); extremes['warning_normal_fdown_1'] = red_up; extremes['warning_normal_fdown_2'] = yellow_threshold; - + threshold_data.push({ // barWidth will be correct on draw time id: 'warning_up', data: [[max_x, yellow_up]], label: null, - color: warning, - bars: {show: true, align: "left", barWidth: 1, lineWidth: 0, horizontal: true} + color: warning, + bars: {show: true, align: "left", barWidth: 1, lineWidth: 0, horizontal: true, fillColor: { colors: [ { opacity: 0.1 }, { opacity: 0.1 } ] }}, + highlightColor: 'rgba(254, 255, 198, 0)' }); extremes['warning_up'] = yellow_up; } else if (red_threshold > yellow_up){ @@ -1250,27 +1171,30 @@ function pandoraFlotArea(graph_id, values, labels, labels_long, legend, id: 'warning_down', data: [[max_x, yellow_threshold]], label: null, - color: warning, - bars: {show: true, align: "left", barWidth: 1, lineWidth: 0, horizontal: true} + color: warning, + bars: {show: true, align: "left", barWidth: 1, lineWidth: 0, horizontal: true, fillColor: { colors: [ { opacity: 0.1 }, { opacity: 0.1 } ] }}, + highlightColor: 'rgba(254, 255, 198, 0)' }); extremes['warning_down'] = yellow_threshold; - + threshold_data.push({ id: 'warning_normal_fup', data: [[max_x, yellow_up]], label: null, - color: warning, - bars: {show: true, align: "left", barWidth: red_threshold - yellow_up, lineWidth: 0, horizontal: true} + color: warning, + bars: {show: true, align: "left", barWidth: red_threshold - yellow_up, lineWidth: 0, horizontal: true, fillColor: { colors: [ { opacity: 0.1 }, { opacity: 0.1 } ] }}, + highlightColor: 'rgba(254, 255, 198, 0)' }); extremes['warning_normal_fup_1'] = yellow_up; extremes['warning_normal_fup_2'] = red_threshold; - + threshold_data.push({ // barWidth will be correct on draw time id: 'warning_up', data: [[max_x, red_up]], label: null, - color: warning, - bars: {show: true, align: "left", barWidth: 1, lineWidth: 0, horizontal: true} + color: warning, + bars: {show: true, align: "left", barWidth: 1, lineWidth: 0, horizontal: true, fillColor: { colors: [ { opacity: 0.1 }, { opacity: 0.1 } ] }}, + highlightColor: 'rgba(254, 255, 198, 0)' }); extremes['warning_up'] = red_up; } else { @@ -1282,22 +1206,24 @@ function pandoraFlotArea(graph_id, values, labels, labels_long, legend, if (yellow_up < red_up) { yellow_up = red_up; } - + threshold_data.push({ // barWidth will be correct on draw time id: 'warning_down', data: [[max_x, yellow_threshold]], label: null, - color: warning, - bars: {show: true, align: "left", barWidth: 1, lineWidth: 0, horizontal: true} + color: warning, + bars: {show: true, align: "left", barWidth: 1, lineWidth: 0, horizontal: true, fillColor: { colors: [ { opacity: 0.1 }, { opacity: 0.1 } ] }}, + highlightColor: 'rgba(254, 255, 198, 0)' }); extremes['warning_down'] = yellow_threshold; - + threshold_data.push({ // barWidth will be correct on draw time id: 'warning_up', data: [[max_x, yellow_up]], label: null, - color: warning, - bars: {show: true, align: "left", barWidth: 1, lineWidth: 0, horizontal: true} + color: warning, + bars: {show: true, align: "left", barWidth: 1, lineWidth: 0, horizontal: true, fillColor: { colors: [ { opacity: 0.1 }, { opacity: 0.1 } ] }}, + highlightColor: 'rgba(254, 255, 198, 0)' }); extremes['warning_up'] = yellow_up; } @@ -1313,11 +1239,11 @@ function pandoraFlotArea(graph_id, values, labels, labels_long, legend, id: 'warning_up', data: [[max_x, yellow_threshold]], label: null, - color: warning, - bars: {show: true, align: "left", barWidth: 1, lineWidth: 0, horizontal: true} + color: warning, + bars: {show: true, align: "left", barWidth: 1, lineWidth: 0, horizontal: true, fillColor: { colors: [ { opacity: 0.1 }, { opacity: 0.1 } ] }}, + highlightColor: 'rgba(254, 255, 198, 0)' }); extremes['warning_up'] = yellow_threshold; - } else if (!yellow_only_min && red_only_min) { // C: |----- | // W: | ยทยท======== | @@ -1329,8 +1255,9 @@ function pandoraFlotArea(graph_id, values, labels, labels_long, legend, id: 'warning_normal', data: [[max_x, yellow_threshold]], label: null, - color: warning, - bars: {show: true, align: "left", barWidth: (yellow_up - yellow_threshold), lineWidth: 0, horizontal: true} + color: warning, + bars: {show: true, align: "left", barWidth: (yellow_up - yellow_threshold), lineWidth: 0, horizontal: true, fillColor: { colors: [ { opacity: 0.1 }, { opacity: 0.1 } ] }}, + highlightColor: 'rgba(254, 255, 198, 0)' }); extremes['warning_normal_1'] = yellow_threshold; extremes['warning_normal_2'] = yellow_up; @@ -1346,8 +1273,9 @@ function pandoraFlotArea(graph_id, values, labels, labels_long, legend, id: 'warning_normal', data: [[max_x, yellow_threshold]], label: null, - color: warning, - bars: {show: true, align: "left", barWidth: (red_up - yellow_threshold), lineWidth: 0, horizontal: true} + color: warning, + bars: {show: true, align: "left", barWidth: (red_up - yellow_threshold), lineWidth: 0, horizontal: true, fillColor: { colors: [ { opacity: 0.1 }, { opacity: 0.1 } ] }}, + highlightColor: 'rgba(254, 255, 198, 0)' }); extremes['warning_normal_1'] = yellow_threshold; extremes['warning_normal_2'] = red_up; @@ -1365,13 +1293,14 @@ function pandoraFlotArea(graph_id, values, labels, labels_long, legend, if (yellow_up > red_up) { yellow_up = red_up; } - + threshold_data.push({ id: 'warning_normal', data: [[max_x, yellow_threshold]], label: null, - color: warning, - bars: {show: true, align: "left", barWidth: (yellow_up - yellow_threshold), lineWidth: 0, horizontal: true} + color: warning, + bars: {show: true, align: "left", barWidth: (yellow_up - yellow_threshold), lineWidth: 0, horizontal: true, fillColor: { colors: [ { opacity: 0.1 }, { opacity: 0.1 } ] }}, + highlightColor: 'rgba(254, 255, 198, 0)' }); extremes['warning_normal_1'] = yellow_threshold; extremes['warning_normal_2'] = yellow_up; @@ -1390,8 +1319,9 @@ function pandoraFlotArea(graph_id, values, labels, labels_long, legend, id: 'warning_normal', data: [[max_x, yellow_threshold]], label: null, - color: warning, - bars: {show: true, align: "left", barWidth: (red_threshold - yellow_threshold), lineWidth: 0, horizontal: true} + color: warning, + bars: {show: true, align: "left", barWidth: (red_threshold - yellow_threshold), lineWidth: 0, horizontal: true, fillColor: { colors: [ { opacity: 0.1 }, { opacity: 0.1 } ] }}, + highlightColor: 'rgba(254, 255, 198, 0)' }); extremes['warning_normal_1'] = yellow_threshold; extremes['warning_normal_2'] = red_threshold; @@ -1407,8 +1337,9 @@ function pandoraFlotArea(graph_id, values, labels, labels_long, legend, id: 'warning_normal', data: [[max_x, yellow_threshold]], label: null, - color: warning, - bars: {show: true, align: "left", barWidth: (yellow_up - yellow_threshold), lineWidth: 0, horizontal: true} + color: warning, + bars: {show: true, align: "left", barWidth: (yellow_up - yellow_threshold), lineWidth: 0, horizontal: true, fillColor: { colors: [ { opacity: 0.1 }, { opacity: 0.1 } ] }}, + highlightColor: 'rgba(254, 255, 198, 0)' }); extremes['warning_normal_1'] = yellow_threshold; extremes['warning_normal_2'] = yellow_up; @@ -1416,32 +1347,33 @@ function pandoraFlotArea(graph_id, values, labels, labels_long, legend, } else if (!red_only_min && yellow_only_min) { // C: | ------- | // W: | ==ยทยทยทยทยทยทยท=====| - if (yellow_threshold < red_threshold) { threshold_data.push({ id: 'warning_normal_fdown', data: [[max_x, yellow_threshold]], label: null, - color: warning, - bars: {show: true, align: "left", barWidth: red_threshold - yellow_threshold, lineWidth: 0, horizontal: true} + color: warning, + bars: {show: true, align: "left", barWidth: red_threshold - yellow_threshold, lineWidth: 0, horizontal: true, fillColor: { colors: [ { opacity: 0.1 }, { opacity: 0.1 } ] }}, + highlightColor: 'rgba(254, 255, 198, 0)' }); extremes['warning_normal_fdown_1'] = yellow_threshold; extremes['warning_normal_fdown_2'] = red_threshold; } - + if (yellow_threshold < red_up) { yellow_threshold = red_up; } - + threshold_data.push({ // barWidth will be correct on draw time id: 'warning_up', data: [[max_x, yellow_threshold]], label: null, - color: warning, - bars: {show: true, align: "left", barWidth: 1, lineWidth: 0, horizontal: true} + color: warning, + bars: {show: true, align: "left", barWidth: 1, lineWidth: 0, horizontal: true, fillColor: { colors: [ { opacity: 0.1 }, { opacity: 0.1 } ] }}, + highlightColor: 'rgba(254, 255, 198, 0)' }); extremes['warning_up'] = yellow_threshold; - + } else { if (red_threshold > yellow_threshold && red_up < yellow_up ) { // C: | ------ | @@ -1450,18 +1382,20 @@ function pandoraFlotArea(graph_id, values, labels, labels_long, legend, id: 'warning_normal_fdown', data: [[max_x, yellow_threshold]], label: null, - color: warning, - bars: {show: true, align: "left", barWidth: red_threshold - yellow_threshold, lineWidth: 0, horizontal: true} + color: warning, + bars: {show: true, align: "left", barWidth: red_threshold - yellow_threshold, lineWidth: 0, horizontal: true, fillColor: { colors: [ { opacity: 0.1 }, { opacity: 0.1 } ] }}, + highlightColor: 'rgba(254, 255, 198, 0)' }); extremes['warning_normal_fdown_1'] = yellow_threshold; extremes['warning_normal_fdown_2'] = red_threshold; - + threshold_data.push({ id: 'warning_normal_fup', data: [[max_x, red_up]], label: null, - color: warning, - bars: {show: true, align: "left", barWidth: yellow_up - red_up, lineWidth: 0, horizontal: true} + color: warning, + bars: {show: true, align: "left", barWidth: yellow_up - red_up, lineWidth: 0, horizontal: true, fillColor: { colors: [ { opacity: 0.1 }, { opacity: 0.1 } ] }}, + highlightColor: 'rgba(254, 255, 198, 0)' }); extremes['warning_normal_fup_1'] = red_up; extremes['warning_normal_fup_2'] = yellow_up; @@ -1482,14 +1416,16 @@ function pandoraFlotArea(graph_id, values, labels, labels_long, legend, id: 'warning_normal', data: [[max_x, yellow_threshold]], label: null, - color: warning, - bars: {show: true, align: "left", barWidth: (yellow_up - yellow_threshold), lineWidth: 0, horizontal: true} + color: warning, + bars: {show: true, align: "left", barWidth: (yellow_up - yellow_threshold), lineWidth: 0, horizontal: true, fillColor: { colors: [ { opacity: 0.1 }, { opacity: 0.1 } ] }}, + highlightColor: 'rgba(254, 255, 198, 0)' }); extremes['warning_normal_1'] = yellow_threshold; extremes['warning_normal_2'] = yellow_up; } } } + // Critical interval if (red_inverse) { if (!red_only_min) { @@ -1497,59 +1433,184 @@ function pandoraFlotArea(graph_id, values, labels, labels_long, legend, id: 'critical_up', data: [[max_x, red_up]], label: null, - color: critical, - bars: {show: true, align: "left", barWidth: 1, lineWidth: 0, horizontal: true} + color: critical, + bars: {show: true, align: "left", barWidth: 1, lineWidth: 0, horizontal: true, fillColor: { colors: [ { opacity: 0.1 }, { opacity: 0.1 } ] }}, + highlightColor: 'rgba(254, 236, 234, 0)' }); + extremes['critical_normal_1'] = red_threshold; + extremes['critical_normal_2'] = red_up; } threshold_data.push({ // barWidth will be correct on draw time id: 'critical_down', data: [[max_x, red_threshold]], label: null, - color: critical, - bars: {show: true, align: "left", barWidth: 1, lineWidth: 0, horizontal: true} + color: critical, + bars: {show: true, align: "left", barWidth: 1, lineWidth: 0, horizontal: true, fillColor: { colors: [ { opacity: 0.1 }, { opacity: 0.1 } ] }}, + highlightColor: 'rgba(254, 236, 234, 0)' }); + extremes['critical_normal_3'] = red_threshold; + extremes['critical_normal_4'] = red_threshold; } else { if (red_up == 0 && red_threshold != 0) { threshold_data.push({ // barWidth will be correct on draw time id: 'critical_up', data: [[max_x, red_threshold]], label: null, - color: critical, - bars: {show: true, align: "left", barWidth: 1, lineWidth: 0, horizontal: true} + color: critical, + bars: {show: true, align: "left", barWidth: 1, lineWidth: 0, horizontal: true, fillColor: { colors: [ { opacity: 0.1 }, { opacity: 0.1 } ] }}, + highlightColor: 'rgba(254, 236, 234, 0)' }); + extremes['critical_normal_1'] = red_threshold; + extremes['critical_normal_2'] = red_up; } else { threshold_data.push({ id: 'critical_normal', data: [[max_x, red_threshold]], label: null, - color: critical, - bars: {show: true, align: "left", barWidth: (red_up - red_threshold), lineWidth: 0, horizontal: true} + color: critical, + bars: {show: true, align: "left", barWidth: (red_up - red_threshold), lineWidth: 0, horizontal: true, fillColor: { colors: [ { opacity: 0.1 }, { opacity: 0.1 } ] }}, + highlightColor: 'rgba(254, 236, 234, 0)' + }); + extremes['critical_normal_1'] = red_threshold; + extremes['critical_normal_2'] = red_up; + } + } + } + + switch (type) { + case 'line': + case 2: + stacked = null; + filled_s = false; + break; + case 3: + stacked = 'stack'; + filled_s = false; + break; + default: + case 'area': + case 0: + stacked = null; + filled_s = 0.3; + break; + case 1: + stacked = 'stack'; + filled_s = 0.3; + break; + } + + var datas = new Array(); + var data_base = new Array(); + var lineWidth = $('#hidden-line_width_graph').val() || 1; + + i=0; + $.each(values, function (index, value) { + if (typeof value.data !== "undefined") { + if(index.search("alert") >= 0){ + fill_color = '#ff7f00'; + } + else if(index.search("event") >= 0){ + fill_color = '#ff0000'; + } + else{ + fill_color = 'green'; + } + + switch (series_type[index]) { + case 'area': + line_show = true; + points_show = false; // XXX - false + filled = filled_s; + steps_chart = false; + radius = false; + fill_points = fill_color; + break; + case 'percentil': + case 'line': + default: + line_show = true; + points_show = false; + filled = false; + steps_chart = false; + radius = false; + fill_points = fill_color; + break; + case 'points': + line_show = false; + points_show = true; + filled = false; + steps_chart = false; + radius = 1.5; + fill_points = fill_color; + break; + case 'unknown': + case 'boolean': + line_show = true; + points_show = false; + filled = filled_s; + steps_chart = true; + radius = false; + fill_points = fill_color; + break; + } + + //in graph stacked unset percentil + if( ! ( (type == 1) && ( /percentil/.test(index) ) == true ) && + ! ( (type == 3) && ( /percentil/.test(index) ) == true ) ){ + data_base.push({ + id: 'serie_' + i, + data: value.data, + label: index, + color: color[index]['color'], + lines: { + show: line_show, + fill: filled, + lineWidth: lineWidth, + steps: steps_chart + }, + points: { + show: points_show, + radius: 3, + fillColor: fill_points + }, + legend: legend.index }); } } - - } + i++; + }); // The first execution, the graph data is the base data - datas = data_base; + datas = data_base; + // minTickSize var count_data = datas[0].data.length; - var min_tick_pixels = 80; - - if (unit != "") { - xaxisname = xaxisname + " (" + unit + ")" + var min_tick = datas[0].data[0][0]; + var max_tick = datas[0].data[count_data - 1][0]; + + var number_ticks = 8; + if(vconsole){ + number_ticks = 5; } - + + var maxticks = date_array['period'] / 3600 / number_ticks; + var options = { series: { stack: stacked, shadowSize: 0.1 }, - crosshair: { mode: 'xy' }, - selection: { mode: 'xy', color: '#777' }, + crosshair: { + mode: 'xy', + color: 'grey' + }, + selection: { + mode: 'xy', + color: '#777' + }, export: { export_data: true, - labels_long: labels_long, + labels_long: legend, homeurl: homeurl }, grid: { @@ -1558,16 +1619,21 @@ function pandoraFlotArea(graph_id, values, labels, labels_long, legend, borderWidth:1, borderColor: '#C1C1C1', tickColor: background_color, - markings: markings, - color: legend_color + color: legend_color, + autoHighlight: true + }, + xaxis: { + min: date_array.start_date * 1000, + max: date_array.final_date * 1000 }, xaxes: [{ + axisLabelUseCanvas: true, axisLabelFontSizePixels: font_size, - axisLabelUseCanvas: false, - tickFormatter: xFormatter, - labelHeight: 50, - color: '', - font: font + axisLabelFontFamily: font+'Font', + axisLabelPadding: 0, + mode: "time", + //tickFormatter: xFormatter, + tickSize: [maxticks, 'hour'] }], yaxes: [{ tickFormatter: yFormatter, @@ -1576,8 +1642,7 @@ function pandoraFlotArea(graph_id, values, labels, labels_long, legend, labelWidth: 30, position: 'left', font: font, - reserveSpace: true, - min: min_check + reserveSpace: true }], legend: { position: 'se', @@ -1585,16 +1650,21 @@ function pandoraFlotArea(graph_id, values, labels, labels_long, legend, labelFormatter: lFormatter } }; + if (vconsole) { options.grid['hoverable'] = false; options.grid['clickable'] = false; options.crosshair = false; options.selection = false; } - - var stack = 0, bars = true, lines = false, steps = false; + + var stack = 0, + bars = true, + lines = false, + steps = false; + var plot = $.plot($('#' + graph_id), datas, options); - + // Re-calculate the graph height with the legend height if (dashboard || vconsole) { var hDiff = $('#'+graph_id).height() - $('#legend_'+graph_id).height(); @@ -1604,18 +1674,19 @@ function pandoraFlotArea(graph_id, values, labels, labels_long, legend, $('#'+graph_id).css('height', hDiff); } } - - if (vconsole) { + +/*//XXXXXXX +if (vconsole) { var myCanvas = plot.getCanvas(); plot.setupGrid(); // redraw plot to new size plot.draw(); var image = myCanvas.toDataURL("image/png"); return; } - +*/ // Adjust the overview plot to the width and position of the main plot adjust_left_width_canvas(graph_id, 'overview_'+graph_id); - update_left_width_canvas(graph_id); + update_left_width_canvas(graph_id); // Adjust overview when main chart is resized $('#'+graph_id).resize(function(){ @@ -1629,43 +1700,42 @@ function pandoraFlotArea(graph_id, values, labels, labels_long, legend, var overview = $.plot($('#overview_'+graph_id),datas, { series: { stack: stacked, - lines: { show: true, lineWidth: 1 }, - shadowSize: 0 + shadowSize: 0.1 }, - grid: { borderWidth: 1, hoverable: true, autoHighlight: false}, - xaxis: { }, - xaxes: [ { - tickFormatter: xFormatter, - minTickSize: steps, - color: '' - } ], - yaxis: {ticks: [], autoscaleMargin: 0.1 }, - selection: {mode: 'xy', color: '#777' }, - legend: {show: false}, - crosshair: {mode: 'x'} - }); - } - // Connection between plot and miniplot - - $('#' + graph_id).bind('plotselected', function (event, ranges) { - // do the zooming if exist menu to undo it - if (menu == 0) { - return; - } - - dataInSelection = ranges.xaxis.to - ranges.xaxis.from; - dataInPlot = plot.getData()[0].data.length; - - factor = dataInSelection / dataInPlot; - - new_steps = parseInt(factor * steps); - - plot = $.plot($('#' + graph_id), data_base, - $.extend(true, {}, options, { - yaxis: { - min: ranges.yaxis.from, - max: ranges.yaxis.to + crosshair: { + mode: 'xy' }, + selection: { + mode: 'xy', + color: '#777' + }, + export: { + export_data: true, + labels_long: legend, + homeurl: homeurl + }, + grid: { + hoverable: true, + clickable: true, + borderWidth:1, + borderColor: '#C1C1C1', + tickColor: background_color, + color: legend_color, + autoHighlight: true + }, + xaxis: { + min: date_array.start_date * 1000, + max: date_array.final_date * 1000 + }, + xaxes: [{ + axisLabelUseCanvas: true, + axisLabelFontSizePixels: font_size, + axisLabelFontFamily: font+'Font', + axisLabelPadding: 0, + mode: "time", + //tickFormatter: xFormatter, + tickSize: [maxticks, 'hour'] + }], yaxes: [{ tickFormatter: yFormatter, color: '', @@ -1673,48 +1743,130 @@ function pandoraFlotArea(graph_id, values, labels, labels_long, legend, labelWidth: 30, position: 'left', font: font, - reserveSpace: true, - min: ranges.yaxis.from + reserveSpace: true }], - xaxis: { - min: ranges.xaxis.from, - max: ranges.xaxis.to - }, - xaxes: [ { - tickFormatter: xFormatter, - minTickSize: new_steps, - color: '' - } ], - legend: { show: false } - })); + legend: { + position: 'se', + container: $('#legend_' + graph_id), + labelFormatter: lFormatter + } + }); + } - if (thresholded) { - var zoom_data_threshold = new Array (); + // Adjust overview when main chart is resized + $('#overview_'+graph_id).resize(function(){ + update_left_width_canvas(graph_id); + }); - var y_recal = axis_thresholded(threshold_data, plot.getAxes().yaxis.min, plot.getAxes().yaxis.max, - red_threshold, extremes, red_up); - plot = $.plot($('#' + graph_id), data_base, - $.extend(true, {}, options, { - yaxis: { - max: y_recal.max, - min: y_recal.min - }, - xaxis: { - min: plot.getAxes().xaxis.min, - max: plot.getAxes().xaxis.max - } - })); - zoom_data_threshold = add_threshold (data_base, threshold_data, plot.getAxes().yaxis.min, plot.getAxes().yaxis.max, - red_threshold, extremes, red_up); - plot.setData(zoom_data_threshold); - plot.draw(); + // Connection between plot and miniplot + $('#' + graph_id).bind('plotselected', function (event, ranges) { + // do the zooming if exist menu to undo it + if (menu == 0) { + return; } + dataInSelection = ranges.xaxis.to - ranges.xaxis.from; - $('#menu_cancelzoom_' + graph_id) - .attr('src', homeurl + '/images/zoom_cross_grey.png'); + var maxticks_zoom = dataInSelection / 3600000 / number_ticks; + if(maxticks_zoom < 0.001){ + maxticks_zoom = dataInSelection / 60000 / number_ticks; + if(maxticks_zoom < 0.001){ + maxticks_zoom = 0; + } + } + + if (thresholded) { + data_base_treshold = add_threshold ( + data_base, + threshold_data, + ranges.yaxis.from, + ranges.yaxis.to, + red_threshold, + extremes, + red_up, + markins_graph + ); + + plot = $.plot($('#' + graph_id), data_base_treshold, + $.extend(true, {}, options, { + grid: { + borderWidth: 1, + hoverable: true, + autoHighlight: true + }, + xaxis: { + min: ranges.xaxis.from, + max: ranges.xaxis.to + }, + xaxes: [{ + axisLabelUseCanvas: true, + axisLabelFontSizePixels: font_size, + axisLabelFontFamily: font+'Font', + axisLabelPadding: 0, + mode: "time", + //tickFormatter: xFormatter, + tickSize: [maxticks_zoom, 'hour'] + }], + yaxis:{ + min: ranges.yaxis.from, + max: ranges.yaxis.to + }, + yaxes: [{ + tickFormatter: yFormatter, + color: '', + alignTicksWithAxis: 1, + labelWidth: 30, + position: 'left', + font: font, + reserveSpace: true, + }], + legend: { + show: true + } + })); + } + else{ + plot = $.plot($('#' + graph_id), data_base, + $.extend(true, {}, options, { + grid: { + borderWidth: 1, + hoverable: true, + autoHighlight: true + }, + xaxis: { + min: ranges.xaxis.from, + max: ranges.xaxis.to + }, + xaxes: [{ + axisLabelUseCanvas: true, + axisLabelFontSizePixels: font_size, + axisLabelFontFamily: font+'Font', + axisLabelPadding: 0, + mode: "time", + //tickFormatter: xFormatter, + tickSize: [maxticks_zoom, 'hour'] + }], + yaxis:{ + min: ranges.yaxis.from, + max: ranges.yaxis.to + }, + yaxes: [{ + tickFormatter: yFormatter, + color: '', + alignTicksWithAxis: 1, + labelWidth: 30, + position: 'left', + font: font, + reserveSpace: true, + }], + legend: { + show: true + } + })); + } + + $('#menu_cancelzoom_' + graph_id).attr('src', homeurl + '/images/zoom_cross_grey.png'); - currentRanges = ranges; // don't fire event on the overview to prevent eternal loop overview.setSelection(ranges, true); }); @@ -1722,47 +1874,81 @@ function pandoraFlotArea(graph_id, values, labels, labels_long, legend, $('#overview_' + graph_id) .bind('plotselected', function (event, ranges) { plot.setSelection(ranges); - }); - - var legends = $('#legend_' + graph_id + ' .legendLabel'); + }); var updateLegendTimeout = null; - var latestPosition = null; - var currentPlot = null; - var currentRanges = null; + var latestPosition = null; + var currentPlot = null; + var currentRanges = null; // Update legend with the data of the plot in the mouse position function updateLegend() { updateLegendTimeout = null; - var pos = latestPosition; - var axes = currentPlot.getAxes(); if (pos.x < axes.xaxis.min || pos.x > axes.xaxis.max || pos.y < axes.yaxis.min || pos.y > axes.yaxis.max) { return; } - var j, dataset = currentPlot.getData(); + $('#timestamp_'+graph_id).show(); + var d = new Date(pos.x); + var monthNames = [ + "Jan", "Feb", "Mar", + "Apr", "May", "Jun", + "Jul", "Aug", "Sep", + "Oct", "Nov", "Dec" + ]; + + date_format = (d.getDate() <10?'0':'') + d.getDate() + " " + + monthNames[d.getMonth()] + " " + + d.getFullYear() + "\n" + + (d.getHours()<10?'0':'') + d.getHours() + ":" + + (d.getMinutes()<10?'0':'') + d.getMinutes() + ":" + + (d.getSeconds()<10?'0':'') + d.getSeconds(); + + $('#timestamp_'+graph_id).text(date_format); + + var timesize = $('#timestamp_'+graph_id).width(); + + dataset = currentPlot.getData(); + + var timenewpos = dataset[0].xaxis.p2c(pos.x)+$('.yAxis>div').eq(0).width(); + var canvaslimit = $('#'+graph_id).width(); + + $('#timestamp_'+graph_id) + .css('top', currentPlot.getPlotOffset().top - + $('#timestamp_'+graph_id).height() + + $('#legend_' + graph_id).height()); + + if (timesize+timenewpos > canvaslimit) { + $('#timestamp_'+graph_id).css('left', timenewpos - timesize); + } + else { + $('#timestamp_'+graph_id).css('left', timenewpos); + } + + var dataset = currentPlot.getData(); var i = 0; for (k = 0; k < dataset.length; k++) { - // k is the real series counter // i is the series counter without thresholds var series = dataset[k]; - if (series.label == null) { continue; } // find the nearest points, x-wise - for (j = 0; j < series.data.length; ++j) + for (j = 0; j < series.data.length; ++j){ if (series.data[j][0] > pos.x) { break; } - var y = series.data[j][1]; + if(series.data[j]){ + var y = series.data[j][1]; + } + } var how_bigger = ""; if (y > 1000000) { @@ -1782,47 +1968,18 @@ function pandoraFlotArea(graph_id, values, labels, labels_long, legend, y = y / 1000; } - if (currentRanges == null || (currentRanges.xaxis.from < j && j < currentRanges.xaxis.to)) { - $('#timestamp_'+graph_id).show(); - // If no legend, the timestamp labels are short and with value - if (legend.length == 0) { - $('#timestamp_'+graph_id).text(labels[j] + ' (' + (short_data ? number_format(y, 0, "", short_data) : parseFloat(y)) + ')'); - } - else { - $('#timestamp_'+graph_id).text(labels_long[j]); - } - - //$('#timestamp_'+graph_id).css('top', plot.offset().top-$('#timestamp_'+graph_id).height()*1.5); - - var timesize = $('#timestamp_'+graph_id).width(); - - if (currentRanges != null) { - dataset = plot.getData(); - } - - var timenewpos = dataset[0].xaxis.p2c(pos.x)+$('.yAxis>div').eq(0).width(); - - var canvaslimit = plot.width(); - - if (timesize+timenewpos > canvaslimit) { - $('#timestamp_'+graph_id).css('left', timenewpos - timesize); - $('#timestamp_'+graph_id).css('top', 50); - } - else { - $('#timestamp_'+graph_id).css('left', timenewpos); - $('#timestamp_'+graph_id).css('top', 50); - } - } - else { - $('#timestamp_'+graph_id).hide(); - } - - var label_aux = series.label; + var label_aux = legend[series.label]; // The graphs of points type and unknown graphs will dont be updated - if (serie_types[i] != 'points' && series.label != $('#hidden-unknown_text').val()) { + if (series_type[dataset[k]["label"]] != 'points' && + series_type[dataset[k]["label"]] != 'unknown' && + series_type[dataset[k]["label"]] != 'percentil' + ) { $('#legend_' + graph_id + ' .legendLabel') - .eq(i).html(label_aux + '= ' + (short_data ? number_format(y, 0, "", short_data) : parseFloat(y)) + how_bigger + ' ' + unit); + .eq(i).html(label_aux + ' value = ' + + (short_data ? number_format(y, 0, "", short_data) : parseFloat(y)) + + how_bigger + ' ' + unit + ); } $('#legend_' + graph_id + ' .legendLabel') @@ -1839,53 +1996,95 @@ function pandoraFlotArea(graph_id, values, labels, labels_long, legend, } // Events - $('#' + graph_id).bind('plothover', function (event, pos, item) { - overview.setCrosshair({ x: pos.x, y: 0 }); + $('#overview_' + graph_id).bind('plothover', function (event, pos, item) { + plot.setCrosshair({ x: pos.x, y: pos.y }); currentPlot = plot; latestPosition = pos; if (!updateLegendTimeout) { updateLegendTimeout = setTimeout(updateLegend, 50); } + }); + $('#' + graph_id).bind('plothover', function (event, pos, item) { + overview.setCrosshair({ x: pos.x, y: pos.y }); + currentPlot = plot; + latestPosition = pos; + if (!updateLegendTimeout) { + updateLegendTimeout = setTimeout(updateLegend, 50); + } }); $('#' + graph_id).bind("plotclick", function (event, pos, item) { plot.unhighlight(); - if (item && item.series.label != '' && (item.series.label == legend_events || item.series.label == legend_events+series_suffix_str || item.series.label == legend_alerts || item.series.label == legend_alerts+series_suffix_str)) { + if(item && item.series.label != '' && item.series.label != null && + ( (item.series.label.search("alert") >= 0) || + (item.series.label.search("event") >= 0) ) + ){ plot.unhighlight(); - var dataset = plot.getData(); - var extra_info = 'No info to show'; - var extra_show = false; + $('#extra_'+graph_id).css('width', '170px'); + $('#extra_'+graph_id).css('height', '60px'); - var coord_x = (item.dataIndex/item.series.xaxis.datamax)* (event.target.clientWidth - event.target.offsetLeft + 1) + event.target.offsetLeft; + var dataset = plot.getData(); + var extra_info = 'No info to show'; + var extra_show = false; + var extra_height = $('#extra_'+graph_id).height(); + var extra_width = parseInt($('#extra_'+graph_id) + .css('width') + .split('px')[0]); + var events_data = new Array(); + var offset_graph = plot.getPlotOffset(); + var offset_relative = plot.offset(); + var width_graph = plot.width(); + var height_legend = $('#legend_' + graph_id).height(); + var coord_x = pos.pageX - offset_relative.left + offset_graph.left; + var coord_y = offset_graph.top + height_legend + extra_height; + if(coord_x + extra_width > width_graph){ + coord_x = coord_x - extra_width; + } + + var coord_y = offset_graph.top + height_legend + extra_height; $('#extra_'+graph_id).css('left',coord_x); - $('#extra_'+graph_id).css('top', event.target.offsetTop + 55 ); + $('#extra_'+graph_id).css('top', coord_y ); - switch(item.series.label) { - case legend_alerts+series_suffix_str: - case legend_alerts: - extra_info = ''+legend_alerts+':
From: '+labels_long[item.dataIndex]; - if (labels_long[item.dataIndex+1] != undefined) { - extra_info += '
To: '+labels_long[item.dataIndex+1]; - } - extra_info += '
'+get_event_details(alertsz[item.dataIndex]); - extra_show = true; - break; - case legend_events+series_suffix_str: - case legend_events: - extra_info = ''+legend_events+':
From: '+labels_long[item.dataIndex]; - if (labels_long[item.dataIndex+1] != undefined) { - extra_info += '
To: '+labels_long[item.dataIndex+1]; - } - extra_info += '
'+get_event_details(eventsz[item.dataIndex]); - extra_show = true; - break; - default: - return; - break; + if( (item.series.label.search("alert") >= 0) || + (item.series.label.search("event") >= 0) ){ + + $.each(events_array, function (i, v) { + $.each(v, function (index, value) { + if(value.utimestamp == item.datapoint[0]/1000 || + value.utimestamp == (item.datapoint[0]/1000) - 1){ + events_data = value; + } + }); + }); + + if(events_data.event_type.search("alert") >= 0){ + $extra_color = '#FFA631'; + } + else if(events_data.event_type.search("critical") >= 0){ + $extra_color = '#FC4444'; + } + else if(events_data.event_type.search("warning") >= 0){ + $extra_color = '#FAD403'; + } + else if(events_data.event_type.search("unknown") >= 0){ + $extra_color = '#3BA0FF'; + } + else if(events_data.event_type.search("normal") >= 0){ + $extra_color = '#80BA27'; + } + else{ + $extra_color = '#ffffff'; + } + + $('#extra_'+graph_id).css('background-color',$extra_color); + + extra_info = ''+events_data.evento+':'; + extra_info += '

Time: '+events_data.timestamp; + extra_show = true; } if (extra_show) { @@ -1900,39 +2099,88 @@ function pandoraFlotArea(graph_id, values, labels, labels_long, legend, } }); - $('#overview_'+graph_id).bind('plothover', function (event, pos, item) { - plot.setCrosshair({ x: pos.x, y: 0 }); - currentPlot = overview; - latestPosition = pos; - if (!updateLegendTimeout) { - updateLegendTimeout = setTimeout(updateLegend, 50); - } - }); + $('#'+graph_id).bind('mouseout',resetInteractivity(vconsole)); - $('#'+graph_id).bind('mouseout',resetInteractivity); - $('#overview_'+graph_id).bind('mouseout',resetInteractivity); - - //~ // Reset interactivity styles - function resetInteractivity() { + if(!vconsole){ + $('#overview_'+graph_id).bind('mouseout',resetInteractivity); + } + + if(image_treshold){ + if(!thresholded){ + // Recalculate the y axis + var y_recal = axis_thresholded( + threshold_data, + plot.getAxes().yaxis.min, + plot.getAxes().yaxis.max, + red_threshold, extremes, + red_up + ); + } + else{ + var y_recal = plot.getAxes().yaxis.max + } + + datas_treshold = add_threshold ( + data_base, + threshold_data, + plot.getAxes().yaxis.min, + plot.getAxes().yaxis.max, + red_threshold, + extremes, + red_up, + markins_graph + ); + + plot = $.plot($('#' + graph_id), datas_treshold, + $.extend(true, {}, options, { + yaxis: { + max: y_recal.max, + }, + xaxis: { + min: plot.getAxes().xaxis.min, + max: plot.getAxes().xaxis.max + } + })); + + thresholded = true; + } + + // Reset interactivity styles + function resetInteractivity(vconsole) { $('#timestamp_'+graph_id).hide(); dataset = plot.getData(); for (i = 0; i < dataset.length; ++i) { var series = dataset[i]; - var label_aux = series.label; + var label_aux = legend[series.label]; $('#legend_' + graph_id + ' .legendLabel') .eq(i).html(label_aux); } plot.clearCrosshair(); - overview.clearCrosshair(); + if(!vconsole){ + overview.clearCrosshair(); + } } // Format functions function xFormatter(v, axis) { - if (labels[v] == undefined) { - return ''; - } - extra_css = ''; - return '
'+labels[v]+'
'; + var d = new Date(v); + var result_date_format = 0; + + var monthNames = [ + "Jan", "Feb", "Mar", + "Apr", "May", "Jun", + "Jul", "Aug", "Sep", + "Oct", "Nov", "Dec" + ]; + + result_date_format = (d.getDate() <10?'0':'') + d.getDate() + " " + + monthNames[d.getMonth()] + " " + + d.getFullYear() + "\n" + + (d.getHours()<10?'0':'') + d.getHours() + ":" + + (d.getMinutes()<10?'0':'') + d.getMinutes() + ":" + + (d.getSeconds()<10?'0':'') + d.getSeconds(); + + return '
'+result_date_format+'
'; } function yFormatter(v, axis) { @@ -1950,57 +2198,49 @@ function pandoraFlotArea(graph_id, values, labels, labels_long, legend, } // Get only two decimals - formatted =round_with_decimals(formatted, 100) + formatted = round_with_decimals(formatted, 100); return '
'+formatted+'
'; } function lFormatter(v, item) { - return '
'+v+'
'; - // Prepared to turn series with a checkbox - //return '
'+v+'
'; + return '
'+legend[v]+'
'; } - + if (menu) { var parent_height; $('#menu_overview_' + graph_id).click(function() { $('#overview_' + graph_id).toggle(); + /* + if($('#overview_' + graph_id).css('visibility') == 'visible'){ + $('#overview_' + graph_id).css('visibility', 'hidden'); + } + else{ + $('#overview_' + graph_id).css('visibility', 'visible'); + } + */ }); - //~ $('#menu_export_csv_' + graph_id).click(function() { - //~ exportData({ type: 'csv' }); - //~ }); - $("#menu_export_csv_"+graph_id) .click(function (event) { event.preventDefault(); plot.exportDataCSV(); }); - - //Not a correct call - //~ $('#menu_export_json_' + graph_id).click(function() { - //~ exportData({ type: 'json' }); - //~ }); - - //This is a correct call to export data in json - //~ $("#menu_export_json_"+graph_id) - //~ .click(function (event) { - //~ event.preventDefault(); - //~ plot.exportDataJSON(); - //~ }); - + $('#menu_threshold_' + graph_id).click(function() { datas = new Array(); if (thresholded) { $.each(data_base, function() { - // Prepared to turning series - //if(showed[this.id.split('_')[1]]) { datas.push(this); - //} }); + + delete data_base[0].threshold; + plot = $.plot($('#' + graph_id), data_base, $.extend(true, {}, options, { - yaxis: {max: max_draw}, + yaxis: { + max: max_draw + }, xaxis: { min: plot.getAxes().xaxis.min, max: plot.getAxes().xaxis.max @@ -2009,47 +2249,60 @@ function pandoraFlotArea(graph_id, values, labels, labels_long, legend, thresholded = false; } else { - var max_draw = plot.getAxes().yaxis.datamax; - // Recalculate the y axis - var y_recal = axis_thresholded(threshold_data, plot.getAxes().yaxis.min, plot.getAxes().yaxis.max, - red_threshold, extremes, red_up); - plot = $.plot($('#' + graph_id), data_base, - $.extend(true, {}, options, { - yaxis: { - max: y_recal.max, - min: y_recal.min - }, - xaxis: { - min: plot.getAxes().xaxis.min, - max: plot.getAxes().xaxis.max - } - })); - datas = add_threshold (data_base, threshold_data, plot.getAxes().yaxis.min, plot.getAxes().yaxis.max, - red_threshold, extremes, red_up); - thresholded = true; - - } - - plot.setData(datas); - plot.draw(); - //~ plot.setSelection(currentRanges); + if(!thresholded){ + // Recalculate the y axis + var y_recal = axis_thresholded( + threshold_data, + plot.getAxes().yaxis.min, + plot.getAxes().yaxis.max, + red_threshold, extremes, + red_up + ); + } + else{ + var y_recal = plot.getAxes().yaxis.max + } + + datas_treshold = add_threshold ( + data_base, + threshold_data, + plot.getAxes().yaxis.min, + plot.getAxes().yaxis.max, + red_threshold, + extremes, + red_up, + markins_graph + ); + + plot = $.plot($('#' + graph_id), datas_treshold, + $.extend(true, {}, options, { + yaxis: { + max: y_recal.max, + }, + xaxis: { + min: plot.getAxes().xaxis.min, + max: plot.getAxes().xaxis.max + } + })); + + thresholded = true; + } + }); $('#menu_cancelzoom_' + graph_id).click(function() { // cancel the zooming + delete data_base[0].threshold; plot = $.plot($('#' + graph_id), data_base, $.extend(true, {}, options, { - xaxis: {max: max_x }, - legend: { show: false } + legend: { show: true } })); - $('#menu_cancelzoom_' + graph_id) .attr('src', homeurl + '/images/zoom_cross.disabled.png'); overview.clearSelection(); currentRanges = null; - thresholded = false; }); @@ -2064,21 +2317,21 @@ function pandoraFlotArea(graph_id, values, labels, labels_long, legend, // Estimated height of 24 (works fine with this data in all browsers) menu_height = 24; var legend_margin_bottom = parseInt( - $('#legend_'+graph_id).css('margin-bottom').split('px')[0]); + $('#legend_'+graph_id).css('margin-bottom').split('px')[0]); $('#legend_'+graph_id).css('margin-bottom', '10px'); - parent_height = parseInt( - $('#menu_'+graph_id).parent().css('height').split('px')[0]); - adjust_menu(graph_id, plot, parent_height, width); + parent_height = parseInt($('#menu_'+graph_id).parent().css('height').split('px')[0]); + adjust_menu(graph_id, plot, parent_height, width, show_legend); } - + if (!dashboard) { - if (water_mark) + if (water_mark){ set_watermark(graph_id, plot, $('#watermark_image_'+graph_id).attr('src')); - adjust_menu(graph_id, plot, parent_height, width); + } + //adjust_menu(graph_id, plot, parent_height, width, show_legend); } } -function adjust_menu(graph_id, plot, parent_height, width) { +function adjust_menu(graph_id, plot, parent_height, width, show_legend) { if ($('#'+graph_id+' .xAxis .tickLabel').eq(0).css('width') != undefined) { left_ticks_width = $('#'+graph_id+' .xAxis .tickLabel').eq(0).css('width').split('px')[0]; } @@ -2088,12 +2341,11 @@ function adjust_menu(graph_id, plot, parent_height, width) { var parent_height_new = 0; - var legend_height = parseInt($('#legend_'+graph_id).css('height').split('px')[0]) + parseInt($('#legend_'+graph_id).css('margin-top').split('px')[0]); - if ($('#overview_'+graph_id).css('display') == 'none') { - overview_height = 0; + if(show_legend){ + var legend_height = parseInt($('#legend_'+graph_id).css('height').split('px')[0]) + parseInt($('#legend_'+graph_id).css('margin-top').split('px')[0]); } - else { - overview_height = parseInt($('#overview_'+graph_id).css('height').split('px')[0]) + parseInt($('#overview_'+graph_id).css('margin-top').split('px')[0]); + else{ + var legend_height = 0; } var menu_height = '25'; @@ -2103,32 +2355,28 @@ function adjust_menu(graph_id, plot, parent_height, width) { } offset = $('#' + graph_id)[0].offsetTop; - + $('#menu_' + graph_id).css('top', ((offset) + 'px')); - //$('#legend_' + graph_id).css('width',plot.width()); - - //~ $('#menu_' + graph_id).css('left', $('#'+graph_id)[0].offsetWidth); - $('#menu_' + graph_id).show(); } function set_watermark(graph_id, plot, watermark_src) { var img = new Image(); + img.src = watermark_src; var context = plot.getCanvas().getContext('2d'); // Once it's loaded draw the image on the canvas. img.addEventListener('load', function () { - //~ // Now resize the image: x, y, w, h. - + // Now resize the image: x, y, w, h. var down_ticks_height = 0; if ($('#'+graph_id+' .yAxis .tickLabel').eq(0).css('height') != undefined) { down_ticks_height = $('#'+graph_id+' .yAxis .tickLabel').eq(0).css('height').split('px')[0]; } - - var left_pos = parseInt(context.canvas.width - 3) - $('#watermark_image_'+graph_id)[0].width; - var top_pos = 6; + + var left_pos = parseInt(context.canvas.width) - $('#watermark_image_'+graph_id)[0].width - 30; + var top_pos = 7; //var top_pos = parseInt(context.canvas.height - down_ticks_height - 10) - $('#watermark_image_'+graph_id)[0].height; //var left_pos = 380; context.drawImage(this, left_pos, top_pos); @@ -2169,19 +2417,18 @@ function get_event_details (event_ids) { return table; } +//Ajusta la grafica pequenรฑa con el desplazamiento del eje y function adjust_left_width_canvas(adapter_id, adapted_id) { var adapter_left_margin = $('#'+adapter_id+' .yAxis .tickLabel').width(); var adapted_pix = $('#'+adapted_id).width(); - var new_adapted_width = adapted_pix - adapter_left_margin; - $('#'+adapted_id).width(new_adapted_width); $('#'+adapted_id).css('margin-left', adapter_left_margin); } - +//Ajusta el ancho de la grafica pequeรฑa con respecto a la grande function update_left_width_canvas(graph_id) { - $('#overview_'+graph_id).width($('#'+graph_id).width() - 30); + $('#overview_'+graph_id).width($('#'+graph_id).width()); $('#overview_'+graph_id).css('margin-left', $('#'+graph_id+' .yAxis .tickLabel').width()); } @@ -2224,22 +2471,22 @@ function number_format(number, force_integer, unit, short_data) { break; } } - + return number + ' ' + shorts[pos] + unit; } -function pad(input, length, padding) { +function pad(input, length, padding) { var str = input + ""; return (length <= str.length) ? str : pad(str+padding, length, padding); } + // Recalculate the threshold data depends on warning and critical function axis_thresholded (threshold_data, y_min, y_max, red_threshold, extremes, red_up) { - var y = { min: 0, max: 0 }; - + // Default values var yaxis_resize = { up: null, @@ -2270,10 +2517,10 @@ function axis_thresholded (threshold_data, y_min, y_max, red_threshold, extremes if (yaxis_resize['normal_down'] > this.data[0][1]) yaxis_resize['normal_down'] = this.data[0][1]; } }); - + // If you need to display a up or a down bar, display 10% of data height var margin_up_or_down = (y_max - y_min)*0.10; - + // Calculate the new axis y['max'] = yaxis_resize['normal_up'] > y_max ? yaxis_resize['normal_up'] : y_max; y['min'] = yaxis_resize['normal_down'] > y_min ? yaxis_resize['normal_down'] : y_min; @@ -2287,55 +2534,157 @@ function axis_thresholded (threshold_data, y_min, y_max, red_threshold, extremes ? yaxis_resize['up'] + margin_up_or_down : y_min; } - + return y; } + +//add treshold function add_threshold (data_base, threshold_data, y_min, y_max, - red_threshold, extremes, red_up) { - + red_threshold, extremes, red_up, markins_graph) { var datas = new Array (); - + $.each(data_base, function() { - // Prepared to turning series - //if(showed[this.id.split('_')[1]]) { - datas.push(this); - //} + datas.push(this); }); + var threshold_array = []; + // Resize the threshold data - $.each(threshold_data, function() { + $.each(threshold_data, function(index, value) { + threshold_array[index] = []; + if (/_up/.test(this.id)){ this.bars.barWidth = y_max - this.data[0][1]; + + if (/critical/.test(this.id)){ + threshold_array[index]['min'] = this.data[0][1]; + threshold_array[index]['max'] = y_max; + threshold_array[index]['color'] = "red"; + } + else{ + threshold_array[index]['min'] = this.data[0][1]; + threshold_array[index]['max'] = y_max; + threshold_array[index]['color'] = "yellow"; + } + + if(y_min > this.data[0][1]){ + this.bars.barWidth = this.bars.barWidth - (y_min - this.data[0][1]); + this.data[0][1] = y_min; + } } + if (/_down/.test(this.id)){ var end; if (/critical/.test(this.id)) { - end = red_threshold; + end = red_threshold; } else { end = extremes[this.id]; } + this.bars.barWidth = end - y_min; this.data[0][1] = y_min; + + if (/critical/.test(this.id)){ + threshold_array[index]['min'] = this.data[0][1]; + threshold_array[index]['max'] = this.bars.barWidth; + threshold_array[index]['color'] = "red"; + } + else{ + threshold_array[index]['min'] = this.data[0][1]; + threshold_array[index]['max'] = this.bars.barWidth; + threshold_array[index]['color'] = "yellow"; + } } + if (/_normal/.test(this.id)){ var end; if (/critical/.test(this.id)) { end = red_up; + threshold_array[index]['min'] = this.data[0][1]; + threshold_array[index]['max'] = end; + threshold_array[index]['color'] = "red"; } else { - end = extremes[this.id + '_2']; + var first = extremes[this.id + '_1']; + var second = extremes[this.id + '_2']; + if(first > second){ + end = first; + } + else{ + end = second; + } + threshold_array[index]['min'] = this.data[0][1]; + threshold_array[index]['max'] = end; + threshold_array[index]['color'] = "yellow"; } + + if (this.data[0][1] < y_min) { this.bars.barWidth = end - y_min; this.data[0][1] = y_min; end = this.bars.barWidth + this.data[0][1]; + + if (/critical/.test(this.id)){ + threshold_array[index]['min'] = this.data[0][1]; + threshold_array[index]['max'] = this.data[0][1] + this.bars.barWidth; + threshold_array[index]['color'] = "red"; + } + else{ + threshold_array[index]['min'] = this.data[0][1]; + threshold_array[index]['max'] = this.data[0][1] + this.bars.barWidth; + threshold_array[index]['color'] = "yellow"; + } + } + if (end > y_max) { this.bars.barWidth = y_max - this.data[0][1]; + + if (/critical/.test(this.id)){ + threshold_array[index]['min'] = this.data[0][1]; + threshold_array[index]['max'] = this.data[0][1] + this.bars.barWidth; + threshold_array[index]['color'] = "red"; + } + else{ + threshold_array[index]['min'] = this.data[0][1]; + threshold_array[index]['max'] = this.data[0][1] + this.bars.barWidth; + threshold_array[index]['color'] = "yellow"; + } + } - } - datas.push(this); + } + + if(markins_graph && this.bars.barWidth > 0){ + datas.push(this); + } }); - + + var extreme_treshold_array = []; + var i = 0; + var flag = true; + + $.each(threshold_array, function(index, value) { + flag = true; + extreme_treshold_array[i] = { + 'below': value['max'], + 'color': value['color'], + } + i++; + $.each(threshold_array, function(i, v) { + if(value['min'] == v['max']){ + return flag = false; + } + }); + if(flag){ + extreme_treshold_array[i] = { + 'below': value['min'], + 'color': datas[0].color, + } + i++; + } + }); + + datas[0].threshold = extreme_treshold_array; + return datas; } diff --git a/pandora_console/include/graphs/functions_flot.php b/pandora_console/include/graphs/functions_flot.php index 4a5deb4443..0621de88cd 100644 --- a/pandora_console/include/graphs/functions_flot.php +++ b/pandora_console/include/graphs/functions_flot.php @@ -20,22 +20,26 @@ global $config; function include_javascript_dependencies_flot_graph($return = false) { global $config; - + static $is_include_javascript = false; - + if (!$is_include_javascript) { $is_include_javascript = true; - + $metaconsole_hack = ''; if (defined('METACONSOLE')) { $metaconsole_hack = '../../'; } - + // NOTE: jquery.flot.threshold is not te original file. Is patched to allow multiple thresholds and filled area $output = ' + + + - '; $output .= " "; - + if (!$return) echo $output; - + return $output; } } @@ -94,103 +100,21 @@ function include_javascript_dependencies_flot_graph($return = false) { /////////////////////////////// ////////// AREA GRAPHS //////// /////////////////////////////// -function flot_area_stacked_graph($chart_data, $width, $height, $color, - $legend, $long_index, $homeurl = '', $font = '', $font_size = 7, - $unit = '', $water_mark = '', $serie_types = array(), - $chart_extra_data = array(), $yellow_threshold = 0, - $red_threshold = 0, $adapt_key= '', $force_integer = false, - $series_suffix_str = '', $menu = true, $background_color = 'white', - $dashboard = false, $vconsole = false, $agent_module_id = 0) { - - global $config; - - return flot_area_graph($chart_data, $width, $height, $color, - $legend, $long_index, $homeurl, $unit, 'area_stacked', - $water_mark, $serie_types, $chart_extra_data, $yellow_threshold, - $red_threshold, $adapt_key, $force_integer, $series_suffix_str, - $menu, $background_color, $dashboard, $vconsole, - $agent_module_id, $font, $font_size); -} +function flot_area_graph ( + $agent_module_id, $array_data, + $legend, $series_type, $color, $date_array, + $data_module_graph, $params, $water_mark, + $array_events_alerts ) { -function flot_area_simple_graph($chart_data, $width, $height, $color, - $legend, $long_index, $homeurl = '', $unit = '', $water_mark = '', - $serie_types = array(), $chart_extra_data = array(), - $yellow_threshold = 0, $red_threshold = 0, $adapt_key= '', - $force_integer = false, $series_suffix_str = '', $menu = true, - $background_color = 'white', $dashboard = false, $vconsole = false, - $agent_module_id = 0, $font = '', $font_size = 7, $xaxisname = '', - $percentil_values = array(), $threshold_data = array()) { - global $config; - return flot_area_graph($chart_data, $width, $height, $color, - $legend, $long_index, $homeurl, $unit, 'area_simple', - $water_mark, $serie_types, $chart_extra_data, $yellow_threshold, - $red_threshold, $adapt_key, $force_integer, $series_suffix_str, - $menu, $background_color, $dashboard, $vconsole, - $agent_module_id, $font, $font_size, $xaxisname, $percentil_values, - $threshold_data); -} - -function flot_line_stacked_graph($chart_data, $width, $height, $color, - $legend, $long_index, $homeurl = '', $font = '', $font_size = 7, - $unit = '', $water_mark = '', $serie_types = array(), - $chart_extra_data = array(), $yellow_threshold = 0, - $red_threshold = 0, $adapt_key= '', $force_integer = false, - $series_suffix_str = '', $menu = true, $background_color = 'white', - $dashboard = false, $vconsole = false, $agent_module_id = 0, - $xaxisname = "") { - - global $config; - - return flot_area_graph($chart_data, $width, $height, $color, - $legend, $long_index, $homeurl, $unit, 'line_stacked', - $water_mark, $serie_types, $chart_extra_data, $yellow_threshold, - $red_threshold, $adapt_key, $force_integer, $series_suffix_str, - $menu, $background_color, $dashboard, $vconsole, - $agent_module_id, $font, $font_size, $xaxisname); -} - -function flot_line_simple_graph($chart_data, $width, $height, $color, - $legend, $long_index, $homeurl = '', $font = '', $font_size = 7, - $unit = '', $water_mark = '', $serie_types = array(), - $chart_extra_data = array(), $yellow_threshold = 0, - $red_threshold = 0, $adapt_key= '', $force_integer = false, - $series_suffix_str = '', $menu = true, $background_color = 'white', - $dashboard = false, $vconsole = false, $agent_module_id = 0, - $percentil_values = array(), $threshold_data = array(), - $xaxisname = '') { - - global $config; - - return flot_area_graph($chart_data, $width, $height, $color, - $legend, $long_index, $homeurl, $unit, 'line_simple', - $water_mark, $serie_types, $chart_extra_data, $yellow_threshold, - $red_threshold, $adapt_key, $force_integer, $series_suffix_str, - $menu, $background_color, $dashboard, $vconsole, - $agent_module_id, $font, $font_size, $xaxisname, $percentil_values, - $threshold_data); -} - -function flot_area_graph($chart_data, $width, $height, $color, $legend, - $long_index, $homeurl, $unit, $type, $water_mark, $serie_types, - $chart_extra_data, $yellow_threshold, $red_threshold, $adapt_key, - $force_integer, $series_suffix_str = '', $menu = true, - $background_color = 'white', $dashboard = false, $vconsole = false, - $agent_module_id = 0, $font = '', $font_size = 7, $xaxisname = '', - $percentil_values = array(), $threshold_data = array()) { - - global $config; - // include_javascript_dependencies_flot_graph(); - $menu = (int)$menu; // Get a unique identifier to graph $graph_id = uniqid('graph_'); - + $background_style = ''; - switch ($background_color) { - default: + switch ($params['backgroundColor']) { case 'white': $background_style = ' background: #fff; '; break; @@ -200,281 +124,128 @@ function flot_area_graph($chart_data, $width, $height, $color, $legend, case 'transparent': $background_style = ''; break; + default: + $background_style = 'background-color: ' . $params['backgroundColor']; + break; } - + // Parent layer - $return = "
"; + $return = "
"; // Set some containers to legend, graph, timestamp tooltip, etc. - $return .= "

"; - - if (!empty($threshold_data)) { - $yellow_up = $threshold_data['yellow_up']; - $red_up = $threshold_data['red_up']; - $yellow_inverse = $threshold_data['yellow_inverse']; - $red_inverse = $threshold_data['red_inverse']; + if($params['show_legend']){ + $return .= "

"; } - else { + if(isset($params['graph_combined']) && $params['graph_combined'] && + (!isset($params['from_interface']) || !$params['from_interface']) ){ + $yellow_up = 0; + $red_up = 0; + $yellow_inverse = false; + $red_inverse = false; + } + elseif(!isset($params['combined']) || !$params['combined']){ + $yellow_threshold = $data_module_graph['w_min']; + $red_threshold = $data_module_graph['c_min']; // Get other required module datas to draw warning and critical if ($agent_module_id == 0) { - $yellow_up = 0; - $red_up = 0; + $yellow_up = 0; + $red_up = 0; $yellow_inverse = false; - $red_inverse = false; + $red_inverse = false; } else { - $module_data = db_get_row_sql ('SELECT * FROM tagente_modulo WHERE id_agente_modulo = ' . $agent_module_id); - $yellow_up = $module_data['max_warning']; - $red_up = $module_data['max_critical']; - $yellow_inverse = !($module_data['warning_inverse'] == 0); - $red_inverse = !($module_data['critical_inverse'] == 0); + $yellow_up = $data_module_graph['w_max']; + $red_up = $data_module_graph['c_max']; + $yellow_inverse = !($data_module_graph['w_inv'] == 0); + $red_inverse = !($data_module_graph['c_inv'] == 0); } } - - if ($menu) { - $threshold = false; - if ($yellow_threshold != $yellow_up || $red_threshold != $red_up) { - $threshold = true; + elseif(isset($params['from_interface']) && $params['from_interface']){ + if( isset($params['threshold_data']) && is_array($params['threshold_data'])){ + $yellow_up = $params['threshold_data']['yellow_up']; + $red_up = $params['threshold_data']['red_up']; + $yellow_inverse = $params['threshold_data']['yellow_inverse']; + $red_inverse = $params['threshold_data']['red_inverse']; } - - $nbuttons = 3; - - if ($threshold) { - $nbuttons++; - } - $menu_width = 25 * $nbuttons + 15; - if ( $dashboard == false AND $vconsole == false) { - $return .= ""; + else{ + $yellow_up = 0; + $red_up = 0; + $yellow_inverse = false; + $red_inverse = false; } + } + else{ + $yellow_up = 0; + $red_up = 0; + $yellow_inverse = false; + $red_inverse = false; + } - if ($dashboard) { - $return .= ""; - } + if ($params['menu']) { + $return .= menu_graph( + $yellow_threshold, + $red_threshold, + $yellow_up, + $red_up, + $yellow_inverse, + $red_inverse, + $graph_id, + $params + ); } $return .= html_print_input_hidden('line_width_graph', $config['custom_graph_width'], true); - $return .= ""; + $return .= "
"; $return .= "
"; - if ($menu) { - $height = 100; + + if($params['type'] == 'area_simple'){ + $return .= "noresizevc "; + } + + $return .= "graph" .$params['adapt_key'] ."' + style=' width: ".$params['width']."px; + height: ".$params['height']."px;'>
"; + + if ($params['menu']) { + $params['height'] = 100; } else { - $height = 1; + $params['height'] = 1; } - if (!$vconsole) - $return .= ""; - + + if (!$vconsole){ + $return .= ""; + } + if ($water_mark != '') { - $return .= ""; + $return .= ""; $watermark = 'true'; } else { $watermark = 'false'; } - - // Set a weird separator to serialize and unserialize passing data from php to javascript - $separator = ';;::;;'; - $separator2 = ':,:,,,:,:'; - - // Transform data from our format to library format - $legend2 = array(); - $labels = array(); - $a = array(); - $vars = array(); - $serie_types2 = array(); - - $colors = array(); - - $index = array_keys(reset($chart_data)); - foreach ($index as $serie_key) { - if (isset($color[$serie_key])) { - $colors[] = $color[$serie_key]['color']; - } - else { - $colors[] = ''; - } - } - - foreach ($chart_data as $label => $values) { - $labels[] = $label; - - foreach($values as $key => $value) { - $jsvar = "data_" . $graph_id . "_" . $key; - - - if (!isset($serie_types[$key])) { - switch ($type) { - case 'line_simple': - case 'line_stacked': - $serie_types2[$jsvar] = 'line'; - break; - case 'area_simple': - case 'area_stacked': - default: - $serie_types2[$jsvar] = 'area'; - break; - } - } - else { - $serie_types2[$jsvar] = $serie_types[$key]; - } - - - if ($serie_types2[$jsvar] == 'points' && $value == 0) { - $data[$jsvar][] = 'null'; - } - else { - $data[$jsvar][] = $value; - } - - if (!isset($legend[$key])) { - $legend2[$jsvar] = 'null'; - } - else { - $legend2[$jsvar] = $legend[$key]; - } - } + + foreach($series_type as $k => $v){ + $series_type_unique["data_" . $graph_id . "_" . $k] = $v; } - if (!empty($percentil_values)) { - foreach($percentil_values as $key => $value) { - $jsvar = "percentil_" . $graph_id . "_" . $key; - $serie_types2[$jsvar] = 'line'; - $data[$jsvar] = $value; - } - } - // Store data series in javascript format - $jsvars = ''; - $jsseries = array(); - $values2 = array(); - $i = 0; - $max_x = 0; - foreach ($data as $jsvar => $values) { - $n_values = count($values); - if ($n_values > $max_x) { - $max_x = $n_values; - } - - $values2[] = implode($separator,$values); - $i ++; - } - - $values = implode($separator2, $values2); - - // Max is "n-1" because start with 0 - $max_x--; - - $extra_width = (int)($width / 3); - - $return .= ""; - - // Process extra data - $events = array(); - $event_ids = array(); - $alerts = array(); - $alert_ids = array(); - $legend_events = ''; - $legend_alerts = ''; - - if (empty($chart_extra_data)) { - $chart_extra_data = array(); - } - - foreach ($chart_extra_data as $i => $data) { - switch ($i) { - case 'legend_alerts': - $legend_alerts = $data; - break; - case 'legend_events': - $legend_events = $data; - break; - default: - if (isset($data['events'])) { - $event_ids[] = $i; - $events[$i] = $data['events']; - } - if (isset($data['alerts'])) { - $alert_ids[] = $i; - $alerts[$i] = $data['alerts']; - } - break; - } - } - - // Store serialized data to use it from javascript - $events = implode($separator,$events); - $event_ids = implode($separator,$event_ids); - $alerts = implode($separator,$alerts); - $alert_ids = implode($separator,$alert_ids); - $labels = implode($separator,$labels); - if (!empty($long_index)) { - $labels_long = implode($separator, $long_index); - } - else { - $labels_long = $labels; - } - if (!empty($legend)) { - $legend = io_safe_output(implode($separator, $legend)); - } - $serie_types = implode($separator, $serie_types2); - $colors = implode($separator, $colors); - - // transform into string to pass to javascript - if ($force_integer) { - $force_integer = 'true'; - } - else { - $force_integer = 'false'; - } - - //modify internal grid lines and legend text color - + $extra_width = (int)($params['width'] / 3); + $return .= "
"; + if(substr($background_style, -6, 4) == '#fff'){ $background_color = "#eee"; $legend_color = "#151515"; - } else if(substr($background_style, -6, 4) == '#000'){ $background_color = "#151515"; @@ -484,68 +255,119 @@ function flot_area_graph($chart_data, $width, $height, $color, $legend, $background_color = "#A4A4A4"; $legend_color = "#A4A4A4"; } - - // Trick to get translated string from javascript - $return .= html_print_input_hidden('unknown_text', __('Unknown'), - true); - if (!isset($config["short_module_graph_data"])) + $force_integer = 0; + + // Trick to get translated string from javascript + $return .= html_print_input_hidden('unknown_text', __('Unknown'), true); + + if (!isset($config["short_module_graph_data"])){ $config["short_module_graph_data"] = ''; - + } + $short_data = $config["short_module_graph_data"]; - - + $values = json_encode($array_data); + $legend = json_encode($legend); + $series_type = json_encode($series_type); + $color = json_encode($color); + $date_array = json_encode($date_array); + $data_module_graph = json_encode($data_module_graph); + $params = json_encode($params); + $array_events_alerts = json_encode($array_events_alerts); + // Javascript code if ($font_size == '') $font_size = '\'\''; $return .= ""; - + // Parent layer $return .= "
"; - + + return $return; +} + +function menu_graph( + $yellow_threshold, $red_threshold, + $yellow_up, $red_up, $yellow_inverse, + $red_inverse, $graph_id, $params +){ + $return = ''; + $threshold = false; + if ($yellow_threshold != $yellow_up || $red_threshold != $red_up) { + $threshold = true; + } + + if ( $params['dashboard'] == false AND $params['vconsole'] == false) { + $return .= ""; + } + + if ($params['dashboard']) { + $return .= ""; + } return $return; } @@ -882,10 +704,10 @@ function flot_slicesbar_graph ($graph_data, $period, $width, $height, $legend, $ // Set some containers to legend, graph, timestamp tooltip, etc. if ($stat_win) { - $return = "
"; + $return = "
"; } else { - $return = "
"; + $return = "
"; } $return .= ""; diff --git a/pandora_console/include/graphs/functions_pchart.php b/pandora_console/include/graphs/functions_pchart.php index 0114d36e32..8ce6faeaa5 100644 --- a/pandora_console/include/graphs/functions_pchart.php +++ b/pandora_console/include/graphs/functions_pchart.php @@ -382,14 +382,7 @@ switch ($graph_type) { $font, $antialiasing, $rgb_color, $xaxisname, $yaxisname, false, $legend, $fine_colors, $water_mark, $font_size); break; - case 'stacked_area': - case 'area': - case 'line': - pch_vertical_graph($graph_type, $data_keys, $data_values, $width, - $height, $rgb_color, $xaxisname, $yaxisname, false, $legend, - $font, $antialiasing, $water_mark, $font_size, - $backgroundColor, $unit, $series_type, $graph_threshold, $id_module); - break; + default: case 'threshold': pch_threshold_graph($graph_type, $data_keys, $data_values, $width, $height, $font, $antialiasing, $xaxisname, $yaxisname, $title, @@ -875,540 +868,6 @@ function pch_bar_graph ($graph_type, $index, $data, $width, $height, $font, $myPicture->stroke(); } -function pch_vertical_graph ($graph_type, $index, $data, $width, $height, - $rgb_color = false, $xaxisname = "", $yaxisname = "", $show_values = false, - $legend = array(), $font, $antialiasing, $water_mark = '', $font_size, - $backgroundColor = 'white', $unit = '', $series_type = array(), - $graph_threshold = false, $id_module) { - - global $config; - - /* CAT:Vertical Charts */ - if (!is_array($legend) || empty($legend)) { - unset($legend); - } - - if (is_array(reset($data))) { - $data2 = array(); - foreach ($data as $i =>$values) { - $c = 0; - foreach ($values as $i2 => $value) { - $data2[$i2][$i] = $value; - $c++; - } - } - $data = $data2; - } - else { - $data = array($data); - } - - /* Create and populate the pData object */ - $MyData = new pData(); - - foreach ($data as $i => $values) { - if (isset($legend)) { - $point_id = $legend[$i]; - - // Translate the id of serie to legend of id - if (!empty($series_type)) { - if (!isset($series_type[$point_id])) { - $series_type[$point_id] = $series_type[$i]; - unset($series_type[$i]); - } - } - } - else { - $point_id = $i; - } - - $MyData->addPoints($values, $point_id); - - - if (!empty($rgb_color)) { - $MyData->setPalette($point_id, - array( - "R" => $rgb_color[$i]['color']["R"], - "G" => $rgb_color[$i]['color']["G"], - "B" => $rgb_color[$i]['color']["B"], - "BorderR" => $rgb_color[$i]['border']["R"], - "BorderG" => $rgb_color[$i]['border']["G"], - "BorderB" => $rgb_color[$i]['border']["B"], - "Alpha" => $rgb_color[$i]['alpha'])); - - $palette_color = array(); - if (isset($rgb_color[$i]['color'])) { - $palette_color["R"] = $rgb_color[$i]['color']["R"]; - $palette_color["G"] = $rgb_color[$i]['color']["G"]; - $palette_color["B"] = $rgb_color[$i]['color']["B"]; - } - if (isset($rgb_color[$i]['color'])) { - $palette_color["BorderR"] = $rgb_color[$i]['border']["R"]; - $palette_color["BorderG"] = $rgb_color[$i]['border']["G"]; - $palette_color["BorderB"] = $rgb_color[$i]['border']["B"]; - } - if (isset($rgb_color[$i]['color'])) { - $palette_color["Alpha"] = $rgb_color[$i]['Alpha']; - } - - $MyData->setPalette($point_id, $palette_color); - } - - // The weight of the line is not calculated in pixels, so it needs to be transformed - $reduction_coefficient = 0.31; - $MyData->setSerieWeight($point_id, $config['custom_graph_width'] * $reduction_coefficient); - } - - $MyData->setAxisName(0,$unit); - $MyData->addPoints($index,"Xaxis"); - $MyData->setSerieDescription("Xaxis", $xaxisname); - $MyData->setAbscissa("Xaxis"); - $MyData->setAxisDisplay(0, AXIS_FORMAT_TWO_SIGNIFICANT, 0); - - switch ($backgroundColor) { - case 'white': - $transparent = false; - $fontColor = array('R' => 0, 'G' => 0, 'B' => 0); - break; - case 'black': - $transparent = false; - $fontColor = array('R' => 200, 'G' => 200, 'B' => 200); - break; - case 'transparent': - $transparent = true; - // $fontColor = array('R' => 0, 'G' => 0, 'B' => 0); - // Now the color of the text will be grey - $fontColor = array('R' => 200, 'G' => 200, 'B' => 200); - break; - - } - /* Create the pChart object */ - $myPicture = new pImage($width, $height + $font_size, $MyData, $transparent, - $backgroundColor, $fontColor); - - /* Turn of Antialiasing */ - $myPicture->Antialias = $antialiasing; - - /* Add a border to the picture */ - //$myPicture->drawRectangle(0,0,$width,$height,array("R"=>0,"G"=>0,"B"=>0)); - - /* Set the default font */ - $myPicture->setFontProperties( - array("FontName" =>$font, "FontSize" => $font_size)); - - // By default, set a top margin of 5 px - $top_margin = 5; - if (isset($legend)) { - /* Set horizontal legend if is posible */ - $legend_mode = LEGEND_HORIZONTAL; - $size = $myPicture->getLegendSize( - array("Style" => LEGEND_NOBORDER,"Mode" => $legend_mode)); - if ($size['Width'] > ($width - 5)) { - $legend_mode = LEGEND_VERTICAL; - $size = $myPicture->getLegendSize(array("Style"=>LEGEND_NOBORDER,"Mode"=>$legend_mode)); - } - - // Update the top margin to add the legend Height - $top_margin = $size['Height']; - - /* Write the chart legend */ - $myPicture->drawLegend($width - $size['Width'], 8, - array("Style" => LEGEND_NOBORDER, "Mode" => $legend_mode)); - } - - //Calculate the bottom margin from the size of string in each index - $max_chars = graph_get_max_index($index); - $margin_bottom = $font_size * $max_chars; - - $water_mark_height = 0; - $water_mark_width = 0; - if (!empty($water_mark)) { - $size_water_mark = getimagesize($water_mark); - $water_mark_height = $size_water_mark[1]; - $water_mark_width = $size_water_mark[0]; - - $myPicture->drawFromPNG( - ($width - $water_mark_width), - $top_margin, - $water_mark); - } - - // Get the max number of scale - $max_all = 0; - - $serie_ne_zero = false; - foreach ($data as $serie) { - $max_this_serie = max($serie); - if ($max_this_serie > $max_all) { - $max_all = $max_this_serie; - } - // Detect if all serie is equal to zero or not - if ($serie != 0) - $serie_ne_zero = true; - } - - // Get the number of digits of the scale - $digits_left = 0; - while ($max_all > 1) { - $digits_left ++; - $max_all /= 10; - } - - // If the number is less than 1 we count the decimals - // Also check if the serie is not all equal to zero (!$serie_ne_zero) - if ($digits_left == 0 and !$serie_ne_zero) { - while($max_all < 1) { - $digits_left ++; - $max_all *= 10; - } - } - - $chart_size = ($digits_left * $font_size) + 20; - - $min_data = 0; - $max_data = 0; - foreach ($data as $k => $v) { - if(min($v) < $min_data){ - $min_data = min($v); - } - if(max($v) > $max_data){ - $max_data = max($v); - } - } - $chart_margin = 36; - - /* Area depends on yaxisname */ - if ($yaxisname != '') { - $chart_margin += $chart_size; - } - - $myPicture->setGraphArea($chart_margin, $top_margin, - $width, - ($height - $margin_bottom)); - - if($graph_threshold){ - $sql_treshold = 'select min_critical, max_critical, min_warning, max_warning, critical_inverse, warning_inverse from tagente_modulo where id_agente_modulo =' . $id_module; - $treshold_position = db_get_all_rows_sql($sql_treshold); - - //min, max and inverse critical and warning - $p_min_crit = $treshold_position[0]['min_critical']; - $p_max_crit = $treshold_position[0]['max_critical']; - $p_inv_crit = $treshold_position[0]['critical_inverse']; - $p_min_warn = $treshold_position[0]['min_warning']; - $p_max_warn = $treshold_position[0]['max_warning']; - $p_inv_warn = $treshold_position[0]['warning_inverse']; - - //interval warning - $print_rectangle_warning = 1; - if($p_min_warn == "0.00" && $p_max_warn == "0.00" && $p_inv_warn == 0){ - $print_rectangle_warning = 0; - } - if($print_rectangle_warning){ - if($p_inv_warn){ - if($p_max_warn == 0){ - $p_max_warn = $p_min_warn; - $p_min_warn = "none"; - } - else{ - $p_max_warn_inv = $p_min_warn; - $p_min_warn_inv = $min_data + 2; - - $p_min_warn = $p_max_warn; - if($p_max_warn > $max_data){ - $p_max_warn = $p_max_warn + 21; - } - else{ - $p_max_warn = $max_data + 21; - } - } - } - else{ - if($p_max_warn == 0){ - if($max_data > $p_min_warn){ - $p_max_warn = $max_data + 21; - } - else{ - $p_max_warn = $p_min_warn + 21; - } - } - } - } - - //interval critical - $print_rectangle_critical = 1; - if($p_min_crit == "0.00" && $p_max_crit == "0.00" && $p_inv_crit == 0){ - $print_rectangle_critical = 0; - } - - if($print_rectangle_critical){ - if($p_inv_crit){ - if($p_max_crit == 0){ - $p_max_crit = $p_min_crit; - $p_min_crit = "none"; - } - else{ - $p_max_crit_inv = $p_min_crit; - $p_min_crit_inv = $min_data + 2; - - $p_min_crit = $p_max_crit; - if($p_inv_warn){ - if($p_max_crit < $p_max_warn){ - $p_max_crit = $p_max_warn; - } - } - else{ - if($p_max_crit > $max_data){ - $p_max_crit = $p_max_crit + 21; - } - else{ - $p_max_crit = $max_data + 21; - } - } - } - } - else{ - if($p_max_crit == 0){ - if($p_max_warn > $p_min_crit){ - $p_max_crit = $p_max_warn; - } - else{ - if($max_data > $p_min_crit){ - $p_max_crit = $max_data + 21; - } - else{ - $p_max_crit = $p_min_crit + 21; - } - } - } - } - } - - //Check size scale - //Which of the thresholds is higher? - if($p_max_crit > $p_max_warn){ - $check_scale = $p_max_crit; - } - else{ - $check_scale = $p_max_warn; - } - - if($p_min_crit < $p_min_warn){ - $check_scale_min = $p_min_crit; - } - else{ - $check_scale_min = $p_min_warn; - } - - //Is the threshold higher than our maximum? - if($max_data > $check_scale){ - $check_scale = $max_data; - } - - if($min_data < $check_scale_min){ - $check_scale_min = $min_data; - } - - $ManualScale = array( 0 => array("Min" => $check_scale_min, "Max" => $check_scale) ); - $mode = SCALE_MODE_MANUAL; - - /* Draw the scale */ - $scaleSettings = array( - "GridR" => 200, - "GridG" => 200, - "GridB" => 200, - "GridAlpha" => 30, - "DrawSubTicks" => true, - "CycleBackground" => true, - "BackgroundAlpha1" => 35, - "BackgroundAlpha2" => 35, - "Mode" => $mode, - "ManualScale" => $ManualScale, - "LabelRotation" => 40, - "XMargin" => 0, - "MinDivHeight" => 15, - "TicksFontSize" => $font_size - 1); - - $scaleSettings['AxisR'] = '200'; - $scaleSettings['AxisG'] = '200'; - $scaleSettings['AxisB'] = '200'; - $scaleSettings['TickR'] = '200'; - $scaleSettings['TickG'] = '200'; - $scaleSettings['TickB'] = '200'; - - $myPicture->drawScale($scaleSettings); - - - //values - $scale_max = $myPicture->DataSet->Data["Axis"][0]["ScaleMax"]; - $scale_min = $myPicture->DataSet->Data["Axis"][0]["ScaleMin"]; - - $position_y1 = $myPicture->GraphAreaY1; - $position_y2 = $myPicture->GraphAreaY2; - - $position1 = $myPicture->GraphAreaX1; - $position3 = $myPicture->GraphAreaX2; - - $cte = ($position_y2 - $position_y1) / ($scale_max - $scale_min); - - //warning - if($print_rectangle_warning){ - $RectangleSettings = array("R"=>255,"G"=>255,"B"=>000,"Dash"=>TRUE,"DashR"=>170,"DashG"=>220,"DashB"=>190); - if($p_min_warn == "none"){ - $p_min_warn = $scale_min; - } - - $position2 = ($scale_max - $p_min_warn)*$cte + $position_y1; - $position4 = ($scale_max - $p_max_warn)*$cte + $position_y1; - - $myPicture->drawFilledRectangle($position1, floor($position2), - $position3, floor($position4), - $RectangleSettings); - if($p_inv_warn){ - $position2 = ($scale_max - $p_min_warn_inv)*$cte + $position_y1; - $position4 = ($scale_max - $p_max_warn_inv)*$cte + $position_y1; - $myPicture->drawFilledRectangle($position1, floor($position2), - $position3, floor($position4), - $RectangleSettings); - } - } - - //critical - if($print_rectangle_critical){ - $RectangleSettings = array("R"=>248,"G"=>000,"B"=>000,"Dash"=>TRUE,"DashR"=>170,"DashG"=>220,"DashB"=>190); - - if($p_min_crit == "none"){ - $p_min_crit = $scale_min; - } - - $position2 = ($scale_max - $p_min_crit)*$cte + $position_y1; - $position4 = ($scale_max - $p_max_crit)*$cte + $position_y1; - $myPicture->drawFilledRectangle($position1, $position2, - $position3, $position4, - $RectangleSettings); - - if($p_inv_crit){ - $position2 = ($scale_max - $p_min_crit_inv)*$cte + $position_y1; - $position4 = ($scale_max - $p_max_crit_inv)*$cte + $position_y1; - $myPicture->drawFilledRectangle($position1, $position2, - $position3, $position4, - $RectangleSettings); - } - } - - } - else{ - - $ManualScale = array( 0 => array( - "Min" => $min_data, - "Max" => $max_data - )); - //html_debug("MAX: $max_data, ROUND: $max_round", true); - $mode = SCALE_MODE_MANUAL; - - /* Draw the scale */ - $scaleSettings = array( - "GridR" => 200, - "GridG" => 200, - "GridB" => 200, - "GridAlpha" => 30, - "DrawSubTicks" => true, - "CycleBackground" => true, - "BackgroundAlpha1" => 35, - "BackgroundAlpha2" => 35, - "Mode" => $mode, - "ManualScale" => $ManualScale, - "LabelRotation" => 40, - "XMargin" => 0, - "MinDivHeight" => 15, - "TicksFontSize" => $font_size - 1); - - $scaleSettings['AxisR'] = '200'; - $scaleSettings['AxisG'] = '200'; - $scaleSettings['AxisB'] = '200'; - $scaleSettings['TickR'] = '200'; - $scaleSettings['TickG'] = '200'; - $scaleSettings['TickB'] = '200'; - - $myPicture->drawScale($scaleSettings); - } - - /* Turn on shadow computing */ - //$myPicture->setShadow(TRUE,array("X"=>0,"Y"=>1,"R"=>0,"G"=>0,"B"=>0,"Alpha"=>10)); - - switch ($graph_type) { - case 'stacked_area': - $ForceTransparency = "-1"; - break; - default: - $ForceTransparency = "100"; - break; - } - - /* Draw the chart */ - $settings = array( - "ForceTransparency" => 20, - "Gradient" => TRUE, - "GradientMode" => GRADIENT_EFFECT_CAN, - "DisplayValues" => $show_values, - "DisplayZeroValues" => FALSE, - "DisplayR" => 100, - "DisplayZeros" => FALSE, - "DisplayG" => 100, - "DisplayB" => 100, - "DisplayShadow" => TRUE, - "Surrounding" => 5, - "AroundZero" => TRUE); - - - if (empty($series_type)) { - switch($graph_type) { - case "stacked_area": - case "area": - $myPicture->drawAreaChart($settings); - break; - case "line": - $myPicture->drawLineChart($settings); - break; - } - } - else { - // Hiden all series for to show each serie as type - foreach ($series_type as $id => $type) { - $MyData->setSerieDrawable($id, false); - } - foreach ($series_type as $id => $type) { - $MyData->setSerieDrawable($id, true); //Enable the serie to paint - switch ($type) { - default: - case 'area': - $myPicture->drawAreaChart($settings); - break; - //~ case "points": - //~ $myPicture->drawPlotChart($settings); - //~ break; - case "line": - $myPicture->drawLineChart($settings); - break; - case 'boolean': - switch($graph_type) { - case "stacked_area": - case "area": - $myPicture->drawFilledStepChart($settings); - break; - case "line": - $myPicture->drawStepChart($settings); - break; - } - break; - } - $MyData->setSerieDrawable($id, false); //Disable the serie to paint the rest - } - } - - /* Render the picture */ - $myPicture->stroke(); -} - function pch_threshold_graph ($graph_type, $index, $data, $width, $height, $font, $antialiasing, $xaxisname = "", $yaxisname = "", $title = "", $show_values = false, $show_legend = false, $font_size) { diff --git a/pandora_console/include/graphs/pChart/pDraw.class.php b/pandora_console/include/graphs/pChart/pDraw.class.php index f1dfe780d5..76ed6030bb 100755 --- a/pandora_console/include/graphs/pChart/pDraw.class.php +++ b/pandora_console/include/graphs/pChart/pDraw.class.php @@ -1758,6 +1758,12 @@ $SkippedTickAlpha = isset($Format["SkippedTickAlpha"]) ? $Format["SkippedTickAlpha"] : $TickAlpha-80; $SkippedInnerTickWidth = isset($Format["SkippedInnerTickWidth"]) ? $Format["SkippedInnerTickWidth"] : 0; $SkippedOuterTickWidth = isset($Format["SkippedOuterTickWidth"]) ? $Format["SkippedOuterTickWidth"] : 2; + $ScaleModeAuto = isset($Format["ScaleModeAuto"]) ? $Format["ScaleModeAuto"] : FALSE; + $LabelSkipOffset = isset($Format["LabelSkipOffset"]) ? $Format["LabelSkipOffset"] : 0; + $LabelShowBoundaries = isset($Format["LabelShowBoundaries"]) ? $Format["LabelShowBoundaries"] : FALSE; + $DrawXBoundaryLines = isset($Format["DrawXBoundaryLines"]) ? $Format["DrawXBoundaryLines"] : FALSE; + $ScaleModeAutoGridInterval = isset($Format["ScaleModeAutoGridInterval"]) ? $Format["ScaleModeAutoGridInterval"] : 0; + $ScaleModeAutoGridLabel = isset($Format["ScaleModeAutoGridLabel"]) ? $Format["ScaleModeAutoGridLabel"] : FALSE; /* Floating scale require X & Y margins to be set manually */ if ( $Floating && ( $XMargin == AUTO || $YMargin == 0 ) ) { $Floating = FALSE; } @@ -1884,8 +1890,10 @@ { $Points = 0; $AxisName = isset($Data["XAxisName"]) ? $Data["XAxisName"] : NULL; - foreach($Data["Series"] as $SerieID => $SerieParameter) - { if ( $SerieParameter["isDrawable"] ) { $Points = max($Points,count($SerieParameter["Data"])); } } + if (count($Data["Series"]) > 0) { + foreach($Data["Series"] as $SerieID => $SerieParameter) + { if ( $SerieParameter["isDrawable"] ) { $Points = max($Points,count($SerieParameter["Data"])); } } + } } $AxisID = count($Data["Axis"]); @@ -1901,7 +1909,7 @@ if ( $Points == 1 ) $Data["Axis"][$AxisID]["Margin"] = $Height / 2; - else + else if ($Points > 1) $Data["Axis"][$AxisID]["Margin"] = ($Height/$Points) / 2; } else @@ -1938,6 +1946,24 @@ { if ( $Pos == SCALE_POS_LEFTRIGHT ) { + $Width = ($this->GraphAreaX2 - $this->GraphAreaX1) - $Parameters["Margin"]*2; + + if ( $ScaleModeAuto && isset($Data["Series"][$Abscissa]["Data"][0]) ) { + $AbscissaScaleValueMin = $Data["Series"][$Abscissa]["Data"][0]; + } else { + $AbscissaScaleValueMin = 0; + } + if ( $ScaleModeAuto && isset($Data["Series"][$Abscissa]["Data"][$Parameters["Rows"]]) ) { + $AbscissaScaleValueMax = $Data["Series"][$Abscissa]["Data"][$Parameters["Rows"]]; + } else { + $AbscissaScaleValueMax = $Width; + } + $AbscissaScaleValueWidth = $AbscissaScaleValueMax - $AbscissaScaleValueMin; + if ($AbscissaScaleValueWidth > 0) + $AbscissaScaleFactor = $AbscissaScaleValueWidth / $Width; + else + $AbscissaScaleFactor = 1; + if ( $Parameters["Position"] == AXIS_POSITION_BOTTOM ) { if ( $LabelRotation == 0 ) { $LabelAlign = TEXT_ALIGN_TOPMIDDLE; $YLabelOffset = 2; } @@ -1951,15 +1977,23 @@ { $FloatingOffset = 0; $this->drawLine($this->GraphAreaX1,$AxisPos["B"],$this->GraphAreaX2,$AxisPos["B"],array("R"=>$AxisR,"G"=>$AxisG,"B"=>$AxisB,"Alpha"=>$AxisAlpha)); } if ( $DrawArrows ) { $this->drawArrow($this->GraphAreaX2-$Parameters["Margin"],$AxisPos["B"],$this->GraphAreaX2+($ArrowSize*2),$AxisPos["B"],array("FillR"=>$AxisR,"FillG"=>$AxisG,"FillB"=>$AxisB,"Size"=>$ArrowSize)); } - - $Width = ($this->GraphAreaX2 - $this->GraphAreaX1) - $Parameters["Margin"]*2; - + if ($Parameters["Rows"] == 0 ) { $Step = $Width; } else { $Step = $Width / ($Parameters["Rows"]); } - + $MaxBottom = $AxisPos["B"]; for($i=0;$i<=$Parameters["Rows"];$i++) { - $XPos = $this->GraphAreaX1 + $Parameters["Margin"] + $Step*$i; + if ( $ScaleModeAuto && $Abscissa != NULL ) { + if ( isset($Data["Series"][$Abscissa]["Data"][$i]) ) { + $AbscissaScaleValue = $Data["Series"][$Abscissa]["Data"][$i] - $AbscissaScaleValueMin; + $XPos = $this->GraphAreaX1 + $Parameters["Margin"] + $AbscissaScaleValue / $AbscissaScaleFactor; + } else { + $XPos = $this->GraphAreaX1 + $Parameters["Margin"] + $Step*$i; + } + } else { + $XPos = $this->GraphAreaX1 + $Parameters["Margin"] + $Step*$i; + } + $YPos = $AxisPos["B"]; if ( $Abscissa != NULL ) @@ -1973,7 +2007,7 @@ } $ID++; $Skipped = TRUE; - if ( $this->isValidLabel($Value,$LastValue,$LabelingMethod,$ID,$LabelSkip) ) + if ( ($LabelShowBoundaries && ($i==0 || $i==$Parameters["Rows"])) || $this->isValidLabel($Value,$LastValue,$LabelingMethod,$ID,$LabelSkip,$LabelSkipOffset) ) { $Bounds = $this->drawText($XPos,$YPos+$OuterTickWidth+$YLabelOffset,$Value,array("FontSize"=>$TicksFontSize,"Angle"=>$LabelRotation,"Align"=>$LabelAlign)); $TxtBottom = $YPos+$OuterTickWidth+2+($Bounds[0]["Y"]-$Bounds[2]["Y"]); @@ -1981,7 +2015,12 @@ $LastValue = $Value; $Skipped = FALSE; } - + + if ( $DrawXBoundaryLines && ($i==0 || $i==$Parameters["Rows"]) ) + { + $this->drawLine($XPos,$this->GraphAreaY1+$FloatingOffset,$XPos,$this->GraphAreaY2-$FloatingOffset,array("R"=>$GridR,"G"=>$GridG,"B"=>$GridB,"Alpha"=>$GridAlpha,"Ticks"=>$GridTicks)); + } + if ( $Skipped ) { if ( $DrawXLines ) { $this->drawLine($XPos,$this->GraphAreaY1+$FloatingOffset,$XPos,$this->GraphAreaY2-$FloatingOffset,$SkippedAxisColor); } @@ -2002,6 +2041,31 @@ } } + + if ( $ScaleModeAuto && ($ScaleModeAutoGridInterval > 0) ) + { + $ScaleModeAutoGridIntervalDraw = true; + $ScaleModeAutoGridIntervalStep = 0; + + while ($ScaleModeAutoGridIntervalDraw) { + $ScaleModeAutoGridIntervalStep++; + $ScaleModeAutoGridIntervalValue = $ScaleModeAutoGridIntervalStep*$ScaleModeAutoGridInterval; + + if ($ScaleModeAutoGridIntervalValue < ($AbscissaScaleValueMax-$AbscissaScaleValueMin)) { + $ScaleModeAutoGridIntervalX = $this->GraphAreaX1 + $Parameters["Margin"] + $ScaleModeAutoGridIntervalValue / $AbscissaScaleFactor; + $this->drawLine($ScaleModeAutoGridIntervalX,$this->GraphAreaY1+$FloatingOffset,$ScaleModeAutoGridIntervalX,$this->GraphAreaY2-$FloatingOffset,array("R"=>$GridR,"G"=>$GridG,"B"=>$GridB,"Alpha"=>$GridAlpha,"Ticks"=>$GridTicks)); + + if ($ScaleModeAutoGridLabel) { + $ScaleModeAutoGridLabelDescription = $this->scaleFormat($AbscissaScaleValueMin+$ScaleModeAutoGridIntervalValue,$Data["XAxisDisplay"],$Data["XAxisFormat"],""); + + $this->drawText($ScaleModeAutoGridIntervalX,$this->GraphAreaY1+$FloatingOffset+5,$ScaleModeAutoGridLabelDescription,array("Align"=>TEXT_ALIGN_MIDDLERIGHT,"Angle"=>90,"R"=>$GridR,"G"=>$GridG,"B"=>$GridB,"Alpha"=>floor($GridAlpha*2))); + } + } else { + $ScaleModeAutoGridIntervalDraw = false; + } + } + } + if ( isset($Parameters["Name"]) ) { $YPos = $MaxBottom+2; @@ -2027,15 +2091,22 @@ { $FloatingOffset = 0; $this->drawLine($this->GraphAreaX1,$AxisPos["T"],$this->GraphAreaX2,$AxisPos["T"],array("R"=>$AxisR,"G"=>$AxisG,"B"=>$AxisB,"Alpha"=>$AxisAlpha)); } if ( $DrawArrows ) { $this->drawArrow($this->GraphAreaX2-$Parameters["Margin"],$AxisPos["T"],$this->GraphAreaX2+($ArrowSize*2),$AxisPos["T"],array("FillR"=>$AxisR,"FillG"=>$AxisG,"FillB"=>$AxisB,"Size"=>$ArrowSize)); } - - $Width = ($this->GraphAreaX2 - $this->GraphAreaX1) - $Parameters["Margin"]*2; - + if ($Parameters["Rows"] == 0 ) { $Step = $Width; } else { $Step = $Width / $Parameters["Rows"]; } $MinTop = $AxisPos["T"]; for($i=0;$i<=$Parameters["Rows"];$i++) { - $XPos = $this->GraphAreaX1 + $Parameters["Margin"] + $Step*$i; + if ( $ScaleModeAuto && $Abscissa != NULL ) { + if ( isset($Data["Series"][$Abscissa]["Data"][$i]) ) { + $AbscissaScaleValue = $Data["Series"][$Abscissa]["Data"][$i] - $AbscissaScaleValueMin; + $XPos = $this->GraphAreaX1 + $Parameters["Margin"] + $AbscissaScaleValue / $AbscissaScaleFactor; + } else { + $XPos = $this->GraphAreaX1 + $Parameters["Margin"] + $Step*$i; + } + } else { + $XPos = $this->GraphAreaX1 + $Parameters["Margin"] + $Step*$i; + } $YPos = $AxisPos["T"]; if ( $Abscissa != NULL ) @@ -2049,7 +2120,7 @@ } $ID++; $Skipped = TRUE; - if ( $this->isValidLabel($Value,$LastValue,$LabelingMethod,$ID,$LabelSkip) ) + if ( ($LabelShowBoundaries && ($i==0 || $i==$Parameters["Rows"])) || $this->isValidLabel($Value,$LastValue,$LabelingMethod,$ID,$LabelSkip,$LabelSkipOffset) ) { $Bounds = $this->drawText($XPos,$YPos-$OuterTickWidth-$YLabelOffset,$Value,array("Angle"=>$LabelRotation,"Align"=>$LabelAlign)); $TxtBox = $YPos-$OuterTickWidth-2-($Bounds[0]["Y"]-$Bounds[2]["Y"]); @@ -2057,6 +2128,11 @@ $LastValue = $Value; $Skipped = FALSE; } + + if ( $DrawXBoundaryLines && ($i==0 || $i==$Parameters["Rows"]) ) + { + $this->drawLine($XPos,$this->GraphAreaY1+$FloatingOffset,$XPos,$this->GraphAreaY2-$FloatingOffset,array("R"=>$GridR,"G"=>$GridG,"B"=>$GridB,"Alpha"=>$GridAlpha,"Ticks"=>$GridTicks)); + } if ( $Skipped ) { @@ -2070,6 +2146,31 @@ } } + + if ( $ScaleModeAuto && ($ScaleModeAutoGridInterval > 0) ) + { + $ScaleModeAutoGridIntervalDraw = true; + $ScaleModeAutoGridIntervalStep = 0; + + while ($ScaleModeAutoGridIntervalDraw) { + $ScaleModeAutoGridIntervalStep++; + $ScaleModeAutoGridIntervalValue = $ScaleModeAutoGridIntervalStep*$ScaleModeAutoGridInterval; + + if ($ScaleModeAutoGridIntervalValue < ($AbscissaScaleValueMax-$AbscissaScaleValueMin)) { + $ScaleModeAutoGridIntervalX = $this->GraphAreaX1 + $Parameters["Margin"] + $ScaleModeAutoGridIntervalValue / $AbscissaScaleFactor; + $this->drawLine($ScaleModeAutoGridIntervalX,$this->GraphAreaY1+$FloatingOffset,$ScaleModeAutoGridIntervalX,$this->GraphAreaY2-$FloatingOffset,array("R"=>$GridR,"G"=>$GridG,"B"=>$GridB,"Alpha"=>$GridAlpha,"Ticks"=>$GridTicks)); + + if ($ScaleModeAutoGridLabel) { + $ScaleModeAutoGridLabelDescription = $this->scaleFormat($AbscissaScaleValueMin+$ScaleModeAutoGridIntervalValue,$Data["XAxisDisplay"],$Data["XAxisFormat"],""); + + $this->drawText($ScaleModeAutoGridIntervalX,$this->GraphAreaY1+$FloatingOffset+5,$ScaleModeAutoGridLabelDescription,array("Align"=>TEXT_ALIGN_MIDDLERIGHT,"Angle"=>90,"R"=>$GridR,"G"=>$GridG,"B"=>$GridB,"Alpha"=>floor($GridAlpha*2))); + } + } else { + $ScaleModeAutoGridIntervalDraw = false; + } + } + } + if ( isset($Parameters["Name"]) ) { $YPos = $MinTop-2; @@ -2085,6 +2186,21 @@ } elseif ( $Pos == SCALE_POS_TOPBOTTOM ) { + $Height = ($this->GraphAreaY2 - $this->GraphAreaY1) - $Parameters["Margin"]*2; + + if ( $ScaleModeAuto && isset($Data["Series"][$Abscissa]["Data"][0]) ) { + $AbscissaScaleValueMin = $Data["Series"][$Abscissa]["Data"][0]; + } else { + $AbscissaScaleValueMin = 0; + } + if ( $ScaleModeAuto && isset($Data["Series"][$Abscissa]["Data"][$Parameters["Rows"]]) ) { + $AbscissaScaleValueMax = $Data["Series"][$Abscissa]["Data"][$Parameters["Rows"]]; + } else { + $AbscissaScaleValueMax = $Height; + } + $AbscissaScaleValueWidth = $AbscissaScaleValueMax - $AbscissaScaleValueMin; + $AbscissaScaleFactor = $AbscissaScaleValueWidth / $Height; + if ( $Parameters["Position"] == AXIS_POSITION_LEFT ) { if ( $LabelRotation == 0 ) { $LabelAlign = TEXT_ALIGN_MIDDLERIGHT; $XLabelOffset = -2; } @@ -2098,15 +2214,22 @@ { $FloatingOffset = 0; $this->drawLine($AxisPos["L"],$this->GraphAreaY1,$AxisPos["L"],$this->GraphAreaY2,array("R"=>$AxisR,"G"=>$AxisG,"B"=>$AxisB,"Alpha"=>$AxisAlpha)); } if ( $DrawArrows ) { $this->drawArrow($AxisPos["L"],$this->GraphAreaY2-$Parameters["Margin"],$AxisPos["L"],$this->GraphAreaY2+($ArrowSize*2),array("FillR"=>$AxisR,"FillG"=>$AxisG,"FillB"=>$AxisB,"Size"=>$ArrowSize)); } - - $Height = ($this->GraphAreaY2 - $this->GraphAreaY1) - $Parameters["Margin"]*2; - + if ($Parameters["Rows"] == 0 ) { $Step = $Height; } else { $Step = $Height / $Parameters["Rows"]; } $MinLeft = $AxisPos["L"]; for($i=0;$i<=$Parameters["Rows"];$i++) { - $YPos = $this->GraphAreaY1 + $Parameters["Margin"] + $Step*$i; + if ( $ScaleModeAuto && $Abscissa != NULL ) { + if ( isset($Data["Series"][$Abscissa]["Data"][$i]) ) { + $AbscissaScaleValue = $Data["Series"][$Abscissa]["Data"][$i] - $AbscissaScaleValueMin; + $YPos = $this->GraphAreaY1 + $Parameters["Margin"] + $AbscissaScaleValue / $AbscissaScaleFactor; + } else { + $YPos = $this->GraphAreaY1 + $Parameters["Margin"] + $Step*$i; + } + } else { + $YPos = $this->GraphAreaY1 + $Parameters["Margin"] + $Step*$i; + } $XPos = $AxisPos["L"]; if ( $Abscissa != NULL ) @@ -2120,7 +2243,7 @@ } $ID++; $Skipped = TRUE; - if ( $this->isValidLabel($Value,$LastValue,$LabelingMethod,$ID,$LabelSkip) ) + if ( ($LabelShowBoundaries && ($i==0 || $i==$Parameters["Rows"])) || $this->isValidLabel($Value,$LastValue,$LabelingMethod,$ID,$LabelSkip,$LabelSkipOffset) ) { $Bounds = $this->drawText($XPos-$OuterTickWidth+$XLabelOffset,$YPos,$Value,array("Angle"=>$LabelRotation,"Align"=>$LabelAlign)); $TxtBox = $XPos-$OuterTickWidth-2-($Bounds[1]["X"]-$Bounds[0]["X"]); @@ -2128,6 +2251,11 @@ $LastValue = $Value; $Skipped = FALSE; } + + if ( $DrawXBoundaryLines && ($i==0 || $i==$Parameters["Rows"]) ) + { + $this->drawLine($this->GraphAreaX1+$FloatingOffset,$YPos,$this->GraphAreaX2-$FloatingOffset,$YPos,array("R"=>$GridR,"G"=>$GridG,"B"=>$GridB,"Alpha"=>$GridAlpha,"Ticks"=>$GridTicks)); + } if ( $Skipped ) { @@ -2141,6 +2269,31 @@ } } + + if ( $ScaleModeAuto && ($ScaleModeAutoGridInterval > 0) ) + { + $ScaleModeAutoGridIntervalDraw = true; + $ScaleModeAutoGridIntervalStep = 0; + + while ($ScaleModeAutoGridIntervalDraw) { + $ScaleModeAutoGridIntervalStep++; + $ScaleModeAutoGridIntervalValue = $ScaleModeAutoGridIntervalStep*$ScaleModeAutoGridInterval; + + if ($ScaleModeAutoGridIntervalValue < ($AbscissaScaleValueMax-$AbscissaScaleValueMin)) { + $ScaleModeAutoGridIntervalY = $this->GraphAreaY1 + $Parameters["Margin"] + $ScaleModeAutoGridIntervalValue / $AbscissaScaleFactor; + $this->drawLine($this->GraphAreaX1+$FloatingOffset,$ScaleModeAutoGridIntervalY,$this->GraphAreaX2-$FloatingOffset,$ScaleModeAutoGridIntervalY,array("R"=>$GridR,"G"=>$GridG,"B"=>$GridB,"Alpha"=>$GridAlpha,"Ticks"=>$GridTicks)); + + if ($ScaleModeAutoGridLabel) { + $ScaleModeAutoGridLabelDescription = $this->scaleFormat($AbscissaScaleValueMin+$ScaleModeAutoGridIntervalValue,$Data["XAxisDisplay"],$Data["XAxisFormat"],""); + + $this->drawText($this->GraphAreaX2+$FloatingOffset-5,$ScaleModeAutoGridIntervalY,$ScaleModeAutoGridLabelDescription,array("Align"=>TEXT_ALIGN_MIDDLERIGHT,"Angle"=>0,"R"=>$GridR,"G"=>$GridG,"B"=>$GridB,"Alpha"=>floor($GridAlpha*2))); + } + } else { + $ScaleModeAutoGridIntervalDraw = false; + } + } + } + if ( isset($Parameters["Name"]) ) { $XPos = $MinLeft-2; @@ -2167,14 +2320,21 @@ if ( $DrawArrows ) { $this->drawArrow($AxisPos["R"],$this->GraphAreaY2-$Parameters["Margin"],$AxisPos["R"],$this->GraphAreaY2+($ArrowSize*2),array("FillR"=>$AxisR,"FillG"=>$AxisG,"FillB"=>$AxisB,"Size"=>$ArrowSize)); } - $Height = ($this->GraphAreaY2 - $this->GraphAreaY1) - $Parameters["Margin"]*2; - if ($Parameters["Rows"] == 0 ) { $Step = $Height; } else { $Step = $Height / $Parameters["Rows"]; } $MaxRight = $AxisPos["R"]; for($i=0;$i<=$Parameters["Rows"];$i++) { - $YPos = $this->GraphAreaY1 + $Parameters["Margin"] + $Step*$i; + if ( $ScaleModeAuto && $Abscissa != NULL ) { + if ( isset($Data["Series"][$Abscissa]["Data"][$i]) ) { + $AbscissaScaleValue = $Data["Series"][$Abscissa]["Data"][$i] - $AbscissaScaleValueMin; + $YPos = $this->GraphAreaY1 + $Parameters["Margin"] + $AbscissaScaleValue / $AbscissaScaleFactor; + } else { + $YPos = $this->GraphAreaY1 + $Parameters["Margin"] + $Step*$i; + } + } else { + $YPos = $this->GraphAreaY1 + $Parameters["Margin"] + $Step*$i; + } $XPos = $AxisPos["R"]; if ( $Abscissa != NULL ) @@ -2188,7 +2348,7 @@ } $ID++; $Skipped = TRUE; - if ( $this->isValidLabel($Value,$LastValue,$LabelingMethod,$ID,$LabelSkip) ) + if ( ($LabelShowBoundaries && ($i==0 || $i==$Parameters["Rows"])) || $this->isValidLabel($Value,$LastValue,$LabelingMethod,$ID,$LabelSkip,$LabelSkipOffset) ) { $Bounds = $this->drawText($XPos+$OuterTickWidth+$XLabelOffset,$YPos,$Value,array("Angle"=>$LabelRotation,"Align"=>$LabelAlign)); $TxtBox = $XPos+$OuterTickWidth+2+($Bounds[1]["X"]-$Bounds[0]["X"]); @@ -2196,6 +2356,11 @@ $LastValue = $Value; $Skipped = FALSE; } + + if ( $DrawXBoundaryLines && ($i==0 || $i==$Parameters["Rows"]) ) + { + $this->drawLine($this->GraphAreaX1+$FloatingOffset,$YPos,$this->GraphAreaX2-$FloatingOffset,$YPos,array("R"=>$GridR,"G"=>$GridG,"B"=>$GridB,"Alpha"=>$GridAlpha,"Ticks"=>$GridTicks)); + } if ( $Skipped ) { @@ -2209,6 +2374,31 @@ } } + + if ( $ScaleModeAuto && ($ScaleModeAutoGridInterval > 0) ) + { + $ScaleModeAutoGridIntervalDraw = true; + $ScaleModeAutoGridIntervalStep = 0; + + while ($ScaleModeAutoGridIntervalDraw) { + $ScaleModeAutoGridIntervalStep++; + $ScaleModeAutoGridIntervalValue = $ScaleModeAutoGridIntervalStep*$ScaleModeAutoGridInterval; + + if ($ScaleModeAutoGridIntervalValue < ($AbscissaScaleValueMax-$AbscissaScaleValueMin)) { + $ScaleModeAutoGridIntervalY = $this->GraphAreaY1 + $Parameters["Margin"] + $ScaleModeAutoGridIntervalValue / $AbscissaScaleFactor; + $this->drawLine($this->GraphAreaX1+$FloatingOffset,$ScaleModeAutoGridIntervalY,$this->GraphAreaX2-$FloatingOffset,$ScaleModeAutoGridIntervalY,array("R"=>$GridR,"G"=>$GridG,"B"=>$GridB,"Alpha"=>$GridAlpha,"Ticks"=>$GridTicks)); + + if ($ScaleModeAutoGridLabel) { + $ScaleModeAutoGridLabelDescription = $this->scaleFormat($AbscissaScaleValueMin+$ScaleModeAutoGridIntervalValue,$Data["XAxisDisplay"],$Data["XAxisFormat"],""); + + $this->drawText($this->GraphAreaX2+$FloatingOffset-5,$ScaleModeAutoGridIntervalY,$ScaleModeAutoGridLabelDescription,array("Align"=>TEXT_ALIGN_MIDDLERIGHT,"Angle"=>0,"R"=>$GridR,"G"=>$GridG,"B"=>$GridB,"Alpha"=>floor($GridAlpha*2))); + } + } else { + $ScaleModeAutoGridIntervalDraw = false; + } + } + } + if ( isset($Parameters["Name"]) ) { $XPos = $MaxRight+4; @@ -2433,12 +2623,12 @@ } } - function isValidLabel($Value,$LastValue,$LabelingMethod,$ID,$LabelSkip) + function isValidLabel($Value,$LastValue,$LabelingMethod,$ID,$LabelSkip,$LabelSkipOffset=0) { if ( $LabelingMethod == LABELING_DIFFERENT && $Value != $LastValue ) { return(TRUE); } if ( $LabelingMethod == LABELING_DIFFERENT && $Value == $LastValue ) { return(FALSE); } - if ( $LabelingMethod == LABELING_ALL && $LabelSkip == 1 ) { return(TRUE); } - if ( $LabelingMethod == LABELING_ALL && ($ID+$LabelSkip) % ($LabelSkip+1) != 1 ) { return(FALSE); } + if ( $LabelingMethod == LABELING_ALL && $LabelSkip == 0 ) { return(TRUE); } + if ( $LabelingMethod == LABELING_ALL && ($ID+$LabelSkip+$LabelSkipOffset) % ($LabelSkip+1) != 1 ) { return(FALSE); } return(TRUE); } @@ -3456,6 +3646,7 @@ $DisplayR = isset($Format["DisplayR"]) ? $Format["DisplayR"] : 0; $DisplayG = isset($Format["DisplayG"]) ? $Format["DisplayG"] : 0; $DisplayB = isset($Format["DisplayB"]) ? $Format["DisplayB"] : 0; + $ScaleModeAuto = isset($Format["ScaleModeAuto"]) ? $Format["ScaleModeAuto"] : FALSE; $Data = $this->DataSet->getData(); list($XMargin,$XDivs) = $this->scaleGetXSettings(); @@ -3481,7 +3672,32 @@ $PosArray = $this->scaleComputeY($Serie["Data"],array("AxisID"=>$Serie["Axis"])); $this->DataSet->Data["Series"][$SerieName]["XOffset"] = 0; - + + $Width = $this->GraphAreaX2-$this->GraphAreaX1-$XMargin*2; + $Height = $this->GraphAreaY2-$this->GraphAreaY1-$XMargin*2; + if ( $ScaleModeAuto && isset($Data["Series"][$Data["Abscissa"]]["Data"][0]) ) { + $AbscissaScaleValueMin = $Data["Series"][$Data["Abscissa"]]["Data"][0]; + } else { + $AbscissaScaleValueMin = 0; + } + if ( $ScaleModeAuto && isset($Data["Series"][$Data["Abscissa"]]["Data"][$XDivs]) ) { + $AbscissaScaleValueMax = $Data["Series"][$Data["Abscissa"]]["Data"][$XDivs]; + } else { + $AbscissaScaleValueMax = $Width; + } + $AbscissaScaleValueWidth = $AbscissaScaleValueMax - $AbscissaScaleValueMin; + if ( $Data["Orientation"] == SCALE_POS_LEFTRIGHT ) { + if ($AbscissaScaleValueWidth > 0) + $AbscissaScaleFactor = $AbscissaScaleValueWidth / $Width; + else + $AbscissaScaleFactor = 1; + } else { + if ($AbscissaScaleValueWidth > 0) + $AbscissaScaleFactor = $AbscissaScaleValueWidth / $Height; + else + $AbscissaScaleFactor = 1; + } + if ( $Data["Orientation"] == SCALE_POS_LEFTRIGHT ) { if ( $XDivs == 0 ) { $XStep = ($this->GraphAreaX2-$this->GraphAreaX1)/4; } else { $XStep = ($this->GraphAreaX2-$this->GraphAreaX1-$XMargin*2)/$XDivs; } @@ -3491,6 +3707,11 @@ $LastGoodY = NULL; $LastGoodX = NULL; $Init = FALSE; foreach($PosArray as $Key => $Y) { + if ($ScaleModeAuto) { + $AbscissaScaleValue = $Data["Series"][$Data["Abscissa"]]["Data"][$Key] - $AbscissaScaleValueMin; + $X = $this->GraphAreaX1 + $XMargin + $AbscissaScaleValue / $AbscissaScaleFactor; + } + if ( $DisplayValues && $Serie["Data"][$Key] != VOID ) { if ( $Y <= $LastY ) { $Align = TEXT_ALIGN_BOTTOMMIDDLE; $Offset = $DisplayOffset; } else { $Align = TEXT_ALIGN_TOPMIDDLE; $Offset = -$DisplayOffset; } @@ -3523,7 +3744,10 @@ if ( !$Init && $ReCenter ) { $X = $X - $XStep/2; $Init = TRUE; } $LastX = $X; $LastY = $Y; if ( $LastX < $this->GraphAreaX1 + $XMargin ) { $LastX = $this->GraphAreaX1 + $XMargin; } - $X = $X + $XStep; + + if (!$ScaleModeAuto) { + $X = $X + $XStep; + } } if ( $ReCenter ) { $this->drawLine($LastX,$LastY,$this->GraphAreaX2 - $XMargin,$LastY,$Color); } } @@ -3536,6 +3760,11 @@ $LastGoodY = NULL; $LastGoodX = NULL; $Init = FALSE; foreach($PosArray as $Key => $X) { + if ($ScaleModeAuto) { + $AbscissaScaleValue = $Data["Series"][$Data["Abscissa"]]["Data"][$Key] - $AbscissaScaleValueMin; + $Y = $this->GraphAreaY1 + $XMargin + $AbscissaScaleValue / $AbscissaScaleFactor; + } + if ( $DisplayValues && $Serie["Data"][$Key] != VOID ) { if ( $X >= $LastX ) { $Align = TEXT_ALIGN_MIDDLELEFT; $Offset = $DisplayOffset; } else { $Align = TEXT_ALIGN_MIDDLERIGHT; $Offset = -$DisplayOffset; } @@ -3565,7 +3794,10 @@ if ( !$Init && $ReCenter ) { $Y = $Y - $YStep/2; $Init = TRUE; } $LastX = $X; $LastY = $Y; if ( $LastY < $this->GraphAreaY1 + $XMargin ) { $LastY = $this->GraphAreaY1 + $XMargin; } - $Y = $Y + $YStep; + + if (!$ScaleModeAuto) { + $Y = $Y + $YStep; + } } if ( $ReCenter ) { $this->drawLine($LastX,$LastY,$LastX,$this->GraphAreaY2 - $XMargin,$Color); } } @@ -3585,6 +3817,7 @@ $DisplayG = isset($Format["DisplayG"]) ? $Format["DisplayG"] : 0; $DisplayB = isset($Format["DisplayB"]) ? $Format["DisplayB"] : 0; $AroundZero = isset($Format["AroundZero"]) ? $Format["AroundZero"] : TRUE; + $ScaleModeAuto = isset($Format["ScaleModeAuto"]) ? $Format["ScaleModeAuto"] : FALSE; $Data = $this->DataSet->getData(); list($XMargin,$XDivs) = $this->scaleGetXSettings(); @@ -3608,6 +3841,31 @@ $YZero = $this->scaleComputeY(0,array("AxisID"=>$Serie["Axis"])); $this->DataSet->Data["Series"][$SerieName]["XOffset"] = 0; + + $Width = $this->GraphAreaX2-$this->GraphAreaX1-$XMargin*2; + $Height = $this->GraphAreaY2-$this->GraphAreaY1-$XMargin*2; + if ( $ScaleModeAuto && isset($Data["Series"][$Data["Abscissa"]]["Data"][0]) ) { + $AbscissaScaleValueMin = $Data["Series"][$Data["Abscissa"]]["Data"][0]; + } else { + $AbscissaScaleValueMin = 0; + } + if ( $ScaleModeAuto && isset($Data["Series"][$Data["Abscissa"]]["Data"][$XDivs]) ) { + $AbscissaScaleValueMax = $Data["Series"][$Data["Abscissa"]]["Data"][$XDivs]; + } else { + $AbscissaScaleValueMax = $Width; + } + $AbscissaScaleValueWidth = $AbscissaScaleValueMax - $AbscissaScaleValueMin; + if ( $Data["Orientation"] == SCALE_POS_LEFTRIGHT ) { + if ($AbscissaScaleValueWidth > 0) + $AbscissaScaleFactor = $AbscissaScaleValueWidth / $Width; + else + $AbscissaScaleFactor = 1; + } else { + if ($AbscissaScaleValueWidth > 0) + $AbscissaScaleFactor = $AbscissaScaleValueWidth / $Height; + else + $AbscissaScaleFactor = 1; + } if ( $Data["Orientation"] == SCALE_POS_LEFTRIGHT ) { @@ -3623,6 +3881,11 @@ $LastGoodY = NULL; $LastGoodX = NULL; $Points = ""; $Init = FALSE; foreach($PosArray as $Key => $Y) { + if ($ScaleModeAuto) { + $AbscissaScaleValue = $Data["Series"][$Data["Abscissa"]]["Data"][$Key] - $AbscissaScaleValueMin; + $X = $this->GraphAreaX1 + $XMargin + $AbscissaScaleValue / $AbscissaScaleFactor; + } + if ( $Y == VOID && $LastX != NULL && $LastY != NULL && $Points !="" ) { $Points[] = $LastX; $Points[] = $LastY; @@ -3646,7 +3909,10 @@ if ( !$Init && $ReCenter ) { $X = $X - $XStep/2; $Init = TRUE; } $LastX = $X; $LastY = $Y; if ( $LastX < $this->GraphAreaX1 + $XMargin ) { $LastX = $this->GraphAreaX1 + $XMargin; } - $X = $X + $XStep; + + if (!$ScaleModeAuto) { + $X = $X + $XStep; + } } if ( $ReCenter ) @@ -3671,6 +3937,11 @@ $LastGoodY = NULL; $LastGoodX = NULL; $Points = ""; foreach($PosArray as $Key => $X) { + if ($ScaleModeAuto) { + $AbscissaScaleValue = $Data["Series"][$Data["Abscissa"]]["Data"][$Key] - $AbscissaScaleValueMin; + $Y = $this->GraphAreaY1 + $XMargin + $AbscissaScaleValue / $AbscissaScaleFactor; + } + if ( $X == VOID && $LastX != NULL && $LastY != NULL && $Points !="" ) { $Points[] = $LastX; $Points[] = $LastY; @@ -3694,7 +3965,10 @@ if ( $LastX == NULL && $ReCenter ) { $Y = $Y - $YStep/2; } $LastX = $X; $LastY = $Y; if ( $LastY < $this->GraphAreaY1 + $XMargin ) { $LastY = $this->GraphAreaY1 + $XMargin; } - $Y = $Y + $YStep; + + if (!$ScaleModeAuto) { + $Y = $Y + $YStep; + } } if ( $ReCenter ) diff --git a/pandora_console/include/graphs/pandora.d3.js b/pandora_console/include/graphs/pandora.d3.js index 5c2f2e0c10..dde73526ef 100644 --- a/pandora_console/include/graphs/pandora.d3.js +++ b/pandora_console/include/graphs/pandora.d3.js @@ -1024,41 +1024,40 @@ function createGauge(name, etiqueta, value, min, max, min_warning,max_warning,wa function createGauges(data, width, height, font_size, no_data_image, font) { var nombre,label,minimun_warning,maximun_warning,minimun_critical,maximun_critical, mininum,maxinum,valor; - + for (key in data) { nombre = data[key].gauge; - + label = data[key].label; - + label = label.replace(/ /g,' '); label = label.replace(/\(/g,'\('); label = label.replace(/\)/g,'\)'); - + label = label.replace(/(/g,'\('); label = label.replace(/)/g,'\)'); - - minimun_warning = round_with_decimals(parseFloat( data[key].min_warning )); - maximun_warning = round_with_decimals(parseFloat( data[key].max_warning )); - minimun_critical = round_with_decimals(parseFloat( data[key].min_critical )); - maximun_critical = round_with_decimals(parseFloat( data[key].max_critical )); + + minimun_warning = round_with_decimals(parseFloat( data[key].min_warning )); + maximun_warning = round_with_decimals(parseFloat( data[key].max_warning )); + minimun_critical = round_with_decimals(parseFloat( data[key].min_critical )); + maximun_critical = round_with_decimals(parseFloat( data[key].max_critical )); mininum = round_with_decimals(parseFloat(data[key].min)); maxinum = round_with_decimals(parseFloat(data[key].max)); - + critical_inverse = parseInt(data[key].critical_inverse); warning_inverse = parseInt(data[key].warning_inverse); valor = round_with_decimals(data[key].value); + if (isNaN(valor)) valor = null; - createGauge(nombre, label, valor, mininum, maxinum, + createGauge(nombre, label, valor, mininum, maxinum, minimun_warning, maximun_warning, warning_inverse, minimun_critical, maximun_critical, critical_inverse, font_size, height, font); - } - } function createthermometers(data, width, height, font_size, no_data_image, font) { @@ -1487,8 +1486,10 @@ function Gauge(placeholderName, configuration, font) this.drawBand = function(start, end, color) { + if (start === undefined) return; + if (end === undefined) return; if (0 >= end - start) return; - + this.body.append("svg:path") .style("fill", color) .attr("d", d3.svg.arc() @@ -1498,13 +1499,13 @@ function Gauge(placeholderName, configuration, font) .outerRadius(Math.round(0.85 * this.config.raduis))) .attr("transform", function() { return "translate(" + self.config.cx + ", " + self.config.cy + ") rotate(270)" }); } - + this.redraw = function(value, transitionDuration) { var pointerContainer = this.body.select(".pointerContainer"); - + pointerContainer.selectAll("text").text(round_with_decimals(value)); - + var pointer = pointerContainer.selectAll("path"); pointer.transition() .duration(undefined != transitionDuration ? transitionDuration : this.config.transitionDuration) @@ -1519,7 +1520,7 @@ function Gauge(placeholderName, configuration, font) var targetRotation = (self.valueToDegrees(pointerValue) - 90); var currentRotation = self._currentRotation || targetRotation; self._currentRotation = targetRotation; - + return function(step) { var rotation = currentRotation + (targetRotation-currentRotation)*step; diff --git a/pandora_console/include/javascript/pandora.js b/pandora_console/include/javascript/pandora.js index 57bcfa82b9..3ad835115d 100644 --- a/pandora_console/include/javascript/pandora.js +++ b/pandora_console/include/javascript/pandora.js @@ -637,7 +637,6 @@ function post_process_select_init_unit(name,selected) { $('#' + name + '_select').change(function() { var value = $('#' + name + '_select').val(); $('#' + name + '_select option[value='+ value +']').attr("selected",true); - console.log(value); }); } @@ -1553,35 +1552,20 @@ function paint_graph_status(min_w, max_w, min_c, max_c, inverse_w, inverse_c, er } } -function round_with_decimals (value, multiplier = 1) { +function round_with_decimals(value, multiplier) { + // Default values + if (typeof(multiplier) === "undefined") multiplier = 1; + // Return non numeric types without modification if (typeof(value) !== "number") return value; if ((value * multiplier) == 0) return 0; - if ((value * multiplier) >= 1) { + if ((Math.abs(value) * multiplier) >= 1) { return Math.round(value * multiplier) / multiplier; } return round_with_decimals (value, multiplier * 10); } -/* - - $("body").append('

' + '' + '

'); - $("#event_delete_confirm_dialog").dialog({ - resizable: false, - draggable: false, - modal: true, - height: 280, - width: 330, - overlay: { - opacity: 0.5, - background: "black" - }, - closeOnEscape: false, - open: function(event, ui) { $(".ui-dialog-titlebar-close").hide(); } - }); -*/ - /** * Display a confirm dialog box * @@ -1590,12 +1574,7 @@ function round_with_decimals (value, multiplier = 1) { * @param string Cancel button text * @param function Callback to action when ok button is pressed */ -function display_confirm_dialog ( - message = '', - ok_text = '', - cancel_text = '', - ok_function = function () {} -) { +function display_confirm_dialog (message, ok_text, cancel_text, ok_function) { // Clean function to close the dialog var clean_function = function () { $("#pandora_confirm_dialog_text").hide(); diff --git a/pandora_console/include/styles/pandora.css b/pandora_console/include/styles/pandora.css index 9ad693a414..e90aec051e 100644 --- a/pandora_console/include/styles/pandora.css +++ b/pandora_console/include/styles/pandora.css @@ -4548,7 +4548,6 @@ form ul.form_flex li ul li{ width: 85%; height:100%; float:left; - } .status_animation { @@ -4577,7 +4576,6 @@ form ul.form_flex li ul li{ width: 15%; height:100%; float:left; - } #last_contact_date { @@ -4587,9 +4585,9 @@ form ul.form_flex li ul li{ margin-left:5%; text-align:center; display: flex; - justify-content: center; - align-content: center; - flex-direction: column; + justify-content: center; + align-content: center; + flex-direction: column; } #last_contact_date_text { @@ -4615,9 +4613,9 @@ form ul.form_flex li ul li{ width: 90%; height:100%; display: flex; - justify-content: center; - align-content: center; - flex-direction: column; + justify-content: center; + align-content: center; + flex-direction: column; } .events_bar { @@ -4631,11 +4629,13 @@ form ul.form_flex li ul li{ border: 0px; } +/* .yAxis.y1Axis > .tickLabel { white-space: nowrap; line-height: 1.05em!important; width: auto!important; } +*/ .pandora_confirm_dialog .ui-dialog-buttonset { display: flex; @@ -4664,4 +4664,4 @@ form ul.form_flex li ul li{ .dialog-grayed .ui-dialog-buttonpane { background: #373737 !important; -} \ No newline at end of file +} diff --git a/pandora_console/include/test.js b/pandora_console/include/test.js new file mode 100644 index 0000000000..e47d8a5e41 --- /dev/null +++ b/pandora_console/include/test.js @@ -0,0 +1,26 @@ +var webPage = require('webpage'); +var page = webPage.create(); +var url = "http://nova/pandora_console/operation/agentes/stat_win.php?type=sparse&period=86400&id=136574&label=QXJ0aWNhJiN4MjA7d2ViJiN4MjA7cGFnZSYjeDIwO2V4YW1wbGU%3D&refresh=600&draw_events=0"; + +var r = page.addCookie({ + 'name' : 'PHPSESSID', /* required property */ + 'value' : '23qu7l1sgb3iq3bkaedr724hp3', /* required property */ + 'path' : '/pandora_console', + 'domain' : 'nova', +}); + +console.log(r); + + + + + + + +page.viewportSize = { width: 750, height: 350 }; +page.open(url, function start(status) { + page.render('output.jpeg', {format: 'jpeg', quality: '100'}); + phantom.exit(); +}); + + diff --git a/pandora_console/include/web2image.js b/pandora_console/include/web2image.js new file mode 100644 index 0000000000..1dab3be266 --- /dev/null +++ b/pandora_console/include/web2image.js @@ -0,0 +1,60 @@ +var system = require('system'); + +if (system.args.length < 3 || system.args.length > 11) { + phantom.exit(1); +} + +var webPage = require('webpage'); +var page = webPage.create(); +var url = system.args[1]; +var type_graph_pdf = system.args[2]; +var url_params = system.args[3]; +var url_params_comb = system.args[4]; +var url_module_list = system.args[5]; +var output_filename = system.args[6]; +var _width = system.args[7]; +var _height = system.args[8]; +var session_id = system.args[9]; +var base_64 = system.args[10]; + + +if (!_width) { + _width = 750; +} + +if (!_height) { + _height = 350; +} + +if(type_graph_pdf == 'combined'){ + finish_url = url + "?" + "data=" + url_params + + "&data_combined=" + url_params_comb + + "&data_module_list=" + url_module_list + + "&type_graph_pdf=" + type_graph_pdf + + "&session_id=" + session_id; +} +else{ + finish_url = url + "?" + "data=" + url_params + + "&type_graph_pdf=" + type_graph_pdf + + "&session_id=" + session_id; +} + +page.viewportSize = { width: _width, height: _height }; +//page.zoomFactor = 1.75; + +page.open(finish_url, function start(status) { + +}); + +page.onLoadFinished = function (status) { + if(!base_64){ + page.render(output_filename, {format: 'png'}); + } + else{ + var base64 = page.renderBase64('png'); + console.log(base64); + } + phantom.exit(); +} + + diff --git a/pandora_console/mobile/include/style/main.css b/pandora_console/mobile/include/style/main.css index 09f1cf373e..67cd549c76 100755 --- a/pandora_console/mobile/include/style/main.css +++ b/pandora_console/mobile/include/style/main.css @@ -316,10 +316,6 @@ div.graph { margin: 0 auto; } -.tickLabels * { - font-size: 10px !important; -} - table.alternate tr:nth-child(odd) td, table.alternate tr:nth-child(odd) th{ background-color: #ffffff; } diff --git a/pandora_console/mobile/operation/module_graph.php b/pandora_console/mobile/operation/module_graph.php index d5335af1e1..31be54c7f8 100644 --- a/pandora_console/mobile/operation/module_graph.php +++ b/pandora_console/mobile/operation/module_graph.php @@ -15,7 +15,7 @@ class ModuleGraph { private $correct_acl = false; private $acl = "AR"; - + private $id = 0; private $id_agent = 0; private $graph_type = "sparse"; @@ -31,14 +31,14 @@ class ModuleGraph { private $unknown_graph = 1; private $zoom = 1; private $baseline = 0; - + private $module = null; - + function __construct() { $system = System::getInstance(); - + $this->start_date = date("Y-m-d"); - + if ($system->checkACL($this->acl)) { $this->correct_acl = true; } @@ -46,17 +46,17 @@ class ModuleGraph { $this->correct_acl = false; } } - + private function getFilters() { $system = System::getInstance(); - + $this->id = (int)$system->getRequest('id', 0); $this->id_agent = (int)$system->getRequest('id_agent', 0); $this->module = modules_get_agentmodule($this->id); $this->graph_type = return_graphtype($this->module["id_tipo_modulo"]); - + $period_hours = $system->getRequest('period_hours', false); - + if ($period_hours == false) { $this->period = SECONDS_1DAY; } @@ -78,29 +78,29 @@ class ModuleGraph { $this->unknown_graph = (int)$system->getRequest('unknown_graph', 0); $this->zoom = (int)$system->getRequest('zoom', 1); $this->baseline = (int)$system->getRequest('baseline', 0); - + $this->width = (int)$system->getRequest('width', 0); $this->width -= 20; //Correct the width $this->height = (int)$system->getRequest('height', 0); - + //Sancho says "put the height to 1/2 for to make more beautyful" $this->height = $this->height / 1.5; - + $this->height -= 80; //Correct the height - + //For to avoid fucking IPHONES when they are in horizontal. if ($this->height < 140) { $this->height = 140; } - + } - + public function ajax($parameter2 = false) { - + global $config; - + $system = System::getInstance(); - + if (!$this->correct_acl) { return; } @@ -110,23 +110,20 @@ class ModuleGraph { $this->getFilters(); $correct = 0; $graph = ''; - $correct = 1; - $label = $this->module["nombre"]; $unit = db_get_value('unit', 'tagente_modulo', 'id_agente_modulo', $this->id); - $utime = get_system_time (); $current = date("Y-m-d", $utime); - + if ($this->start_date != $current) $date = strtotime($this->start_date); else $date = $utime; - + $urlImage = ui_get_full_url(false); - + $time_compare = false; if ($this->time_compare_separated) { $time_compare = 'separated'; @@ -134,61 +131,33 @@ class ModuleGraph { else if ($this->time_compare_overlapped) { $time_compare = 'overlapped'; } - + ob_start(); switch ($this->graph_type) { case 'boolean': - $graph = grafico_modulo_boolean ( - $this->id, - $this->period, - $this->draw_events, - $this->width, - $this->height, - false, - $unit, - $this->draw_alerts, - $this->avg_only, - false, - $date, - false, - $urlImage, - 'adapter_' . $this->graph_type, - $time_compare, - $this->unknown_graph, false); - if ($this->draw_events) { - $graph .= '
'; - $graph .= graphic_module_events( - $this->id, - $this->width, $this->height, - $this->period, $config['homeurl'], - $this->zoom, - 'adapted_' . $this->graph_type, $date); - } - break; case 'sparse': - $graph = grafico_modulo_sparse( - $this->id, - $this->period, - $this->draw_events, - $this->width, - $this->height, - false, - null, - $this->draw_alerts, - $this->avg_only, - false, - $date, - $unit, - $this->baseline, - 0, - true, - false, - $urlImage, - 1, - false, - 'adapter_' . $this->graph_type, - $time_compare, $this->unknown_graph, false, - 'white', null, false, false, $config['type_module_charts']); + case 'string': + $params =array( + 'agent_module_id' => $this->id, + 'period' => $this->period, + 'show_events' => $this->draw_events, + 'width' => $this->width, + 'height' => $this->height, + 'show_alerts' => $this->draw_alerts, + 'avg_only' => $this->avg_only, + 'date' => $date, + 'unit' => $unit, + 'baseline' => $this->baseline, + 'homeurl' => $urlImage, + 'adapt_key' => 'adapter_' . $this->graph_type, + 'compare' => $time_compare, + 'show_unknown' => $this->unknown_graph, + 'menu' => false, + 'type_graph' => $config['type_module_charts'], + 'vconsole' => true + ); + + $graph = grafico_modulo_sparse($params); if ($this->draw_events) { $graph .= '
'; $graph .= graphic_module_events($this->id, @@ -197,64 +166,18 @@ class ModuleGraph { $this->zoom, 'adapted_'.$this->graph_type, $date); } break; - case 'string': - $graph = grafico_modulo_string( - $this->id, - $this->period, - $this->draw_events, - $this->width, - $this->height, - false, - null, - $this->draw_alerts, - 1, - false, - $date, - false, - $urlImage, - 'adapter_' . $this->graph_type, - 1, - false); - if ($this->draw_events) { - $graph .= '
'; - $graph .= graphic_module_events($this->id, - $this->width, $this->height, - $this->period, $config['homeurl'], - $this->zoom, 'adapted_' . $this->graph_type, $date); - } - break; - case 'log4x': - $graph = grafico_modulo_log4x( - $this->id, - $this->period, - $this->draw_events, - $this->width, - $this->height, - false, - $unit_name, - $this->draw_alerts, - 1, - $pure, - $date); - if ($this->draw_events) { - $graph .= '
'; - $graph .= graphic_module_events($this->id, - $this->width, $this->height, - $this->period, $config['homeurl'], $this->zoom, '', $date); - } - break; default: $graph .= fs_error_image ('../images'); break; } $graph = ob_get_clean() . $graph; - + echo json_encode(array('correct' => $correct, 'graph' => $graph)); break; } } } - + public function show() { if (!$this->correct_acl) { $this->show_fail_acl(); @@ -264,7 +187,7 @@ class ModuleGraph { $this->showModuleGraph(); } } - + private function show_fail_acl() { $error['type'] = 'onStart'; $error['title_text'] = __('You don\'t have access to this page'); @@ -275,7 +198,7 @@ class ModuleGraph { $home = new Home(); $home->show($error); } - + private function javascript_code() { ob_start(); @@ -291,24 +214,24 @@ class ModuleGraph { $(document).ready(function() { function load_graph() { $("#loading_graph").show(); - - var heigth = $(document).height() + + var heigth = $(document).height() - $(".ui-header").height() - $(".ui-collapsible").height() - 55; var width = $(document).width() - 25; ajax_get_graph($("#id_module").val(), heigth, width); } - + load_graph(); - + // Detect orientation change to refresh dinamic content window.addEventListener("resize", function() { // Reload dinamic content load_graph(); }); }); - + function ajax_get_graph(id, heigth_graph, width_graph) { postvars = {}; postvars["action"] = "ajax"; @@ -316,20 +239,20 @@ class ModuleGraph { postvars["parameter2"] = "get_graph"; postvars["width"] = width_graph; postvars["height"] = heigth_graph; - + postvars["draw_alerts"] = ($("input[name = 'draw_alerts']").is(":checked"))?1:0; postvars["draw_events"] = ($("input[name = 'draw_events']").is(":checked"))?1:0; postvars["time_compare_separated"] = ($("input[name = 'time_compare_separated']").is(":checked"))?1:0; postvars["time_compare_overlapped"] = ($("input[name = 'time_compare_overlapped']").is(":checked"))?1:0; postvars["unknown_graph"] = ($("input[name = 'unknown_graph']").is(":checked"))?1:0;; postvars["avg_only"] = ($("input[name = 'avg_only']").is(":checked"))?1:0;; - + postvars["period_hours"] = $("input[name = 'period_hours']").val(); postvars["zoom"] = $("input[name = 'zoom']").val(); postvars["start_date"] = $("input[name = 'start_date']").val(); - + postvars["id"] = id; - + $.ajax ({ type: "POST", url: "index.php", @@ -356,17 +279,17 @@ class ModuleGraph { module['id_agente']); - + $ui = Ui::getInstance(); - + $ui->createPage(); - + if ($this->id_agent) { $ui->createDefaultHeader( sprintf(__("%s: %s"), get_product_name(), $this->module["nombre"]), @@ -402,7 +325,7 @@ class ModuleGraph { 'label' => __('Show Alerts') ); $ui->formAddCheckbox($options); - + $options = array( 'name' => 'draw_events', 'value' => 1, @@ -410,7 +333,7 @@ class ModuleGraph { 'label' => __('Show Events') ); $ui->formAddCheckbox($options); - + $options = array( 'name' => 'time_compare_separated', 'value' => 1, @@ -418,7 +341,7 @@ class ModuleGraph { 'label' => __('Time compare (Separated)') ); $ui->formAddCheckbox($options); - + $options = array( 'name' => 'time_compare_overlapped', 'value' => 1, @@ -426,7 +349,7 @@ class ModuleGraph { 'label' => __('Time compare (Overlapped)') ); $ui->formAddCheckbox($options); - + $options = array( 'name' => 'unknown_graph', 'value' => 1, @@ -434,7 +357,7 @@ class ModuleGraph { 'label' => __('Show unknown graph') ); $ui->formAddCheckbox($options); - + $options = array( 'name' => 'avg_only', 'value' => 1, @@ -442,7 +365,7 @@ class ModuleGraph { 'label' => __('Avg Only') ); $ui->formAddCheckbox($options); - + $options = array( 'label' => __('Time range (hours)'), 'name' => 'period_hours', @@ -452,22 +375,21 @@ class ModuleGraph { 'step' => 4 ); $ui->formAddSlider($options); - - + $options = array( 'name' => 'start_date', 'value' => $this->start_date, 'label' => __('Begin date') ); $ui->formAddInpuDate($options); - + $options = array( 'icon' => 'refresh', 'icon_pos' => 'right', 'text' => __('Update graph') ); $ui->formAddSubmitButton($options); - + $html = $ui->getEndForm(); $ui->contentCollapsibleAddItem($html); $ui->contentEndCollapsible(); diff --git a/pandora_console/operation/agentes/estado_generalagente.php b/pandora_console/operation/agentes/estado_generalagente.php index 57b87cb6eb..57a50e6c9a 100755 --- a/pandora_console/operation/agentes/estado_generalagente.php +++ b/pandora_console/operation/agentes/estado_generalagente.php @@ -280,13 +280,15 @@ $access_agent = db_get_value_sql("SELECT COUNT(id_agent) WHERE id_agent = " . $id_agente); if ($config["agentaccess"] && $access_agent > 0) { $data[2] = - '
+ '
' . __('Agent access rate (24h)') . '' . - graphic_agentaccess($id_agente, 300, 100, SECONDS_1DAY, true) . + graphic_agentaccess($id_agente, '90%', 150, SECONDS_1DAY, true) . '
'; - $table_data->style[1] = 'width: 40%;'; + $table_data->style[0] = 'width: 20%;'; + $table_data->style[1] = 'width: 30%;'; + $table_data->style[2] = 'width: 50%;'; $table_data->rowspan[0][2] = 5; } @@ -298,8 +300,6 @@ if (!empty($addresses)) { $data[1] = '
' . implode('
',$addresses) . '
'; - //~ $table_data->data[] = '
'; } ?> @@ -99,10 +99,10 @@ $interface_traffic_modules = array( window.onload = function() { // Hack to repeat the init process to period select var periodSelectId = $('[name="period"]').attr('class'); - + period_select_init(periodSelectId); }; - + function show_others() { if (!$("#checkbox-avg_only").attr('checked')) { $("#hidden-show_other").val(1); @@ -116,20 +116,18 @@ $interface_traffic_modules = array( 1) { $height = $height * ($zoom / 2.1); $width = $width * ($zoom / 1.4); - echo ""; } - + /*$current = date("Y-m-d"); - + if ($start_date != $current) $date = strtotime($start_date); else @@ -183,45 +180,49 @@ $interface_traffic_modules = array( */ $date = strtotime("$start_date $start_time"); $now = time(); - + if ($date > $now){ $date = $now; } - + $urlImage = ui_get_full_url(false); - + if ($config['flash_charts'] == 1) echo '
'; else echo '
'; - - custom_graphs_print(0, - $height, - $width, - $period, - null, - false, - $date, - false, - 'white', + + $height = 400; + $width = '90%'; + + $params =array( + 'period' => $period, + 'width' => $width, + 'height' => $height, + 'unit_name' => array_fill(0, count($interface_traffic_modules), $config["interface_unit"]), + 'date' => $date, + 'homeurl' => $config['homeurl'], + 'percentil' => (($show_percentil)? $config['percentil'] : null), + 'fullscale' => $fullscale + ); + + $params_combined = array( + 'weight_list' => array(), + 'projection' => false, + 'labels' => array_keys($interface_traffic_modules), + 'from_interface' => true, + 'modules_series' => array_values($interface_traffic_modules), + 'return' => 0 + ); + + graphic_combined_module( array_values($interface_traffic_modules), - $config['homeurl'], - array_keys($interface_traffic_modules), - array_fill(0, count($interface_traffic_modules), $config["interface_unit"]), - false, - true, - true, - true, - 1, - false, - false, - (($show_percentil)? $config['percentil'] : null), - true, - false, - $fullscale); - + $params, + $params_combined + ); + echo '
'; - + /////////////////////////// // SIDE MENU /////////////////////////// @@ -231,13 +232,12 @@ $interface_traffic_modules = array( $side_layer_params['top_text'] = "
" . html_print_image('/images/config.disabled.png', true, array('width' => '16px'),false,false,false,true) . ' ' . __('Graph configuration menu') . "
"; $side_layer_params['body_text'] = "'; // outer - + // ICONS $side_layer_params['icon_closed'] = '/images/graphmenu_arrow_hide.png'; $side_layer_params['icon_open'] = '/images/graphmenu_arrow.png'; - + // SIZE $side_layer_params['width'] = 500; - + // POSITION $side_layer_params['position'] = 'left'; - + html_print_side_layer($side_layer_params); - + // Hidden div to forced title html_print_div(array('id' => 'forced_title_layer', 'class' => 'forced_title_layer', 'hidden' => true)); ?> - + diff --git a/pandora_console/operation/agentes/stat_win.php b/pandora_console/operation/agentes/stat_win.php index 0cb77497fe..f433fb626a 100644 --- a/pandora_console/operation/agentes/stat_win.php +++ b/pandora_console/operation/agentes/stat_win.php @@ -36,7 +36,6 @@ check_login (); $server_id = (int) get_parameter("server"); if (is_metaconsole() && !empty($server_id)) { $server = metaconsole_get_connection_by_id($server_id); - // Error connecting if (metaconsole_connect($server) !== NOERR) { echo ""; @@ -56,11 +55,11 @@ if (file_exists ('../../include/languages/'.$user_language.'.mo')) { echo ''; -$label = get_parameter('label'); -$label = base64_decode($label); -$id = get_parameter('id'); +$label = get_parameter('label'); +$label = base64_decode($label); +$id = get_parameter('id'); $id_agent = db_get_value ("id_agente","tagente_modulo","id_agente_modulo",$id); -$alias = db_get_value ("alias","tagente","id_agente",$id_agent); +$alias = db_get_value ("alias","tagente","id_agente",$id_agent); //$agent = agents_get_agent_with_ip ("192.168.50.31"); //$label = rawurldecode(urldecode(base64_decode(get_parameter('label', '')))); ?> @@ -72,7 +71,6 @@ $alias = db_get_value ("alias","tagente","id_agente",$id_agent); $refresh = (int) get_parameter ("refresh", -1); if ($refresh > 0) { $query = ui_get_url_refresh (false); - echo ''; } ?> @@ -92,14 +90,13 @@ $alias = db_get_value ("alias","tagente","id_agente",$id_agent); } ?> @@ -125,18 +114,18 @@ $alias = db_get_value ("alias","tagente","id_agente",$id_agent); $id = (int) get_parameter ("id", 0); // Agent id $agent_id = (int) modules_get_agentmodule_agent($id); - + if (empty($id) || empty($agent_id)) { ui_print_error_message(__('There was a problem locating the source of the graph')); exit; } - + // ACL - $permission = false; + $permission = false; $agent_group = (int) agents_get_agent_group($agent_id); $strict_user = (bool) db_get_value("strict_acl", "tusuario", "id_user", $config['id_user']); - + if (!empty($agent_group)) { if ($strict_user) { $permission = tags_check_acl_by_module($id, $config['id_user'], 'RR') === true; @@ -145,28 +134,26 @@ $alias = db_get_value ("alias","tagente","id_agente",$id_agent); $permission = check_acl($config['id_user'], $agent_group, "RR"); } } - + if (!$permission) { require ($config['homedir'] . "/general/noaccess.php"); exit; } - + $draw_alerts = get_parameter("draw_alerts", 0); if(isset($config['only_average'])){ $avg_only = $config['only_average']; } - + $show_other = get_parameter('show_other',-1); - + if ($show_other != -1) { $avg_only = $show_other; } - + $period = get_parameter ("period"); - $id = get_parameter ("id", 0); - $width = get_parameter ("width", STATWIN_DEFAULT_CHART_WIDTH); - $height = get_parameter ("height", STATWIN_DEFAULT_CHART_HEIGHT); + $id = get_parameter ("id", 0); $label = get_parameter ("label", ""); $label_graph = base64_decode(get_parameter ("label", "")); $start_date = get_parameter ("start_date", date("Y/m/d")); @@ -191,7 +178,7 @@ $alias = db_get_value ("alias","tagente","id_agente",$id_agent); } elseif($config['full_scale_option'] == 2){ if($graph_type == 'boolean'){ - $fullscale = 1; + $fullscale = 1; }else{ $fullscale = 0; } @@ -203,94 +190,79 @@ $alias = db_get_value ("alias","tagente","id_agente",$id_agent); // To avoid the horizontal overflow $width -= 20; - + $time_compare = false; - + if ($time_compare_separated) { $time_compare = 'separated'; } else if ($time_compare_overlapped) { $time_compare = 'overlapped'; } - + if ($zoom > 1) { $height = $height * ($zoom / 2.1); $width = $width * ($zoom / 1.4); } - echo ""; - + // Build date $date = strtotime("$start_date $start_time"); $now = time(); - + if ($date > $now) $date = $now; - + $urlImage = ui_get_full_url(false, false, false, false); - + $unit = db_get_value('unit', 'tagente_modulo', 'id_agente_modulo', $id); - + // log4x doesnt support flash yet - // if ($config['flash_charts'] == 1) echo '
'; else echo '
'; - + + $width = '90%'; + $height = '450'; + switch ($graph_type) { case 'boolean': - echo grafico_modulo_boolean ($id, $period, $draw_events, - $width, $height, $label_graph, $unit, $draw_alerts, - $avg_only, false, $date, false, $urlImage, - 'adapter_' . $graph_type, $time_compare, - $unknown_graph, true, $fullscale); - echo '
'; - if ($show_events_graph) - echo graphic_module_events($id, $width, $height, - $period, $config['homeurl'], $zoom, - 'adapted_' . $graph_type, $date, true); - break; case 'sparse': - echo grafico_modulo_sparse ($id, $period, $draw_events, - $width, $height, $label_graph, $unit, $draw_alerts, - $avg_only, false, $date, $unit, $baseline, 0, true, - false, $urlImage, 1, false, - 'adapter_' . $graph_type, $time_compare, - $unknown_graph, true, 'white', - (($show_percentil)? $config['percentil'] : null), - false, false, $config['type_module_charts'], $fullscale); - echo '
'; - if ($show_events_graph) - echo graphic_module_events($id, $width, $height, - $period, $config['homeurl'], $zoom, - 'adapted_' . $graph_type, $date, true); - break; case 'string': - echo grafico_modulo_string ($id, $period, $draw_events, - $width, $height, $label_graph, null, $draw_alerts, 1, - false, $date, false, $urlImage, - 'adapter_' . $graph_type); + $params =array( + 'agent_module_id' => $id, + 'period' => $period, + 'show_events' => $draw_events, + 'title' => $label_graph, + 'unit_name' => $unit, + 'show_alerts' => $draw_alerts, + 'avg_only' => $avg_only, + 'date' => $date, + 'unit' => $unit, + 'baseline' => $baseline, + 'homeurl' => $urlImage, + 'adapt_key' => 'adapter_' . $graph_type, + 'compare' => $time_compare, + 'show_unknown' => $unknown_graph, + 'percentil' => (($show_percentil)? $config['percentil'] : null), + 'type_graph' => $config['type_module_charts'], + 'fullscale' => $fullscale + ); + echo grafico_modulo_sparse ($params); echo '
'; - if ($show_events_graph) + if ($show_events_graph){ + $width = '500'; echo graphic_module_events($id, $width, $height, $period, $config['homeurl'], $zoom, 'adapted_' . $graph_type, $date, true); - break; - case 'log4x': - echo grafico_modulo_log4x ($id, $period, $draw_events, - $width, $height, $label_graph, $unit, $draw_alerts, 1, - $pure, $date); - echo '
'; - if ($show_events_graph) - echo graphic_module_events($id, $width, $height, - $period, $config['homeurl'], $zoom, '', $date, true); + } break; default: echo fs_error_image ('../images'); break; } echo '
'; - + //////////////////////////////////////////////////////////////// // SIDE MENU //////////////////////////////////////////////////////////////// @@ -300,22 +272,21 @@ $alias = db_get_value ("alias","tagente","id_agente",$id_agent); $params['top_text'] = "
" . html_print_image('images/wrench_blanco.png', true, array('width' => '16px'), false, false, true) . ' ' . __('Graph configuration menu') . ui_print_help_icon ("graphs",true, $config["homeurl"], "images/help_w.png", true) . "
"; $params['body_text'] = "'; // outer - + // ICONS $params['icon_closed'] = '/images/graphmenu_arrow_hide.png'; $params['icon_open'] = '/images/graphmenu_arrow.png'; - + // SIZE $params['width'] = 500; - + // POSITION $params['position'] = 'left'; - + html_print_side_layer($params); - + // Hidden div to forced title html_print_div(array('id' => 'forced_title_layer', 'class' => 'forced_title_layer', 'hidden' => true)); ?> - + @@ -502,38 +473,7 @@ ui_include_time_picker(true); $('#checkbox-time_compare_overlapped').click(function() { $('#checkbox-time_compare_separated').removeAttr('checked'); }); - - - - var show_overview = false; - var height_window; - var width_window; - $(window).ready(function() { - height_window = window.innerHeight; - width_window = window.innerWidth; - }); - - $("*").filter(function() { - if (typeof(this.id) == "string") - return this.id.match(/menu_overview_graph.*/); - else - return false; - }).click(function() { - if (show_overview) { - window.resizeTo(width_window, height_window); - } - else { - window.resizeTo(width_window, height_window + 150); - } - show_overview = !show_overview; - }); - - + // Add datepicker and timepicker $("#text-start_date").datepicker({ dateFormat: "" @@ -549,13 +489,13 @@ ui_include_time_picker(true); currentText: '', closeText: '' }); - + $.datepicker.setDefaults($.datepicker.regional[""]); - + $(window).ready(function() { $("#field_list").css('height', ($(window).height() - 160) + 'px'); }); - + $(window).resize(function() { $("#field_list").css('height', ($(window).height() - 160) + 'px'); }); diff --git a/pandora_console/operation/reporting/graph_viewer.php b/pandora_console/operation/reporting/graph_viewer.php index f5d1143623..eb08db35e3 100644 --- a/pandora_console/operation/reporting/graph_viewer.php +++ b/pandora_console/operation/reporting/graph_viewer.php @@ -63,7 +63,7 @@ if ($delete_graph) { if ($view_graph) { $sql="SELECT * FROM tgraph_source WHERE id_graph = $id_graph"; $sources = db_get_all_rows_sql($sql); - + $sql="SELECT * FROM tgraph WHERE id_graph = $id_graph"; $graph = db_get_row_sql($sql); @@ -71,7 +71,7 @@ if ($view_graph) { $private = $graph["private"]; $width = $graph["width"]; $height = $graph["height"] + count($sources) * 10; - + $zoom = (int) get_parameter ('zoom', 0); //Increase the height to fix the leyend rise if ($zoom > 0) { @@ -160,12 +160,12 @@ if ($view_graph) { html_print_image("images/builder.png", true, array ("title" => __('Graph editor'))) .'') ); } - + $options['view']['text'] = '' . html_print_image("images/operation.png", true, array ("title" => __('View graph'))) .''; $options['view']['active'] = true; - + if ($config["pure"] == 0) { $options['screen']['text'] = "" . html_print_image ("images/full_screen.png", true, array ("title" => __('Full screen mode'))) @@ -175,18 +175,42 @@ if ($view_graph) { $options['screen']['text'] = "" . html_print_image ("images/normal_screen.png", true, array ("title" => __('Back to normal mode'))) . ""; - + // In full screen, the manage options are not available $options = array('view' => $options['view'], 'screen' => $options['screen']); } - + // Header - - ui_print_page_header ($graph['name'], - "images/chart.png", false, "", false, $options); - - $graph_return = custom_graphs_print($id_graph, $height, $width, $period, $stacked, true, $unixdate, false, 'white', - array(), '', array(), array(), true, true, true, true, 1, false, false, $percentil, false, false, $fullscale); + ui_print_page_header ( + $graph['name'], + "images/chart.png", + false, + "", + false, + $options + ); + + $width = null; + $height = null; + $params =array( + 'period' => $period, + 'width' => $width, + 'height' => $height, + 'date' => $unixdate, + 'percentil' => $percentil, + 'fullscale' => $fullscale + ); + + $params_combined = array( + 'stacked' => $stacked, + 'id_graph' => $id_graph + ); + + $graph_return = graphic_combined_module( + false, + $params, + $params_combined + ); if ($graph_return){ echo "
"; @@ -310,7 +334,6 @@ if ($view_graph) { $("#stacked").change(function(){ if ($(this).val() == '4') { - console.log($(this).val()); $("#thresholdDiv").show(); $(".stacked").show(); } else { diff --git a/pandora_plugins/intel_dcm/extensions/intel_dcm_agent_view.php b/pandora_plugins/intel_dcm/extensions/intel_dcm_agent_view.php index 69e2a9cd81..837e5d35ea 100644 --- a/pandora_plugins/intel_dcm/extensions/intel_dcm_agent_view.php +++ b/pandora_plugins/intel_dcm/extensions/intel_dcm_agent_view.php @@ -168,9 +168,23 @@ function main_intel_dcm_agent_view () { $module = modules_get_agentmodule_id (io_safe_input("Avg. Power"), $id_agent); $unit = modules_get_unit ($module['id_agente_modulo']); - - $data[1] = grafico_modulo_sparse($module['id_agente_modulo'], 7200, $draw_events, 400, 250, - $module['nombre'], null, $draw_alerts, $avg_only, false, $date, $unit); + + $params_1 =array( + 'agent_module_id' => $module['id_agente_modulo'], + 'period' => 7200, + 'show_events' => $draw_events, + 'width' => 400, + 'height' => 250, + 'title' => $module['nombre'], + 'unit_name' => null, + 'show_alerts' => $draw_alerts, + 'avg_only' => $avg_only, + 'pure' => false, + 'date' => $date, + 'unit' => $unit + ); + + $data[1] = grafico_modulo_sparse($params_1); array_push ($table->data, $data); @@ -190,19 +204,44 @@ function main_intel_dcm_agent_view () { $module = modules_get_agentmodule_id (io_safe_input("Avg. Inlet Temperature"), $id_agent); $unit = modules_get_unit ($module['id_agente_modulo']); - - $data[0] = grafico_modulo_sparse($module['id_agente_modulo'], 7200, $draw_events, 400, 250, - $module['nombre'], null, $draw_alerts, $avg_only, false, $date, $unit); - + $params_2 =array( + 'agent_module_id' => $module['id_agente_modulo'], + 'period' => 7200, + 'show_events' => $draw_events, + 'width' => 400, + 'height' => 250, + 'title' => $module['nombre'], + 'unit_name' => null, + 'show_alerts' => $draw_alerts, + 'avg_only' => $avg_only, + 'pure' => false, + 'date' => $date, + 'unit' => $unit + ); + $data[0] = grafico_modulo_sparse($params_2); $module = modules_get_agentmodule_id (io_safe_input("Calculated Cooling Power"), $id_agent); $unit = modules_get_unit ($module['id_agente_modulo']); - - $data[1] = grafico_modulo_sparse($module['id_agente_modulo'], 7200, $draw_events, 400, 250, - $module['nombre'], null, $draw_alerts, $avg_only, false, $date, $unit); - + + $params_3 =array( + 'agent_module_id' => $module['id_agente_modulo'], + 'period' => 7200, + 'show_events' => $draw_events, + 'width' => 400, + 'height' => 250, + 'title' => $module['nombre'], + 'unit_name' => null, + 'show_alerts' => $draw_alerts, + 'avg_only' => $avg_only, + 'pure' => false, + 'date' => $date, + 'unit' => $unit + ); + + $data[1] = grafico_modulo_sparse($params_3); + array_push ($table->data, $data); - + echo "
"; html_print_table($table); echo "
";