From fae8070e2978d58194f7139f58239c13e173765e Mon Sep 17 00:00:00 2001 From: Luis Calvo Date: Tue, 15 Sep 2020 19:57:52 +0200 Subject: [PATCH 001/263] Report macros now apply on showing report --- .../godmode/reporting/reporting_builder.php | 10 +- .../include/functions_reporting.php | 583 ++++++++++++++++-- 2 files changed, 536 insertions(+), 57 deletions(-) diff --git a/pandora_console/godmode/reporting/reporting_builder.php b/pandora_console/godmode/reporting/reporting_builder.php index b88cc5d64e..499dd6a671 100755 --- a/pandora_console/godmode/reporting/reporting_builder.php +++ b/pandora_console/godmode/reporting/reporting_builder.php @@ -1419,10 +1419,7 @@ switch ($action) { 'module_description' => $module_description, ]; - $values['name'] = reporting_label_macro( - $items_label, - $name_it - ); + $values['name'] = $name_it; $values['landscape'] = get_parameter('landscape'); $values['pagebreak'] = get_parameter('pagebreak'); @@ -2115,10 +2112,7 @@ switch ($action) { 'module_description' => $module_description, ]; - $values['name'] = reporting_label_macro( - $items_label, - $name_it - ); + $values['name'] = $name_it; $values['landscape'] = get_parameter('landscape'); $values['pagebreak'] = get_parameter('pagebreak'); diff --git a/pandora_console/include/functions_reporting.php b/pandora_console/include/functions_reporting.php index a27be4f530..3e830b2398 100755 --- a/pandora_console/include/functions_reporting.php +++ b/pandora_console/include/functions_reporting.php @@ -1840,10 +1840,23 @@ function reporting_event_report_group( $history = true; } - $return['title'] = $content['name']; + $group_name = groups_get_name($content['id_group'], true); + + $items_label = ['agent_group' => $group_name]; + + // Apply macros. + $title = (isset($content['name'])) ? $content['name'] : ''; + if ($title != '') { + $title = reporting_label_macro( + $items_label, + $title + ); + } + + $return['title'] = $title; $return['landscape'] = $content['landscape']; $return['pagebreak'] = $content['pagebreak']; - $return['subtitle'] = groups_get_name($content['id_group'], true); + $return['subtitle'] = $group_name; if (!empty($content['style']['event_filter_search'])) { $return['subtitle'] .= ' ('.$content['style']['event_filter_search'].')'; } @@ -2122,7 +2135,16 @@ function reporting_event_report_module( 'module_description' => $module_description, ]; - $return['title'] = $content['name']; + // Apply macros. + $title = (isset($content['name'])) ? $content['name'] : ''; + if ($title != '') { + $title = reporting_label_macro( + $items_label, + $title + ); + } + + $return['title'] = $title; $return['landscape'] = $content['landscape']; $return['pagebreak'] = $content['pagebreak']; $return['subtitle'] = $agent_alias.' - '.io_safe_output($module_name); @@ -2230,18 +2252,44 @@ function reporting_inventory_changes($report, $content, $type) metaconsole_connect($server); } - $return['title'] = $content['name']; + $es = json_decode($content['external_source'], true); + + $id_agent = $es['id_agents']; + $module_name = $es['inventory_modules']; + $id_agent_module = modules_get_agentmodule_id($module_name, $id_agent); + $agent_description = agents_get_description($id_agent); + $agent_group = agents_get_agent_group($id_agent); + $agent_address = agents_get_address($id_agent); + $agent_alias = agents_get_alias($id_agent); + $module_description = modules_get_agentmodule_descripcion( + $id_agent_module + ); + + $items_label = [ + 'id_agent' => $id_agent, + 'id_agent_module' => $id_agent_module, + 'agent_description' => $agent_description, + 'agent_group' => $agent_group, + 'agent_address' => $agent_address, + 'agent_alias' => $agent_alias, + ]; + + // Apply macros. + $title = (isset($content['name'])) ? $content['name'] : ''; + if ($title != '') { + $title = reporting_label_macro( + $items_label, + $title + ); + } + + $return['title'] = $title; $return['landscape'] = $content['landscape']; $return['pagebreak'] = $content['pagebreak']; $return['subtitle'] = agents_get_alias($content['id_agent']); $return['description'] = $content['description']; $return['date'] = reporting_get_date_text($report, $content); - $es = json_decode($content['external_source'], true); - - $id_agent = $es['id_agents']; - $module_name = $es['inventory_modules']; - switch ($type) { case 'data': $inventory_changes = inventory_get_changes( @@ -2299,12 +2347,6 @@ function reporting_inventory($report, $content, $type) metaconsole_connect($server); } - $return['title'] = $content['name']; - $return['landscape'] = $content['landscape']; - $return['pagebreak'] = $content['pagebreak']; - $return['description'] = $content['description']; - $return['date'] = reporting_get_date_text($report, $content); - $es = json_decode($content['external_source'], true); $id_agent = $es['id_agents']; @@ -2313,6 +2355,35 @@ function reporting_inventory($report, $content, $type) $module_name = [0 => 0]; } + $agent_description = agents_get_description($id_agent); + $agent_group = agents_get_agent_group($id_agent); + $agent_address = agents_get_address($id_agent); + $agent_alias = agents_get_alias($id_agent); + + $items_label = [ + 'type' => $return['type'], + 'id_agent' => $id_agent, + 'agent_description' => $agent_description, + 'agent_group' => $agent_group, + 'agent_address' => $agent_address, + 'agent_alias' => $agent_alias, + ]; + + // Apply macros. + $title = (isset($content['name'])) ? $content['name'] : ''; + if ($title != '') { + $title = reporting_label_macro( + $items_label, + $title + ); + } + + $return['title'] = $title; + $return['landscape'] = $content['landscape']; + $return['pagebreak'] = $content['pagebreak']; + $return['description'] = $content['description']; + $return['date'] = reporting_get_date_text($report, $content); + $date = $es['date']; $description = $content['description']; @@ -2942,8 +3013,21 @@ function reporting_group_report($report, $content) $connection = metaconsole_get_connection($server); } + $items_label = [ + 'agent_group' => groups_get_name($content['id_group'], true), + ]; + + // Apply macros + $title = (isset($content['name'])) ? $content['name'] : ''; + if ($title != '') { + $title = reporting_label_macro( + $items_label, + $title + ); + } + $return['server_name'] = $server[0]; - $return['title'] = io_safe_output($content['name']); + $return['title'] = $title; $return['landscape'] = $content['landscape']; $return['pagebreak'] = $content['pagebreak']; $return['subtitle'] = groups_get_name($content['id_group'], true); @@ -3034,6 +3118,15 @@ function reporting_event_report_agent( 'agent_alias' => $agent_alias, ]; + // Apply macros. + $title = (isset($content['name'])) ? $content['name'] : ''; + if ($title != '') { + $title = reporting_label_macro( + $items_label, + $title + ); + } + if ($config['metaconsole']) { metaconsole_restore_db(); } @@ -3047,7 +3140,7 @@ function reporting_event_report_agent( } $return['label'] = $label; - $return['title'] = io_safe_output($content['name']); + $return['title'] = $title; $return['landscape'] = $content['landscape']; $return['pagebreak'] = $content['pagebreak']; $return['subtitle'] = io_safe_output($agent_alias); @@ -3258,7 +3351,16 @@ function reporting_historical_data($report, $content) 'module_description' => $module_description, ]; - $return['title'] = $content['name']; + // Apply macros. + $title = (isset($content['name'])) ? $content['name'] : ''; + if ($title != '') { + $title = reporting_label_macro( + $items_label, + $title + ); + } + + $return['title'] = $title; $return['landscape'] = $content['landscape']; $return['pagebreak'] = $content['pagebreak']; $return['subtitle'] = $agent_alias.' - '.$module_name; @@ -3383,7 +3485,16 @@ function reporting_database_serialized($report, $content) 'module_description' => $module_description, ]; - $return['title'] = $content['name']; + // Apply macros. + $title = (isset($content['name'])) ? $content['name'] : ''; + if ($title != '') { + $title = reporting_label_macro( + $items_label, + $title + ); + } + + $return['title'] = $title; $return['landscape'] = $content['landscape']; $return['pagebreak'] = $content['pagebreak']; $return['subtitle'] = $agent_alias.' - '.$module_name; @@ -3537,7 +3648,37 @@ function reporting_last_value($report, $content) $content['id_agent_module'] ); - $return['title'] = $content['name']; + $id_agent_module = $content['id_agent_module']; + $agent_description = agents_get_description($id_agent); + $agent_group = agents_get_agent_group($id_agent); + $agent_address = agents_get_address($id_agent); + + $module_description = modules_get_agentmodule_descripcion( + $id_agent_module + ); + + $items_label = [ + 'type' => $return['type'], + 'id_agent' => $id_agent, + 'id_agent_module' => $id_agent_module, + 'agent_description' => $agent_description, + 'agent_group' => $agent_group, + 'agent_address' => $agent_address, + 'agent_alias' => $agent_alias, + 'module_name' => $module_name, + 'module_description' => $module_description, + ]; + + // Apply macros + $title = (isset($content['name'])) ? $content['name'] : ''; + if ($title != '') { + $title = reporting_label_macro( + $items_label, + $title + ); + } + + $return['title'] = $title; $return['landscape'] = $content['landscape']; $return['pagebreak'] = $content['pagebreak']; $return['subtitle'] = $agent_alias.' - '.$module_name; @@ -3592,7 +3733,18 @@ function reporting_group_configuration($report, $content) $group_name = groups_get_name($content['id_group'], true); - $return['title'] = $content['name']; + $items_label = ['agent_group' => $group_name]; + + // Apply macros. + $title = (isset($content['name'])) ? $content['name'] : ''; + if ($title != '') { + $title = reporting_label_macro( + $items_label, + $title + ); + } + + $return['title'] = $title; $return['landscape'] = $content['landscape']; $return['pagebreak'] = $content['pagebreak']; $return['subtitle'] = $group_name; @@ -3667,7 +3819,18 @@ function reporting_network_interfaces_report($report, $content, $type='dinamic', $group_name = groups_get_name($content['id_group']); - $return['title'] = $content['name']; + $items_label = ['_agentgroup_' => $group_name]; + + // Apply macros + $title = (isset($content['name'])) ? $content['name'] : ''; + if ($title != '') { + $title = reporting_label_macro( + $items_label, + $title + ); + } + + $return['title'] = $title; $return['landscape'] = $content['landscape']; $return['pagebreak'] = $content['pagebreak']; $return['subtitle'] = $group_name; @@ -3886,7 +4049,18 @@ function reporting_alert_report_group($report, $content) $group_name = groups_get_name($content['id_group'], true); - $return['title'] = $content['name']; + $items_label = ['agent_group' => $group_name]; + + // Apply macros. + $title = (isset($content['name'])) ? $content['name'] : ''; + if ($title != '') { + $title = reporting_label_macro( + $items_label, + $title + ); + } + + $return['title'] = $title; $return['landscape'] = $content['landscape']; $return['pagebreak'] = $content['pagebreak']; $return['subtitle'] = $group_name; @@ -4089,7 +4263,16 @@ function reporting_alert_report_agent($report, $content) 'agent_alias' => $agent_alias, ]; - $return['title'] = $content['name']; + // Apply macros. + $title = (isset($content['name'])) ? $content['name'] : ''; + if ($title != '') { + $title = reporting_label_macro( + $items_label, + $title + ); + } + + $return['title'] = $title; $return['landscape'] = $content['landscape']; $return['pagebreak'] = $content['pagebreak']; $return['subtitle'] = $agent_alias; @@ -4259,7 +4442,16 @@ function reporting_alert_report_module($report, $content) 'module_description' => $module_description, ]; - $return['title'] = $content['name']; + // Apply macros. + $title = (isset($content['name'])) ? $content['name'] : ''; + if ($title != '') { + $title = reporting_label_macro( + $items_label, + $title + ); + } + + $return['title'] = $title; $return['landscape'] = $content['landscape']; $return['pagebreak'] = $content['pagebreak']; $return['subtitle'] = $agent_alias.' - '.$module_name; @@ -4520,7 +4712,16 @@ function reporting_monitor_report($report, $content) 'module_description' => $module_description, ]; - $return['title'] = $content['name']; + // Apply macros. + $title = (isset($content['name'])) ? $content['name'] : ''; + if ($title != '') { + $title = reporting_label_macro( + $items_label, + $title + ); + } + + $return['title'] = $title; $return['landscape'] = $content['landscape']; $return['pagebreak'] = $content['pagebreak']; $return['subtitle'] = $agent_alias.' - '.$module_name; @@ -4712,11 +4913,57 @@ function reporting_prediction_date($report, $content) ); $agent_name_db = io_safe_output(modules_get_agentmodule_agent_name($content['id_agent_module'])); - $return['title'] = $content['name']; + $id_agent = agents_get_module_id( + $content['id_agent_module'] + ); + + $id_agent_module = $content['id_agent_module']; + $agent_description = agents_get_description($id_agent); + $agent_group = agents_get_agent_group($id_agent); + $agent_address = agents_get_address($id_agent); + $agent_alias = agents_get_alias($id_agent); + $module_name = modules_get_agentmodule_name( + $id_agent_module + ); + + $module_description = modules_get_agentmodule_descripcion( + $id_agent_module + ); + + $items_label = [ + 'type' => $return['type'], + 'id_agent' => $id_agent, + 'id_agent_module' => $id_agent_module, + 'agent_description' => $agent_description, + 'agent_group' => $agent_group, + 'agent_address' => $agent_address, + 'agent_alias' => $agent_alias, + 'module_name' => $module_name, + 'module_description' => $module_description, + ]; + + // Apply macros + $title = (isset($content['name'])) ? $content['name'] : ''; + if ($title != '') { + $title = reporting_label_macro( + $items_label, + $title + ); + } + + $description = (isset($content['description'])) ? $content['description'] : ''; + if ($description != '') { + $description = reporting_label_macro( + $items_label, + $description + ); + } + + $return['title'] = $title; $return['landscape'] = $content['landscape']; $return['pagebreak'] = $content['pagebreak']; $return['subtitle'] = $agent_name.' - '.$module_name; - $return['description'] = $content['description']; + $return['description'] = $description; $return['date'] = reporting_get_date_text($report, $content); $return['label'] = (isset($content['style']['label'])) ? $content['style']['label'] : ''; @@ -4767,7 +5014,45 @@ function reporting_projection_graph( $agent_name = io_safe_output(modules_get_agentmodule_agent_alias($content['id_agent_module'])); $agent_name_db = io_safe_output(modules_get_agentmodule_agent_name($content['id_agent_module'])); - $return['title'] = $content['name']; + $id_agent = agents_get_module_id( + $content['id_agent_module'] + ); + + $id_agent_module = $content['id_agent_module']; + $agent_description = agents_get_description($id_agent); + $agent_group = agents_get_agent_group($id_agent); + $agent_address = agents_get_address($id_agent); + $agent_alias = agents_get_alias($id_agent); + $module_name = modules_get_agentmodule_name( + $id_agent_module + ); + + $module_description = modules_get_agentmodule_descripcion( + $id_agent_module + ); + + $items_label = [ + 'type' => $return['type'], + 'id_agent' => $id_agent, + 'id_agent_module' => $id_agent_module, + 'agent_description' => $agent_description, + 'agent_group' => $agent_group, + 'agent_address' => $agent_address, + 'agent_alias' => $agent_alias, + 'module_name' => $module_name, + 'module_description' => $module_description, + ]; + + // Apply macros + $title = (isset($content['name'])) ? $content['name'] : ''; + if ($title != '') { + $title = reporting_label_macro( + $items_label, + $title + ); + } + + $return['title'] = $title; $return['subtitle'] = $agent_name.' - '.$module_name; $return['description'] = $content['description']; $return['date'] = reporting_get_date_text($report, $content); @@ -4846,7 +5131,36 @@ function reporting_agent_configuration($report, $content) $content['name'] = __('Agent configuration'); } - $return['title'] = $content['name']; + $sql = ' + SELECT * + FROM tagente + WHERE id_agente='.$content['id_agent']; + $agent_data = db_get_row_sql($sql); + + $id_agent = $content['id_agent']; + $agent_alias = $agent_data['alias']; + $agent_group = groups_get_name($agent_data['id_grupo']); + $agent_description = $agent_data['comentarios']; + $agent_address = $agent_data['direccion']; + + $items_label = [ + 'id_agent' => $id_agent, + 'agent_description' => $agent_description, + 'agent_group' => $agent_group, + 'agent_address' => $agent_address, + 'agent_alias' => $agent_alias, + ]; + + // Apply macros. + $title = (isset($content['name'])) ? $content['name'] : ''; + if ($title != '') { + $title = reporting_label_macro( + $items_label, + $title + ); + } + + $return['title'] = $title; $return['landscape'] = $content['landscape']; $return['pagebreak'] = $content['pagebreak']; $return['description'] = $content['description']; @@ -4860,20 +5174,14 @@ function reporting_agent_configuration($report, $content) metaconsole_connect($server); } - $sql = ' - SELECT * - FROM tagente - WHERE id_agente='.$content['id_agent']; - $agent_data = db_get_row_sql($sql); - $agent_configuration = []; - $agent_configuration['name'] = $agent_data['alias']; - $agent_configuration['group'] = groups_get_name($agent_data['id_grupo']); + $agent_configuration['name'] = $agent_alias; + $agent_configuration['group'] = $agent_group; $agent_configuration['group_icon'] = ui_print_group_icon($agent_data['id_grupo'], true, '', '', false); $agent_configuration['os'] = os_get_name($agent_data['id_os']); $agent_configuration['os_icon'] = ui_print_os_icon($agent_data['id_os'], true, true); - $agent_configuration['address'] = $agent_data['direccion']; - $agent_configuration['description'] = $agent_data['comentarios']; + $agent_configuration['address'] = $agent_address; + $agent_configuration['description'] = $agent_description; $agent_configuration['enabled'] = (int) !$agent_data['disabled']; $agent_configuration['group'] = $report['group']; $modules = agents_get_modules($content['id_agent']); @@ -5026,13 +5334,59 @@ function reporting_value($report, $content, $type, $pdf=false) $content['id_agent_module'] ); - $return['title'] = $content['name']; + $id_agent = agents_get_module_id( + $content['id_agent_module'] + ); + + $id_agent_module = $content['id_agent_module']; + $agent_description = agents_get_description($id_agent); + $agent_group = agents_get_agent_group($id_agent); + $agent_address = agents_get_address($id_agent); + $agent_alias = agents_get_alias($id_agent); + $module_name = modules_get_agentmodule_name( + $id_agent_module + ); + + $module_description = modules_get_agentmodule_descripcion( + $id_agent_module + ); + + $items_label = [ + 'type' => $return['type'], + 'id_agent' => $id_agent, + 'id_agent_module' => $id_agent_module, + 'agent_description' => $agent_description, + 'agent_group' => $agent_group, + 'agent_address' => $agent_address, + 'agent_alias' => $agent_alias, + 'module_name' => $module_name, + 'module_description' => $module_description, + ]; + + // Apply macros + $title = (isset($content['name'])) ? $content['name'] : ''; + if ($title != '') { + $title = reporting_label_macro( + $items_label, + $title + ); + } + + $label = (isset($content['name'])) ? $content['name'] : ''; + if ($label != '') { + $label = reporting_label_macro( + $items_label, + $label + ); + } + + $return['title'] = $title; $return['landscape'] = $content['landscape']; $return['pagebreak'] = $content['pagebreak']; $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['label'] = $label; $return['agents'] = [$content['id_agent']]; $return['id_agent'] = $content['id_agent']; $return['id_agent_module'] = $content['id_agent_module']; @@ -7573,7 +7927,42 @@ function reporting_increment($report, $content) $content['name'] = __('Increment'); } - $return['title'] = $content['name']; + $id_agent = $content['id_agent']; + $id_agent_module = $content['id_agent_module']; + $agent_description = agents_get_description($id_agent); + $agent_group = agents_get_agent_group($id_agent); + $agent_address = agents_get_address($id_agent); + $agent_alias = agents_get_alias($id_agent); + $module_name = modules_get_agentmodule_name( + $id_agent_module + ); + + $module_description = modules_get_agentmodule_descripcion( + $id_agent_module + ); + + $items_label = [ + 'type' => $return['type'], + 'id_agent' => $id_agent, + 'id_agent_module' => $id_agent_module, + 'agent_description' => $agent_description, + 'agent_group' => $agent_group, + 'agent_address' => $agent_address, + 'agent_alias' => $agent_alias, + 'module_name' => $module_name, + 'module_description' => $module_description, + ]; + + // Apply macros + $title = (isset($content['name'])) ? $content['name'] : ''; + if ($title != '') { + $title = reporting_label_macro( + $items_label, + $title + ); + } + + $return['title'] = $title; $return['landscape'] = $content['landscape']; $return['pagebreak'] = $content['pagebreak']; $return['description'] = $content['description']; @@ -8197,6 +8586,10 @@ function reporting_custom_graph( $content['name'] = __('Simple graph'); } + $id_agent = agents_get_module_id( + $content['id_agent_module'] + ); + $agent_description = agents_get_description($id_agent); $agent_group = agents_get_agent_group($id_agent); $agent_address = agents_get_address($id_agent); @@ -8209,13 +8602,42 @@ function reporting_custom_graph( $id_agent_module ); - $return['title'] = $content['name']; + $items_label = [ + 'type' => $return['type'], + 'id_agent' => $id_agent, + 'id_agent_module' => $id_agent_module, + 'agent_description' => $agent_description, + 'agent_group' => $agent_group, + 'agent_address' => $agent_address, + 'agent_alias' => $agent_alias, + 'module_name' => $module_name, + 'module_description' => $module_description, + ]; + + // Apply macros + $title = (isset($content['name'])) ? $content['name'] : ''; + if ($title != '') { + $title = reporting_label_macro( + $items_label, + $title + ); + } + + $description = (isset($content['description'])) ? $content['description'] : ''; + if ($description != '') { + $description = reporting_label_macro( + $items_label, + $description + ); + } + + $return['title'] = $title; $return['landscape'] = $content['landscape']; $return['pagebreak'] = $content['pagebreak']; $return['subtitle'] = $graphs[0]['name']; $return['agent_name'] = $agent_alias; $return['module_name'] = $module_name; - $return['description'] = $content['description']; + $return['description'] = $description; $return['date'] = reporting_get_date_text( $report, $content @@ -8358,6 +8780,23 @@ function reporting_simple_graph( 'module_description' => $module_description, ]; + // Apply macros + $title = (isset($content['name'])) ? $content['name'] : ''; + if ($title != '') { + $title = reporting_label_macro( + $items_label, + $title + ); + } + + $description = (isset($content['description'])) ? $content['description'] : ''; + if ($description != '') { + $description = reporting_label_macro( + $items_label, + $description + ); + } + $label = (isset($content['style']['label'])) ? $content['style']['label'] : ''; if ($label != '') { $label = reporting_label_macro( @@ -8366,14 +8805,14 @@ function reporting_simple_graph( ); } - $return['title'] = io_safe_output($content['name']); + $return['title'] = $title; $return['landscape'] = $content['landscape']; $return['pagebreak'] = $content['pagebreak']; $return['subtitle'] = $agent_alias.' - '.$module_name; $return['agent_name_db'] = agents_get_name($id_agent); $return['agent_name'] = $agent_alias; $return['module_name'] = $module_name; - $return['description'] = io_safe_output($content['description']); + $return['description'] = $description; $return['date'] = reporting_get_date_text( $report, $content @@ -12858,11 +13297,57 @@ function reporting_module_histogram_graph($report, $content, $pdf=0) ) ); - $return['title'] = $content['name']; + $id_agent = agents_get_module_id( + $content['id_agent_module'] + ); + + $id_agent_module = $content['id_agent_module']; + $agent_description = agents_get_description($id_agent); + $agent_group = agents_get_agent_group($id_agent); + $agent_address = agents_get_address($id_agent); + $agent_alias = agents_get_alias($id_agent); + $module_name = modules_get_agentmodule_name( + $id_agent_module + ); + + $module_description = modules_get_agentmodule_descripcion( + $id_agent_module + ); + + $items_label = [ + 'type' => $return['type'], + 'id_agent' => $id_agent, + 'id_agent_module' => $id_agent_module, + 'agent_description' => $agent_description, + 'agent_group' => $agent_group, + 'agent_address' => $agent_address, + 'agent_alias' => $agent_alias, + 'module_name' => $module_name, + 'module_description' => $module_description, + ]; + + // Apply macros + $title = (isset($content['name'])) ? $content['name'] : ''; + if ($title != '') { + $title = reporting_label_macro( + $items_label, + $title + ); + } + + $description = (isset($content['description'])) ? $content['description'] : ''; + if ($description != '') { + $description = reporting_label_macro( + $items_label, + $description + ); + } + + $return['title'] = $title; $return['landscape'] = $content['landscape']; $return['pagebreak'] = $content['pagebreak']; $return['subtitle'] = $agent_name.' - '.$module_name; - $return['description'] = $content['description']; + $return['description'] = $description; $return['date'] = reporting_get_date_text( $report, $content From 4394a8cf6ae5cc356fdcd75e2504e3cee8cb4222 Mon Sep 17 00:00:00 2001 From: fbsanchez Date: Tue, 22 Sep 2020 16:55:46 +0200 Subject: [PATCH 002/263] wip discovery.cloud.gcp --- pandora_console/include/class/CredentialStore.class.php | 2 +- pandora_console/include/constants.php | 4 +++- pandora_server/lib/PandoraFMS/Recon/Base.pm | 2 ++ 3 files changed, 6 insertions(+), 2 deletions(-) diff --git a/pandora_console/include/class/CredentialStore.class.php b/pandora_console/include/class/CredentialStore.class.php index adf9d6ed06..616c56d0cb 100644 --- a/pandora_console/include/class/CredentialStore.class.php +++ b/pandora_console/include/class/CredentialStore.class.php @@ -875,7 +875,7 @@ class CredentialStore extends Wizard 'AWS' => __('Aws'), 'AZURE' => __('Azure'), 'SAP' => __('SAP'), - // 'GOOGLE' => __('Google'), + 'GOOGLE' => __('Google'), ], 'selected' => (isset($values['product']) ? $values['product'] : 'CUSTOM'), 'disabled' => (bool) $values['product'], diff --git a/pandora_console/include/constants.php b/pandora_console/include/constants.php index d32dacb1fe..7d955973c0 100644 --- a/pandora_console/include/constants.php +++ b/pandora_console/include/constants.php @@ -618,7 +618,8 @@ define('DISCOVERY_CLOUD_AZURE_COMPUTE', 8); define('DISCOVERY_DEPLOY_AGENTS', 9); define('DISCOVERY_APP_SAP', 10); define('DISCOVERY_APP_DB2', 11); - +define('DISCOVERY_APP_MICROSOFT_SQL_SERVER', 12); +define('DISCOVERY_CLOUD_GCP_COMPUTE_ENGINE', 13); // Force task build tmp results. define('DISCOVERY_REVIEW', 0); @@ -637,6 +638,7 @@ define('DISCOVERY_SCRIPT_IPMI_RECON', 4); // Discovery task descriptions. define('CLOUDWIZARD_AZURE_DESCRIPTION', 'Discovery.Cloud.Azure.Compute'); define('CLOUDWIZARD_AWS_DESCRIPTION', 'Discovery.Cloud.AWS.EC2'); +define('CLOUDWIZARD_GOOGLE_DESCRIPTION', 'Discovery.Cloud.GCP'); define('CLOUDWIZARD_VMWARE_DESCRIPTION', 'Discovery.App.VMware'); // Background options. diff --git a/pandora_server/lib/PandoraFMS/Recon/Base.pm b/pandora_server/lib/PandoraFMS/Recon/Base.pm index 1f6e13f2e6..aaf60eb581 100644 --- a/pandora_server/lib/PandoraFMS/Recon/Base.pm +++ b/pandora_server/lib/PandoraFMS/Recon/Base.pm @@ -38,9 +38,11 @@ use constant { DISCOVERY_CLOUD_AWS_EC2 => 6, DISCOVERY_CLOUD_AWS_RDS => 7, DISCOVERY_CLOUD_AZURE_COMPUTE => 8, + DISCOVERY_CLOUD_GCP_COMPUTE_ENGINE => 13, DISCOVERY_DEPLOY_AGENTS => 9, DISCOVERY_APP_SAP => 10, DISCOVERY_APP_DB2 => 11, + DISCOVERY_APP_MICROSOFT_SQL_SERVER => 12, DISCOVERY_REVIEW => 0, DISCOVERY_STANDARD => 1, DISCOVERY_RESULTS => 2, From 9ce54213b65c4c7d3c89b05f4a416b70c7af3745 Mon Sep 17 00:00:00 2001 From: fbsanchez Date: Wed, 23 Sep 2020 14:07:23 +0200 Subject: [PATCH 003/263] WIP GCP --- .../include/class/CredentialStore.class.php | 134 ++++++++++++++---- pandora_console/include/functions_html.php | 13 +- .../include/javascript/pandora_ui.js | 26 ++-- .../include/styles/credential_store.css | 11 +- 4 files changed, 136 insertions(+), 48 deletions(-) diff --git a/pandora_console/include/class/CredentialStore.class.php b/pandora_console/include/class/CredentialStore.class.php index 616c56d0cb..c609947e4a 100644 --- a/pandora_console/include/class/CredentialStore.class.php +++ b/pandora_console/include/class/CredentialStore.class.php @@ -248,7 +248,12 @@ class CredentialStore extends Wizard ); } else { $groups = [ $filter['filter_id_group'] ]; - $childrens = groups_get_childrens($id_group, null, true); + $childrens = groups_get_childrens( + $filter['filter_id_group'], + null, + true + ); + if (!empty($childrens)) { foreach ($childrens as $child) { $groups[] = (int) $child['id_grupo']; @@ -385,6 +390,8 @@ class CredentialStore extends Wizard // Decrypt content. $key['username'] = io_output_password($key['username']); $key['password'] = io_output_password($key['password']); + $key['extra_1'] = io_output_password($key['extra_1']); + $key['extra_2'] = io_output_password($key['extra_2']); return $key; } @@ -425,6 +432,8 @@ class CredentialStore extends Wizard function ($carry, $item) { $item['username'] = io_output_password($item['username']); $item['password'] = io_output_password($item['password']); + $item['extra_1'] = io_output_password($item['extra_1']); + $item['extra_2'] = io_output_password($item['extra_2']); $carry[$item['identifier']] = $item['identifier']; return $carry; } @@ -561,13 +570,28 @@ class CredentialStore extends Wizard $extra_1 = get_parameter('extra_1', null); $extra_2 = get_parameter('extra_2', null); - if (empty($identifier)) { + if ($product === 'GOOGLE') { + $google_creds = json_decode(io_safe_output($extra_1)); + + if (json_last_error() !== JSON_ERROR_NONE) { + $this->ajaxMsg( + 'error', + __('Not a valid JSON: %s', json_last_error_msg()) + ); + exit; + } + + $username = $google_creds->client_email; + $password = $google_creds->private_key_id; + } + + if (empty($identifier) === true) { $error = __('Key identifier is required'); } else if ($id_group === null) { $error = __('You must select a group where store this key!'); - } else if (empty($product)) { + } else if (empty($product) === true) { $error = __('You must specify a product type'); - } else if (empty($username) && (empty($password))) { + } else if (empty($username) === true && (empty($password) === true)) { $error = __('You must specify a username and/or password'); } @@ -583,8 +607,8 @@ class CredentialStore extends Wizard 'product' => $product, 'username' => io_input_password($username), 'password' => io_input_password($password), - 'extra_1' => $extra_1, - 'extra_2' => $extra_2, + 'extra_1' => io_input_password($extra_1), + 'extra_2' => io_input_password($extra_2), ]; // Spaces are not allowed. @@ -887,6 +911,9 @@ class CredentialStore extends Wizard $pass_label = __('Password'); $extra_1_label = __('Extra'); $extra_2_label = __('Extra (2)'); + $extra1_type = 'text'; + $user = true; + $pass = true; $extra1 = true; $extra2 = true; @@ -907,7 +934,14 @@ class CredentialStore extends Wizard break; case 'GOOGLE': - // Need further investigation. + $extra_1_label = __('Auth JSON'); + $user = false; + $pass = false; + $extra1 = true; + $extra2 = false; + $extra1_type = 'textarea'; + break; + case 'CUSTOM': case 'SAP': $user_label = __('Account ID'); @@ -919,29 +953,33 @@ class CredentialStore extends Wizard break; } - $inputs[] = [ - 'label' => $user_label, - 'id' => 'div-username', - 'arguments' => [ - 'name' => 'username', - 'input_class' => 'flex-row', - 'type' => 'text', - 'value' => $values['username'], - 'return' => true, - ], - ]; + if ($user) { + $inputs[] = [ + 'label' => $user_label, + 'id' => 'div-username', + 'arguments' => [ + 'name' => 'username', + 'input_class' => 'flex-row', + 'type' => 'text', + 'value' => $values['username'], + 'return' => true, + ], + ]; + } - $inputs[] = [ - 'label' => $pass_label, - 'id' => 'div-password', - 'arguments' => [ - 'name' => 'password', - 'input_class' => 'flex-row', - 'type' => 'password', - 'value' => $values['password'], - 'return' => true, - ], - ]; + if ($pass) { + $inputs[] = [ + 'label' => $pass_label, + 'id' => 'div-password', + 'arguments' => [ + 'name' => 'password', + 'input_class' => 'flex-row', + 'type' => 'password', + 'value' => $values['password'], + 'return' => true, + ], + ]; + } if ($extra1) { $inputs[] = [ @@ -949,8 +987,9 @@ class CredentialStore extends Wizard 'id' => 'div-extra_1', 'arguments' => [ 'name' => 'extra_1', + 'id' => 'text-extra_1', 'input_class' => 'flex-row', - 'type' => 'text', + 'type' => $extra1_type, 'value' => $values['extra_1'], 'return' => true, ], @@ -1031,14 +1070,30 @@ class CredentialStore extends Wizard * Handles inputs visibility based on selected product. */ function calculate_inputs() { + if ($('#product :selected').val() != "GOOGLE") { + // Restore text-extra_1. + var val = $('#text-extra_1').val(); + if(typeof val == 'undefined') { + val = ''; + } + $('#text-extra_1').remove(); + $('#div-extra_1').append( + $('') + ); + } + if ($('#product :selected').val() == "CUSTOM") { $('#div-username label').text(''); $('#div-password label').text(''); + $('#div-username').show(); + $('#div-password').show(); $('#div-extra_1').hide(); $('#div-extra_2').hide(); } else if ($('#product :selected').val() == "AWS") { $('#div-username label').text(''); $('#div-password label').text(''); + $('#div-username').show(); + $('#div-password').show(); $('#div-extra_1').hide(); $('#div-extra_2').hide(); } else if ($('#product :selected').val() == "AZURE") { @@ -1046,13 +1101,32 @@ class CredentialStore extends Wizard $('#div-password label').text(''); $('#div-extra_1 label').text(''); $('#div-extra_2 label').text(''); + $('#div-username').show(); + $('#div-password').show(); $('#div-extra_1').show(); $('#div-extra_2').show(); } else if ($('#product :selected').val() == "SAP") { $('#div-username label').text(''); $('#div-password label').text(''); + $('#div-username').show(); + $('#div-password').show(); $('#div-extra_1').hide(); $('#div-extra_2').hide(); + } else if ($('#product :selected').val() == "GOOGLE") { + $('#div-username').hide(); + $('#div-password').hide(); + $('#div-extra_2').hide(); + $('#div-extra_1 label').text(''); + var val = $('#text-extra_1').val(); + if(typeof val == 'undefined') { + val = ''; + } + + $('#text-extra_1').remove(); + $('#div-extra_1').append( + $('') + ); + $('#div-extra_1').show(); } } diff --git a/pandora_console/include/functions_html.php b/pandora_console/include/functions_html.php index 6b66357652..44d88bdcc4 100644 --- a/pandora_console/include/functions_html.php +++ b/pandora_console/include/functions_html.php @@ -2681,13 +2681,17 @@ function html_print_textarea( $attributes='', $return=false, $class='', - $disable=false + $disable=false, + $id=false ) { $disabled = ($disable) ? 'disabled' : ''; - $output = ''; - if ($return) { return $output; } @@ -4361,7 +4365,8 @@ function html_print_input($data, $wrapper='div', $input_only=false) ((isset($data['attributes']) === true) ? $data['attributes'] : ''), ((isset($data['return']) === true) ? $data['return'] : false), ((isset($data['class']) === true) ? $data['class'] : ''), - ((isset($data['disabled']) === true) ? $data['disabled'] : false) + ((isset($data['disabled']) === true) ? $data['disabled'] : false), + ((isset($data['id']) === true) ? $data['id'] : false) ); break; diff --git a/pandora_console/include/javascript/pandora_ui.js b/pandora_console/include/javascript/pandora_ui.js index 2445473591..efebf39e9f 100644 --- a/pandora_console/include/javascript/pandora_ui.js +++ b/pandora_console/include/javascript/pandora_ui.js @@ -249,22 +249,24 @@ function load_modal(settings) { }); } else { settings.form.forEach(function(element) { - $("#" + element + " :input").each(function() { - // TODO VALIDATE ALL INPUTS. - if (this.type == "file") { - if ($(this).prop("files")[0]) { - formdata.append(this.name, $(this).prop("files")[0]); - } - } else { - if ($(this).attr("type") == "checkbox") { - if (this.checked) { - formdata.append(this.name, "on"); + $("#" + element + " :input, #" + element + " textarea").each( + function() { + // TODO VALIDATE ALL INPUTS. + if (this.type == "file") { + if ($(this).prop("files")[0]) { + formdata.append(this.name, $(this).prop("files")[0]); } } else { - formdata.append(this.name, $(this).val()); + if ($(this).attr("type") == "checkbox") { + if (this.checked) { + formdata.append(this.name, "on"); + } + } else { + formdata.append(this.name, $(this).val()); + } } } - }); + ); }); } diff --git a/pandora_console/include/styles/credential_store.css b/pandora_console/include/styles/credential_store.css index 5707d90780..3f5ff8d23c 100644 --- a/pandora_console/include/styles/credential_store.css +++ b/pandora_console/include/styles/credential_store.css @@ -24,7 +24,7 @@ ul.wizard li { margin-right: 1em; } -form.modal ul.wizard li { +form#modal_form ul.wizard li { display: flex; flex-direction: row; width: 90%; @@ -32,7 +32,7 @@ form.modal ul.wizard li { justify-items: center; } -form.modal ul.wizard li * { +form#modal_form ul.wizard li * { flex: 1; } @@ -40,3 +40,10 @@ ul.wizard li.flex-indep { flex: 1; margin: 0; } + +form#modal_form ul.wizard li textarea { + font-size: 0.8em; + font-family: "Courier New", Courier, monospace; + flex: 1 1 400px; + max-width: 400px; +} From d292b06e9f6040d1c6b3fe81bb8cc30f594dec2f Mon Sep 17 00:00:00 2001 From: fbsanchez Date: Thu, 24 Sep 2020 18:35:50 +0200 Subject: [PATCH 004/263] WIP GCP CE --- pandora_server/lib/PandoraFMS/Core.pm | 8 ++++++++ pandora_server/lib/PandoraFMS/Tools.pm | 8 ++++++++ 2 files changed, 16 insertions(+) diff --git a/pandora_server/lib/PandoraFMS/Core.pm b/pandora_server/lib/PandoraFMS/Core.pm index 3eff8e2384..1b9ac457ee 100644 --- a/pandora_server/lib/PandoraFMS/Core.pm +++ b/pandora_server/lib/PandoraFMS/Core.pm @@ -3319,6 +3319,14 @@ sub pandora_get_credential ($$$) { $pa_config, safe_output($key->{'password'}) ); + $key->{'extra_1'} = pandora_output_password( + $pa_config, + safe_output($key->{'extra_1'}) + ); + $key->{'extra_2'} = pandora_output_password( + $pa_config, + safe_output($key->{'extra_2'}) + ); return $key; } diff --git a/pandora_server/lib/PandoraFMS/Tools.pm b/pandora_server/lib/PandoraFMS/Tools.pm index 300e66e5a6..f28985b744 100755 --- a/pandora_server/lib/PandoraFMS/Tools.pm +++ b/pandora_server/lib/PandoraFMS/Tools.pm @@ -88,6 +88,10 @@ our @EXPORT = qw( DISCOVERY_CLOUD_AWS_RDS DISCOVERY_CLOUD_AZURE_COMPUTE DISCOVERY_DEPLOY_AGENTS + DISCOVERY_APP_SAP + DISCOVERY_APP_DB2 + DISCOVERY_APP_MICROSOFT_SQL_SERVER + DISCOVERY_CLOUD_GCP_COMPUTE_ENGINE $DEVNULL $OS $OS_VERSION @@ -211,6 +215,10 @@ use constant DISCOVERY_CLOUD_AWS_EC2 => 6; use constant DISCOVERY_CLOUD_AWS_RDS => 7; use constant DISCOVERY_CLOUD_AZURE_COMPUTE => 8; use constant DISCOVERY_DEPLOY_AGENTS => 9; +use constant DISCOVERY_APP_SAP => 10; +use constant DISCOVERY_APP_DB2 => 11; +use constant DISCOVERY_APP_MICROSOFT_SQL_SERVER => 12; +use constant DISCOVERY_CLOUD_GCP_COMPUTE_ENGINE => 13; # Set OS, OS version and /dev/null our $OS = $^O; From b2d819aa29e5ca013374656f17553911953b6572 Mon Sep 17 00:00:00 2001 From: fbsanchez Date: Fri, 25 Sep 2020 19:35:19 +0200 Subject: [PATCH 005/263] Minor fixes --- pandora_console/include/class/CredentialStore.class.php | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/pandora_console/include/class/CredentialStore.class.php b/pandora_console/include/class/CredentialStore.class.php index c609947e4a..b8a29c16cc 100644 --- a/pandora_console/include/class/CredentialStore.class.php +++ b/pandora_console/include/class/CredentialStore.class.php @@ -605,10 +605,10 @@ class CredentialStore extends Wizard 'identifier' => $identifier, 'id_group' => $id_group, 'product' => $product, - 'username' => io_input_password($username), - 'password' => io_input_password($password), - 'extra_1' => io_input_password($extra_1), - 'extra_2' => io_input_password($extra_2), + 'username' => io_input_password(io_safe_output($username)), + 'password' => io_input_password(io_safe_output($password)), + 'extra_1' => io_input_password(io_safe_output($extra_1)), + 'extra_2' => io_input_password(io_safe_output($extra_2)), ]; // Spaces are not allowed. From 029a447e701cb74f6a19e6565941e8e645c83190 Mon Sep 17 00:00:00 2001 From: fbsanchez Date: Mon, 28 Sep 2020 13:55:26 +0200 Subject: [PATCH 006/263] gcp minor fixes --- pandora_console/godmode/wizards/DiscoveryTaskList.class.php | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/pandora_console/godmode/wizards/DiscoveryTaskList.class.php b/pandora_console/godmode/wizards/DiscoveryTaskList.class.php index 59a9fab84a..f1acf0c62a 100644 --- a/pandora_console/godmode/wizards/DiscoveryTaskList.class.php +++ b/pandora_console/godmode/wizards/DiscoveryTaskList.class.php @@ -1200,7 +1200,10 @@ class DiscoveryTaskList extends HTML $output = ''; // Header information. - if ((int) $task['status'] <= 0 && empty($summary)) { + if ((int) $task['status'] <= 0 + && empty($summary) + && $task['id_recon_script'] == 0 + ) { $output .= ui_print_info_message( __('This task has never executed'), '', From 8d5e3adf6a362c1043cd3d8c68595fed6b846b3f Mon Sep 17 00:00:00 2001 From: fbsanchez Date: Wed, 30 Sep 2020 13:09:48 +0200 Subject: [PATCH 007/263] WIP Omnishell common libraries --- pandora_agents/unix/pandora_agent | 349 +-------------- .../win32/omnishell/omnishell_client.pl | 21 + pandora_server/lib/PandoraFMS/Omnishell.pm | 400 ++++++++++++++++++ 3 files changed, 426 insertions(+), 344 deletions(-) create mode 100644 pandora_agents/win32/omnishell/omnishell_client.pl create mode 100644 pandora_server/lib/PandoraFMS/Omnishell.pm diff --git a/pandora_agents/unix/pandora_agent b/pandora_agents/unix/pandora_agent index 35caf972ea..50195aea2a 100755 --- a/pandora_agents/unix/pandora_agent +++ b/pandora_agents/unix/pandora_agent @@ -33,18 +33,6 @@ use IO::Socket; use Sys::Syslog; use Time::Local; -my $YAML = 0; -# Dynamic load. Avoid unwanted behaviour. -eval { - eval 'require YAML::Tiny;1' or die('YAML::Tiny lib not found, commands feature won\'t be available'); -}; -if ($@) { - $YAML = 0; - print STDERR $@; -} else { - $YAML = 1; -} - # Agent XML data my $Xml; @@ -1488,336 +1476,6 @@ sub check_collections () { } } -################################################################################ -# Check for remote commands defined. -################################################################################ -sub prepare_commands { - if ($YAML == 0) { - log_message( - 'error', - 'Cannot use commands without YAML dependency, please install it.' - ); - return; - } - - # Force configuration file read. - my @commands = read_config('cmd_file'); - - if (empty(\@commands)) { - $Conf{'commands'} = {}; - } else { - foreach my $rcmd (@commands) { - $Conf{'commands'}->{trim($rcmd)} = {}; - } - } - - # Cleanup old commands. Not registered. - cleanup_old_commands(); - - foreach my $ref (keys %{$Conf{'commands'}}) { - my $file_content; - my $download = 0; - my $rcmd_file = $ConfDir.'/commands/'.$ref.'.rcmd'; - - # Check for local .rcmd.done files - if (-e $rcmd_file.'.done') { - # Ignore. - delete $Conf{'commands'}->{$ref}; - next; - } - - # Search for local .rcmd file - if (-e $rcmd_file) { - my $remote_md5_file = $Conf{'temporal'}.'/'.$ref.'.md5'; - - $file_content = read_file($rcmd_file); - if (recv_file($ref.'.md5', $remote_md5_file) != 0) { - # Remote file could not be retrieved, skip. - delete $Conf{'commands'}->{$ref}; - next; - } - - my $local_md5 = md5($file_content); - my $remote_md5 = md5(read_file($remote_md5_file)); - - if ($local_md5 ne $remote_md5) { - # Must be downloaded again. - $download = 1; - } - } else { - $download = 1; - } - - # Search for remote .rcmd file - if ($download == 1) { - # Download .rcmd file - if (recv_file($ref.'.rcmd') != 0) { - # Remote file could not be retrieved, skip. - delete $Conf{'commands'}->{$ref}; - next; - } else { - # Success - move($Conf{'temporal'}.'/'.$ref.'.rcmd', $rcmd_file); - } - } - - # Parse and prepare in memory skel. - eval { - $Conf{'commands'}->{$ref} = YAML::Tiny->read($rcmd_file); - }; - if ($@) { - # Failed. - log_message('error', 'Failed to decode command. ' . "\n".$@); - delete $Conf{'commands'}->{$ref}; - next; - } - - } -} - -################################################################################ -# Command report. -################################################################################ -sub report_command { - my ($ref, $err_level) = @_; - - # Retrieve content from .stdout and .stderr - my $stdout_file = $Conf{'temporal'}.'/'.$ref.'.stdout'; - my $stderr_file = $Conf{'temporal'}.'/'.$ref.'.stderr'; - - my $return; - eval { - $return = { - 'error_level' => $err_level, - 'stdout' => read_file($stdout_file), - 'stderr' => read_file($stderr_file), - }; - - $return->{'name'} = $Conf{'commands'}->{$ref}->[0]->{'name'}; - }; - if ($@) { - log_message('error', 'Failed to report command output. ' . $@); - } - - # Cleanup - unlink($stdout_file) if (-e $stdout_file); - unlink($stderr_file) if (-e $stderr_file); - - # Mark command as done. - open (my $R_FILE, '> '.$ConfDir.'/commands/'.$ref.'.rcmd.done'); - print $R_FILE $err_level; - close($R_FILE); - - - $return->{'stdout'} = '' unless defined ($return->{'stdout'}); - $return->{'stderr'} = '' unless defined ($return->{'stderr'}); - - return $return; -} - -################################################################################ -# Cleanup unreferenced rcmd and rcmd.done files. -################################################################################ -sub cleanup_old_commands { - # Cleanup old .rcmd and .rcmd.done files. - my %registered = map { $_.'.rcmd' => 1 } keys %{$Conf{'commands'}}; - if(opendir(my $dir, $ConfDir.'/commands/')) { - while (my $item = readdir($dir)) { - - # Skip other files. - next if ($item !~ /\.rcmd$/); - - # Clean .rcmd.done file if its command is not referenced in conf. - if (!defined($registered{$item})) { - if (-e $ConfDir.'/commands/'.$item) { - unlink($ConfDir.'/commands/'.$item); - } - if (-e $ConfDir.'/commands/'.$item.'.done') { - unlink($ConfDir.'/commands/'.$item.'.done'); - } - } - } - - # Close dir. - closedir($dir); - } - -} - -################################################################################ -# Executes a command using defined timeout. -################################################################################ -sub execute_command_timeout { - my ($cmd, $timeout) = @_; - - if (!defined($timeout) - || !looks_like_number($timeout) - || $timeout <= 0 - ) { - `$cmd`; - return $?>>8; - } - - my $remaining_timeout = $timeout; - - my $RET; - my $output; - - my $pid = open ($RET, "-|"); - if (!defined($pid)) { - # Failed to fork. - log_message('error', '[command] Failed to fork.'); - return undef; - } - if ($pid == 0) { - # Child. - my $ret; - eval { - local $SIG{ALRM} = sub { die "timeout\n" }; - alarm $timeout; - `$cmd`; - alarm 0; - }; - - my $result = ($?>>8); - return $result; - - # Exit child. - # Child finishes. - exit; - - } else { - # Parent waiting. - while( --$remaining_timeout > 0 ){ - if (wait == -1) { - last; - } - # Wait child up to timeout seconds. - sleep 1; - } - } - - if ($remaining_timeout > 0) { - # Retrieve output from child. - $output = do { local $/; <$RET> }; - $output = $output>>8; - } - else { - # Timeout expired. - return 124; - } - - close($RET); - - return $output; -} - -################################################################################ -# Executes a block of commands, returns error level, leaves output in -# redirection set by $std_files. E.g: -# $std_files = ' >> /tmp/stdout 2>> /tmp/stderr -################################################################################ -sub execute_command_block { - my ($commands, $std_files, $timeout, $retry) = @_; - - return 0 unless defined($commands); - - my $retries = $retry; - - $retries = 1 unless looks_like_number($retries) && $retries > 0; - - my $err_level = 0; - $std_files = '' unless defined ($std_files); - - if (ref($commands) ne "ARRAY") { - return 0 if $commands eq ''; - - do { - $err_level = execute_command_timeout( - "($commands) $std_files", - $timeout - ); - - # Do not retry if success. - last if looks_like_number($err_level) && $err_level == 0; - } while ((--$retries) > 0); - - } else { - foreach my $comm (@{$commands}) { - next unless defined($comm); - $retries = $retry; - $retries = 1 unless looks_like_number($retries) && $retries > 0; - - do { - $err_level = execute_command_timeout( - "($comm) $std_files", - $timeout - ); - - # Do not retry if success. - $retries = 0 if looks_like_number($err_level) && $err_level == 0; - - } while ((--$retries) > 0); - - # Do not continue evaluating block if failed. - last unless ($err_level == 0); - } - } - - return $err_level; -} - -################################################################################ -# Evalate given command. -################################################################################ -sub evaluate_command { - my ($ref) = @_; - - # Not found. - return unless defined $Conf{'commands'}->{$ref}; - - # Already completed. - return if (-e $ConfDir.'/commands/'.$ref.'.rcmd.done'); - - # [0] because how library works. - my $cmd = $Conf{'commands'}->{$ref}->[0]; - - my $std_files = ' >> '.$Conf{'temporal'}.'/'.$ref.'.stdout '; - $std_files .= ' 2>> '.$Conf{'temporal'}.'/'.$ref.'.stderr '; - - # Check preconditions - my $err_level; - - $err_level = execute_command_block( - $cmd->{'preconditions'}, - $std_files, - $cmd->{'timeout'} - ); - - # Precondition not satisfied. - return report_command($ref, $err_level) unless ($err_level == 0); - - # Main run. - $err_level = execute_command_block( - $cmd->{'script'}, - $std_files, - $cmd->{'timeout'} - ); - - # Script not success. - return report_command($ref, $err_level) unless ($err_level == 0); - - # Check postconditions - $err_level = execute_command_block( - $cmd->{'postconditions'}, - $std_files, - $cmd->{'timeout'} - ); - - # Return results. - return report_command($ref, $err_level); -} - ################################################################################ # Sleep function ################################################################################ @@ -3453,8 +3111,11 @@ while (1) { # Check file collections check_collections () unless ($Conf{'debug'} eq '1'); - # Check scheduled commands - prepare_commands() unless ($Conf{'debug'} eq '1'); + if ($Conf{'debug'} ne '1') { + # Check scheduled commands + my $omni = new PandoraFMS::Omnishell(\%Conf); + $omni->prepare_commands(); + } # Launch broker agents @BrokerPid = (); diff --git a/pandora_agents/win32/omnishell/omnishell_client.pl b/pandora_agents/win32/omnishell/omnishell_client.pl new file mode 100644 index 0000000000..5abfec8797 --- /dev/null +++ b/pandora_agents/win32/omnishell/omnishell_client.pl @@ -0,0 +1,21 @@ +#!/usr/bin/perl +################################################################################ +# Pandora FMS Omnishell client. +# +# (c) Fco de Borja Sánchez +# +################################################################################ +use strict; +use warnings; + +use lib '/usr/lib/perl5'; +use PandoraFMS::Tools; +use PandoraFMS::Omnishell; + +my %Conf; + +if ($Conf{'debug'} ne '1') { + # Check scheduled commands + my $omni = new PandoraFMS::Omnishell(\%Conf); + $omni->prepare_commands(); +} \ No newline at end of file diff --git a/pandora_server/lib/PandoraFMS/Omnishell.pm b/pandora_server/lib/PandoraFMS/Omnishell.pm new file mode 100644 index 0000000000..b47955470c --- /dev/null +++ b/pandora_server/lib/PandoraFMS/Omnishell.pm @@ -0,0 +1,400 @@ +package PandoraFMS::Omnishell; +################################################################################ +# Pandora FMS Omnishell common functions. +# +# (c) Fco de Borja Sánchez +# +################################################################################ +use strict; +use warnings; + +my $YAML = 0; +# Dynamic load. Avoid unwanted behaviour. +eval { + eval 'require YAML::Tiny;1' or die('YAML::Tiny lib not found, commands feature won\'t be available'); +}; +if ($@) { + $YAML = 0; +} else { + $YAML = 1; +} + +use lib '/usr/lib/perl5'; + +our @ISA = ("Exporter"); +our %EXPORT_TAGS = ( 'all' => [ qw( ) ] ); +our @EXPORT_OK = ( @{ $EXPORT_TAGS{'all'} } ); +our @EXPORT = qw(); + + +################################################################################ +# return last error. +################################################################################ +sub get_last_error { + my ($self) = @_; + + if (!is_empty($self->{'last_error'})) { + return $self->{'last_error'}; + } + + return ''; +} + + +################################################################################ +# Create new omnishell handler. +################################################################################ +sub new { + my ($class,$args) = @_; + + if (ref($args) ne 'HASH') { + return undef; + } + + my $self = { + 'last_error' => undef, + %{$args} + }; + + + $self = bless($self, $class); + + return $self; +} + + +################################################################################ +# Check for remote commands defined. +################################################################################ +sub prepare_commands { + my ($self) = @_; + + if ($YAML == 0) { + log_message( + 'error', + 'Cannot use commands without YAML dependency, please install it.' + ); + return; + } + + # Force configuration file read. + my @commands = read_config('cmd_file'); + + if (empty(\@commands)) { + $self->{'commands'} = {}; + } else { + foreach my $rcmd (@commands) { + $self->{'commands'}->{trim($rcmd)} = {}; + } + } + + # Cleanup old commands. Not registered. + cleanup_old_commands(); + + foreach my $ref (keys %{$self->{'commands'}}) { + my $file_content; + my $download = 0; + my $rcmd_file = $self->{'ConfDir'}.'/commands/'.$ref.'.rcmd'; + + # Check for local .rcmd.done files + if (-e $rcmd_file.'.done') { + # Ignore. + delete $self->{'commands'}->{$ref}; + next; + } + + # Search for local .rcmd file + if (-e $rcmd_file) { + my $remote_md5_file = $self->{'temporal'}.'/'.$ref.'.md5'; + + $file_content = read_file($rcmd_file); + if (recv_file($ref.'.md5', $remote_md5_file) != 0) { + # Remote file could not be retrieved, skip. + delete $self->{'commands'}->{$ref}; + next; + } + + my $local_md5 = md5($file_content); + my $remote_md5 = md5(read_file($remote_md5_file)); + + if ($local_md5 ne $remote_md5) { + # Must be downloaded again. + $download = 1; + } + } else { + $download = 1; + } + + # Search for remote .rcmd file + if ($download == 1) { + # Download .rcmd file + if (recv_file($ref.'.rcmd') != 0) { + # Remote file could not be retrieved, skip. + delete $self->{'commands'}->{$ref}; + next; + } else { + # Success + move($self->{'temporal'}.'/'.$ref.'.rcmd', $rcmd_file); + } + } + + # Parse and prepare in memory skel. + eval { + $self->{'commands'}->{$ref} = YAML::Tiny->read($rcmd_file); + }; + if ($@) { + # Failed. + log_message('error', 'Failed to decode command. ' . "\n".$@); + delete $self->{'commands'}->{$ref}; + next; + } + + } +} + +################################################################################ +# Command report. +################################################################################ +sub report_command { + my ($self, $ref, $err_level) = @_; + + # Retrieve content from .stdout and .stderr + my $stdout_file = $self->{'temporal'}.'/'.$ref.'.stdout'; + my $stderr_file = $self->{'temporal'}.'/'.$ref.'.stderr'; + + my $return; + eval { + $return = { + 'error_level' => $err_level, + 'stdout' => read_file($stdout_file), + 'stderr' => read_file($stderr_file), + }; + + $return->{'name'} = $self->{'commands'}->{$ref}->[0]->{'name'}; + }; + if ($@) { + log_message('error', 'Failed to report command output. ' . $@); + } + + # Cleanup + unlink($stdout_file) if (-e $stdout_file); + unlink($stderr_file) if (-e $stderr_file); + + # Mark command as done. + open (my $R_FILE, '> '.$self->{'ConfDir'}.'/commands/'.$ref.'.rcmd.done'); + print $R_FILE $err_level; + close($R_FILE); + + + $return->{'stdout'} = '' unless defined ($return->{'stdout'}); + $return->{'stderr'} = '' unless defined ($return->{'stderr'}); + + return $return; +} + +################################################################################ +# Cleanup unreferenced rcmd and rcmd.done files. +################################################################################ +sub cleanup_old_commands { + my ($self) = @_; + + # Cleanup old .rcmd and .rcmd.done files. + my %registered = map { $_.'.rcmd' => 1 } keys %{$self->{'commands'}}; + if(opendir(my $dir, $self->{'ConfDir'}.'/commands/')) { + while (my $item = readdir($dir)) { + + # Skip other files. + next if ($item !~ /\.rcmd$/); + + # Clean .rcmd.done file if its command is not referenced in conf. + if (!defined($registered{$item})) { + if (-e $self->{'ConfDir'}.'/commands/'.$item) { + unlink($self->{'ConfDir'}.'/commands/'.$item); + } + if (-e $self->{'ConfDir'}.'/commands/'.$item.'.done') { + unlink($self->{'ConfDir'}.'/commands/'.$item.'.done'); + } + } + } + + # Close dir. + closedir($dir); + } + +} + +################################################################################ +# Executes a command using defined timeout. +################################################################################ +sub execute_command_timeout { + my ($cmd, $timeout) = @_; + + if (!defined($timeout) + || !looks_like_number($timeout) + || $timeout <= 0 + ) { + `$cmd`; + return $?>>8; + } + + my $remaining_timeout = $timeout; + + my $RET; + my $output; + + my $pid = open ($RET, "-|"); + if (!defined($pid)) { + # Failed to fork. + log_message('error', '[command] Failed to fork.'); + return undef; + } + if ($pid == 0) { + # Child. + my $ret; + eval { + local $SIG{ALRM} = sub { die "timeout\n" }; + alarm $timeout; + `$cmd`; + alarm 0; + }; + + my $result = ($?>>8); + return $result; + + # Exit child. + # Child finishes. + exit; + + } else { + # Parent waiting. + while( --$remaining_timeout > 0 ){ + if (wait == -1) { + last; + } + # Wait child up to timeout seconds. + sleep 1; + } + } + + if ($remaining_timeout > 0) { + # Retrieve output from child. + $output = do { local $/; <$RET> }; + $output = $output>>8; + } + else { + # Timeout expired. + return 124; + } + + close($RET); + + return $output; +} + +################################################################################ +# Executes a block of commands, returns error level, leaves output in +# redirection set by $std_files. E.g: +# $std_files = ' >> /tmp/stdout 2>> /tmp/stderr +################################################################################ +sub execute_command_block { + my ($commands, $std_files, $timeout, $retry) = @_; + + return 0 unless defined($commands); + + my $retries = $retry; + + $retries = 1 unless looks_like_number($retries) && $retries > 0; + + my $err_level = 0; + $std_files = '' unless defined ($std_files); + + if (ref($commands) ne "ARRAY") { + return 0 if $commands eq ''; + + do { + $err_level = execute_command_timeout( + "($commands) $std_files", + $timeout + ); + + # Do not retry if success. + last if looks_like_number($err_level) && $err_level == 0; + } while ((--$retries) > 0); + + } else { + foreach my $comm (@{$commands}) { + next unless defined($comm); + $retries = $retry; + $retries = 1 unless looks_like_number($retries) && $retries > 0; + + do { + $err_level = execute_command_timeout( + "($comm) $std_files", + $timeout + ); + + # Do not retry if success. + $retries = 0 if looks_like_number($err_level) && $err_level == 0; + + } while ((--$retries) > 0); + + # Do not continue evaluating block if failed. + last unless ($err_level == 0); + } + } + + return $err_level; +} + +################################################################################ +# Evalate given command. +################################################################################ +sub evaluate_command { + my ($self, $ref) = @_; + + # Not found. + return unless defined $self->{'commands'}->{$ref}; + + # Already completed. + return if (-e $self->{'ConfDir'}.'/commands/'.$ref.'.rcmd.done'); + + # [0] because how library works. + my $cmd = $self->{'commands'}->{$ref}->[0]; + + my $std_files = ' >> '.$self->{'temporal'}.'/'.$ref.'.stdout '; + $std_files .= ' 2>> '.$self->{'temporal'}.'/'.$ref.'.stderr '; + + # Check preconditions + my $err_level; + + $err_level = execute_command_block( + $cmd->{'preconditions'}, + $std_files, + $cmd->{'timeout'} + ); + + # Precondition not satisfied. + return report_command($ref, $err_level) unless ($err_level == 0); + + # Main run. + $err_level = execute_command_block( + $cmd->{'script'}, + $std_files, + $cmd->{'timeout'} + ); + + # Script not success. + return report_command($ref, $err_level) unless ($err_level == 0); + + # Check postconditions + $err_level = execute_command_block( + $cmd->{'postconditions'}, + $std_files, + $cmd->{'timeout'} + ); + + # Return results. + return report_command($ref, $err_level); +} + +1; \ No newline at end of file From f8666adf0f0fc51d13072c8954d044ae99bcc148 Mon Sep 17 00:00:00 2001 From: marcos Date: Wed, 30 Sep 2020 14:20:57 +0200 Subject: [PATCH 008/263] F2 feature --- pandora_console/godmode/setup/setup_auth.php | 31 ++++++++++++++++++++ pandora_console/include/functions_config.php | 4 +++ 2 files changed, 35 insertions(+) diff --git a/pandora_console/godmode/setup/setup_auth.php b/pandora_console/godmode/setup/setup_auth.php index db436224c3..9ba6c00c4f 100644 --- a/pandora_console/godmode/setup/setup_auth.php +++ b/pandora_console/godmode/setup/setup_auth.php @@ -221,6 +221,20 @@ if (is_ajax()) { ); $table->data['double_auth_enabled'] = $row; + // Enable 2FA for all users. + // Set default value. + set_unless_defined($config['2FA_all_users'], false); + $row = []; + $row['name'] = __('Force 2FA for all users is enabled'); + $row['control'] .= html_print_checkbox_switch( + ' + ', + 1, + $config['2FA_all_users'], + true + ); + $table->data['2FA_all_users'] = $row; + // Session timeout. // Default session timeout. set_when_empty($config['session_timeout'], 90); @@ -313,6 +327,23 @@ echo ''; ?> + '; +} + $newsletter = null; ?> diff --git a/pandora_console/godmode/setup/setup_auth.php b/pandora_console/godmode/setup/setup_auth.php index acd4e99833..273fa17de5 100644 --- a/pandora_console/godmode/setup/setup_auth.php +++ b/pandora_console/godmode/setup/setup_auth.php @@ -329,12 +329,20 @@ echo ''; $( document ).ready(function() { //For change autocreate remote users + console.log($('input[type=checkbox][id=checkbox-double_auth_enabled]:checked').val()); + if ($('input[type=checkbox][name=double_auth_enabled]:checked').val() == 1) { + $('#table1-2FA_all_users').show(); + } + else { + $('#table1-2FA_all_users').hide(); + } $('input[type=checkbox][name=double_auth_enabled]').change(function () { if ($('input[type=checkbox][name=double_auth_enabled]:checked').val() == 1) { $('#table1-2FA_all_users').show(); } else { $('#table1-2FA_all_users').hide(); + $('input[type=checkbox][name=2FA_all_users][value=0]').prop('checked', false); } }).change(); }); diff --git a/pandora_console/godmode/users/configure_user.php b/pandora_console/godmode/users/configure_user.php index b98d35e003..daac4addeb 100644 --- a/pandora_console/godmode/users/configure_user.php +++ b/pandora_console/godmode/users/configure_user.php @@ -1128,6 +1128,28 @@ if ($config['ehorus_user_level_conf']) { $ehorus .= ''; } +$double_auth_enabled = (bool) db_get_value('id', 'tuser_double_auth', 'id_user', $id); + +if ($config['double_auth_enabled'] && check_acl($config['id_user'], 0, 'PM')) { + $double_authentication = '

'.__('Double authentication').'

'; + if (($config['2FA_all_users'] == '' && !$double_auth_enabled) + || ($config['double_auth_enabled'] == '' && $double_auth_enabled) + || check_acl($config['id_user'], 0, 'PM') + ) { + $double_authentication .= html_print_checkbox_switch('double_auth', 1, $double_auth_enabled, true); + } + + // Dialog. + $double_authentication .= ''; +} + +if ($double_auth_enabled && $config['double_auth_enabled'] && $config['2FA_all_users'] != '') { + $double_authentication .= html_print_button(__('Show information'), 'show_info', false, 'javascript:show_double_auth_info();', '', true); +} + +if (isset($double_authentication)) { + $double_authentication .= '
'; +} if ($meta) { enterprise_include_once('include/functions_metaconsole.php'); @@ -1179,7 +1201,7 @@ echo '

Extra info

'.$email.$phone.$not_login.$session_time.'
-
'.$language.$access_or_pagination.$skin.$home_screen.$default_event_filter.$newsletter.'
+
'.$language.$access_or_pagination.$skin.$home_screen.$default_event_filter.$newsletter.$double_authentication.'
'.$timezone; if (!is_metaconsole()) { @@ -1278,6 +1300,15 @@ if (!is_metaconsole()) { var json_profile = $('#hidden-json_profile'); /* '; + + var $loadingSpinner = $("/images/spinner.gif\" />"); + var $dialogContainer = $("div#dialog-double_auth-container"); + + $dialogContainer.html($loadingSpinner); +console.log(userID); + // Load the info page + var request = $.ajax({ + url: "", + type: 'POST', + dataType: 'html', + data: { + page: 'include/ajax/double_auth.ajax', + id_user: userID, + get_double_auth_data_page: 1, + FA_forced: 1, + containerID: $dialogContainer.prop('id') + }, + complete: function(xhr, textStatus) { + + }, + success: function(data, textStatus, xhr) { + // isNaN = is not a number + if (isNaN(data)) { + $dialogContainer.html(data); + } + // data is a number, convert it to integer to do the compare + else if (Number(data) === -1) { + $dialogContainer.html("
'.__('Authentication error').'
'; ?>"); + } + else { + $dialogContainer.html("
'.__('Error').'
'; ?>"); + } + }, + error: function(xhr, textStatus, errorThrown) { + $dialogContainer.html("
'.__('There was an error loading the data').'
'; ?>"); + } + }); + + $("div#dialog-double_auth") + .css('display','block') + .append($dialogContainer) + .dialog({ + resizable: true, + draggable: true, + modal: true, + title: "", + overlay: { + opacity: 0.5, + background: "black" + }, + width: 400, + height: 375, + close: function(event, ui) { + // Abort the ajax request + if (typeof request != 'undefined') + request.abort(); + // Remove the contained html + $dialogContainer.empty(); + } + }) + .show(); + +} + +function show_double_auth_activation () { + var userID = ''; + + var $loadingSpinner = $("/images/spinner.gif\" />"); + var $dialogContainer = $("div#dialog-double_auth-container"); + + $dialogContainer.html($loadingSpinner); + + // Load the info page + var request = $.ajax({ + url: "", + type: 'POST', + dataType: 'html', + data: { + page: 'include/ajax/double_auth.ajax', + id_user: userID, + FA_forced: 1, + get_double_auth_info_page: 1, + containerID: $dialogContainer.prop('id') + }, + complete: function(xhr, textStatus) { + + }, + success: function(data, textStatus, xhr) { + // isNaN = is not a number + if (isNaN(data)) { + $dialogContainer.html(data); + } + // data is a number, convert it to integer to do the compare + else if (Number(data) === -1) { + $dialogContainer.html("
'.__('Authentication error').'
'; ?>"); + } + else { + $dialogContainer.html("
'.__('Error').'
'; ?>"); + } + }, + error: function(xhr, textStatus, errorThrown) { + $dialogContainer.html("
'.__('There was an error loading the data').'
'; ?>"); + } + }); + + $("div#dialog-double_auth").dialog({ + resizable: true, + draggable: true, + modal: true, + title: "", + overlay: { + opacity: 0.5, + background: "black" + }, + width: 500, + height: 400, + close: function(event, ui) { + // Abort the ajax request + if (typeof request != 'undefined') + request.abort(); + // Remove the contained html + $dialogContainer.empty(); + + document.location.reload(); + } + }) + .show(); +} + +function show_double_auth_deactivation () { + var userID = ''; + console.log(userID); + var $loadingSpinner = $("/images/spinner.gif\" />"); + var $dialogContainer = $("div#dialog-double_auth-container"); + + var message = "

'.__('The double authentication will be deactivated'); ?>

"; + var $button = $("\" />"); + + $dialogContainer + .empty() + .append(message) + .append($button); + + var request; + + $button.click(function(e) { + e.preventDefault(); + + $dialogContainer.html($loadingSpinner); + + // Deactivate the double auth + request = $.ajax({ + url: "", + type: 'POST', + dataType: 'json', + data: { + page: 'include/ajax/double_auth.ajax', + id_user: userID, + FA_forced: 1, + deactivate_double_auth: 1 + }, + complete: function(xhr, textStatus) { + + }, + success: function(data, textStatus, xhr) { + console.log(data); + if (data === -1) { + $dialogContainer.html("
'.__('Authentication error').'
'; ?>"); + } + else if (data) { + $dialogContainer.html("
'.__('The double autentication was deactivated successfully').'
'; ?>"); + } + else { + $dialogContainer.html("
'.__('There was an error deactivating the double autentication').'
'; ?>"); + } + }, + error: function(xhr, textStatus, errorThrown) { + $dialogContainer.html("
'.__('There was an error deactivating the double autentication').'
'; ?>"); + } + }); + }); + + + $("div#dialog-double_auth").dialog({ + resizable: true, + draggable: true, + modal: true, + title: "", + overlay: { + opacity: 0.5, + background: "black" + }, + width: 300, + height: 150, + close: function(event, ui) { + // Abort the ajax request + if (typeof request != 'undefined') + request.abort(); + // Remove the contained html + $dialogContainer.empty(); + + document.location.reload(); + } + }) + .show(); +} + + /* ]]> */ diff --git a/pandora_console/include/ajax/double_auth.ajax.php b/pandora_console/include/ajax/double_auth.ajax.php index 7633a28979..2f2970ee23 100644 --- a/pandora_console/include/ajax/double_auth.ajax.php +++ b/pandora_console/include/ajax/double_auth.ajax.php @@ -17,7 +17,9 @@ check_login(); // Security check $id_user = (string) get_parameter('id_user'); -if ($id_user !== $config['id_user']) { +$FA_forced = (int) get_parameter('FA_forced'); + +if ($id_user !== $config['id_user'] && $FA_forced != 1) { db_pandora_audit( 'ACL Violation', 'Trying to access Double Authentication' diff --git a/pandora_console/include/functions_config.php b/pandora_console/include/functions_config.php index f4c48ec0bb..d65720b44f 100644 --- a/pandora_console/include/functions_config.php +++ b/pandora_console/include/functions_config.php @@ -1990,6 +1990,10 @@ function config_process_config() config_update_value('welcome_state', WELCOME_STARTED); } + if (!isset($config['2Fa_auth'])) { + config_update_value('2Fa_auth', ''); + } + /* * Parse the ACL IP list for access API */ diff --git a/pandora_console/index.php b/pandora_console/index.php index af8671ded7..289a91b3f1 100755 --- a/pandora_console/index.php +++ b/pandora_console/index.php @@ -389,6 +389,7 @@ if (! isset($config['id_user'])) { } $login_button_saml = get_parameter('login_button_saml', false); + config_update_value('2Fa_auth', ''); if (isset($double_auth_success) && $double_auth_success) { // This values are true cause there are checked before complete // the 2nd auth step. @@ -709,6 +710,8 @@ if (! isset($config['id_user'])) { $redirect_url .= '&'.safe_url_extraclean($key).'='.safe_url_extraclean($value); } + $double_auth_enabled = (bool) db_get_value('id', 'tuser_double_auth', 'id_user', $config['id_user']); + header('Location: '.ui_get_full_url('index.php'.$redirect_url)); exit; // Always exit after sending location headers. diff --git a/pandora_console/operation/users/user_edit.php b/pandora_console/operation/users/user_edit.php index 1e04ad89e0..b55a0952e4 100644 --- a/pandora_console/operation/users/user_edit.php +++ b/pandora_console/operation/users/user_edit.php @@ -393,11 +393,13 @@ $timezone .= html_print_timezone_select('timezone', $user_info['timezone']).''; - if ($config['double_auth_enabled']) { - $double_authentication .= html_print_checkbox_switch('double_auth', 1, 1, true); - } else { + if (($config['2FA_all_users'] == '' && !$double_auth_enabled) + || ($config['2FA_all_users'] != '' && !$double_auth_enabled) + || ($config['double_auth_enabled'] == '' && $double_auth_enabled) + || check_acl($config['id_user'], 0, 'PM') + ) { $double_authentication .= html_print_checkbox_switch('double_auth', 1, $double_auth_enabled, true); } @@ -405,7 +407,7 @@ if ($config['double_auth_enabled']) { $double_authentication .= ''; } -if ($double_auth_enabled) { +if ($double_auth_enabled && $config['double_auth_enabled'] && $config['2FA_all_users'] != '') { $double_authentication .= html_print_button(__('Show information'), 'show_info', false, 'javascript:show_double_auth_info();', '', true); } @@ -883,17 +885,14 @@ $(document).ready (function () { $("#text-block_size").removeAttr('disabled'); } } - var fa_enable = $("input#checkbox-double_auth").change(function (e) { e.preventDefault(); - if(fa_enable != 1){ if (this.checked) { show_double_auth_activation(); } else { show_double_auth_deactivation(); } - } }); From 929310d0e92b360d328c8193e3bb6faa4e055bc4 Mon Sep 17 00:00:00 2001 From: alejandro-campos Date: Tue, 6 Oct 2020 17:33:34 +0200 Subject: [PATCH 020/263] fixed bad module id reference --- pandora_console/include/functions_modules.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pandora_console/include/functions_modules.php b/pandora_console/include/functions_modules.php index 81991face5..fef8c7d302 100755 --- a/pandora_console/include/functions_modules.php +++ b/pandora_console/include/functions_modules.php @@ -2359,7 +2359,7 @@ function modules_get_agentmodule_data_for_humans($module) } else { $salida = ui_print_module_string_value( $module['datos'], - $module['id_agente_modulo'], + $module['id'], $module['current_interval'], $module['module_name'] ); From 5a5fb6eeeab3944b3d5390db6397c0a4c5443de4 Mon Sep 17 00:00:00 2001 From: fbsanchez Date: Wed, 7 Oct 2020 14:49:46 +0200 Subject: [PATCH 021/263] Allow define time start greater than time end in weekly planed downtimes --- pandora_console/godmode/agentes/planned_downtime.editor.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pandora_console/godmode/agentes/planned_downtime.editor.php b/pandora_console/godmode/agentes/planned_downtime.editor.php index b3e7029dbd..aab9d9360d 100644 --- a/pandora_console/godmode/agentes/planned_downtime.editor.php +++ b/pandora_console/godmode/agentes/planned_downtime.editor.php @@ -354,8 +354,8 @@ if ($create_downtime || $update_downtime) { __('Not created. Error inserting data').'. '.__('The end date must be higher than the current time') ); } else if ($type_execution == 'periodically' - && (($type_periodicity == 'weekly' && $periodically_time_from >= $periodically_time_to) - || ($type_periodicity == 'monthly' && $periodically_day_from == $periodically_day_to && $periodically_time_from >= $periodically_time_to)) + && $type_periodicity == 'monthly' + && $periodically_day_from == $periodically_day_to ) { ui_print_error_message( __('Not created. Error inserting data').'. '.__('The end time must be higher than the start time') From 2e0321fc6e2a75cdfcee0ab41826a176a64cfd1f Mon Sep 17 00:00:00 2001 From: fbsanchez Date: Wed, 7 Oct 2020 18:48:17 +0200 Subject: [PATCH 022/263] Limit server down notifications once a day --- .../include/class/ConsoleSupervisor.php | 45 +++++++++++++------ 1 file changed, 32 insertions(+), 13 deletions(-) diff --git a/pandora_console/include/class/ConsoleSupervisor.php b/pandora_console/include/class/ConsoleSupervisor.php index 5caad6f9e8..62acd97a93 100644 --- a/pandora_console/include/class/ConsoleSupervisor.php +++ b/pandora_console/include/class/ConsoleSupervisor.php @@ -45,6 +45,13 @@ enterprise_include_once('include/functions_cron.php'); class ConsoleSupervisor { + /** + * Being executed while navigation. + * + * @var boolean + */ + private $quick; + /** * Show if console supervisor is enabled or not. * @@ -149,8 +156,6 @@ class ConsoleSupervisor */ public function runBasic() { - global $config; - /* * PHP configuration warnings: * NOTIF.PHP.SAFE_MODE @@ -213,30 +218,35 @@ class ConsoleSupervisor * Check if the Server and Console has * the same versions. */ + $this->checkConsoleServerVersions(); /* * Check if AllowOverride is None or All. * NOTIF.ALLOWOVERIDE.MESSAGE */ + $this->checkAllowOverrideEnabled(); /* * Check if AllowOverride is None or All. * NOTIF.HAMASTER.MESSAGE */ + $this->checkHaStatus(); /* * Check if the Pandora Console log * file remains in old location. */ + $this->checkPandoraConsoleLogOldLocation(); /* * Check if the audit log file * remains in old location. */ + $this->checkAuditLogOldLocation(); } @@ -462,16 +472,19 @@ class ConsoleSupervisor * Check if the Server and Console has * the same versions. */ + $this->checkConsoleServerVersions(); /* * Check if AllowOverride is None or All. */ - $this->checkAllowOverrideEnabled(); - /* - * Check if HA status. - */ + $this->checkAllowOverrideEnabled(); + + /* + * Check if HA status. + */ + if (enterprise_installed()) { $this->checkHaStatus(); } @@ -480,11 +493,13 @@ class ConsoleSupervisor * Check if the audit log file * remains in old location. */ + $this->checkAuditLogOldLocation(); /* - Check if AllowOverride is None or All. - */ + * Check if AllowOverride is None or All. + */ + $this->checkAllowOverrideEnabled(); } @@ -594,7 +609,7 @@ class ConsoleSupervisor public function notify( array $data, int $source_id=0, - int $max_age=86400 + int $max_age=SECONDS_1DAY ) { // Uses 'check failed' logic. if (is_array($data) === false) { @@ -663,8 +678,8 @@ class ConsoleSupervisor // NOTIF.SERVER.MASTER. // NOTIF.SERVER.STATUS.ID_SERVER. if (preg_match('/^NOTIF.SERVER/', $data['type']) === true) { - // Component notifications require be inmediate. - $max_age = 0; + // Send notification once a day. + $max_age = SECONDS_1DAY; } // Else ignored. @@ -943,7 +958,7 @@ class ConsoleSupervisor { global $config; - $remote_config_dir = io_safe_output($config['remote_config']); + $remote_config_dir = (string) io_safe_output($config['remote_config']); if (enterprise_installed() && isset($config['license_nms']) @@ -1183,6 +1198,8 @@ class ConsoleSupervisor */ public function checkPandoraServers() { + global $config; + $servers = db_get_all_rows_sql( 'SELECT id_server, @@ -1280,6 +1297,8 @@ class ConsoleSupervisor */ public function checkPandoraServerMasterAvailable() { + global $config; + $n_masters = db_get_value_sql( 'SELECT count(*) as n @@ -2524,7 +2543,7 @@ class ConsoleSupervisor } - /* + /** * Check if Pandora console log file remains in old location. * * @return void From 683985fbbfdd8f8ec27f1403bd71d2a2a5b6ce63 Mon Sep 17 00:00:00 2001 From: fbsanchez Date: Wed, 7 Oct 2020 19:12:54 +0200 Subject: [PATCH 023/263] fixes chrome extension credentials using POST instead GET --- extras/chrome_extension/js/background.js | 516 +++++++++--------- extras/chrome_extension/js/popup.js | 5 +- .../chrome/content/js/main.js | 475 ++++++++-------- 3 files changed, 514 insertions(+), 482 deletions(-) diff --git a/extras/chrome_extension/js/background.js b/extras/chrome_extension/js/background.js index 521ecc19eb..037c01a472 100644 --- a/extras/chrome_extension/js/background.js +++ b/extras/chrome_extension/js/background.js @@ -3,309 +3,321 @@ var isFetching = null; var storedEvents = new Array(); var notVisited = {}; -$(window).on('load', function() { - initilise(); - // Wait some ms to throw main function - var delay = setTimeout(main, 100); - resetInterval(); +$(window).on("load", function() { + initilise(); + // Wait some ms to throw main function + var delay = setTimeout(main, 100); + resetInterval(); }); function fetchEvents() { - return storedEvents; + return storedEvents; } function fetchNotVisited() { - return notVisited; + return notVisited; } function removeNotVisited(eventId) { - if (notVisited[eventId] === true) delete notVisited[eventId]; + if (notVisited[eventId] === true) delete notVisited[eventId]; } function main() { + chrome.runtime.sendMessage({ text: "FETCH_EVENTS" }); + // Do not fetch if is fetching now + if (isFetching) return; + isFetching = true; - chrome.runtime.sendMessage({text: "FETCH_EVENTS"}); - // Do not fetch if is fetching now - if (isFetching) return; - isFetching = true; + var url = + localStorage["ip_address"] + + "/include/api.php?op=get&op2=events&return_type=json"; + var feedUrl = url; + var data = new FormData(); - var feedUrl = localStorage["ip_address"]+'/include/api.php?op=get&op2=events&return_type=json&apipass='+localStorage["api_pass"]+'&user='+localStorage["user_name"]+'&pass='+localStorage["pass"]; + data.append("apipass", localStorage["api_pass"]); + data.append("user", localStorage["user_name"]); + data.append("pass", localStorage["pass"]); - req = new XMLHttpRequest(); - req.onload = handleResponse; - req.onerror = handleError; - req.open("GET", feedUrl, true); - req.withCredentials = true - req.send(null); + req = new XMLHttpRequest(); + req.onload = handleResponse; + req.onerror = handleError; + req.open("POST", feedUrl, true); + req.withCredentials = true; + req.send(data); } function handleError() { - chrome.runtime.sendMessage({text: "FETCH_EVENTS_URL_ERROR"}); - isFetching = false; + chrome.runtime.sendMessage({ text: "FETCH_EVENTS_URL_ERROR" }); + isFetching = false; } function handleResponse() { - var doc = req.responseText; - if (doc=="auth error") { - chrome.runtime.sendMessage({text: "FETCH_EVENTS_URL_ERROR"}); - } else { - var n = doc.search("404 Not Found"); - if (n>0) { - chrome.runtime.sendMessage({text: "FETCH_EVENTS_DATA_ERROR"}); - } else { - getEvents(doc); - chrome.runtime.sendMessage({text: "FETCH_EVENTS_SUCCESS"}); - } - } - isFetching = false; + var doc = req.responseText; + if (doc == "auth error") { + chrome.runtime.sendMessage({ text: "FETCH_EVENTS_URL_ERROR" }); + } else { + var n = doc.search("404 Not Found"); + if (n > 0) { + chrome.runtime.sendMessage({ text: "FETCH_EVENTS_DATA_ERROR" }); + } else { + getEvents(doc); + chrome.runtime.sendMessage({ text: "FETCH_EVENTS_SUCCESS" }); + } + } + isFetching = false; } -function getEvents(reply){ - var fetchedEvents = parseReplyEvents(reply); +function getEvents(reply) { + var fetchedEvents = parseReplyEvents(reply); - // If there is no events requested, mark all as visited - if (storedEvents.length == 0) { - notVisited = {}; - storedEvents = fetchedEvents; - return; - } + // If there is no events requested, mark all as visited + if (typeof storedEvents != "undefined" && storedEvents.length == 0) { + notVisited = {}; + storedEvents = fetchedEvents; + return; + } - // Discriminate the new events - newEvents=fetchNewEvents(fetchedEvents,storedEvents); - var newNotVisited = {}; - var notVisitedCount = 0; - - // Check if popup is displayed to make some actions - var views = chrome.extension.getViews({ type: "popup" }); - for(var k=0;k0){ + prefManager.setBoolPref("data_check", true); + req = new XMLHttpRequest(); + req.onload = PandoraChrome.fn.handleResponse; + req.onerror = PandoraChrome.fn.handleError; + req.open("POST", feedUrl, true); + req.send(data); + }, - prefManager.setCharPref("data",null); - prefManager.setBoolPref("data_check", false); - if(timer) { - clearTimeout(timer); - } - timer =setTimeout(PandoraChrome.fn.main , 1000); - } - - else{ - prefManager.setBoolPref("data_check", true); + handleError: function() { + //alert("error"); + prefManager.setCharPref("data", null); + prefManager.setBoolPref("data_check", false); + if (timer) { + clearTimeout(timer); + } + timer = setTimeout(PandoraChrome.fn.main, 1000); + }, - prefManager.setCharPref("data",doc); - PandoraChrome.fn.getEvents(doc); - } - } - }, + handleResponse: function() { + var doc = req.responseText; + if (doc == "auth error") { + prefManager.setCharPref("data", null); + prefManager.setBoolPref("data_check", false); + if (timer) { + clearTimeout(timer); + } + timer = setTimeout(PandoraChrome.fn.main, 1000); + } else { + var n = doc.search("404 Not Found"); + if (n > 0) { + prefManager.setCharPref("data", null); + prefManager.setBoolPref("data_check", false); + if (timer) { + clearTimeout(timer); + } + timer = setTimeout(PandoraChrome.fn.main, 1000); + } else { + prefManager.setBoolPref("data_check", true); - getEvents: function (reply){ - if(reply.length>100){ - all_event_array=reply.split("\n"); - allEvents=PandoraChrome.fn.divideArray(all_event_array); - if(oldEvents.length==0){ - oldEvents=allEvents; - } + prefManager.setCharPref("data", doc); + PandoraChrome.fn.getEvents(doc); + } + } + }, - - newEvents=PandoraChrome.fn.fetchNewEvents(allEvents,oldEvents); - if(newEvents.length!=0){ - for(var k=0;k0){ - PandoraChrome.fn.showBadge(prefManager.getIntPref("new_events")); - } - else{ - PandoraChrome.fn.hideBadge(); - } - - - - if(timer) { - clearTimeout(timer); - } - timer =setTimeout(PandoraChrome.fn.main , prefManager.getIntPref("refresh")*1000 ); - } - }, - - showNotification: function(eventId){ - //alert("notify"+eventId); - if(prefManager.getBoolPref("sound_alert")){ - if(newEvents[eventId][19]=="Critical"){ - Sounds.playSound(prefManager.getIntPref("critical")); - } - if(newEvents[eventId][19]=="Informational"){ - Sounds.playSound(prefManager.getIntPref("informational")); - } - if(newEvents[eventId][19]=="Maintenance"){ - Sounds.playSound(prefManager.getIntPref("maintenance")); - } - if(newEvents[eventId][19]=="Normal"){ - Sounds.playSound(prefManager.getIntPref("normal")); - } - if(newEvents[eventId][19]=="Warning"){ - Sounds.playSound(prefManager.getIntPref("warning")); - } - - } - - var newEve = document.getElementById('newEvent'); - newEve.label="Last Event : "+newEvents[eventId][6]; - var id; - if(newEvents[eventId][9]==0){ - id="."; - } - else { - id= " in the module with Id "+ newEvents[eventId][9] + "."; - } - - var event = newEvents[eventId][14]+" : "+newEvents[eventId][17]+". Event occured at "+ newEvents[eventId][5]+id; - newEve.tooltipText=event; - $('#newEvent').show(); - return; - }, - - - hideNotification:function(){ - //alert("Hide Notif"); - $('#newEvent').hide(); - }, - - - showBadge: function (txt) { - //alert(txt); - var updateCount = document.getElementById('temp'); - updateCount.setAttribute("style","cursor:pointer; font-size:11px; color:#123863; font-weight:bold; display:none;") ; - updateCount.label=txt; - $('#temp').show(); - }, - - hideBadge: function () { - var updateCount = document.getElementById('temp'); - //alert("hide B"); - $('#temp').hide(); - }, - - divideArray: function (e_array){ - var Events=new Array(); - for(var i=0;i' + title + '
' + event + ''; - + getEvents: function(reply) { + if (reply.length > 100) { + all_event_array = reply.split("\n"); + allEvents = PandoraChrome.fn.divideArray(all_event_array); + if (oldEvents.length == 0) { + oldEvents = allEvents; } - }; -}(); + newEvents = PandoraChrome.fn.fetchNewEvents(allEvents, oldEvents); + if (newEvents.length != 0) { + for (var k = 0; k < newEvents.length; k++) { + var temp = prefManager.getIntPref("new_events") + 1; + prefManager.setIntPref("new_events", temp); + PandoraChrome.fn.showNotification(k); + PandoraChrome.fn.showBadge(prefManager.getIntPref("new_events")); + } + } + oldEvents = allEvents; + if (prefManager.getIntPref("new_events") > 0) { + PandoraChrome.fn.showBadge(prefManager.getIntPref("new_events")); + } else { + PandoraChrome.fn.hideBadge(); + } + + if (timer) { + clearTimeout(timer); + } + timer = setTimeout( + PandoraChrome.fn.main, + prefManager.getIntPref("refresh") * 1000 + ); + } + }, + + showNotification: function(eventId) { + //alert("notify"+eventId); + if (prefManager.getBoolPref("sound_alert")) { + if (newEvents[eventId][19] == "Critical") { + Sounds.playSound(prefManager.getIntPref("critical")); + } + if (newEvents[eventId][19] == "Informational") { + Sounds.playSound(prefManager.getIntPref("informational")); + } + if (newEvents[eventId][19] == "Maintenance") { + Sounds.playSound(prefManager.getIntPref("maintenance")); + } + if (newEvents[eventId][19] == "Normal") { + Sounds.playSound(prefManager.getIntPref("normal")); + } + if (newEvents[eventId][19] == "Warning") { + Sounds.playSound(prefManager.getIntPref("warning")); + } + } + + var newEve = document.getElementById("newEvent"); + newEve.label = "Last Event : " + newEvents[eventId][6]; + var id; + if (newEvents[eventId][9] == 0) { + id = "."; + } else { + id = " in the module with Id " + newEvents[eventId][9] + "."; + } + + var event = + newEvents[eventId][14] + + " : " + + newEvents[eventId][17] + + ". Event occured at " + + newEvents[eventId][5] + + id; + newEve.tooltipText = event; + $("#newEvent").show(); + return; + }, + + hideNotification: function() { + //alert("Hide Notif"); + $("#newEvent").hide(); + }, + + showBadge: function(txt) { + //alert(txt); + var updateCount = document.getElementById("temp"); + updateCount.setAttribute( + "style", + "cursor:pointer; font-size:11px; color:#123863; font-weight:bold; display:none;" + ); + updateCount.label = txt; + $("#temp").show(); + }, + + hideBadge: function() { + var updateCount = document.getElementById("temp"); + //alert("hide B"); + $("#temp").hide(); + }, + + divideArray: function(e_array) { + var Events = new Array(); + for (var i = 0; i < e_array.length; i++) { + var event = e_array[i].split(";"); + Events.push(event); + } + return Events; + }, + + fetchNewEvents: function(A, B) { + var arrDiff = new Array(); + // alert(A.length); + //alert(B.length); + for (var i = 0; i < A.length; i++) { + var id = false; + for (var j = 0; j < B.length; j++) { + if (A[i][0] == B[j][0]) { + id = true; + break; + } + } + if (!id) { + arrDiff.push(A[i]); + } + } + return arrDiff; + }, + + getNotification: function(eventId) { + var title = newEvents[eventId][6]; + var id; + if (newEvents[eventId][9] == 0) { + id = "."; + } else { + id = " in the module with Id " + newEvents[eventId][9] + "."; + } + + var event = + newEvents[eventId][14] + + " : " + + newEvents[eventId][17] + + ". Event occured at " + + newEvents[eventId][5] + + id; + //var event=newEvents[eventId][14]+' '+newEvents[eventId][17]+' Event occured at:'+ newEvents[eventId][5] +'in the module with Id '+ newEvents[eventId][9]; + return ( + "" + + title + + '
' + + event + + "" + ); + } + }; +})(); /* Add Event Listener */ window.addEventListener("load", PandoraChrome.fn.Onloading(), false); From 58e34536370d2fed5af52cdc7beba25381c58bef Mon Sep 17 00:00:00 2001 From: fbsanchez Date: Wed, 7 Oct 2020 19:40:31 +0200 Subject: [PATCH 024/263] Updated version manifest chrome extension --- extras/chrome_extension/manifest.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/extras/chrome_extension/manifest.json b/extras/chrome_extension/manifest.json index bff5586f0c..1dcefd58ef 100644 --- a/extras/chrome_extension/manifest.json +++ b/extras/chrome_extension/manifest.json @@ -1,6 +1,6 @@ { "name": "__MSG_name__", - "version": "2.2", + "version": "2.3", "manifest_version": 2, "description": "Pandora FMS Event viewer Chrome extension", "homepage_url": "http://pandorafms.com", From 3b14453d1e64cb359905294499b4da4c461fbb52 Mon Sep 17 00:00:00 2001 From: Jose Gonzalez Date: Thu, 8 Oct 2020 13:47:26 +0200 Subject: [PATCH 025/263] WIP: Added QueryResult ui type --- pandora_console/include/functions_ui.php | 104 +- pandora_console/include/javascript/ace/ace.js | 25468 ++++++++++++++++ .../include/javascript/ace/mode-json.js | 396 + .../include/javascript/ace/textmate.css | 154 + .../include/javascript/ace/textmate.js | 41 + .../include/javascript/ace/worker-json.js | 2468 ++ .../javascript/elasticsearch_queryResult.js | 69 + pandora_console/include/styles/ace.css | 14 +- 8 files changed, 28706 insertions(+), 8 deletions(-) create mode 100644 pandora_console/include/javascript/ace/ace.js create mode 100644 pandora_console/include/javascript/ace/mode-json.js create mode 100644 pandora_console/include/javascript/ace/textmate.css create mode 100644 pandora_console/include/javascript/ace/textmate.js create mode 100644 pandora_console/include/javascript/ace/worker-json.js create mode 100644 pandora_console/include/javascript/elasticsearch_queryResult.js diff --git a/pandora_console/include/functions_ui.php b/pandora_console/include/functions_ui.php index ab6079fb36..425d90a42b 100755 --- a/pandora_console/include/functions_ui.php +++ b/pandora_console/include/functions_ui.php @@ -14,7 +14,7 @@ * |___| |___._|__|__|_____||_____|__| |___._| |___| |__|_|__|_______| * * ============================================================================ - * Copyright (c) 2005-2019 Artica Soluciones Tecnologicas + * Copyright (c) 2005-2020 Artica Soluciones Tecnologicas * Please see http://pandorafms.org for full contribution list * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License @@ -6124,3 +6124,105 @@ function ui_print_message_dialog($title, $text, $id='', $img='', $text_button='' echo '
'; echo '
'; } + + +/** + * Build a Query-Result editor structure + * + * @param string $name Name of the structure + * + * @return null + */ + + +function ui_query_result_editor($name='default') +{ + $editorSubContainer = html_print_div( + [ + 'id' => $name.'_editor_title', + 'content' => '

'.__('Query').'

', + ], + true + ); + + $editorSubContainer .= html_print_div( + [ + 'id' => $name.'_editor', + 'class' => 'query_result_editor', + ], + true + ); + + $editorSubContainer .= html_print_div( + [ + 'class' => 'action-buttons edit-button', + 'content' => html_print_submit_button( + __('Execute query'), + 'execute_query', + false, + 'class="sub next"', + true + ), + ], + true + ); + + $editorContainer = html_print_div( + [ + 'id' => $name.'_editor_container', + 'class' => 'query_result_editor_container', + 'content' => $editorSubContainer, + ], + true + ); + + $viewSubContainer = html_print_div( + [ + 'id' => $name.'_view_title', + 'content' => '

'.__('Results').'

', + ], + true + ); + + $viewSubContainer .= html_print_div( + [ + 'id' => $name.'_view', + 'class' => 'query_result_view', + ], + true + ); + + $viewSubContainer .= html_print_div( + [ + 'class' => 'action-buttons', + 'content' => '', + ], + true + ); + + $viewContainer = html_print_div( + [ + 'id' => $name.'_view_container', + 'class' => 'query_result_view_container', + 'content' => $viewSubContainer, + ], + true + ); + + html_print_div( + [ + 'id' => 'query_result_container', + 'class' => 'databox', + 'content' => $editorContainer.$viewContainer, + ] + ); + // This is needed for Javascript + html_print_div( + [ + 'id' => 'pandora_full_url', + 'hidden' => true, + 'content' => ui_get_full_url(false, false, false, false), + ] + ); + +} diff --git a/pandora_console/include/javascript/ace/ace.js b/pandora_console/include/javascript/ace/ace.js new file mode 100644 index 0000000000..2742f5cb5f --- /dev/null +++ b/pandora_console/include/javascript/ace/ace.js @@ -0,0 +1,25468 @@ +/* ***** BEGIN LICENSE BLOCK ***** + * Distributed under the BSD license: + * + * Copyright (c) 2010, Ajax.org B.V. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name of Ajax.org B.V. nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL AJAX.ORG B.V. BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * ***** END LICENSE BLOCK ***** */ + +/** + * Define a module along with a payload + * @param module a name for the payload + * @param payload a function to call with (require, exports, module) params + */ + +(function() { + var ACE_NAMESPACE = "ace"; + + var global = (function() { + return this; + })(); + if (!global && typeof window != "undefined") global = window; // strict mode + + if (!ACE_NAMESPACE && typeof requirejs !== "undefined") return; + + var define = function(module, deps, payload) { + if (typeof module !== "string") { + if (define.original) define.original.apply(this, arguments); + else { + console.error("dropping module because define wasn't a string."); + console.trace(); + } + return; + } + if (arguments.length == 2) payload = deps; + if (!define.modules[module]) { + define.payloads[module] = payload; + define.modules[module] = null; + } + }; + + define.modules = {}; + define.payloads = {}; + + /** + * Get at functionality define()ed using the function above + */ + var _require = function(parentId, module, callback) { + if (typeof module === "string") { + var payload = lookup(parentId, module); + if (payload != undefined) { + callback && callback(); + return payload; + } + } else if (Object.prototype.toString.call(module) === "[object Array]") { + var params = []; + for (var i = 0, l = module.length; i < l; ++i) { + var dep = lookup(parentId, module[i]); + if (dep == undefined && require.original) return; + params.push(dep); + } + return (callback && callback.apply(null, params)) || true; + } + }; + + var require = function(module, callback) { + var packagedModule = _require("", module, callback); + if (packagedModule == undefined && require.original) + return require.original.apply(this, arguments); + return packagedModule; + }; + + var normalizeModule = function(parentId, moduleName) { + // normalize plugin requires + if (moduleName.indexOf("!") !== -1) { + var chunks = moduleName.split("!"); + return ( + normalizeModule(parentId, chunks[0]) + + "!" + + normalizeModule(parentId, chunks[1]) + ); + } + // normalize relative requires + if (moduleName.charAt(0) == ".") { + var base = parentId + .split("/") + .slice(0, -1) + .join("/"); + moduleName = base + "/" + moduleName; + + while (moduleName.indexOf(".") !== -1 && previous != moduleName) { + var previous = moduleName; + moduleName = moduleName + .replace(/\/\.\//, "/") + .replace(/[^\/]+\/\.\.\//, ""); + } + } + return moduleName; + }; + + /** + * Internal function to lookup moduleNames and resolve them by calling the + * definition function if needed. + */ + var lookup = function(parentId, moduleName) { + moduleName = normalizeModule(parentId, moduleName); + + var module = define.modules[moduleName]; + if (!module) { + module = define.payloads[moduleName]; + if (typeof module === "function") { + var exports = {}; + var mod = { + id: moduleName, + uri: "", + exports: exports, + packaged: true + }; + + var req = function(module, callback) { + return _require(moduleName, module, callback); + }; + + var returnValue = module(req, exports, mod); + exports = returnValue || mod.exports; + define.modules[moduleName] = exports; + delete define.payloads[moduleName]; + } + module = define.modules[moduleName] = exports || module; + } + return module; + }; + + function exportAce(ns) { + var root = global; + if (ns) { + if (!global[ns]) global[ns] = {}; + root = global[ns]; + } + + if (!root.define || !root.define.packaged) { + define.original = root.define; + root.define = define; + root.define.packaged = true; + } + + if (!root.require || !root.require.packaged) { + require.original = root.require; + root.require = require; + root.require.packaged = true; + } + } + + exportAce(ACE_NAMESPACE); +})(); + +ace.define("ace/lib/regexp", ["require", "exports", "module"], function( + require, + exports, + module +) { + "use strict"; + + var real = { + exec: RegExp.prototype.exec, + test: RegExp.prototype.test, + match: String.prototype.match, + replace: String.prototype.replace, + split: String.prototype.split + }, + compliantExecNpcg = real.exec.call(/()??/, "")[1] === undefined, // check `exec` handling of nonparticipating capturing groups + compliantLastIndexIncrement = (function() { + var x = /^/g; + real.test.call(x, ""); + return !x.lastIndex; + })(); + + if (compliantLastIndexIncrement && compliantExecNpcg) return; + RegExp.prototype.exec = function(str) { + var match = real.exec.apply(this, arguments), + name, + r2; + if (typeof str == "string" && match) { + if (!compliantExecNpcg && match.length > 1 && indexOf(match, "") > -1) { + r2 = RegExp( + this.source, + real.replace.call(getNativeFlags(this), "g", "") + ); + real.replace.call(str.slice(match.index), r2, function() { + for (var i = 1; i < arguments.length - 2; i++) { + if (arguments[i] === undefined) match[i] = undefined; + } + }); + } + if (this._xregexp && this._xregexp.captureNames) { + for (var i = 1; i < match.length; i++) { + name = this._xregexp.captureNames[i - 1]; + if (name) match[name] = match[i]; + } + } + if ( + !compliantLastIndexIncrement && + this.global && + !match[0].length && + this.lastIndex > match.index + ) + this.lastIndex--; + } + return match; + }; + if (!compliantLastIndexIncrement) { + RegExp.prototype.test = function(str) { + var match = real.exec.call(this, str); + if ( + match && + this.global && + !match[0].length && + this.lastIndex > match.index + ) + this.lastIndex--; + return !!match; + }; + } + + function getNativeFlags(regex) { + return ( + (regex.global ? "g" : "") + + (regex.ignoreCase ? "i" : "") + + (regex.multiline ? "m" : "") + + (regex.extended ? "x" : "") + // Proposed for ES4; included in AS3 + (regex.sticky ? "y" : "") + ); + } + + function indexOf(array, item, from) { + if (Array.prototype.indexOf) + // Use the native array method if available + return array.indexOf(item, from); + for (var i = from || 0; i < array.length; i++) { + if (array[i] === item) return i; + } + return -1; + } +}); + +ace.define("ace/lib/es5-shim", ["require", "exports", "module"], function( + require, + exports, + module +) { + function Empty() {} + + if (!Function.prototype.bind) { + Function.prototype.bind = function bind(that) { + // .length is 1 + var target = this; + if (typeof target != "function") { + throw new TypeError( + "Function.prototype.bind called on incompatible " + target + ); + } + var args = slice.call(arguments, 1); // for normal call + var bound = function() { + if (this instanceof bound) { + var result = target.apply(this, args.concat(slice.call(arguments))); + if (Object(result) === result) { + return result; + } + return this; + } else { + return target.apply(that, args.concat(slice.call(arguments))); + } + }; + if (target.prototype) { + Empty.prototype = target.prototype; + bound.prototype = new Empty(); + Empty.prototype = null; + } + return bound; + }; + } + var call = Function.prototype.call; + var prototypeOfArray = Array.prototype; + var prototypeOfObject = Object.prototype; + var slice = prototypeOfArray.slice; + var _toString = call.bind(prototypeOfObject.toString); + var owns = call.bind(prototypeOfObject.hasOwnProperty); + var defineGetter; + var defineSetter; + var lookupGetter; + var lookupSetter; + var supportsAccessors; + if ((supportsAccessors = owns(prototypeOfObject, "__defineGetter__"))) { + defineGetter = call.bind(prototypeOfObject.__defineGetter__); + defineSetter = call.bind(prototypeOfObject.__defineSetter__); + lookupGetter = call.bind(prototypeOfObject.__lookupGetter__); + lookupSetter = call.bind(prototypeOfObject.__lookupSetter__); + } + if ([1, 2].splice(0).length != 2) { + if ( + (function() { + // test IE < 9 to splice bug - see issue #138 + function makeArray(l) { + var a = new Array(l + 2); + a[0] = a[1] = 0; + return a; + } + var array = [], + lengthBefore; + + array.splice.apply(array, makeArray(20)); + array.splice.apply(array, makeArray(26)); + + lengthBefore = array.length; //46 + array.splice(5, 0, "XXX"); // add one element + + lengthBefore + 1 == array.length; + + if (lengthBefore + 1 == array.length) { + return true; // has right splice implementation without bugs + } + })() + ) { + //IE 6/7 + var array_splice = Array.prototype.splice; + Array.prototype.splice = function(start, deleteCount) { + if (!arguments.length) { + return []; + } else { + return array_splice.apply( + this, + [ + start === void 0 ? 0 : start, + deleteCount === void 0 ? this.length - start : deleteCount + ].concat(slice.call(arguments, 2)) + ); + } + }; + } else { + //IE8 + Array.prototype.splice = function(pos, removeCount) { + var length = this.length; + if (pos > 0) { + if (pos > length) pos = length; + } else if (pos == void 0) { + pos = 0; + } else if (pos < 0) { + pos = Math.max(length + pos, 0); + } + + if (!(pos + removeCount < length)) removeCount = length - pos; + + var removed = this.slice(pos, pos + removeCount); + var insert = slice.call(arguments, 2); + var add = insert.length; + if (pos === length) { + if (add) { + this.push.apply(this, insert); + } + } else { + var remove = Math.min(removeCount, length - pos); + var tailOldPos = pos + remove; + var tailNewPos = tailOldPos + add - remove; + var tailCount = length - tailOldPos; + var lengthAfterRemove = length - remove; + + if (tailNewPos < tailOldPos) { + // case A + for (var i = 0; i < tailCount; ++i) { + this[tailNewPos + i] = this[tailOldPos + i]; + } + } else if (tailNewPos > tailOldPos) { + // case B + for (i = tailCount; i--; ) { + this[tailNewPos + i] = this[tailOldPos + i]; + } + } // else, add == remove (nothing to do) + + if (add && pos === lengthAfterRemove) { + this.length = lengthAfterRemove; // truncate array + this.push.apply(this, insert); + } else { + this.length = lengthAfterRemove + add; // reserves space + for (i = 0; i < add; ++i) { + this[pos + i] = insert[i]; + } + } + } + return removed; + }; + } + } + if (!Array.isArray) { + Array.isArray = function isArray(obj) { + return _toString(obj) == "[object Array]"; + }; + } + var boxedString = Object("a"), + splitString = boxedString[0] != "a" || !(0 in boxedString); + + if (!Array.prototype.forEach) { + Array.prototype.forEach = function forEach(fun /*, thisp*/) { + var object = toObject(this), + self = + splitString && _toString(this) == "[object String]" + ? this.split("") + : object, + thisp = arguments[1], + i = -1, + length = self.length >>> 0; + if (_toString(fun) != "[object Function]") { + throw new TypeError(); // TODO message + } + + while (++i < length) { + if (i in self) { + fun.call(thisp, self[i], i, object); + } + } + }; + } + if (!Array.prototype.map) { + Array.prototype.map = function map(fun /*, thisp*/) { + var object = toObject(this), + self = + splitString && _toString(this) == "[object String]" + ? this.split("") + : object, + length = self.length >>> 0, + result = Array(length), + thisp = arguments[1]; + if (_toString(fun) != "[object Function]") { + throw new TypeError(fun + " is not a function"); + } + + for (var i = 0; i < length; i++) { + if (i in self) result[i] = fun.call(thisp, self[i], i, object); + } + return result; + }; + } + if (!Array.prototype.filter) { + Array.prototype.filter = function filter(fun /*, thisp */) { + var object = toObject(this), + self = + splitString && _toString(this) == "[object String]" + ? this.split("") + : object, + length = self.length >>> 0, + result = [], + value, + thisp = arguments[1]; + if (_toString(fun) != "[object Function]") { + throw new TypeError(fun + " is not a function"); + } + + for (var i = 0; i < length; i++) { + if (i in self) { + value = self[i]; + if (fun.call(thisp, value, i, object)) { + result.push(value); + } + } + } + return result; + }; + } + if (!Array.prototype.every) { + Array.prototype.every = function every(fun /*, thisp */) { + var object = toObject(this), + self = + splitString && _toString(this) == "[object String]" + ? this.split("") + : object, + length = self.length >>> 0, + thisp = arguments[1]; + if (_toString(fun) != "[object Function]") { + throw new TypeError(fun + " is not a function"); + } + + for (var i = 0; i < length; i++) { + if (i in self && !fun.call(thisp, self[i], i, object)) { + return false; + } + } + return true; + }; + } + if (!Array.prototype.some) { + Array.prototype.some = function some(fun /*, thisp */) { + var object = toObject(this), + self = + splitString && _toString(this) == "[object String]" + ? this.split("") + : object, + length = self.length >>> 0, + thisp = arguments[1]; + if (_toString(fun) != "[object Function]") { + throw new TypeError(fun + " is not a function"); + } + + for (var i = 0; i < length; i++) { + if (i in self && fun.call(thisp, self[i], i, object)) { + return true; + } + } + return false; + }; + } + if (!Array.prototype.reduce) { + Array.prototype.reduce = function reduce(fun /*, initial*/) { + var object = toObject(this), + self = + splitString && _toString(this) == "[object String]" + ? this.split("") + : object, + length = self.length >>> 0; + if (_toString(fun) != "[object Function]") { + throw new TypeError(fun + " is not a function"); + } + if (!length && arguments.length == 1) { + throw new TypeError("reduce of empty array with no initial value"); + } + + var i = 0; + var result; + if (arguments.length >= 2) { + result = arguments[1]; + } else { + do { + if (i in self) { + result = self[i++]; + break; + } + if (++i >= length) { + throw new TypeError("reduce of empty array with no initial value"); + } + } while (true); + } + + for (; i < length; i++) { + if (i in self) { + result = fun.call(void 0, result, self[i], i, object); + } + } + + return result; + }; + } + if (!Array.prototype.reduceRight) { + Array.prototype.reduceRight = function reduceRight(fun /*, initial*/) { + var object = toObject(this), + self = + splitString && _toString(this) == "[object String]" + ? this.split("") + : object, + length = self.length >>> 0; + if (_toString(fun) != "[object Function]") { + throw new TypeError(fun + " is not a function"); + } + if (!length && arguments.length == 1) { + throw new TypeError("reduceRight of empty array with no initial value"); + } + + var result, + i = length - 1; + if (arguments.length >= 2) { + result = arguments[1]; + } else { + do { + if (i in self) { + result = self[i--]; + break; + } + if (--i < 0) { + throw new TypeError( + "reduceRight of empty array with no initial value" + ); + } + } while (true); + } + + do { + if (i in this) { + result = fun.call(void 0, result, self[i], i, object); + } + } while (i--); + + return result; + }; + } + if (!Array.prototype.indexOf || [0, 1].indexOf(1, 2) != -1) { + Array.prototype.indexOf = function indexOf(sought /*, fromIndex */) { + var self = + splitString && _toString(this) == "[object String]" + ? this.split("") + : toObject(this), + length = self.length >>> 0; + + if (!length) { + return -1; + } + + var i = 0; + if (arguments.length > 1) { + i = toInteger(arguments[1]); + } + i = i >= 0 ? i : Math.max(0, length + i); + for (; i < length; i++) { + if (i in self && self[i] === sought) { + return i; + } + } + return -1; + }; + } + if (!Array.prototype.lastIndexOf || [0, 1].lastIndexOf(0, -3) != -1) { + Array.prototype.lastIndexOf = function lastIndexOf( + sought /*, fromIndex */ + ) { + var self = + splitString && _toString(this) == "[object String]" + ? this.split("") + : toObject(this), + length = self.length >>> 0; + + if (!length) { + return -1; + } + var i = length - 1; + if (arguments.length > 1) { + i = Math.min(i, toInteger(arguments[1])); + } + i = i >= 0 ? i : length - Math.abs(i); + for (; i >= 0; i--) { + if (i in self && sought === self[i]) { + return i; + } + } + return -1; + }; + } + if (!Object.getPrototypeOf) { + Object.getPrototypeOf = function getPrototypeOf(object) { + return ( + object.__proto__ || + (object.constructor ? object.constructor.prototype : prototypeOfObject) + ); + }; + } + if (!Object.getOwnPropertyDescriptor) { + var ERR_NON_OBJECT = + "Object.getOwnPropertyDescriptor called on a " + "non-object: "; + Object.getOwnPropertyDescriptor = function getOwnPropertyDescriptor( + object, + property + ) { + if ( + (typeof object != "object" && typeof object != "function") || + object === null + ) + throw new TypeError(ERR_NON_OBJECT + object); + if (!owns(object, property)) return; + + var descriptor, getter, setter; + descriptor = { enumerable: true, configurable: true }; + if (supportsAccessors) { + var prototype = object.__proto__; + object.__proto__ = prototypeOfObject; + + var getter = lookupGetter(object, property); + var setter = lookupSetter(object, property); + object.__proto__ = prototype; + + if (getter || setter) { + if (getter) descriptor.get = getter; + if (setter) descriptor.set = setter; + return descriptor; + } + } + descriptor.value = object[property]; + return descriptor; + }; + } + if (!Object.getOwnPropertyNames) { + Object.getOwnPropertyNames = function getOwnPropertyNames(object) { + return Object.keys(object); + }; + } + if (!Object.create) { + var createEmpty; + if (Object.prototype.__proto__ === null) { + createEmpty = function() { + return { __proto__: null }; + }; + } else { + createEmpty = function() { + var empty = {}; + for (var i in empty) empty[i] = null; + empty.constructor = empty.hasOwnProperty = empty.propertyIsEnumerable = empty.isPrototypeOf = empty.toLocaleString = empty.toString = empty.valueOf = empty.__proto__ = null; + return empty; + }; + } + + Object.create = function create(prototype, properties) { + var object; + if (prototype === null) { + object = createEmpty(); + } else { + if (typeof prototype != "object") + throw new TypeError( + "typeof prototype[" + typeof prototype + "] != 'object'" + ); + var Type = function() {}; + Type.prototype = prototype; + object = new Type(); + object.__proto__ = prototype; + } + if (properties !== void 0) Object.defineProperties(object, properties); + return object; + }; + } + + function doesDefinePropertyWork(object) { + try { + Object.defineProperty(object, "sentinel", {}); + return "sentinel" in object; + } catch (exception) {} + } + if (Object.defineProperty) { + var definePropertyWorksOnObject = doesDefinePropertyWork({}); + var definePropertyWorksOnDom = + typeof document == "undefined" || + doesDefinePropertyWork(document.createElement("div")); + if (!definePropertyWorksOnObject || !definePropertyWorksOnDom) { + var definePropertyFallback = Object.defineProperty; + } + } + + if (!Object.defineProperty || definePropertyFallback) { + var ERR_NON_OBJECT_DESCRIPTOR = "Property description must be an object: "; + var ERR_NON_OBJECT_TARGET = "Object.defineProperty called on non-object: "; + var ERR_ACCESSORS_NOT_SUPPORTED = + "getters & setters can not be defined " + "on this javascript engine"; + + Object.defineProperty = function defineProperty( + object, + property, + descriptor + ) { + if ( + (typeof object != "object" && typeof object != "function") || + object === null + ) + throw new TypeError(ERR_NON_OBJECT_TARGET + object); + if ( + (typeof descriptor != "object" && typeof descriptor != "function") || + descriptor === null + ) + throw new TypeError(ERR_NON_OBJECT_DESCRIPTOR + descriptor); + if (definePropertyFallback) { + try { + return definePropertyFallback.call( + Object, + object, + property, + descriptor + ); + } catch (exception) {} + } + if (owns(descriptor, "value")) { + if ( + supportsAccessors && + (lookupGetter(object, property) || lookupSetter(object, property)) + ) { + var prototype = object.__proto__; + object.__proto__ = prototypeOfObject; + delete object[property]; + object[property] = descriptor.value; + object.__proto__ = prototype; + } else { + object[property] = descriptor.value; + } + } else { + if (!supportsAccessors) + throw new TypeError(ERR_ACCESSORS_NOT_SUPPORTED); + if (owns(descriptor, "get")) + defineGetter(object, property, descriptor.get); + if (owns(descriptor, "set")) + defineSetter(object, property, descriptor.set); + } + + return object; + }; + } + if (!Object.defineProperties) { + Object.defineProperties = function defineProperties(object, properties) { + for (var property in properties) { + if (owns(properties, property)) + Object.defineProperty(object, property, properties[property]); + } + return object; + }; + } + if (!Object.seal) { + Object.seal = function seal(object) { + return object; + }; + } + if (!Object.freeze) { + Object.freeze = function freeze(object) { + return object; + }; + } + try { + Object.freeze(function() {}); + } catch (exception) { + Object.freeze = (function freeze(freezeObject) { + return function freeze(object) { + if (typeof object == "function") { + return object; + } else { + return freezeObject(object); + } + }; + })(Object.freeze); + } + if (!Object.preventExtensions) { + Object.preventExtensions = function preventExtensions(object) { + return object; + }; + } + if (!Object.isSealed) { + Object.isSealed = function isSealed(object) { + return false; + }; + } + if (!Object.isFrozen) { + Object.isFrozen = function isFrozen(object) { + return false; + }; + } + if (!Object.isExtensible) { + Object.isExtensible = function isExtensible(object) { + if (Object(object) === object) { + throw new TypeError(); // TODO message + } + var name = ""; + while (owns(object, name)) { + name += "?"; + } + object[name] = true; + var returnValue = owns(object, name); + delete object[name]; + return returnValue; + }; + } + if (!Object.keys) { + var hasDontEnumBug = true, + dontEnums = [ + "toString", + "toLocaleString", + "valueOf", + "hasOwnProperty", + "isPrototypeOf", + "propertyIsEnumerable", + "constructor" + ], + dontEnumsLength = dontEnums.length; + + for (var key in { toString: null }) { + hasDontEnumBug = false; + } + + Object.keys = function keys(object) { + if ( + (typeof object != "object" && typeof object != "function") || + object === null + ) { + throw new TypeError("Object.keys called on a non-object"); + } + + var keys = []; + for (var name in object) { + if (owns(object, name)) { + keys.push(name); + } + } + + if (hasDontEnumBug) { + for (var i = 0, ii = dontEnumsLength; i < ii; i++) { + var dontEnum = dontEnums[i]; + if (owns(object, dontEnum)) { + keys.push(dontEnum); + } + } + } + return keys; + }; + } + if (!Date.now) { + Date.now = function now() { + return new Date().getTime(); + }; + } + var ws = + "\x09\x0A\x0B\x0C\x0D\x20\xA0\u1680\u2000\u2001\u2002\u2003" + + "\u2004\u2005\u2006\u2007\u2008\u2009\u200A\u202F\u205F\u3000\u2028" + + "\u2029\uFEFF"; + if (!String.prototype.trim) { + ws = "[" + ws + "]"; + var trimBeginRegexp = new RegExp("^" + ws + ws + "*"), + trimEndRegexp = new RegExp(ws + ws + "*$"); + String.prototype.trim = function trim() { + return String(this) + .replace(trimBeginRegexp, "") + .replace(trimEndRegexp, ""); + }; + } + + function toInteger(n) { + n = +n; + if (n !== n) { + // isNaN + n = 0; + } else if (n !== 0 && n !== 1 / 0 && n !== -(1 / 0)) { + n = (n > 0 || -1) * Math.floor(Math.abs(n)); + } + return n; + } + + function isPrimitive(input) { + var type = typeof input; + return ( + input === null || + type === "undefined" || + type === "boolean" || + type === "number" || + type === "string" + ); + } + + function toPrimitive(input) { + var val, valueOf, toString; + if (isPrimitive(input)) { + return input; + } + valueOf = input.valueOf; + if (typeof valueOf === "function") { + val = valueOf.call(input); + if (isPrimitive(val)) { + return val; + } + } + toString = input.toString; + if (typeof toString === "function") { + val = toString.call(input); + if (isPrimitive(val)) { + return val; + } + } + throw new TypeError(); + } + var toObject = function(o) { + if (o == null) { + // this matches both null and undefined + throw new TypeError("can't convert " + o + " to object"); + } + return Object(o); + }; +}); + +ace.define( + "ace/lib/fixoldbrowsers", + ["require", "exports", "module", "ace/lib/regexp", "ace/lib/es5-shim"], + function(require, exports, module) { + "use strict"; + + require("./regexp"); + require("./es5-shim"); + if (typeof Element != "undefined" && !Element.prototype.remove) { + Object.defineProperty(Element.prototype, "remove", { + enumerable: false, + writable: true, + configurable: true, + value: function() { + this.parentNode && this.parentNode.removeChild(this); + } + }); + } + } +); + +ace.define("ace/lib/useragent", ["require", "exports", "module"], function( + require, + exports, + module +) { + "use strict"; + exports.OS = { + LINUX: "LINUX", + MAC: "MAC", + WINDOWS: "WINDOWS" + }; + exports.getOS = function() { + if (exports.isMac) { + return exports.OS.MAC; + } else if (exports.isLinux) { + return exports.OS.LINUX; + } else { + return exports.OS.WINDOWS; + } + }; + var _navigator = typeof navigator == "object" ? navigator : {}; + + var os = (/mac|win|linux/i.exec(_navigator.platform) || [ + "other" + ])[0].toLowerCase(); + var ua = _navigator.userAgent || ""; + var appName = _navigator.appName || ""; + exports.isWin = os == "win"; + exports.isMac = os == "mac"; + exports.isLinux = os == "linux"; + exports.isIE = + appName == "Microsoft Internet Explorer" || + appName.indexOf("MSAppHost") >= 0 + ? parseFloat( + (ua.match( + /(?:MSIE |Trident\/[0-9]+[\.0-9]+;.*rv:)([0-9]+[\.0-9]+)/ + ) || [])[1] + ) + : parseFloat( + (ua.match(/(?:Trident\/[0-9]+[\.0-9]+;.*rv:)([0-9]+[\.0-9]+)/) || + [])[1] + ); // for ie + + exports.isOldIE = exports.isIE && exports.isIE < 9; + exports.isGecko = exports.isMozilla = ua.match(/ Gecko\/\d+/); + exports.isOpera = + typeof opera == "object" && + Object.prototype.toString.call(window.opera) == "[object Opera]"; + exports.isWebKit = parseFloat(ua.split("WebKit/")[1]) || undefined; + + exports.isChrome = parseFloat(ua.split(" Chrome/")[1]) || undefined; + + exports.isEdge = parseFloat(ua.split(" Edge/")[1]) || undefined; + + exports.isAIR = ua.indexOf("AdobeAIR") >= 0; + + exports.isAndroid = ua.indexOf("Android") >= 0; + + exports.isChromeOS = ua.indexOf(" CrOS ") >= 0; + + exports.isIOS = /iPad|iPhone|iPod/.test(ua) && !window.MSStream; + + if (exports.isIOS) exports.isMac = true; + + exports.isMobile = exports.isIOS || exports.isAndroid; +}); + +ace.define( + "ace/lib/dom", + ["require", "exports", "module", "ace/lib/useragent"], + function(require, exports, module) { + "use strict"; + + var useragent = require("./useragent"); + var XHTML_NS = "http://www.w3.org/1999/xhtml"; + + exports.buildDom = function buildDom(arr, parent, refs) { + if (typeof arr == "string" && arr) { + var txt = document.createTextNode(arr); + if (parent) parent.appendChild(txt); + return txt; + } + + if (!Array.isArray(arr)) { + if (arr && arr.appendChild && parent) parent.appendChild(arr); + return arr; + } + if (typeof arr[0] != "string" || !arr[0]) { + var els = []; + for (var i = 0; i < arr.length; i++) { + var ch = buildDom(arr[i], parent, refs); + ch && els.push(ch); + } + return els; + } + + var el = document.createElement(arr[0]); + var options = arr[1]; + var childIndex = 1; + if (options && typeof options == "object" && !Array.isArray(options)) + childIndex = 2; + for (var i = childIndex; i < arr.length; i++) buildDom(arr[i], el, refs); + if (childIndex == 2) { + Object.keys(options).forEach(function(n) { + var val = options[n]; + if (n === "class") { + el.className = Array.isArray(val) ? val.join(" ") : val; + } else if (typeof val == "function" || n == "value" || n[0] == "$") { + el[n] = val; + } else if (n === "ref") { + if (refs) refs[val] = el; + } else if (val != null) { + el.setAttribute(n, val); + } + }); + } + if (parent) parent.appendChild(el); + return el; + }; + + exports.getDocumentHead = function(doc) { + if (!doc) doc = document; + return ( + doc.head || doc.getElementsByTagName("head")[0] || doc.documentElement + ); + }; + + exports.createElement = function(tag, ns) { + return document.createElementNS + ? document.createElementNS(ns || XHTML_NS, tag) + : document.createElement(tag); + }; + + exports.removeChildren = function(element) { + element.innerHTML = ""; + }; + + exports.createTextNode = function(textContent, element) { + var doc = element ? element.ownerDocument : document; + return doc.createTextNode(textContent); + }; + + exports.createFragment = function(element) { + var doc = element ? element.ownerDocument : document; + return doc.createDocumentFragment(); + }; + + exports.hasCssClass = function(el, name) { + var classes = (el.className + "").split(/\s+/g); + return classes.indexOf(name) !== -1; + }; + exports.addCssClass = function(el, name) { + if (!exports.hasCssClass(el, name)) { + el.className += " " + name; + } + }; + exports.removeCssClass = function(el, name) { + var classes = el.className.split(/\s+/g); + while (true) { + var index = classes.indexOf(name); + if (index == -1) { + break; + } + classes.splice(index, 1); + } + el.className = classes.join(" "); + }; + + exports.toggleCssClass = function(el, name) { + var classes = el.className.split(/\s+/g), + add = true; + while (true) { + var index = classes.indexOf(name); + if (index == -1) { + break; + } + add = false; + classes.splice(index, 1); + } + if (add) classes.push(name); + + el.className = classes.join(" "); + return add; + }; + exports.setCssClass = function(node, className, include) { + if (include) { + exports.addCssClass(node, className); + } else { + exports.removeCssClass(node, className); + } + }; + + exports.hasCssString = function(id, doc) { + var index = 0, + sheets; + doc = doc || document; + if ((sheets = doc.querySelectorAll("style"))) { + while (index < sheets.length) + if (sheets[index++].id === id) return true; + } + }; + + exports.importCssString = function importCssString(cssText, id, target) { + var container = target; + if (!target || !target.getRootNode) { + container = document; + } else { + container = target.getRootNode(); + if (!container || container == target) container = document; + } + + var doc = container.ownerDocument || container; + if (id && exports.hasCssString(id, container)) return null; + + if (id) cssText += "\n/*# sourceURL=ace/css/" + id + " */"; + + var style = exports.createElement("style"); + style.appendChild(doc.createTextNode(cssText)); + if (id) style.id = id; + + if (container == doc) container = exports.getDocumentHead(doc); + container.insertBefore(style, container.firstChild); + }; + + exports.importCssStylsheet = function(uri, doc) { + exports.buildDom( + ["link", { rel: "stylesheet", href: uri }], + exports.getDocumentHead(doc) + ); + }; + exports.scrollbarWidth = function(document) { + var inner = exports.createElement("ace_inner"); + inner.style.width = "100%"; + inner.style.minWidth = "0px"; + inner.style.height = "200px"; + inner.style.display = "block"; + + var outer = exports.createElement("ace_outer"); + var style = outer.style; + + style.position = "absolute"; + style.left = "-10000px"; + style.overflow = "hidden"; + style.width = "200px"; + style.minWidth = "0px"; + style.height = "150px"; + style.display = "block"; + + outer.appendChild(inner); + + var body = document.documentElement; + body.appendChild(outer); + + var noScrollbar = inner.offsetWidth; + + style.overflow = "scroll"; + var withScrollbar = inner.offsetWidth; + + if (noScrollbar == withScrollbar) { + withScrollbar = outer.clientWidth; + } + + body.removeChild(outer); + + return noScrollbar - withScrollbar; + }; + + if (typeof document == "undefined") { + exports.importCssString = function() {}; + } + + exports.computedStyle = function(element, style) { + return window.getComputedStyle(element, "") || {}; + }; + + exports.setStyle = function(styles, property, value) { + if (styles[property] !== value) { + styles[property] = value; + } + }; + + exports.HAS_CSS_ANIMATION = false; + exports.HAS_CSS_TRANSFORMS = false; + exports.HI_DPI = useragent.isWin + ? typeof window !== "undefined" && window.devicePixelRatio >= 1.5 + : true; + + if (typeof document !== "undefined") { + var div = document.createElement("div"); + if (exports.HI_DPI && div.style.transform !== undefined) + exports.HAS_CSS_TRANSFORMS = true; + if (!useragent.isEdge && typeof div.style.animationName !== "undefined") + exports.HAS_CSS_ANIMATION = true; + div = null; + } + + if (exports.HAS_CSS_TRANSFORMS) { + exports.translate = function(element, tx, ty) { + element.style.transform = + "translate(" + Math.round(tx) + "px, " + Math.round(ty) + "px)"; + }; + } else { + exports.translate = function(element, tx, ty) { + element.style.top = Math.round(ty) + "px"; + element.style.left = Math.round(tx) + "px"; + }; + } + } +); + +ace.define("ace/lib/oop", ["require", "exports", "module"], function( + require, + exports, + module +) { + "use strict"; + + exports.inherits = function(ctor, superCtor) { + ctor.super_ = superCtor; + ctor.prototype = Object.create(superCtor.prototype, { + constructor: { + value: ctor, + enumerable: false, + writable: true, + configurable: true + } + }); + }; + + exports.mixin = function(obj, mixin) { + for (var key in mixin) { + obj[key] = mixin[key]; + } + return obj; + }; + + exports.implement = function(proto, mixin) { + exports.mixin(proto, mixin); + }; +}); + +ace.define( + "ace/lib/keys", + ["require", "exports", "module", "ace/lib/oop"], + function(require, exports, module) { + "use strict"; + + var oop = require("./oop"); + var Keys = (function() { + var ret = { + MODIFIER_KEYS: { + 16: "Shift", + 17: "Ctrl", + 18: "Alt", + 224: "Meta", + 91: "MetaLeft", + 92: "MetaRight", + 93: "ContextMenu" + }, + + KEY_MODS: { + ctrl: 1, + alt: 2, + option: 2, + shift: 4, + super: 8, + meta: 8, + command: 8, + cmd: 8, + control: 1 + }, + + FUNCTION_KEYS: { + 8: "Backspace", + 9: "Tab", + 13: "Return", + 19: "Pause", + 27: "Esc", + 32: "Space", + 33: "PageUp", + 34: "PageDown", + 35: "End", + 36: "Home", + 37: "Left", + 38: "Up", + 39: "Right", + 40: "Down", + 44: "Print", + 45: "Insert", + 46: "Delete", + 96: "Numpad0", + 97: "Numpad1", + 98: "Numpad2", + 99: "Numpad3", + 100: "Numpad4", + 101: "Numpad5", + 102: "Numpad6", + 103: "Numpad7", + 104: "Numpad8", + 105: "Numpad9", + "-13": "NumpadEnter", + 112: "F1", + 113: "F2", + 114: "F3", + 115: "F4", + 116: "F5", + 117: "F6", + 118: "F7", + 119: "F8", + 120: "F9", + 121: "F10", + 122: "F11", + 123: "F12", + 144: "Numlock", + 145: "Scrolllock" + }, + + PRINTABLE_KEYS: { + 32: " ", + 48: "0", + 49: "1", + 50: "2", + 51: "3", + 52: "4", + 53: "5", + 54: "6", + 55: "7", + 56: "8", + 57: "9", + 59: ";", + 61: "=", + 65: "a", + 66: "b", + 67: "c", + 68: "d", + 69: "e", + 70: "f", + 71: "g", + 72: "h", + 73: "i", + 74: "j", + 75: "k", + 76: "l", + 77: "m", + 78: "n", + 79: "o", + 80: "p", + 81: "q", + 82: "r", + 83: "s", + 84: "t", + 85: "u", + 86: "v", + 87: "w", + 88: "x", + 89: "y", + 90: "z", + 107: "+", + 109: "-", + 110: ".", + 186: ";", + 187: "=", + 188: ",", + 189: "-", + 190: ".", + 191: "/", + 192: "`", + 219: "[", + 220: "\\", + 221: "]", + 222: "'", + 111: "/", + 106: "*" + } + }; + var name, i; + for (i in ret.FUNCTION_KEYS) { + name = ret.FUNCTION_KEYS[i].toLowerCase(); + ret[name] = parseInt(i, 10); + } + for (i in ret.PRINTABLE_KEYS) { + name = ret.PRINTABLE_KEYS[i].toLowerCase(); + ret[name] = parseInt(i, 10); + } + oop.mixin(ret, ret.MODIFIER_KEYS); + oop.mixin(ret, ret.PRINTABLE_KEYS); + oop.mixin(ret, ret.FUNCTION_KEYS); + ret.enter = ret["return"]; + ret.escape = ret.esc; + ret.del = ret["delete"]; + ret[173] = "-"; + + (function() { + var mods = ["cmd", "ctrl", "alt", "shift"]; + for (var i = Math.pow(2, mods.length); i--; ) { + ret.KEY_MODS[i] = + mods + .filter(function(x) { + return i & ret.KEY_MODS[x]; + }) + .join("-") + "-"; + } + })(); + + ret.KEY_MODS[0] = ""; + ret.KEY_MODS[-1] = "input-"; + + return ret; + })(); + oop.mixin(exports, Keys); + + exports.keyCodeToString = function(keyCode) { + var keyString = Keys[keyCode]; + if (typeof keyString != "string") + keyString = String.fromCharCode(keyCode); + return keyString.toLowerCase(); + }; + } +); + +ace.define( + "ace/lib/event", + ["require", "exports", "module", "ace/lib/keys", "ace/lib/useragent"], + function(require, exports, module) { + "use strict"; + + var keys = require("./keys"); + var useragent = require("./useragent"); + + var pressedKeys = null; + var ts = 0; + + var activeListenerOptions; + function detectListenerOptionsSupport() { + activeListenerOptions = false; + try { + document.createComment("").addEventListener("test", function() {}, { + get passive() { + activeListenerOptions = { passive: false }; + } + }); + } catch (e) {} + } + + function getListenerOptions() { + if (activeListenerOptions == undefined) detectListenerOptionsSupport(); + return activeListenerOptions; + } + + function EventListener(elem, type, callback) { + this.elem = elem; + this.type = type; + this.callback = callback; + } + EventListener.prototype.destroy = function() { + removeListener(this.elem, this.type, this.callback); + this.elem = this.type = this.callback = undefined; + }; + + var addListener = (exports.addListener = function( + elem, + type, + callback, + destroyer + ) { + elem.addEventListener(type, callback, getListenerOptions()); + if (destroyer) + destroyer.$toDestroy.push(new EventListener(elem, type, callback)); + }); + + var removeListener = (exports.removeListener = function( + elem, + type, + callback + ) { + elem.removeEventListener(type, callback, getListenerOptions()); + }); + exports.stopEvent = function(e) { + exports.stopPropagation(e); + exports.preventDefault(e); + return false; + }; + + exports.stopPropagation = function(e) { + if (e.stopPropagation) e.stopPropagation(); + }; + + exports.preventDefault = function(e) { + if (e.preventDefault) e.preventDefault(); + }; + exports.getButton = function(e) { + if (e.type == "dblclick") return 0; + if ( + e.type == "contextmenu" || + (useragent.isMac && (e.ctrlKey && !e.altKey && !e.shiftKey)) + ) + return 2; + return e.button; + }; + + exports.capture = function(el, eventHandler, releaseCaptureHandler) { + function onMouseUp(e) { + eventHandler && eventHandler(e); + releaseCaptureHandler && releaseCaptureHandler(e); + + removeListener(document, "mousemove", eventHandler); + removeListener(document, "mouseup", onMouseUp); + removeListener(document, "dragstart", onMouseUp); + } + + addListener(document, "mousemove", eventHandler); + addListener(document, "mouseup", onMouseUp); + addListener(document, "dragstart", onMouseUp); + + return onMouseUp; + }; + + exports.addMouseWheelListener = function(el, callback, destroyer) { + if ("onmousewheel" in el) { + addListener( + el, + "mousewheel", + function(e) { + var factor = 8; + if (e.wheelDeltaX !== undefined) { + e.wheelX = -e.wheelDeltaX / factor; + e.wheelY = -e.wheelDeltaY / factor; + } else { + e.wheelX = 0; + e.wheelY = -e.wheelDelta / factor; + } + callback(e); + }, + destroyer + ); + } else if ("onwheel" in el) { + addListener( + el, + "wheel", + function(e) { + var factor = 0.35; + switch (e.deltaMode) { + case e.DOM_DELTA_PIXEL: + e.wheelX = e.deltaX * factor || 0; + e.wheelY = e.deltaY * factor || 0; + break; + case e.DOM_DELTA_LINE: + case e.DOM_DELTA_PAGE: + e.wheelX = (e.deltaX || 0) * 5; + e.wheelY = (e.deltaY || 0) * 5; + break; + } + + callback(e); + }, + destroyer + ); + } else { + addListener( + el, + "DOMMouseScroll", + function(e) { + if (e.axis && e.axis == e.HORIZONTAL_AXIS) { + e.wheelX = (e.detail || 0) * 5; + e.wheelY = 0; + } else { + e.wheelX = 0; + e.wheelY = (e.detail || 0) * 5; + } + callback(e); + }, + destroyer + ); + } + }; + + exports.addMultiMouseDownListener = function( + elements, + timeouts, + eventHandler, + callbackName, + destroyer + ) { + var clicks = 0; + var startX, startY, timer; + var eventNames = { + 2: "dblclick", + 3: "tripleclick", + 4: "quadclick" + }; + + function onMousedown(e) { + if (exports.getButton(e) !== 0) { + clicks = 0; + } else if (e.detail > 1) { + clicks++; + if (clicks > 4) clicks = 1; + } else { + clicks = 1; + } + if (useragent.isIE) { + var isNewClick = + Math.abs(e.clientX - startX) > 5 || + Math.abs(e.clientY - startY) > 5; + if (!timer || isNewClick) clicks = 1; + if (timer) clearTimeout(timer); + timer = setTimeout(function() { + timer = null; + }, timeouts[clicks - 1] || 600); + + if (clicks == 1) { + startX = e.clientX; + startY = e.clientY; + } + } + + e._clicks = clicks; + + eventHandler[callbackName]("mousedown", e); + + if (clicks > 4) clicks = 0; + else if (clicks > 1) + return eventHandler[callbackName](eventNames[clicks], e); + } + if (!Array.isArray(elements)) elements = [elements]; + elements.forEach(function(el) { + addListener(el, "mousedown", onMousedown, destroyer); + }); + }; + + var getModifierHash = function(e) { + return ( + 0 | + (e.ctrlKey ? 1 : 0) | + (e.altKey ? 2 : 0) | + (e.shiftKey ? 4 : 0) | + (e.metaKey ? 8 : 0) + ); + }; + + exports.getModifierString = function(e) { + return keys.KEY_MODS[getModifierHash(e)]; + }; + + function normalizeCommandKeys(callback, e, keyCode) { + var hashId = getModifierHash(e); + + if (!useragent.isMac && pressedKeys) { + if ( + e.getModifierState && + (e.getModifierState("OS") || e.getModifierState("Win")) + ) + hashId |= 8; + if (pressedKeys.altGr) { + if ((3 & hashId) != 3) pressedKeys.altGr = 0; + else return; + } + if (keyCode === 18 || keyCode === 17) { + var location = "location" in e ? e.location : e.keyLocation; + if (keyCode === 17 && location === 1) { + if (pressedKeys[keyCode] == 1) ts = e.timeStamp; + } else if (keyCode === 18 && hashId === 3 && location === 2) { + var dt = e.timeStamp - ts; + if (dt < 50) pressedKeys.altGr = true; + } + } + } + + if (keyCode in keys.MODIFIER_KEYS) { + keyCode = -1; + } + + if (!hashId && keyCode === 13) { + var location = "location" in e ? e.location : e.keyLocation; + if (location === 3) { + callback(e, hashId, -keyCode); + if (e.defaultPrevented) return; + } + } + + if (useragent.isChromeOS && hashId & 8) { + callback(e, hashId, keyCode); + if (e.defaultPrevented) return; + else hashId &= ~8; + } + if ( + !hashId && + !(keyCode in keys.FUNCTION_KEYS) && + !(keyCode in keys.PRINTABLE_KEYS) + ) { + return false; + } + + return callback(e, hashId, keyCode); + } + + exports.addCommandKeyListener = function(el, callback, destroyer) { + if ( + useragent.isOldGecko || + (useragent.isOpera && !("KeyboardEvent" in window)) + ) { + var lastKeyDownKeyCode = null; + addListener( + el, + "keydown", + function(e) { + lastKeyDownKeyCode = e.keyCode; + }, + destroyer + ); + addListener( + el, + "keypress", + function(e) { + return normalizeCommandKeys(callback, e, lastKeyDownKeyCode); + }, + destroyer + ); + } else { + var lastDefaultPrevented = null; + + addListener( + el, + "keydown", + function(e) { + pressedKeys[e.keyCode] = (pressedKeys[e.keyCode] || 0) + 1; + var result = normalizeCommandKeys(callback, e, e.keyCode); + lastDefaultPrevented = e.defaultPrevented; + return result; + }, + destroyer + ); + + addListener( + el, + "keypress", + function(e) { + if ( + lastDefaultPrevented && + (e.ctrlKey || e.altKey || e.shiftKey || e.metaKey) + ) { + exports.stopEvent(e); + lastDefaultPrevented = null; + } + }, + destroyer + ); + + addListener( + el, + "keyup", + function(e) { + pressedKeys[e.keyCode] = null; + }, + destroyer + ); + + if (!pressedKeys) { + resetPressedKeys(); + addListener(window, "focus", resetPressedKeys); + } + } + }; + function resetPressedKeys() { + pressedKeys = Object.create(null); + } + + if (typeof window == "object" && window.postMessage && !useragent.isOldIE) { + var postMessageId = 1; + exports.nextTick = function(callback, win) { + win = win || window; + var messageName = "zero-timeout-message-" + postMessageId++; + + var listener = function(e) { + if (e.data == messageName) { + exports.stopPropagation(e); + removeListener(win, "message", listener); + callback(); + } + }; + + addListener(win, "message", listener); + win.postMessage(messageName, "*"); + }; + } + + exports.$idleBlocked = false; + exports.onIdle = function(cb, timeout) { + return setTimeout(function handler() { + if (!exports.$idleBlocked) { + cb(); + } else { + setTimeout(handler, 100); + } + }, timeout); + }; + + exports.$idleBlockId = null; + exports.blockIdle = function(delay) { + if (exports.$idleBlockId) clearTimeout(exports.$idleBlockId); + + exports.$idleBlocked = true; + exports.$idleBlockId = setTimeout(function() { + exports.$idleBlocked = false; + }, delay || 100); + }; + + exports.nextFrame = + typeof window == "object" && + (window.requestAnimationFrame || + window.mozRequestAnimationFrame || + window.webkitRequestAnimationFrame || + window.msRequestAnimationFrame || + window.oRequestAnimationFrame); + + if (exports.nextFrame) exports.nextFrame = exports.nextFrame.bind(window); + else + exports.nextFrame = function(callback) { + setTimeout(callback, 17); + }; + } +); + +ace.define("ace/range", ["require", "exports", "module"], function( + require, + exports, + module +) { + "use strict"; + var comparePoints = function(p1, p2) { + return p1.row - p2.row || p1.column - p2.column; + }; + var Range = function(startRow, startColumn, endRow, endColumn) { + this.start = { + row: startRow, + column: startColumn + }; + + this.end = { + row: endRow, + column: endColumn + }; + }; + + (function() { + this.isEqual = function(range) { + return ( + this.start.row === range.start.row && + this.end.row === range.end.row && + this.start.column === range.start.column && + this.end.column === range.end.column + ); + }; + this.toString = function() { + return ( + "Range: [" + + this.start.row + + "/" + + this.start.column + + "] -> [" + + this.end.row + + "/" + + this.end.column + + "]" + ); + }; + + this.contains = function(row, column) { + return this.compare(row, column) == 0; + }; + this.compareRange = function(range) { + var cmp, + end = range.end, + start = range.start; + + cmp = this.compare(end.row, end.column); + if (cmp == 1) { + cmp = this.compare(start.row, start.column); + if (cmp == 1) { + return 2; + } else if (cmp == 0) { + return 1; + } else { + return 0; + } + } else if (cmp == -1) { + return -2; + } else { + cmp = this.compare(start.row, start.column); + if (cmp == -1) { + return -1; + } else if (cmp == 1) { + return 42; + } else { + return 0; + } + } + }; + this.comparePoint = function(p) { + return this.compare(p.row, p.column); + }; + this.containsRange = function(range) { + return ( + this.comparePoint(range.start) == 0 && this.comparePoint(range.end) == 0 + ); + }; + this.intersects = function(range) { + var cmp = this.compareRange(range); + return cmp == -1 || cmp == 0 || cmp == 1; + }; + this.isEnd = function(row, column) { + return this.end.row == row && this.end.column == column; + }; + this.isStart = function(row, column) { + return this.start.row == row && this.start.column == column; + }; + this.setStart = function(row, column) { + if (typeof row == "object") { + this.start.column = row.column; + this.start.row = row.row; + } else { + this.start.row = row; + this.start.column = column; + } + }; + this.setEnd = function(row, column) { + if (typeof row == "object") { + this.end.column = row.column; + this.end.row = row.row; + } else { + this.end.row = row; + this.end.column = column; + } + }; + this.inside = function(row, column) { + if (this.compare(row, column) == 0) { + if (this.isEnd(row, column) || this.isStart(row, column)) { + return false; + } else { + return true; + } + } + return false; + }; + this.insideStart = function(row, column) { + if (this.compare(row, column) == 0) { + if (this.isEnd(row, column)) { + return false; + } else { + return true; + } + } + return false; + }; + this.insideEnd = function(row, column) { + if (this.compare(row, column) == 0) { + if (this.isStart(row, column)) { + return false; + } else { + return true; + } + } + return false; + }; + this.compare = function(row, column) { + if (!this.isMultiLine()) { + if (row === this.start.row) { + return column < this.start.column + ? -1 + : column > this.end.column + ? 1 + : 0; + } + } + + if (row < this.start.row) return -1; + + if (row > this.end.row) return 1; + + if (this.start.row === row) return column >= this.start.column ? 0 : -1; + + if (this.end.row === row) return column <= this.end.column ? 0 : 1; + + return 0; + }; + this.compareStart = function(row, column) { + if (this.start.row == row && this.start.column == column) { + return -1; + } else { + return this.compare(row, column); + } + }; + this.compareEnd = function(row, column) { + if (this.end.row == row && this.end.column == column) { + return 1; + } else { + return this.compare(row, column); + } + }; + this.compareInside = function(row, column) { + if (this.end.row == row && this.end.column == column) { + return 1; + } else if (this.start.row == row && this.start.column == column) { + return -1; + } else { + return this.compare(row, column); + } + }; + this.clipRows = function(firstRow, lastRow) { + if (this.end.row > lastRow) var end = { row: lastRow + 1, column: 0 }; + else if (this.end.row < firstRow) var end = { row: firstRow, column: 0 }; + + if (this.start.row > lastRow) var start = { row: lastRow + 1, column: 0 }; + else if (this.start.row < firstRow) + var start = { row: firstRow, column: 0 }; + + return Range.fromPoints(start || this.start, end || this.end); + }; + this.extend = function(row, column) { + var cmp = this.compare(row, column); + + if (cmp == 0) return this; + else if (cmp == -1) var start = { row: row, column: column }; + else var end = { row: row, column: column }; + + return Range.fromPoints(start || this.start, end || this.end); + }; + + this.isEmpty = function() { + return ( + this.start.row === this.end.row && this.start.column === this.end.column + ); + }; + this.isMultiLine = function() { + return this.start.row !== this.end.row; + }; + this.clone = function() { + return Range.fromPoints(this.start, this.end); + }; + this.collapseRows = function() { + if (this.end.column == 0) + return new Range( + this.start.row, + 0, + Math.max(this.start.row, this.end.row - 1), + 0 + ); + else return new Range(this.start.row, 0, this.end.row, 0); + }; + this.toScreenRange = function(session) { + var screenPosStart = session.documentToScreenPosition(this.start); + var screenPosEnd = session.documentToScreenPosition(this.end); + + return new Range( + screenPosStart.row, + screenPosStart.column, + screenPosEnd.row, + screenPosEnd.column + ); + }; + this.moveBy = function(row, column) { + this.start.row += row; + this.start.column += column; + this.end.row += row; + this.end.column += column; + }; + }.call(Range.prototype)); + Range.fromPoints = function(start, end) { + return new Range(start.row, start.column, end.row, end.column); + }; + Range.comparePoints = comparePoints; + + Range.comparePoints = function(p1, p2) { + return p1.row - p2.row || p1.column - p2.column; + }; + + exports.Range = Range; +}); + +ace.define("ace/lib/lang", ["require", "exports", "module"], function( + require, + exports, + module +) { + "use strict"; + + exports.last = function(a) { + return a[a.length - 1]; + }; + + exports.stringReverse = function(string) { + return string + .split("") + .reverse() + .join(""); + }; + + exports.stringRepeat = function(string, count) { + var result = ""; + while (count > 0) { + if (count & 1) result += string; + + if ((count >>= 1)) string += string; + } + return result; + }; + + var trimBeginRegexp = /^\s\s*/; + var trimEndRegexp = /\s\s*$/; + + exports.stringTrimLeft = function(string) { + return string.replace(trimBeginRegexp, ""); + }; + + exports.stringTrimRight = function(string) { + return string.replace(trimEndRegexp, ""); + }; + + exports.copyObject = function(obj) { + var copy = {}; + for (var key in obj) { + copy[key] = obj[key]; + } + return copy; + }; + + exports.copyArray = function(array) { + var copy = []; + for (var i = 0, l = array.length; i < l; i++) { + if (array[i] && typeof array[i] == "object") + copy[i] = this.copyObject(array[i]); + else copy[i] = array[i]; + } + return copy; + }; + + exports.deepCopy = function deepCopy(obj) { + if (typeof obj !== "object" || !obj) return obj; + var copy; + if (Array.isArray(obj)) { + copy = []; + for (var key = 0; key < obj.length; key++) { + copy[key] = deepCopy(obj[key]); + } + return copy; + } + if (Object.prototype.toString.call(obj) !== "[object Object]") return obj; + + copy = {}; + for (var key in obj) copy[key] = deepCopy(obj[key]); + return copy; + }; + + exports.arrayToMap = function(arr) { + var map = {}; + for (var i = 0; i < arr.length; i++) { + map[arr[i]] = 1; + } + return map; + }; + + exports.createMap = function(props) { + var map = Object.create(null); + for (var i in props) { + map[i] = props[i]; + } + return map; + }; + exports.arrayRemove = function(array, value) { + for (var i = 0; i <= array.length; i++) { + if (value === array[i]) { + array.splice(i, 1); + } + } + }; + + exports.escapeRegExp = function(str) { + return str.replace(/([.*+?^${}()|[\]\/\\])/g, "\\$1"); + }; + + exports.escapeHTML = function(str) { + return ("" + str) + .replace(/&/g, "&") + .replace(/"/g, """) + .replace(/'/g, "'") + .replace(/ Date.now() - 50) return true; + return ($cancelT = false); + }, + cancel: function() { + $cancelT = Date.now(); + } + }; +}); + +ace.define( + "ace/keyboard/textinput", + [ + "require", + "exports", + "module", + "ace/lib/event", + "ace/lib/useragent", + "ace/lib/dom", + "ace/lib/lang", + "ace/clipboard", + "ace/lib/keys" + ], + function(require, exports, module) { + "use strict"; + + var event = require("../lib/event"); + var useragent = require("../lib/useragent"); + var dom = require("../lib/dom"); + var lang = require("../lib/lang"); + var clipboard = require("../clipboard"); + var BROKEN_SETDATA = useragent.isChrome < 18; + var USE_IE_MIME_TYPE = useragent.isIE; + var HAS_FOCUS_ARGS = useragent.isChrome > 63; + var MAX_LINE_LENGTH = 400; + + var KEYS = require("../lib/keys"); + var MODS = KEYS.KEY_MODS; + var isIOS = useragent.isIOS; + var valueResetRegex = isIOS ? /\s/ : /\n/; + var isMobile = useragent.isMobile; + + var TextInput = function(parentNode, host) { + var text = dom.createElement("textarea"); + text.className = "ace_text-input"; + + text.setAttribute("wrap", "off"); + text.setAttribute("autocorrect", "off"); + text.setAttribute("autocapitalize", "off"); + text.setAttribute("spellcheck", false); + + text.style.opacity = "0"; + parentNode.insertBefore(text, parentNode.firstChild); + + var copied = false; + var pasted = false; + var inComposition = false; + var sendingText = false; + var tempStyle = ""; + + if (!isMobile) text.style.fontSize = "1px"; + + var commandMode = false; + var ignoreFocusEvents = false; + + var lastValue = ""; + var lastSelectionStart = 0; + var lastSelectionEnd = 0; + var lastRestoreEnd = 0; + try { + var isFocused = document.activeElement === text; + } catch (e) {} + + event.addListener( + text, + "blur", + function(e) { + if (ignoreFocusEvents) return; + host.onBlur(e); + isFocused = false; + }, + host + ); + event.addListener( + text, + "focus", + function(e) { + if (ignoreFocusEvents) return; + isFocused = true; + if (useragent.isEdge) { + try { + if (!document.hasFocus()) return; + } catch (e) {} + } + host.onFocus(e); + if (useragent.isEdge) setTimeout(resetSelection); + else resetSelection(); + }, + host + ); + this.$focusScroll = false; + this.focus = function() { + if (tempStyle || HAS_FOCUS_ARGS || this.$focusScroll == "browser") + return text.focus({ preventScroll: true }); + + var top = text.style.top; + text.style.position = "fixed"; + text.style.top = "0px"; + try { + var isTransformed = text.getBoundingClientRect().top != 0; + } catch (e) { + return; + } + var ancestors = []; + if (isTransformed) { + var t = text.parentElement; + while (t && t.nodeType == 1) { + ancestors.push(t); + t.setAttribute("ace_nocontext", true); + if (!t.parentElement && t.getRootNode) t = t.getRootNode().host; + else t = t.parentElement; + } + } + text.focus({ preventScroll: true }); + if (isTransformed) { + ancestors.forEach(function(p) { + p.removeAttribute("ace_nocontext"); + }); + } + setTimeout(function() { + text.style.position = ""; + if (text.style.top == "0px") text.style.top = top; + }, 0); + }; + this.blur = function() { + text.blur(); + }; + this.isFocused = function() { + return isFocused; + }; + + host.on("beforeEndOperation", function() { + var curOp = host.curOp; + var commandName = curOp && curOp.command && curOp.command.name; + if (commandName == "insertstring") return; + var isUserAction = + commandName && (curOp.docChanged || curOp.selectionChanged); + if (inComposition && isUserAction) { + lastValue = text.value = ""; + onCompositionEnd(); + } + resetSelection(); + }); + + var resetSelection = isIOS + ? function(value) { + if (!isFocused || (copied && !value) || sendingText) return; + if (!value) value = ""; + var newValue = "\n ab" + value + "cde fg\n"; + if (newValue != text.value) text.value = lastValue = newValue; + + var selectionStart = 4; + var selectionEnd = + 4 + (value.length || (host.selection.isEmpty() ? 0 : 1)); + + if ( + lastSelectionStart != selectionStart || + lastSelectionEnd != selectionEnd + ) { + text.setSelectionRange(selectionStart, selectionEnd); + } + lastSelectionStart = selectionStart; + lastSelectionEnd = selectionEnd; + } + : function() { + if (inComposition || sendingText) return; + if (!isFocused && !afterContextMenu) return; + inComposition = true; + + var selectionStart = 0; + var selectionEnd = 0; + var line = ""; + + if (host.session) { + var selection = host.selection; + var range = selection.getRange(); + var row = selection.cursor.row; + selectionStart = range.start.column; + selectionEnd = range.end.column; + line = host.session.getLine(row); + + if (range.start.row != row) { + var prevLine = host.session.getLine(row - 1); + selectionStart = range.start.row < row - 1 ? 0 : selectionStart; + selectionEnd += prevLine.length + 1; + line = prevLine + "\n" + line; + } else if (range.end.row != row) { + var nextLine = host.session.getLine(row + 1); + selectionEnd = + range.end.row > row + 1 ? nextLine.length : selectionEnd; + selectionEnd += line.length + 1; + line = line + "\n" + nextLine; + } else if (isMobile && row > 0) { + line = "\n" + line; + selectionEnd += 1; + selectionStart += 1; + } + + if (line.length > MAX_LINE_LENGTH) { + if ( + selectionStart < MAX_LINE_LENGTH && + selectionEnd < MAX_LINE_LENGTH + ) { + line = line.slice(0, MAX_LINE_LENGTH); + } else { + line = "\n"; + selectionStart = 0; + selectionEnd = 1; + } + } + } + + var newValue = line + "\n\n"; + if (newValue != lastValue) { + text.value = lastValue = newValue; + lastSelectionStart = lastSelectionEnd = newValue.length; + } + if (afterContextMenu) { + lastSelectionStart = text.selectionStart; + lastSelectionEnd = text.selectionEnd; + } + if ( + lastSelectionEnd != selectionEnd || + lastSelectionStart != selectionStart || + text.selectionEnd != lastSelectionEnd // on ie edge selectionEnd changes silently after the initialization + ) { + try { + text.setSelectionRange(selectionStart, selectionEnd); + lastSelectionStart = selectionStart; + lastSelectionEnd = selectionEnd; + } catch (e) {} + } + inComposition = false; + }; + this.resetSelection = resetSelection; + + if (isFocused) host.onFocus(); + + var isAllSelected = function(text) { + return ( + text.selectionStart === 0 && + text.selectionEnd >= lastValue.length && + text.value === lastValue && + lastValue && + text.selectionEnd !== lastSelectionEnd + ); + }; + + var onSelect = function(e) { + if (inComposition) return; + if (copied) { + copied = false; + } else if (isAllSelected(text)) { + host.selectAll(); + resetSelection(); + } else if (isMobile && text.selectionStart != lastSelectionStart) { + resetSelection(); + } + }; + + var inputHandler = null; + this.setInputHandler = function(cb) { + inputHandler = cb; + }; + this.getInputHandler = function() { + return inputHandler; + }; + var afterContextMenu = false; + + var sendText = function(value, fromInput) { + if (afterContextMenu) afterContextMenu = false; + if (pasted) { + resetSelection(); + if (value) host.onPaste(value); + pasted = false; + return ""; + } else { + var selectionStart = text.selectionStart; + var selectionEnd = text.selectionEnd; + + var extendLeft = lastSelectionStart; + var extendRight = lastValue.length - lastSelectionEnd; + + var inserted = value; + var restoreStart = value.length - selectionStart; + var restoreEnd = value.length - selectionEnd; + + var i = 0; + while (extendLeft > 0 && lastValue[i] == value[i]) { + i++; + extendLeft--; + } + inserted = inserted.slice(i); + i = 1; + while ( + extendRight > 0 && + lastValue.length - i > lastSelectionStart - 1 && + lastValue[lastValue.length - i] == value[value.length - i] + ) { + i++; + extendRight--; + } + restoreStart -= i - 1; + restoreEnd -= i - 1; + var endIndex = inserted.length - i + 1; + if (endIndex < 0) { + extendLeft = -endIndex; + endIndex = 0; + } + inserted = inserted.slice(0, endIndex); + if ( + !fromInput && + !inserted && + !restoreStart && + !extendLeft && + !extendRight && + !restoreEnd + ) + return ""; + sendingText = true; + if ( + (inserted && + !extendLeft && + !extendRight && + !restoreStart && + !restoreEnd) || + commandMode + ) { + host.onTextInput(inserted); + } else { + host.onTextInput(inserted, { + extendLeft: extendLeft, + extendRight: extendRight, + restoreStart: restoreStart, + restoreEnd: restoreEnd + }); + } + sendingText = false; + + lastValue = value; + lastSelectionStart = selectionStart; + lastSelectionEnd = selectionEnd; + lastRestoreEnd = restoreEnd; + return inserted; + } + }; + var onInput = function(e) { + if (inComposition) return onCompositionUpdate(); + if (e && e.inputType) { + if (e.inputType == "historyUndo") return host.execCommand("undo"); + if (e.inputType == "historyRedo") return host.execCommand("redo"); + } + var data = text.value; + var inserted = sendText(data, true); + if ( + data.length > MAX_LINE_LENGTH + 100 || + valueResetRegex.test(inserted) || + (isMobile && + lastSelectionStart < 1 && + lastSelectionStart == lastSelectionEnd) + ) { + resetSelection(); + } + }; + + var handleClipboardData = function(e, data, forceIEMime) { + var clipboardData = e.clipboardData || window.clipboardData; + if (!clipboardData || BROKEN_SETDATA) return; + var mime = USE_IE_MIME_TYPE || forceIEMime ? "Text" : "text/plain"; + try { + if (data) { + return clipboardData.setData(mime, data) !== false; + } else { + return clipboardData.getData(mime); + } + } catch (e) { + if (!forceIEMime) return handleClipboardData(e, data, true); + } + }; + + var doCopy = function(e, isCut) { + var data = host.getCopyText(); + if (!data) return event.preventDefault(e); + + if (handleClipboardData(e, data)) { + if (isIOS) { + resetSelection(data); + copied = data; + setTimeout(function() { + copied = false; + }, 10); + } + isCut ? host.onCut() : host.onCopy(); + event.preventDefault(e); + } else { + copied = true; + text.value = data; + text.select(); + setTimeout(function() { + copied = false; + resetSelection(); + isCut ? host.onCut() : host.onCopy(); + }); + } + }; + + var onCut = function(e) { + doCopy(e, true); + }; + + var onCopy = function(e) { + doCopy(e, false); + }; + + var onPaste = function(e) { + var data = handleClipboardData(e); + if (clipboard.pasteCancelled()) return; + if (typeof data == "string") { + if (data) host.onPaste(data, e); + if (useragent.isIE) setTimeout(resetSelection); + event.preventDefault(e); + } else { + text.value = ""; + pasted = true; + } + }; + + event.addCommandKeyListener(text, host.onCommandKey.bind(host), host); + + event.addListener(text, "select", onSelect, host); + event.addListener(text, "input", onInput, host); + + event.addListener(text, "cut", onCut, host); + event.addListener(text, "copy", onCopy, host); + event.addListener(text, "paste", onPaste, host); + if (!("oncut" in text) || !("oncopy" in text) || !("onpaste" in text)) { + event.addListener( + parentNode, + "keydown", + function(e) { + if ((useragent.isMac && !e.metaKey) || !e.ctrlKey) return; + + switch (e.keyCode) { + case 67: + onCopy(e); + break; + case 86: + onPaste(e); + break; + case 88: + onCut(e); + break; + } + }, + host + ); + } + var onCompositionStart = function(e) { + if (inComposition || !host.onCompositionStart || host.$readOnly) return; + + inComposition = {}; + + if (commandMode) return; + + if (e.data) inComposition.useTextareaForIME = false; + + setTimeout(onCompositionUpdate, 0); + host._signal("compositionStart"); + host.on("mousedown", cancelComposition); + + var range = host.getSelectionRange(); + range.end.row = range.start.row; + range.end.column = range.start.column; + inComposition.markerRange = range; + inComposition.selectionStart = lastSelectionStart; + host.onCompositionStart(inComposition); + + if (inComposition.useTextareaForIME) { + lastValue = text.value = ""; + lastSelectionStart = 0; + lastSelectionEnd = 0; + } else { + if (text.msGetInputContext) + inComposition.context = text.msGetInputContext(); + if (text.getInputContext) + inComposition.context = text.getInputContext(); + } + }; + + var onCompositionUpdate = function() { + if (!inComposition || !host.onCompositionUpdate || host.$readOnly) + return; + if (commandMode) return cancelComposition(); + + if (inComposition.useTextareaForIME) { + host.onCompositionUpdate(text.value); + } else { + var data = text.value; + sendText(data); + if (inComposition.markerRange) { + if (inComposition.context) { + inComposition.markerRange.start.column = inComposition.selectionStart = + inComposition.context.compositionStartOffset; + } + inComposition.markerRange.end.column = + inComposition.markerRange.start.column + + lastSelectionEnd - + inComposition.selectionStart + + lastRestoreEnd; + } + } + }; + + var onCompositionEnd = function(e) { + if (!host.onCompositionEnd || host.$readOnly) return; + inComposition = false; + host.onCompositionEnd(); + host.off("mousedown", cancelComposition); + if (e) onInput(); + }; + + function cancelComposition() { + ignoreFocusEvents = true; + text.blur(); + text.focus(); + ignoreFocusEvents = false; + } + + var syncComposition = lang + .delayedCall(onCompositionUpdate, 50) + .schedule.bind(null, null); + + function onKeyup(e) { + if (e.keyCode == 27 && text.value.length < text.selectionStart) { + if (!inComposition) lastValue = text.value; + lastSelectionStart = lastSelectionEnd = -1; + resetSelection(); + } + syncComposition(); + } + + event.addListener(text, "compositionstart", onCompositionStart, host); + event.addListener(text, "compositionupdate", onCompositionUpdate, host); + event.addListener(text, "keyup", onKeyup, host); + event.addListener(text, "keydown", syncComposition, host); + event.addListener(text, "compositionend", onCompositionEnd, host); + + this.getElement = function() { + return text; + }; + this.setCommandMode = function(value) { + commandMode = value; + text.readOnly = false; + }; + + this.setReadOnly = function(readOnly) { + if (!commandMode) text.readOnly = readOnly; + }; + + this.setCopyWithEmptySelection = function(value) {}; + + this.onContextMenu = function(e) { + afterContextMenu = true; + resetSelection(); + host._emit("nativecontextmenu", { target: host, domEvent: e }); + this.moveToMouse(e, true); + }; + + this.moveToMouse = function(e, bringToFront) { + if (!tempStyle) tempStyle = text.style.cssText; + text.style.cssText = + (bringToFront ? "z-index:100000;" : "") + + (useragent.isIE ? "opacity:0.1;" : "") + + "text-indent: -" + + (lastSelectionStart + lastSelectionEnd) * + host.renderer.characterWidth * + 0.5 + + "px;"; + + var rect = host.container.getBoundingClientRect(); + var style = dom.computedStyle(host.container); + var top = rect.top + (parseInt(style.borderTopWidth) || 0); + var left = rect.left + (parseInt(rect.borderLeftWidth) || 0); + var maxTop = rect.bottom - top - text.clientHeight - 2; + var move = function(e) { + dom.translate( + text, + e.clientX - left - 2, + Math.min(e.clientY - top - 2, maxTop) + ); + }; + move(e); + + if (e.type != "mousedown") return; + + host.renderer.$isMousePressed = true; + + clearTimeout(closeTimeout); + if (useragent.isWin) + event.capture(host.container, move, onContextMenuClose); + }; + + this.onContextMenuClose = onContextMenuClose; + var closeTimeout; + function onContextMenuClose() { + clearTimeout(closeTimeout); + closeTimeout = setTimeout(function() { + if (tempStyle) { + text.style.cssText = tempStyle; + tempStyle = ""; + } + host.renderer.$isMousePressed = false; + if (host.renderer.$keepTextAreaAtCursor) + host.renderer.$moveTextAreaToCursor(); + }, 0); + } + + var onContextMenu = function(e) { + host.textInput.onContextMenu(e); + onContextMenuClose(); + }; + event.addListener(text, "mouseup", onContextMenu, host); + event.addListener( + text, + "mousedown", + function(e) { + e.preventDefault(); + onContextMenuClose(); + }, + host + ); + event.addListener( + host.renderer.scroller, + "contextmenu", + onContextMenu, + host + ); + event.addListener(text, "contextmenu", onContextMenu, host); + + if (isIOS) addIosSelectionHandler(parentNode, host, text); + + function addIosSelectionHandler(parentNode, host, text) { + var typingResetTimeout = null; + var typing = false; + + text.addEventListener( + "keydown", + function(e) { + if (typingResetTimeout) clearTimeout(typingResetTimeout); + typing = true; + }, + true + ); + + text.addEventListener( + "keyup", + function(e) { + typingResetTimeout = setTimeout(function() { + typing = false; + }, 100); + }, + true + ); + var detectArrowKeys = function(e) { + if (document.activeElement !== text) return; + if (typing || inComposition || host.$mouseHandler.isMousePressed) + return; + + if (copied) { + return; + } + var selectionStart = text.selectionStart; + var selectionEnd = text.selectionEnd; + + var key = null; + var modifier = 0; + if (selectionStart == 0) { + key = KEYS.up; + } else if (selectionStart == 1) { + key = KEYS.home; + } else if ( + selectionEnd > lastSelectionEnd && + lastValue[selectionEnd] == "\n" + ) { + key = KEYS.end; + } else if ( + selectionStart < lastSelectionStart && + lastValue[selectionStart - 1] == " " + ) { + key = KEYS.left; + modifier = MODS.option; + } else if ( + selectionStart < lastSelectionStart || + (selectionStart == lastSelectionStart && + lastSelectionEnd != lastSelectionStart && + selectionStart == selectionEnd) + ) { + key = KEYS.left; + } else if ( + selectionEnd > lastSelectionEnd && + lastValue.slice(0, selectionEnd).split("\n").length > 2 + ) { + key = KEYS.down; + } else if ( + selectionEnd > lastSelectionEnd && + lastValue[selectionEnd - 1] == " " + ) { + key = KEYS.right; + modifier = MODS.option; + } else if ( + selectionEnd > lastSelectionEnd || + (selectionEnd == lastSelectionEnd && + lastSelectionEnd != lastSelectionStart && + selectionStart == selectionEnd) + ) { + key = KEYS.right; + } + + if (selectionStart !== selectionEnd) modifier |= MODS.shift; + + if (key) { + var result = host.onCommandKey({}, modifier, key); + if (!result && host.commands) { + key = KEYS.keyCodeToString(key); + var command = host.commands.findKeyCommand(modifier, key); + if (command) host.execCommand(command); + } + lastSelectionStart = selectionStart; + lastSelectionEnd = selectionEnd; + resetSelection(""); + } + }; + document.addEventListener("selectionchange", detectArrowKeys); + host.on("destroy", function() { + document.removeEventListener("selectionchange", detectArrowKeys); + }); + } + }; + + exports.TextInput = TextInput; + exports.$setUserAgentForTests = function(_isMobile, _isIOS) { + isMobile = _isMobile; + isIOS = _isIOS; + }; + } +); + +ace.define( + "ace/mouse/default_handlers", + ["require", "exports", "module", "ace/lib/useragent"], + function(require, exports, module) { + "use strict"; + + var useragent = require("../lib/useragent"); + + var DRAG_OFFSET = 0; // pixels + var SCROLL_COOLDOWN_T = 550; // milliseconds + + function DefaultHandlers(mouseHandler) { + mouseHandler.$clickSelection = null; + + var editor = mouseHandler.editor; + editor.setDefaultHandler( + "mousedown", + this.onMouseDown.bind(mouseHandler) + ); + editor.setDefaultHandler( + "dblclick", + this.onDoubleClick.bind(mouseHandler) + ); + editor.setDefaultHandler( + "tripleclick", + this.onTripleClick.bind(mouseHandler) + ); + editor.setDefaultHandler( + "quadclick", + this.onQuadClick.bind(mouseHandler) + ); + editor.setDefaultHandler( + "mousewheel", + this.onMouseWheel.bind(mouseHandler) + ); + + var exports = [ + "select", + "startSelect", + "selectEnd", + "selectAllEnd", + "selectByWordsEnd", + "selectByLinesEnd", + "dragWait", + "dragWaitEnd", + "focusWait" + ]; + + exports.forEach(function(x) { + mouseHandler[x] = this[x]; + }, this); + + mouseHandler.selectByLines = this.extendSelectionBy.bind( + mouseHandler, + "getLineRange" + ); + mouseHandler.selectByWords = this.extendSelectionBy.bind( + mouseHandler, + "getWordRange" + ); + } + + (function() { + this.onMouseDown = function(ev) { + var inSelection = ev.inSelection(); + var pos = ev.getDocumentPosition(); + this.mousedownEvent = ev; + var editor = this.editor; + + var button = ev.getButton(); + if (button !== 0) { + var selectionRange = editor.getSelectionRange(); + var selectionEmpty = selectionRange.isEmpty(); + if (selectionEmpty || button == 1) + editor.selection.moveToPosition(pos); + if (button == 2) { + editor.textInput.onContextMenu(ev.domEvent); + if (!useragent.isMozilla) ev.preventDefault(); + } + return; + } + + this.mousedownEvent.time = Date.now(); + if (inSelection && !editor.isFocused()) { + editor.focus(); + if ( + this.$focusTimeout && + !this.$clickSelection && + !editor.inMultiSelectMode + ) { + this.setState("focusWait"); + this.captureMouse(ev); + return; + } + } + + this.captureMouse(ev); + this.startSelect(pos, ev.domEvent._clicks > 1); + return ev.preventDefault(); + }; + + this.startSelect = function(pos, waitForClickSelection) { + pos = + pos || this.editor.renderer.screenToTextCoordinates(this.x, this.y); + var editor = this.editor; + if (!this.mousedownEvent) return; + if (this.mousedownEvent.getShiftKey()) + editor.selection.selectToPosition(pos); + else if (!waitForClickSelection) editor.selection.moveToPosition(pos); + if (!waitForClickSelection) this.select(); + if (editor.renderer.scroller.setCapture) { + editor.renderer.scroller.setCapture(); + } + editor.setStyle("ace_selecting"); + this.setState("select"); + }; + + this.select = function() { + var anchor, + editor = this.editor; + var cursor = editor.renderer.screenToTextCoordinates(this.x, this.y); + if (this.$clickSelection) { + var cmp = this.$clickSelection.comparePoint(cursor); + + if (cmp == -1) { + anchor = this.$clickSelection.end; + } else if (cmp == 1) { + anchor = this.$clickSelection.start; + } else { + var orientedRange = calcRangeOrientation( + this.$clickSelection, + cursor + ); + cursor = orientedRange.cursor; + anchor = orientedRange.anchor; + } + editor.selection.setSelectionAnchor(anchor.row, anchor.column); + } + editor.selection.selectToPosition(cursor); + editor.renderer.scrollCursorIntoView(); + }; + + this.extendSelectionBy = function(unitName) { + var anchor, + editor = this.editor; + var cursor = editor.renderer.screenToTextCoordinates(this.x, this.y); + var range = editor.selection[unitName](cursor.row, cursor.column); + if (this.$clickSelection) { + var cmpStart = this.$clickSelection.comparePoint(range.start); + var cmpEnd = this.$clickSelection.comparePoint(range.end); + + if (cmpStart == -1 && cmpEnd <= 0) { + anchor = this.$clickSelection.end; + if ( + range.end.row != cursor.row || + range.end.column != cursor.column + ) + cursor = range.start; + } else if (cmpEnd == 1 && cmpStart >= 0) { + anchor = this.$clickSelection.start; + if ( + range.start.row != cursor.row || + range.start.column != cursor.column + ) + cursor = range.end; + } else if (cmpStart == -1 && cmpEnd == 1) { + cursor = range.end; + anchor = range.start; + } else { + var orientedRange = calcRangeOrientation( + this.$clickSelection, + cursor + ); + cursor = orientedRange.cursor; + anchor = orientedRange.anchor; + } + editor.selection.setSelectionAnchor(anchor.row, anchor.column); + } + editor.selection.selectToPosition(cursor); + editor.renderer.scrollCursorIntoView(); + }; + + this.selectEnd = this.selectAllEnd = this.selectByWordsEnd = this.selectByLinesEnd = function() { + this.$clickSelection = null; + this.editor.unsetStyle("ace_selecting"); + if (this.editor.renderer.scroller.releaseCapture) { + this.editor.renderer.scroller.releaseCapture(); + } + }; + + this.focusWait = function() { + var distance = calcDistance( + this.mousedownEvent.x, + this.mousedownEvent.y, + this.x, + this.y + ); + var time = Date.now(); + + if ( + distance > DRAG_OFFSET || + time - this.mousedownEvent.time > this.$focusTimeout + ) + this.startSelect(this.mousedownEvent.getDocumentPosition()); + }; + + this.onDoubleClick = function(ev) { + var pos = ev.getDocumentPosition(); + var editor = this.editor; + var session = editor.session; + + var range = session.getBracketRange(pos); + if (range) { + if (range.isEmpty()) { + range.start.column--; + range.end.column++; + } + this.setState("select"); + } else { + range = editor.selection.getWordRange(pos.row, pos.column); + this.setState("selectByWords"); + } + this.$clickSelection = range; + this.select(); + }; + + this.onTripleClick = function(ev) { + var pos = ev.getDocumentPosition(); + var editor = this.editor; + + this.setState("selectByLines"); + var range = editor.getSelectionRange(); + if (range.isMultiLine() && range.contains(pos.row, pos.column)) { + this.$clickSelection = editor.selection.getLineRange(range.start.row); + this.$clickSelection.end = editor.selection.getLineRange( + range.end.row + ).end; + } else { + this.$clickSelection = editor.selection.getLineRange(pos.row); + } + this.select(); + }; + + this.onQuadClick = function(ev) { + var editor = this.editor; + + editor.selectAll(); + this.$clickSelection = editor.getSelectionRange(); + this.setState("selectAll"); + }; + + this.onMouseWheel = function(ev) { + if (ev.getAccelKey()) return; + if (ev.getShiftKey() && ev.wheelY && !ev.wheelX) { + ev.wheelX = ev.wheelY; + ev.wheelY = 0; + } + + var editor = this.editor; + + if (!this.$lastScroll) + this.$lastScroll = { t: 0, vx: 0, vy: 0, allowed: 0 }; + + var prevScroll = this.$lastScroll; + var t = ev.domEvent.timeStamp; + var dt = t - prevScroll.t; + var vx = dt ? ev.wheelX / dt : prevScroll.vx; + var vy = dt ? ev.wheelY / dt : prevScroll.vy; + if (dt < SCROLL_COOLDOWN_T) { + vx = (vx + prevScroll.vx) / 2; + vy = (vy + prevScroll.vy) / 2; + } + + var direction = Math.abs(vx / vy); + + var canScroll = false; + if ( + direction >= 1 && + editor.renderer.isScrollableBy(ev.wheelX * ev.speed, 0) + ) + canScroll = true; + if ( + direction <= 1 && + editor.renderer.isScrollableBy(0, ev.wheelY * ev.speed) + ) + canScroll = true; + + if (canScroll) { + prevScroll.allowed = t; + } else if (t - prevScroll.allowed < SCROLL_COOLDOWN_T) { + var isSlower = + Math.abs(vx) <= 1.5 * Math.abs(prevScroll.vx) && + Math.abs(vy) <= 1.5 * Math.abs(prevScroll.vy); + if (isSlower) { + canScroll = true; + prevScroll.allowed = t; + } else { + prevScroll.allowed = 0; + } + } + + prevScroll.t = t; + prevScroll.vx = vx; + prevScroll.vy = vy; + + if (canScroll) { + editor.renderer.scrollBy(ev.wheelX * ev.speed, ev.wheelY * ev.speed); + return ev.stop(); + } + }; + }.call(DefaultHandlers.prototype)); + + exports.DefaultHandlers = DefaultHandlers; + + function calcDistance(ax, ay, bx, by) { + return Math.sqrt(Math.pow(bx - ax, 2) + Math.pow(by - ay, 2)); + } + + function calcRangeOrientation(range, cursor) { + if (range.start.row == range.end.row) + var cmp = 2 * cursor.column - range.start.column - range.end.column; + else if ( + range.start.row == range.end.row - 1 && + !range.start.column && + !range.end.column + ) + var cmp = cursor.column - 4; + else var cmp = 2 * cursor.row - range.start.row - range.end.row; + + if (cmp < 0) return { cursor: range.start, anchor: range.end }; + else return { cursor: range.end, anchor: range.start }; + } + } +); + +ace.define( + "ace/tooltip", + ["require", "exports", "module", "ace/lib/oop", "ace/lib/dom"], + function(require, exports, module) { + "use strict"; + + var oop = require("./lib/oop"); + var dom = require("./lib/dom"); + function Tooltip(parentNode) { + this.isOpen = false; + this.$element = null; + this.$parentNode = parentNode; + } + + (function() { + this.$init = function() { + this.$element = dom.createElement("div"); + this.$element.className = "ace_tooltip"; + this.$element.style.display = "none"; + this.$parentNode.appendChild(this.$element); + return this.$element; + }; + this.getElement = function() { + return this.$element || this.$init(); + }; + this.setText = function(text) { + this.getElement().textContent = text; + }; + this.setHtml = function(html) { + this.getElement().innerHTML = html; + }; + this.setPosition = function(x, y) { + this.getElement().style.left = x + "px"; + this.getElement().style.top = y + "px"; + }; + this.setClassName = function(className) { + dom.addCssClass(this.getElement(), className); + }; + this.show = function(text, x, y) { + if (text != null) this.setText(text); + if (x != null && y != null) this.setPosition(x, y); + if (!this.isOpen) { + this.getElement().style.display = "block"; + this.isOpen = true; + } + }; + + this.hide = function() { + if (this.isOpen) { + this.getElement().style.display = "none"; + this.isOpen = false; + } + }; + this.getHeight = function() { + return this.getElement().offsetHeight; + }; + this.getWidth = function() { + return this.getElement().offsetWidth; + }; + + this.destroy = function() { + this.isOpen = false; + if (this.$element && this.$element.parentNode) { + this.$element.parentNode.removeChild(this.$element); + } + }; + }.call(Tooltip.prototype)); + + exports.Tooltip = Tooltip; + } +); + +ace.define( + "ace/mouse/default_gutter_handler", + [ + "require", + "exports", + "module", + "ace/lib/dom", + "ace/lib/oop", + "ace/lib/event", + "ace/tooltip" + ], + function(require, exports, module) { + "use strict"; + var dom = require("../lib/dom"); + var oop = require("../lib/oop"); + var event = require("../lib/event"); + var Tooltip = require("../tooltip").Tooltip; + + function GutterHandler(mouseHandler) { + var editor = mouseHandler.editor; + var gutter = editor.renderer.$gutterLayer; + var tooltip = new GutterTooltip(editor.container); + + mouseHandler.editor.setDefaultHandler("guttermousedown", function(e) { + if (!editor.isFocused() || e.getButton() != 0) return; + var gutterRegion = gutter.getRegion(e); + + if (gutterRegion == "foldWidgets") return; + + var row = e.getDocumentPosition().row; + var selection = editor.session.selection; + + if (e.getShiftKey()) selection.selectTo(row, 0); + else { + if (e.domEvent.detail == 2) { + editor.selectAll(); + return e.preventDefault(); + } + mouseHandler.$clickSelection = editor.selection.getLineRange(row); + } + mouseHandler.setState("selectByLines"); + mouseHandler.captureMouse(e); + return e.preventDefault(); + }); + + var tooltipTimeout, mouseEvent, tooltipAnnotation; + + function showTooltip() { + var row = mouseEvent.getDocumentPosition().row; + var annotation = gutter.$annotations[row]; + if (!annotation) return hideTooltip(); + + var maxRow = editor.session.getLength(); + if (row == maxRow) { + var screenRow = editor.renderer.pixelToScreenCoordinates( + 0, + mouseEvent.y + ).row; + var pos = mouseEvent.$pos; + if ( + screenRow > editor.session.documentToScreenRow(pos.row, pos.column) + ) + return hideTooltip(); + } + + if (tooltipAnnotation == annotation) return; + tooltipAnnotation = annotation.text.join("
"); + + tooltip.setHtml(tooltipAnnotation); + tooltip.show(); + editor._signal("showGutterTooltip", tooltip); + editor.on("mousewheel", hideTooltip); + + if (mouseHandler.$tooltipFollowsMouse) { + moveTooltip(mouseEvent); + } else { + var gutterElement = mouseEvent.domEvent.target; + var rect = gutterElement.getBoundingClientRect(); + var style = tooltip.getElement().style; + style.left = rect.right + "px"; + style.top = rect.bottom + "px"; + } + } + + function hideTooltip() { + if (tooltipTimeout) tooltipTimeout = clearTimeout(tooltipTimeout); + if (tooltipAnnotation) { + tooltip.hide(); + tooltipAnnotation = null; + editor._signal("hideGutterTooltip", tooltip); + editor.off("mousewheel", hideTooltip); + } + } + + function moveTooltip(e) { + tooltip.setPosition(e.x, e.y); + } + + mouseHandler.editor.setDefaultHandler("guttermousemove", function(e) { + var target = e.domEvent.target || e.domEvent.srcElement; + if (dom.hasCssClass(target, "ace_fold-widget")) return hideTooltip(); + + if (tooltipAnnotation && mouseHandler.$tooltipFollowsMouse) + moveTooltip(e); + + mouseEvent = e; + if (tooltipTimeout) return; + tooltipTimeout = setTimeout(function() { + tooltipTimeout = null; + if (mouseEvent && !mouseHandler.isMousePressed) showTooltip(); + else hideTooltip(); + }, 50); + }); + + event.addListener( + editor.renderer.$gutter, + "mouseout", + function(e) { + mouseEvent = null; + if (!tooltipAnnotation || tooltipTimeout) return; + + tooltipTimeout = setTimeout(function() { + tooltipTimeout = null; + hideTooltip(); + }, 50); + }, + editor + ); + + editor.on("changeSession", hideTooltip); + } + + function GutterTooltip(parentNode) { + Tooltip.call(this, parentNode); + } + + oop.inherits(GutterTooltip, Tooltip); + + (function() { + this.setPosition = function(x, y) { + var windowWidth = + window.innerWidth || document.documentElement.clientWidth; + var windowHeight = + window.innerHeight || document.documentElement.clientHeight; + var width = this.getWidth(); + var height = this.getHeight(); + x += 15; + y += 15; + if (x + width > windowWidth) { + x -= x + width - windowWidth; + } + if (y + height > windowHeight) { + y -= 20 + height; + } + Tooltip.prototype.setPosition.call(this, x, y); + }; + }.call(GutterTooltip.prototype)); + + exports.GutterHandler = GutterHandler; + } +); + +ace.define( + "ace/mouse/mouse_event", + ["require", "exports", "module", "ace/lib/event", "ace/lib/useragent"], + function(require, exports, module) { + "use strict"; + + var event = require("../lib/event"); + var useragent = require("../lib/useragent"); + var MouseEvent = (exports.MouseEvent = function(domEvent, editor) { + this.domEvent = domEvent; + this.editor = editor; + + this.x = this.clientX = domEvent.clientX; + this.y = this.clientY = domEvent.clientY; + + this.$pos = null; + this.$inSelection = null; + + this.propagationStopped = false; + this.defaultPrevented = false; + }); + + (function() { + this.stopPropagation = function() { + event.stopPropagation(this.domEvent); + this.propagationStopped = true; + }; + + this.preventDefault = function() { + event.preventDefault(this.domEvent); + this.defaultPrevented = true; + }; + + this.stop = function() { + this.stopPropagation(); + this.preventDefault(); + }; + this.getDocumentPosition = function() { + if (this.$pos) return this.$pos; + + this.$pos = this.editor.renderer.screenToTextCoordinates( + this.clientX, + this.clientY + ); + return this.$pos; + }; + this.inSelection = function() { + if (this.$inSelection !== null) return this.$inSelection; + + var editor = this.editor; + + var selectionRange = editor.getSelectionRange(); + if (selectionRange.isEmpty()) this.$inSelection = false; + else { + var pos = this.getDocumentPosition(); + this.$inSelection = selectionRange.contains(pos.row, pos.column); + } + + return this.$inSelection; + }; + this.getButton = function() { + return event.getButton(this.domEvent); + }; + this.getShiftKey = function() { + return this.domEvent.shiftKey; + }; + + this.getAccelKey = useragent.isMac + ? function() { + return this.domEvent.metaKey; + } + : function() { + return this.domEvent.ctrlKey; + }; + }.call(MouseEvent.prototype)); + } +); + +ace.define( + "ace/mouse/dragdrop_handler", + [ + "require", + "exports", + "module", + "ace/lib/dom", + "ace/lib/event", + "ace/lib/useragent" + ], + function(require, exports, module) { + "use strict"; + + var dom = require("../lib/dom"); + var event = require("../lib/event"); + var useragent = require("../lib/useragent"); + + var AUTOSCROLL_DELAY = 200; + var SCROLL_CURSOR_DELAY = 200; + var SCROLL_CURSOR_HYSTERESIS = 5; + + function DragdropHandler(mouseHandler) { + var editor = mouseHandler.editor; + + var blankImage = dom.createElement("img"); + blankImage.src = + "data:image/gif;base64,R0lGODlhAQABAAAAACH5BAEKAAEALAAAAAABAAEAAAICTAEAOw=="; + if (useragent.isOpera) + blankImage.style.cssText = + "width:1px;height:1px;position:fixed;top:0;left:0;z-index:2147483647;opacity:0;"; + + var exports = [ + "dragWait", + "dragWaitEnd", + "startDrag", + "dragReadyEnd", + "onMouseDrag" + ]; + + exports.forEach(function(x) { + mouseHandler[x] = this[x]; + }, this); + editor.on("mousedown", this.onMouseDown.bind(mouseHandler)); + + var mouseTarget = editor.container; + var dragSelectionMarker, x, y; + var timerId, range; + var dragCursor, + counter = 0; + var dragOperation; + var isInternal; + var autoScrollStartTime; + var cursorMovedTime; + var cursorPointOnCaretMoved; + + this.onDragStart = function(e) { + if (this.cancelDrag || !mouseTarget.draggable) { + var self = this; + setTimeout(function() { + self.startSelect(); + self.captureMouse(e); + }, 0); + return e.preventDefault(); + } + range = editor.getSelectionRange(); + + var dataTransfer = e.dataTransfer; + dataTransfer.effectAllowed = editor.getReadOnly() ? "copy" : "copyMove"; + if (useragent.isOpera) { + editor.container.appendChild(blankImage); + blankImage.scrollTop = 0; + } + dataTransfer.setDragImage && + dataTransfer.setDragImage(blankImage, 0, 0); + if (useragent.isOpera) { + editor.container.removeChild(blankImage); + } + dataTransfer.clearData(); + dataTransfer.setData("Text", editor.session.getTextRange()); + + isInternal = true; + this.setState("drag"); + }; + + this.onDragEnd = function(e) { + mouseTarget.draggable = false; + isInternal = false; + this.setState(null); + if (!editor.getReadOnly()) { + var dropEffect = e.dataTransfer.dropEffect; + if (!dragOperation && dropEffect == "move") + editor.session.remove(editor.getSelectionRange()); + editor.$resetCursorStyle(); + } + this.editor.unsetStyle("ace_dragging"); + this.editor.renderer.setCursorStyle(""); + }; + + this.onDragEnter = function(e) { + if (editor.getReadOnly() || !canAccept(e.dataTransfer)) return; + x = e.clientX; + y = e.clientY; + if (!dragSelectionMarker) addDragMarker(); + counter++; + e.dataTransfer.dropEffect = dragOperation = getDropEffect(e); + return event.preventDefault(e); + }; + + this.onDragOver = function(e) { + if (editor.getReadOnly() || !canAccept(e.dataTransfer)) return; + x = e.clientX; + y = e.clientY; + if (!dragSelectionMarker) { + addDragMarker(); + counter++; + } + if (onMouseMoveTimer !== null) onMouseMoveTimer = null; + + e.dataTransfer.dropEffect = dragOperation = getDropEffect(e); + return event.preventDefault(e); + }; + + this.onDragLeave = function(e) { + counter--; + if (counter <= 0 && dragSelectionMarker) { + clearDragMarker(); + dragOperation = null; + return event.preventDefault(e); + } + }; + + this.onDrop = function(e) { + if (!dragCursor) return; + var dataTransfer = e.dataTransfer; + if (isInternal) { + switch (dragOperation) { + case "move": + if (range.contains(dragCursor.row, dragCursor.column)) { + range = { + start: dragCursor, + end: dragCursor + }; + } else { + range = editor.moveText(range, dragCursor); + } + break; + case "copy": + range = editor.moveText(range, dragCursor, true); + break; + } + } else { + var dropData = dataTransfer.getData("Text"); + range = { + start: dragCursor, + end: editor.session.insert(dragCursor, dropData) + }; + editor.focus(); + dragOperation = null; + } + clearDragMarker(); + return event.preventDefault(e); + }; + + event.addListener( + mouseTarget, + "dragstart", + this.onDragStart.bind(mouseHandler), + editor + ); + event.addListener( + mouseTarget, + "dragend", + this.onDragEnd.bind(mouseHandler), + editor + ); + event.addListener( + mouseTarget, + "dragenter", + this.onDragEnter.bind(mouseHandler), + editor + ); + event.addListener( + mouseTarget, + "dragover", + this.onDragOver.bind(mouseHandler), + editor + ); + event.addListener( + mouseTarget, + "dragleave", + this.onDragLeave.bind(mouseHandler), + editor + ); + event.addListener( + mouseTarget, + "drop", + this.onDrop.bind(mouseHandler), + editor + ); + + function scrollCursorIntoView(cursor, prevCursor) { + var now = Date.now(); + var vMovement = !prevCursor || cursor.row != prevCursor.row; + var hMovement = !prevCursor || cursor.column != prevCursor.column; + if (!cursorMovedTime || vMovement || hMovement) { + editor.moveCursorToPosition(cursor); + cursorMovedTime = now; + cursorPointOnCaretMoved = { x: x, y: y }; + } else { + var distance = calcDistance( + cursorPointOnCaretMoved.x, + cursorPointOnCaretMoved.y, + x, + y + ); + if (distance > SCROLL_CURSOR_HYSTERESIS) { + cursorMovedTime = null; + } else if (now - cursorMovedTime >= SCROLL_CURSOR_DELAY) { + editor.renderer.scrollCursorIntoView(); + cursorMovedTime = null; + } + } + } + + function autoScroll(cursor, prevCursor) { + var now = Date.now(); + var lineHeight = editor.renderer.layerConfig.lineHeight; + var characterWidth = editor.renderer.layerConfig.characterWidth; + var editorRect = editor.renderer.scroller.getBoundingClientRect(); + var offsets = { + x: { + left: x - editorRect.left, + right: editorRect.right - x + }, + y: { + top: y - editorRect.top, + bottom: editorRect.bottom - y + } + }; + var nearestXOffset = Math.min(offsets.x.left, offsets.x.right); + var nearestYOffset = Math.min(offsets.y.top, offsets.y.bottom); + var scrollCursor = { row: cursor.row, column: cursor.column }; + if (nearestXOffset / characterWidth <= 2) { + scrollCursor.column += offsets.x.left < offsets.x.right ? -3 : +2; + } + if (nearestYOffset / lineHeight <= 1) { + scrollCursor.row += offsets.y.top < offsets.y.bottom ? -1 : +1; + } + var vScroll = cursor.row != scrollCursor.row; + var hScroll = cursor.column != scrollCursor.column; + var vMovement = !prevCursor || cursor.row != prevCursor.row; + if (vScroll || (hScroll && !vMovement)) { + if (!autoScrollStartTime) autoScrollStartTime = now; + else if (now - autoScrollStartTime >= AUTOSCROLL_DELAY) + editor.renderer.scrollCursorIntoView(scrollCursor); + } else { + autoScrollStartTime = null; + } + } + + function onDragInterval() { + var prevCursor = dragCursor; + dragCursor = editor.renderer.screenToTextCoordinates(x, y); + scrollCursorIntoView(dragCursor, prevCursor); + autoScroll(dragCursor, prevCursor); + } + + function addDragMarker() { + range = editor.selection.toOrientedRange(); + dragSelectionMarker = editor.session.addMarker( + range, + "ace_selection", + editor.getSelectionStyle() + ); + editor.clearSelection(); + if (editor.isFocused()) editor.renderer.$cursorLayer.setBlinking(false); + clearInterval(timerId); + onDragInterval(); + timerId = setInterval(onDragInterval, 20); + counter = 0; + event.addListener(document, "mousemove", onMouseMove); + } + + function clearDragMarker() { + clearInterval(timerId); + editor.session.removeMarker(dragSelectionMarker); + dragSelectionMarker = null; + editor.selection.fromOrientedRange(range); + if (editor.isFocused() && !isInternal) editor.$resetCursorStyle(); + range = null; + dragCursor = null; + counter = 0; + autoScrollStartTime = null; + cursorMovedTime = null; + event.removeListener(document, "mousemove", onMouseMove); + } + var onMouseMoveTimer = null; + function onMouseMove() { + if (onMouseMoveTimer == null) { + onMouseMoveTimer = setTimeout(function() { + if (onMouseMoveTimer != null && dragSelectionMarker) + clearDragMarker(); + }, 20); + } + } + + function canAccept(dataTransfer) { + var types = dataTransfer.types; + return ( + !types || + Array.prototype.some.call(types, function(type) { + return type == "text/plain" || type == "Text"; + }) + ); + } + + function getDropEffect(e) { + var copyAllowed = ["copy", "copymove", "all", "uninitialized"]; + var moveAllowed = [ + "move", + "copymove", + "linkmove", + "all", + "uninitialized" + ]; + + var copyModifierState = useragent.isMac ? e.altKey : e.ctrlKey; + var effectAllowed = "uninitialized"; + try { + effectAllowed = e.dataTransfer.effectAllowed.toLowerCase(); + } catch (e) {} + var dropEffect = "none"; + + if (copyModifierState && copyAllowed.indexOf(effectAllowed) >= 0) + dropEffect = "copy"; + else if (moveAllowed.indexOf(effectAllowed) >= 0) dropEffect = "move"; + else if (copyAllowed.indexOf(effectAllowed) >= 0) dropEffect = "copy"; + + return dropEffect; + } + } + + (function() { + this.dragWait = function() { + var interval = Date.now() - this.mousedownEvent.time; + if (interval > this.editor.getDragDelay()) this.startDrag(); + }; + + this.dragWaitEnd = function() { + var target = this.editor.container; + target.draggable = false; + this.startSelect(this.mousedownEvent.getDocumentPosition()); + this.selectEnd(); + }; + + this.dragReadyEnd = function(e) { + this.editor.$resetCursorStyle(); + this.editor.unsetStyle("ace_dragging"); + this.editor.renderer.setCursorStyle(""); + this.dragWaitEnd(); + }; + + this.startDrag = function() { + this.cancelDrag = false; + var editor = this.editor; + var target = editor.container; + target.draggable = true; + editor.renderer.$cursorLayer.setBlinking(false); + editor.setStyle("ace_dragging"); + var cursorStyle = useragent.isWin ? "default" : "move"; + editor.renderer.setCursorStyle(cursorStyle); + this.setState("dragReady"); + }; + + this.onMouseDrag = function(e) { + var target = this.editor.container; + if (useragent.isIE && this.state == "dragReady") { + var distance = calcDistance( + this.mousedownEvent.x, + this.mousedownEvent.y, + this.x, + this.y + ); + if (distance > 3) target.dragDrop(); + } + if (this.state === "dragWait") { + var distance = calcDistance( + this.mousedownEvent.x, + this.mousedownEvent.y, + this.x, + this.y + ); + if (distance > 0) { + target.draggable = false; + this.startSelect(this.mousedownEvent.getDocumentPosition()); + } + } + }; + + this.onMouseDown = function(e) { + if (!this.$dragEnabled) return; + this.mousedownEvent = e; + var editor = this.editor; + + var inSelection = e.inSelection(); + var button = e.getButton(); + var clickCount = e.domEvent.detail || 1; + if (clickCount === 1 && button === 0 && inSelection) { + if ( + e.editor.inMultiSelectMode && + (e.getAccelKey() || e.getShiftKey()) + ) + return; + this.mousedownEvent.time = Date.now(); + var eventTarget = e.domEvent.target || e.domEvent.srcElement; + if ("unselectable" in eventTarget) eventTarget.unselectable = "on"; + if (editor.getDragDelay()) { + if (useragent.isWebKit) { + this.cancelDrag = true; + var mouseTarget = editor.container; + mouseTarget.draggable = true; + } + this.setState("dragWait"); + } else { + this.startDrag(); + } + this.captureMouse(e, this.onMouseDrag.bind(this)); + e.defaultPrevented = true; + } + }; + }.call(DragdropHandler.prototype)); + + function calcDistance(ax, ay, bx, by) { + return Math.sqrt(Math.pow(bx - ax, 2) + Math.pow(by - ay, 2)); + } + + exports.DragdropHandler = DragdropHandler; + } +); + +ace.define( + "ace/mouse/touch_handler", + [ + "require", + "exports", + "module", + "ace/mouse/mouse_event", + "ace/lib/event", + "ace/lib/dom" + ], + function(require, exports, module) { + "use strict"; + + var MouseEvent = require("./mouse_event").MouseEvent; + var event = require("../lib/event"); + var dom = require("../lib/dom"); + + exports.addTouchListeners = function(el, editor) { + var mode = "scroll"; + var startX; + var startY; + var touchStartT; + var lastT; + var longTouchTimer; + var animationTimer; + var animationSteps = 0; + var pos; + var clickCount = 0; + var vX = 0; + var vY = 0; + var pressed; + var contextMenu; + + function createContextMenu() { + var clipboard = window.navigator && window.navigator.clipboard; + var isOpen = false; + var updateMenu = function() { + var selected = editor.getCopyText(); + var hasUndo = editor.session.getUndoManager().hasUndo(); + contextMenu.replaceChild( + dom.buildDom( + isOpen + ? [ + "span", + !selected && [ + "span", + { class: "ace_mobile-button", action: "selectall" }, + "Select All" + ], + selected && [ + "span", + { class: "ace_mobile-button", action: "copy" }, + "Copy" + ], + selected && [ + "span", + { class: "ace_mobile-button", action: "cut" }, + "Cut" + ], + clipboard && [ + "span", + { class: "ace_mobile-button", action: "paste" }, + "Paste" + ], + hasUndo && [ + "span", + { class: "ace_mobile-button", action: "undo" }, + "Undo" + ], + [ + "span", + { class: "ace_mobile-button", action: "find" }, + "Find" + ], + [ + "span", + { + class: "ace_mobile-button", + action: "openCommandPallete" + }, + "Pallete" + ] + ] + : ["span"] + ), + contextMenu.firstChild + ); + }; + var handleClick = function(e) { + var action = e.target.getAttribute("action"); + + if (action == "more" || !isOpen) { + isOpen = !isOpen; + return updateMenu(); + } + if (action == "paste") { + clipboard.readText().then(function(text) { + editor.execCommand(action, text); + }); + } else if (action) { + if (action == "cut" || action == "copy") { + if (clipboard) clipboard.writeText(editor.getCopyText()); + else document.execCommand("copy"); + } + editor.execCommand(action); + } + contextMenu.firstChild.style.display = "none"; + isOpen = false; + if (action != "openCommandPallete") editor.focus(); + }; + contextMenu = dom.buildDom( + [ + "div", + { + class: "ace_mobile-menu", + ontouchstart: function(e) { + mode = "menu"; + e.stopPropagation(); + e.preventDefault(); + editor.textInput.focus(); + }, + ontouchend: function(e) { + e.stopPropagation(); + e.preventDefault(); + handleClick(e); + }, + onclick: handleClick + }, + ["span"], + ["span", { class: "ace_mobile-button", action: "more" }, "..."] + ], + editor.container + ); + } + function showContextMenu() { + if (!contextMenu) createContextMenu(); + var cursor = editor.selection.cursor; + var pagePos = editor.renderer.textToScreenCoordinates( + cursor.row, + cursor.column + ); + var rect = editor.container.getBoundingClientRect(); + contextMenu.style.top = pagePos.pageY - rect.top - 3 + "px"; + contextMenu.style.right = "10px"; + contextMenu.style.display = ""; + contextMenu.firstChild.style.display = "none"; + editor.on("input", hideContextMenu); + } + function hideContextMenu(e) { + if (contextMenu) contextMenu.style.display = "none"; + editor.off("input", hideContextMenu); + } + + function handleLongTap() { + longTouchTimer = null; + clearTimeout(longTouchTimer); + var range = editor.selection.getRange(); + var inSelection = range.contains(pos.row, pos.column); + if (range.isEmpty() || !inSelection) { + editor.selection.moveToPosition(pos); + editor.selection.selectWord(); + } + mode = "wait"; + showContextMenu(); + } + function switchToSelectionMode() { + longTouchTimer = null; + clearTimeout(longTouchTimer); + editor.selection.moveToPosition(pos); + var range = + clickCount >= 2 + ? editor.selection.getLineRange(pos.row) + : editor.session.getBracketRange(pos); + if (range && !range.isEmpty()) { + editor.selection.setRange(range); + } else { + editor.selection.selectWord(); + } + mode = "wait"; + } + event.addListener( + el, + "contextmenu", + function(e) { + if (!pressed) return; + var textarea = editor.textInput.getElement(); + textarea.focus(); + }, + editor + ); + event.addListener( + el, + "touchstart", + function(e) { + var touches = e.touches; + if (longTouchTimer || touches.length > 1) { + clearTimeout(longTouchTimer); + longTouchTimer = null; + touchStartT = -1; + mode = "zoom"; + return; + } + + pressed = editor.$mouseHandler.isMousePressed = true; + var h = editor.renderer.layerConfig.lineHeight; + var w = editor.renderer.layerConfig.lineHeight; + var t = e.timeStamp; + lastT = t; + var touchObj = touches[0]; + var x = touchObj.clientX; + var y = touchObj.clientY; + if (Math.abs(startX - x) + Math.abs(startY - y) > h) touchStartT = -1; + + startX = e.clientX = x; + startY = e.clientY = y; + vX = vY = 0; + + var ev = new MouseEvent(e, editor); + pos = ev.getDocumentPosition(); + + if (t - touchStartT < 500 && touches.length == 1 && !animationSteps) { + clickCount++; + e.preventDefault(); + e.button = 0; + switchToSelectionMode(); + } else { + clickCount = 0; + var cursor = editor.selection.cursor; + var anchor = editor.selection.isEmpty() + ? cursor + : editor.selection.anchor; + + var cursorPos = editor.renderer.$cursorLayer.getPixelPosition( + cursor, + true + ); + var anchorPos = editor.renderer.$cursorLayer.getPixelPosition( + anchor, + true + ); + var rect = editor.renderer.scroller.getBoundingClientRect(); + var weightedDistance = function(x, y) { + x = x / w; + y = y / h - 0.75; + return x * x + y * y; + }; + + if (e.clientX < rect.left) { + mode = "zoom"; + return; + } + + var diff1 = weightedDistance( + e.clientX - rect.left - cursorPos.left, + e.clientY - rect.top - cursorPos.top + ); + var diff2 = weightedDistance( + e.clientX - rect.left - anchorPos.left, + e.clientY - rect.top - anchorPos.top + ); + if (diff1 < 3.5 && diff2 < 3.5) + mode = diff1 > diff2 ? "cursor" : "anchor"; + + if (diff2 < 3.5) mode = "anchor"; + else if (diff1 < 3.5) mode = "cursor"; + else mode = "scroll"; + longTouchTimer = setTimeout(handleLongTap, 450); + } + touchStartT = t; + }, + editor + ); + + event.addListener( + el, + "touchend", + function(e) { + pressed = editor.$mouseHandler.isMousePressed = false; + if (animationTimer) clearInterval(animationTimer); + if (mode == "zoom") { + mode = ""; + animationSteps = 0; + } else if (longTouchTimer) { + editor.selection.moveToPosition(pos); + animationSteps = 0; + showContextMenu(); + } else if (mode == "scroll") { + animate(); + hideContextMenu(); + } else { + showContextMenu(); + } + clearTimeout(longTouchTimer); + longTouchTimer = null; + }, + editor + ); + event.addListener( + el, + "touchmove", + function(e) { + if (longTouchTimer) { + clearTimeout(longTouchTimer); + longTouchTimer = null; + } + var touches = e.touches; + if (touches.length > 1 || mode == "zoom") return; + + var touchObj = touches[0]; + + var wheelX = startX - touchObj.clientX; + var wheelY = startY - touchObj.clientY; + + if (mode == "wait") { + if (wheelX * wheelX + wheelY * wheelY > 4) mode = "cursor"; + else return e.preventDefault(); + } + + startX = touchObj.clientX; + startY = touchObj.clientY; + + e.clientX = touchObj.clientX; + e.clientY = touchObj.clientY; + + var t = e.timeStamp; + var dt = t - lastT; + lastT = t; + if (mode == "scroll") { + var mouseEvent = new MouseEvent(e, editor); + mouseEvent.speed = 1; + mouseEvent.wheelX = wheelX; + mouseEvent.wheelY = wheelY; + if (10 * Math.abs(wheelX) < Math.abs(wheelY)) wheelX = 0; + if (10 * Math.abs(wheelY) < Math.abs(wheelX)) wheelY = 0; + if (dt != 0) { + vX = wheelX / dt; + vY = wheelY / dt; + } + editor._emit("mousewheel", mouseEvent); + if (!mouseEvent.propagationStopped) { + vX = vY = 0; + } + } else { + var ev = new MouseEvent(e, editor); + var pos = ev.getDocumentPosition(); + if (mode == "cursor") editor.selection.moveCursorToPosition(pos); + else if (mode == "anchor") + editor.selection.setSelectionAnchor(pos.row, pos.column); + editor.renderer.scrollCursorIntoView(pos); + e.preventDefault(); + } + }, + editor + ); + + function animate() { + animationSteps += 60; + animationTimer = setInterval(function() { + if (animationSteps-- <= 0) { + clearInterval(animationTimer); + animationTimer = null; + } + if (Math.abs(vX) < 0.01) vX = 0; + if (Math.abs(vY) < 0.01) vY = 0; + if (animationSteps < 20) vX = 0.9 * vX; + if (animationSteps < 20) vY = 0.9 * vY; + var oldScrollTop = editor.session.getScrollTop(); + editor.renderer.scrollBy(10 * vX, 10 * vY); + if (oldScrollTop == editor.session.getScrollTop()) animationSteps = 0; + }, 10); + } + }; + } +); + +ace.define( + "ace/lib/net", + ["require", "exports", "module", "ace/lib/dom"], + function(require, exports, module) { + "use strict"; + var dom = require("./dom"); + + exports.get = function(url, callback) { + var xhr = new XMLHttpRequest(); + xhr.open("GET", url, true); + xhr.onreadystatechange = function() { + if (xhr.readyState === 4) { + callback(xhr.responseText); + } + }; + xhr.send(null); + }; + + exports.loadScript = function(path, callback) { + var head = dom.getDocumentHead(); + var s = document.createElement("script"); + + s.src = path; + head.appendChild(s); + + s.onload = s.onreadystatechange = function(_, isAbort) { + if ( + isAbort || + !s.readyState || + s.readyState == "loaded" || + s.readyState == "complete" + ) { + s = s.onload = s.onreadystatechange = null; + if (!isAbort) callback(); + } + }; + }; + exports.qualifyURL = function(url) { + var a = document.createElement("a"); + a.href = url; + return a.href; + }; + } +); + +ace.define("ace/lib/event_emitter", ["require", "exports", "module"], function( + require, + exports, + module +) { + "use strict"; + + var EventEmitter = {}; + var stopPropagation = function() { + this.propagationStopped = true; + }; + var preventDefault = function() { + this.defaultPrevented = true; + }; + + EventEmitter._emit = EventEmitter._dispatchEvent = function(eventName, e) { + this._eventRegistry || (this._eventRegistry = {}); + this._defaultHandlers || (this._defaultHandlers = {}); + + var listeners = this._eventRegistry[eventName] || []; + var defaultHandler = this._defaultHandlers[eventName]; + if (!listeners.length && !defaultHandler) return; + + if (typeof e != "object" || !e) e = {}; + + if (!e.type) e.type = eventName; + if (!e.stopPropagation) e.stopPropagation = stopPropagation; + if (!e.preventDefault) e.preventDefault = preventDefault; + + listeners = listeners.slice(); + for (var i = 0; i < listeners.length; i++) { + listeners[i](e, this); + if (e.propagationStopped) break; + } + + if (defaultHandler && !e.defaultPrevented) return defaultHandler(e, this); + }; + + EventEmitter._signal = function(eventName, e) { + var listeners = (this._eventRegistry || {})[eventName]; + if (!listeners) return; + listeners = listeners.slice(); + for (var i = 0; i < listeners.length; i++) listeners[i](e, this); + }; + + EventEmitter.once = function(eventName, callback) { + var _self = this; + this.on(eventName, function newCallback() { + _self.off(eventName, newCallback); + callback.apply(null, arguments); + }); + if (!callback) { + return new Promise(function(resolve) { + callback = resolve; + }); + } + }; + + EventEmitter.setDefaultHandler = function(eventName, callback) { + var handlers = this._defaultHandlers; + if (!handlers) handlers = this._defaultHandlers = { _disabled_: {} }; + + if (handlers[eventName]) { + var old = handlers[eventName]; + var disabled = handlers._disabled_[eventName]; + if (!disabled) handlers._disabled_[eventName] = disabled = []; + disabled.push(old); + var i = disabled.indexOf(callback); + if (i != -1) disabled.splice(i, 1); + } + handlers[eventName] = callback; + }; + EventEmitter.removeDefaultHandler = function(eventName, callback) { + var handlers = this._defaultHandlers; + if (!handlers) return; + var disabled = handlers._disabled_[eventName]; + + if (handlers[eventName] == callback) { + if (disabled) this.setDefaultHandler(eventName, disabled.pop()); + } else if (disabled) { + var i = disabled.indexOf(callback); + if (i != -1) disabled.splice(i, 1); + } + }; + + EventEmitter.on = EventEmitter.addEventListener = function( + eventName, + callback, + capturing + ) { + this._eventRegistry = this._eventRegistry || {}; + + var listeners = this._eventRegistry[eventName]; + if (!listeners) listeners = this._eventRegistry[eventName] = []; + + if (listeners.indexOf(callback) == -1) + listeners[capturing ? "unshift" : "push"](callback); + return callback; + }; + + EventEmitter.off = EventEmitter.removeListener = EventEmitter.removeEventListener = function( + eventName, + callback + ) { + this._eventRegistry = this._eventRegistry || {}; + + var listeners = this._eventRegistry[eventName]; + if (!listeners) return; + + var index = listeners.indexOf(callback); + if (index !== -1) listeners.splice(index, 1); + }; + + EventEmitter.removeAllListeners = function(eventName) { + if (!eventName) this._eventRegistry = this._defaultHandlers = undefined; + if (this._eventRegistry) this._eventRegistry[eventName] = undefined; + if (this._defaultHandlers) this._defaultHandlers[eventName] = undefined; + }; + + exports.EventEmitter = EventEmitter; +}); + +ace.define( + "ace/lib/app_config", + ["require", "exports", "module", "ace/lib/oop", "ace/lib/event_emitter"], + function(require, exports, module) { + "no use strict"; + + var oop = require("./oop"); + var EventEmitter = require("./event_emitter").EventEmitter; + + var optionsProvider = { + setOptions: function(optList) { + Object.keys(optList).forEach(function(key) { + this.setOption(key, optList[key]); + }, this); + }, + getOptions: function(optionNames) { + var result = {}; + if (!optionNames) { + var options = this.$options; + optionNames = Object.keys(options).filter(function(key) { + return !options[key].hidden; + }); + } else if (!Array.isArray(optionNames)) { + result = optionNames; + optionNames = Object.keys(result); + } + optionNames.forEach(function(key) { + result[key] = this.getOption(key); + }, this); + return result; + }, + setOption: function(name, value) { + if (this["$" + name] === value) return; + var opt = this.$options[name]; + if (!opt) { + return warn('misspelled option "' + name + '"'); + } + if (opt.forwardTo) + return ( + this[opt.forwardTo] && this[opt.forwardTo].setOption(name, value) + ); + + if (!opt.handlesSet) this["$" + name] = value; + if (opt && opt.set) opt.set.call(this, value); + }, + getOption: function(name) { + var opt = this.$options[name]; + if (!opt) { + return warn('misspelled option "' + name + '"'); + } + if (opt.forwardTo) + return this[opt.forwardTo] && this[opt.forwardTo].getOption(name); + return opt && opt.get ? opt.get.call(this) : this["$" + name]; + } + }; + + function warn(message) { + if (typeof console != "undefined" && console.warn) + console.warn.apply(console, arguments); + } + + function reportError(msg, data) { + var e = new Error(msg); + e.data = data; + if (typeof console == "object" && console.error) console.error(e); + setTimeout(function() { + throw e; + }); + } + + var AppConfig = function() { + this.$defaultOptions = {}; + }; + + (function() { + oop.implement(this, EventEmitter); + this.defineOptions = function(obj, path, options) { + if (!obj.$options) this.$defaultOptions[path] = obj.$options = {}; + + Object.keys(options).forEach(function(key) { + var opt = options[key]; + if (typeof opt == "string") opt = { forwardTo: opt }; + + opt.name || (opt.name = key); + obj.$options[opt.name] = opt; + if ("initialValue" in opt) obj["$" + opt.name] = opt.initialValue; + }); + oop.implement(obj, optionsProvider); + + return this; + }; + + this.resetOptions = function(obj) { + Object.keys(obj.$options).forEach(function(key) { + var opt = obj.$options[key]; + if ("value" in opt) obj.setOption(key, opt.value); + }); + }; + + this.setDefaultValue = function(path, name, value) { + if (!path) { + for (path in this.$defaultOptions) + if (this.$defaultOptions[path][name]) break; + if (!this.$defaultOptions[path][name]) return false; + } + var opts = + this.$defaultOptions[path] || (this.$defaultOptions[path] = {}); + if (opts[name]) { + if (opts.forwardTo) this.setDefaultValue(opts.forwardTo, name, value); + else opts[name].value = value; + } + }; + + this.setDefaultValues = function(path, optionHash) { + Object.keys(optionHash).forEach(function(key) { + this.setDefaultValue(path, key, optionHash[key]); + }, this); + }; + + this.warn = warn; + this.reportError = reportError; + }.call(AppConfig.prototype)); + + exports.AppConfig = AppConfig; + } +); + +ace.define( + "ace/config", + [ + "require", + "exports", + "module", + "ace/lib/lang", + "ace/lib/oop", + "ace/lib/net", + "ace/lib/app_config" + ], + function(require, exports, module) { + "no use strict"; + + var lang = require("./lib/lang"); + var oop = require("./lib/oop"); + var net = require("./lib/net"); + var AppConfig = require("./lib/app_config").AppConfig; + + module.exports = exports = new AppConfig(); + + var global = (function() { + return this || (typeof window != "undefined" && window); + })(); + + var options = { + packaged: false, + workerPath: null, + modePath: null, + themePath: null, + basePath: "", + suffix: ".js", + $moduleUrls: {}, + loadWorkerFromBlob: true, + sharedPopups: false + }; + + exports.get = function(key) { + if (!options.hasOwnProperty(key)) + throw new Error("Unknown config key: " + key); + + return options[key]; + }; + + exports.set = function(key, value) { + if (options.hasOwnProperty(key)) options[key] = value; + else if (this.setDefaultValue("", key, value) == false) + throw new Error("Unknown config key: " + key); + }; + + exports.all = function() { + return lang.copyObject(options); + }; + + exports.$modes = {}; + exports.moduleUrl = function(name, component) { + if (options.$moduleUrls[name]) return options.$moduleUrls[name]; + + var parts = name.split("/"); + component = component || parts[parts.length - 2] || ""; + var sep = component == "snippets" ? "/" : "-"; + var base = parts[parts.length - 1]; + if (component == "worker" && sep == "-") { + var re = new RegExp( + "^" + component + "[\\-_]|[\\-_]" + component + "$", + "g" + ); + base = base.replace(re, ""); + } + + if ((!base || base == component) && parts.length > 1) + base = parts[parts.length - 2]; + var path = options[component + "Path"]; + if (path == null) { + path = options.basePath; + } else if (sep == "/") { + component = sep = ""; + } + if (path && path.slice(-1) != "/") path += "/"; + return path + component + sep + base + this.get("suffix"); + }; + + exports.setModuleUrl = function(name, subst) { + return (options.$moduleUrls[name] = subst); + }; + + exports.$loading = {}; + exports.loadModule = function(moduleName, onLoad) { + var module, moduleType; + if (Array.isArray(moduleName)) { + moduleType = moduleName[0]; + moduleName = moduleName[1]; + } + + try { + module = require(moduleName); + } catch (e) {} + if (module && !exports.$loading[moduleName]) + return onLoad && onLoad(module); + + if (!exports.$loading[moduleName]) exports.$loading[moduleName] = []; + + exports.$loading[moduleName].push(onLoad); + + if (exports.$loading[moduleName].length > 1) return; + + var afterLoad = function() { + require([moduleName], function(module) { + exports._emit("load.module", { name: moduleName, module: module }); + var listeners = exports.$loading[moduleName]; + exports.$loading[moduleName] = null; + listeners.forEach(function(onLoad) { + onLoad && onLoad(module); + }); + }); + }; + + if (!exports.get("packaged")) return afterLoad(); + + net.loadScript(exports.moduleUrl(moduleName, moduleType), afterLoad); + reportErrorIfPathIsNotConfigured(); + }; + + var reportErrorIfPathIsNotConfigured = function() { + if ( + !options.basePath && + !options.workerPath && + !options.modePath && + !options.themePath && + !Object.keys(options.$moduleUrls).length + ) { + console.error( + "Unable to infer path to ace from script src,", + "use ace.config.set('basePath', 'path') to enable dynamic loading of modes and themes", + "or with webpack use ace/webpack-resolver" + ); + reportErrorIfPathIsNotConfigured = function() {}; + } + }; + init(true); + function init(packaged) { + if (!global || !global.document) return; + + options.packaged = + packaged || + require.packaged || + module.packaged || + (global.define && define.packaged); + + var scriptOptions = {}; + var scriptUrl = ""; + var currentScript = document.currentScript || document._currentScript; // native or polyfill + var currentDocument = + (currentScript && currentScript.ownerDocument) || document; + + var scripts = currentDocument.getElementsByTagName("script"); + for (var i = 0; i < scripts.length; i++) { + var script = scripts[i]; + + var src = script.src || script.getAttribute("src"); + if (!src) continue; + + var attributes = script.attributes; + for (var j = 0, l = attributes.length; j < l; j++) { + var attr = attributes[j]; + if (attr.name.indexOf("data-ace-") === 0) { + scriptOptions[deHyphenate(attr.name.replace(/^data-ace-/, ""))] = + attr.value; + } + } + + var m = src.match(/^(.*)\/ace(\-\w+)?\.js(\?|$)/); + if (m) scriptUrl = m[1]; + } + + if (scriptUrl) { + scriptOptions.base = scriptOptions.base || scriptUrl; + scriptOptions.packaged = true; + } + + scriptOptions.basePath = scriptOptions.base; + scriptOptions.workerPath = scriptOptions.workerPath || scriptOptions.base; + scriptOptions.modePath = scriptOptions.modePath || scriptOptions.base; + scriptOptions.themePath = scriptOptions.themePath || scriptOptions.base; + delete scriptOptions.base; + + for (var key in scriptOptions) + if (typeof scriptOptions[key] !== "undefined") + exports.set(key, scriptOptions[key]); + } + + exports.init = init; + + function deHyphenate(str) { + return str.replace(/-(.)/g, function(m, m1) { + return m1.toUpperCase(); + }); + } + + exports.version = "1.4.10"; + } +); + +ace.define( + "ace/mouse/mouse_handler", + [ + "require", + "exports", + "module", + "ace/lib/event", + "ace/lib/useragent", + "ace/mouse/default_handlers", + "ace/mouse/default_gutter_handler", + "ace/mouse/mouse_event", + "ace/mouse/dragdrop_handler", + "ace/mouse/touch_handler", + "ace/config" + ], + function(require, exports, module) { + "use strict"; + + var event = require("../lib/event"); + var useragent = require("../lib/useragent"); + var DefaultHandlers = require("./default_handlers").DefaultHandlers; + var DefaultGutterHandler = require("./default_gutter_handler") + .GutterHandler; + var MouseEvent = require("./mouse_event").MouseEvent; + var DragdropHandler = require("./dragdrop_handler").DragdropHandler; + var addTouchListeners = require("./touch_handler").addTouchListeners; + var config = require("../config"); + + var MouseHandler = function(editor) { + var _self = this; + this.editor = editor; + + new DefaultHandlers(this); + new DefaultGutterHandler(this); + new DragdropHandler(this); + + var focusEditor = function(e) { + var windowBlurred = + !document.hasFocus || + !document.hasFocus() || + (!editor.isFocused() && + document.activeElement == + (editor.textInput && editor.textInput.getElement())); + if (windowBlurred) window.focus(); + editor.focus(); + }; + + var mouseTarget = editor.renderer.getMouseEventTarget(); + event.addListener( + mouseTarget, + "click", + this.onMouseEvent.bind(this, "click"), + editor + ); + event.addListener( + mouseTarget, + "mousemove", + this.onMouseMove.bind(this, "mousemove"), + editor + ); + event.addMultiMouseDownListener( + [ + mouseTarget, + editor.renderer.scrollBarV && editor.renderer.scrollBarV.inner, + editor.renderer.scrollBarH && editor.renderer.scrollBarH.inner, + editor.textInput && editor.textInput.getElement() + ].filter(Boolean), + [400, 300, 250], + this, + "onMouseEvent", + editor + ); + event.addMouseWheelListener( + editor.container, + this.onMouseWheel.bind(this, "mousewheel"), + editor + ); + addTouchListeners(editor.container, editor); + + var gutterEl = editor.renderer.$gutter; + event.addListener( + gutterEl, + "mousedown", + this.onMouseEvent.bind(this, "guttermousedown"), + editor + ); + event.addListener( + gutterEl, + "click", + this.onMouseEvent.bind(this, "gutterclick"), + editor + ); + event.addListener( + gutterEl, + "dblclick", + this.onMouseEvent.bind(this, "gutterdblclick"), + editor + ); + event.addListener( + gutterEl, + "mousemove", + this.onMouseEvent.bind(this, "guttermousemove"), + editor + ); + + event.addListener(mouseTarget, "mousedown", focusEditor, editor); + event.addListener(gutterEl, "mousedown", focusEditor, editor); + if (useragent.isIE && editor.renderer.scrollBarV) { + event.addListener( + editor.renderer.scrollBarV.element, + "mousedown", + focusEditor, + editor + ); + event.addListener( + editor.renderer.scrollBarH.element, + "mousedown", + focusEditor, + editor + ); + } + + editor.on( + "mousemove", + function(e) { + if (_self.state || _self.$dragDelay || !_self.$dragEnabled) return; + + var character = editor.renderer.screenToTextCoordinates(e.x, e.y); + var range = editor.session.selection.getRange(); + var renderer = editor.renderer; + + if ( + !range.isEmpty() && + range.insideStart(character.row, character.column) + ) { + renderer.setCursorStyle("default"); + } else { + renderer.setCursorStyle(""); + } + }, + editor + ); + }; + + (function() { + this.onMouseEvent = function(name, e) { + this.editor._emit(name, new MouseEvent(e, this.editor)); + }; + + this.onMouseMove = function(name, e) { + var listeners = + this.editor._eventRegistry && this.editor._eventRegistry.mousemove; + if (!listeners || !listeners.length) return; + + this.editor._emit(name, new MouseEvent(e, this.editor)); + }; + + this.onMouseWheel = function(name, e) { + var mouseEvent = new MouseEvent(e, this.editor); + mouseEvent.speed = this.$scrollSpeed * 2; + mouseEvent.wheelX = e.wheelX; + mouseEvent.wheelY = e.wheelY; + + this.editor._emit(name, mouseEvent); + }; + + this.setState = function(state) { + this.state = state; + }; + + this.captureMouse = function(ev, mouseMoveHandler) { + this.x = ev.x; + this.y = ev.y; + + this.isMousePressed = true; + var editor = this.editor; + var renderer = this.editor.renderer; + renderer.$isMousePressed = true; + + var self = this; + var onMouseMove = function(e) { + if (!e) return; + if (useragent.isWebKit && !e.which && self.releaseMouse) + return self.releaseMouse(); + + self.x = e.clientX; + self.y = e.clientY; + mouseMoveHandler && mouseMoveHandler(e); + self.mouseEvent = new MouseEvent(e, self.editor); + self.$mouseMoved = true; + }; + + var onCaptureEnd = function(e) { + editor.off("beforeEndOperation", onOperationEnd); + clearInterval(timerId); + onCaptureInterval(); + self[self.state + "End"] && self[self.state + "End"](e); + self.state = ""; + self.isMousePressed = renderer.$isMousePressed = false; + if (renderer.$keepTextAreaAtCursor) renderer.$moveTextAreaToCursor(); + self.$onCaptureMouseMove = self.releaseMouse = null; + e && self.onMouseEvent("mouseup", e); + editor.endOperation(); + }; + + var onCaptureInterval = function() { + self[self.state] && self[self.state](); + self.$mouseMoved = false; + }; + + if (useragent.isOldIE && ev.domEvent.type == "dblclick") { + return setTimeout(function() { + onCaptureEnd(ev); + }); + } + + var onOperationEnd = function(e) { + if (!self.releaseMouse) return; + if (editor.curOp.command.name && editor.curOp.selectionChanged) { + self[self.state + "End"] && self[self.state + "End"](); + self.state = ""; + self.releaseMouse(); + } + }; + + editor.on("beforeEndOperation", onOperationEnd); + editor.startOperation({ command: { name: "mouse" } }); + + self.$onCaptureMouseMove = onMouseMove; + self.releaseMouse = event.capture( + this.editor.container, + onMouseMove, + onCaptureEnd + ); + var timerId = setInterval(onCaptureInterval, 20); + }; + this.releaseMouse = null; + this.cancelContextMenu = function() { + var stop = function(e) { + if (e && e.domEvent && e.domEvent.type != "contextmenu") return; + this.editor.off("nativecontextmenu", stop); + if (e && e.domEvent) event.stopEvent(e.domEvent); + }.bind(this); + setTimeout(stop, 10); + this.editor.on("nativecontextmenu", stop); + }; + }.call(MouseHandler.prototype)); + + config.defineOptions(MouseHandler.prototype, "mouseHandler", { + scrollSpeed: { initialValue: 2 }, + dragDelay: { initialValue: useragent.isMac ? 150 : 0 }, + dragEnabled: { initialValue: true }, + focusTimeout: { initialValue: 0 }, + tooltipFollowsMouse: { initialValue: true } + }); + + exports.MouseHandler = MouseHandler; + } +); + +ace.define( + "ace/mouse/fold_handler", + ["require", "exports", "module", "ace/lib/dom"], + function(require, exports, module) { + "use strict"; + var dom = require("../lib/dom"); + + function FoldHandler(editor) { + editor.on("click", function(e) { + var position = e.getDocumentPosition(); + var session = editor.session; + var fold = session.getFoldAt(position.row, position.column, 1); + if (fold) { + if (e.getAccelKey()) session.removeFold(fold); + else session.expandFold(fold); + + e.stop(); + } + + var target = e.domEvent && e.domEvent.target; + if (target && dom.hasCssClass(target, "ace_inline_button")) { + if (dom.hasCssClass(target, "ace_toggle_wrap")) { + session.setOption("wrap", !session.getUseWrapMode()); + editor.renderer.scrollCursorIntoView(); + } + } + }); + + editor.on("gutterclick", function(e) { + var gutterRegion = editor.renderer.$gutterLayer.getRegion(e); + + if (gutterRegion == "foldWidgets") { + var row = e.getDocumentPosition().row; + var session = editor.session; + if (session.foldWidgets && session.foldWidgets[row]) + editor.session.onFoldWidgetClick(row, e); + if (!editor.isFocused()) editor.focus(); + e.stop(); + } + }); + + editor.on("gutterdblclick", function(e) { + var gutterRegion = editor.renderer.$gutterLayer.getRegion(e); + + if (gutterRegion == "foldWidgets") { + var row = e.getDocumentPosition().row; + var session = editor.session; + var data = session.getParentFoldRangeData(row, true); + var range = data.range || data.firstRange; + + if (range) { + row = range.start.row; + var fold = session.getFoldAt(row, session.getLine(row).length, 1); + + if (fold) { + session.removeFold(fold); + } else { + session.addFold("...", range); + editor.renderer.scrollCursorIntoView({ + row: range.start.row, + column: 0 + }); + } + } + e.stop(); + } + }); + } + + exports.FoldHandler = FoldHandler; + } +); + +ace.define( + "ace/keyboard/keybinding", + ["require", "exports", "module", "ace/lib/keys", "ace/lib/event"], + function(require, exports, module) { + "use strict"; + + var keyUtil = require("../lib/keys"); + var event = require("../lib/event"); + + var KeyBinding = function(editor) { + this.$editor = editor; + this.$data = { editor: editor }; + this.$handlers = []; + this.setDefaultHandler(editor.commands); + }; + + (function() { + this.setDefaultHandler = function(kb) { + this.removeKeyboardHandler(this.$defaultHandler); + this.$defaultHandler = kb; + this.addKeyboardHandler(kb, 0); + }; + + this.setKeyboardHandler = function(kb) { + var h = this.$handlers; + if (h[h.length - 1] == kb) return; + + while (h[h.length - 1] && h[h.length - 1] != this.$defaultHandler) + this.removeKeyboardHandler(h[h.length - 1]); + + this.addKeyboardHandler(kb, 1); + }; + + this.addKeyboardHandler = function(kb, pos) { + if (!kb) return; + if (typeof kb == "function" && !kb.handleKeyboard) + kb.handleKeyboard = kb; + var i = this.$handlers.indexOf(kb); + if (i != -1) this.$handlers.splice(i, 1); + + if (pos == undefined) this.$handlers.push(kb); + else this.$handlers.splice(pos, 0, kb); + + if (i == -1 && kb.attach) kb.attach(this.$editor); + }; + + this.removeKeyboardHandler = function(kb) { + var i = this.$handlers.indexOf(kb); + if (i == -1) return false; + this.$handlers.splice(i, 1); + kb.detach && kb.detach(this.$editor); + return true; + }; + + this.getKeyboardHandler = function() { + return this.$handlers[this.$handlers.length - 1]; + }; + + this.getStatusText = function() { + var data = this.$data; + var editor = data.editor; + return this.$handlers + .map(function(h) { + return (h.getStatusText && h.getStatusText(editor, data)) || ""; + }) + .filter(Boolean) + .join(" "); + }; + + this.$callKeyboardHandlers = function(hashId, keyString, keyCode, e) { + var toExecute; + var success = false; + var commands = this.$editor.commands; + + for (var i = this.$handlers.length; i--; ) { + toExecute = this.$handlers[i].handleKeyboard( + this.$data, + hashId, + keyString, + keyCode, + e + ); + if (!toExecute || !toExecute.command) continue; + if (toExecute.command == "null") { + success = true; + } else { + success = commands.exec( + toExecute.command, + this.$editor, + toExecute.args, + e + ); + } + if ( + success && + e && + hashId != -1 && + toExecute.passEvent != true && + toExecute.command.passEvent != true + ) { + event.stopEvent(e); + } + if (success) break; + } + + if (!success && hashId == -1) { + toExecute = { command: "insertstring" }; + success = commands.exec("insertstring", this.$editor, keyString); + } + + if (success && this.$editor._signal) + this.$editor._signal("keyboardActivity", toExecute); + + return success; + }; + + this.onCommandKey = function(e, hashId, keyCode) { + var keyString = keyUtil.keyCodeToString(keyCode); + return this.$callKeyboardHandlers(hashId, keyString, keyCode, e); + }; + + this.onTextInput = function(text) { + return this.$callKeyboardHandlers(-1, text); + }; + }.call(KeyBinding.prototype)); + + exports.KeyBinding = KeyBinding; + } +); + +ace.define("ace/lib/bidiutil", ["require", "exports", "module"], function( + require, + exports, + module +) { + "use strict"; + + var ArabicAlefBetIntervalsBegine = ["\u0621", "\u0641"]; + var ArabicAlefBetIntervalsEnd = ["\u063A", "\u064a"]; + var dir = 0, + hiLevel = 0; + var lastArabic = false, + hasUBAT_AL = false, + hasUBAT_B = false, + hasUBAT_S = false, + hasBlockSep = false, + hasSegSep = false; + + var impTab_LTR = [ + [0, 3, 0, 1, 0, 0, 0], + [0, 3, 0, 1, 2, 2, 0], + [0, 3, 0, 0x11, 2, 0, 1], + [0, 3, 5, 5, 4, 1, 0], + [0, 3, 0x15, 0x15, 4, 0, 1], + [0, 3, 5, 5, 4, 2, 0] + ]; + + var impTab_RTL = [ + [2, 0, 1, 1, 0, 1, 0], + [2, 0, 1, 1, 0, 2, 0], + [2, 0, 2, 1, 3, 2, 0], + [2, 0, 2, 0x21, 3, 1, 1] + ]; + + var LTR = 0, + RTL = 1; + + var L = 0; + var R = 1; + var EN = 2; + var AN = 3; + var ON = 4; + var B = 5; + var S = 6; + var AL = 7; + var WS = 8; + var CS = 9; + var ES = 10; + var ET = 11; + var NSM = 12; + var LRE = 13; + var RLE = 14; + var PDF = 15; + var LRO = 16; + var RLO = 17; + var BN = 18; + + var UnicodeTBL00 = [ + BN, + BN, + BN, + BN, + BN, + BN, + BN, + BN, + BN, + S, + B, + S, + WS, + B, + BN, + BN, + BN, + BN, + BN, + BN, + BN, + BN, + BN, + BN, + BN, + BN, + BN, + BN, + B, + B, + B, + S, + WS, + ON, + ON, + ET, + ET, + ET, + ON, + ON, + ON, + ON, + ON, + ES, + CS, + ES, + CS, + CS, + EN, + EN, + EN, + EN, + EN, + EN, + EN, + EN, + EN, + EN, + CS, + ON, + ON, + ON, + ON, + ON, + ON, + L, + L, + L, + L, + L, + L, + L, + L, + L, + L, + L, + L, + L, + L, + L, + L, + L, + L, + L, + L, + L, + L, + L, + L, + L, + L, + ON, + ON, + ON, + ON, + ON, + ON, + L, + L, + L, + L, + L, + L, + L, + L, + L, + L, + L, + L, + L, + L, + L, + L, + L, + L, + L, + L, + L, + L, + L, + L, + L, + L, + ON, + ON, + ON, + ON, + BN, + BN, + BN, + BN, + BN, + BN, + B, + BN, + BN, + BN, + BN, + BN, + BN, + BN, + BN, + BN, + BN, + BN, + BN, + BN, + BN, + BN, + BN, + BN, + BN, + BN, + BN, + BN, + BN, + BN, + BN, + BN, + BN, + CS, + ON, + ET, + ET, + ET, + ET, + ON, + ON, + ON, + ON, + L, + ON, + ON, + BN, + ON, + ON, + ET, + ET, + EN, + EN, + ON, + L, + ON, + ON, + ON, + EN, + L, + ON, + ON, + ON, + ON, + ON + ]; + + var UnicodeTBL20 = [ + WS, + WS, + WS, + WS, + WS, + WS, + WS, + WS, + WS, + WS, + WS, + BN, + BN, + BN, + L, + R, + ON, + ON, + ON, + ON, + ON, + ON, + ON, + ON, + ON, + ON, + ON, + ON, + ON, + ON, + ON, + ON, + ON, + ON, + ON, + ON, + ON, + ON, + ON, + ON, + WS, + B, + LRE, + RLE, + PDF, + LRO, + RLO, + CS, + ET, + ET, + ET, + ET, + ET, + ON, + ON, + ON, + ON, + ON, + ON, + ON, + ON, + ON, + ON, + ON, + ON, + ON, + ON, + ON, + CS, + ON, + ON, + ON, + ON, + ON, + ON, + ON, + ON, + ON, + ON, + ON, + ON, + ON, + ON, + ON, + ON, + ON, + ON, + ON, + ON, + ON, + ON, + ON, + ON, + ON, + ON, + WS + ]; + + function _computeLevels(chars, levels, len, charTypes) { + var impTab = dir ? impTab_RTL : impTab_LTR, + prevState = null, + newClass = null, + newLevel = null, + newState = 0, + action = null, + cond = null, + condPos = -1, + i = null, + ix = null, + classes = []; + + if (!charTypes) { + for (i = 0, charTypes = []; i < len; i++) { + charTypes[i] = _getCharacterType(chars[i]); + } + } + hiLevel = dir; + lastArabic = false; + hasUBAT_AL = false; + hasUBAT_B = false; + hasUBAT_S = false; + for (ix = 0; ix < len; ix++) { + prevState = newState; + classes[ix] = newClass = _getCharClass(chars, charTypes, classes, ix); + newState = impTab[prevState][newClass]; + action = newState & 0xf0; + newState &= 0x0f; + levels[ix] = newLevel = impTab[newState][5]; + if (action > 0) { + if (action == 0x10) { + for (i = condPos; i < ix; i++) { + levels[i] = 1; + } + condPos = -1; + } else { + condPos = -1; + } + } + cond = impTab[newState][6]; + if (cond) { + if (condPos == -1) { + condPos = ix; + } + } else { + if (condPos > -1) { + for (i = condPos; i < ix; i++) { + levels[i] = newLevel; + } + condPos = -1; + } + } + if (charTypes[ix] == B) { + levels[ix] = 0; + } + hiLevel |= newLevel; + } + if (hasUBAT_S) { + for (i = 0; i < len; i++) { + if (charTypes[i] == S) { + levels[i] = dir; + for (var j = i - 1; j >= 0; j--) { + if (charTypes[j] == WS) { + levels[j] = dir; + } else { + break; + } + } + } + } + } + } + + function _invertLevel(lev, levels, _array) { + if (hiLevel < lev) { + return; + } + if (lev == 1 && dir == RTL && !hasUBAT_B) { + _array.reverse(); + return; + } + var len = _array.length, + start = 0, + end, + lo, + hi, + tmp; + while (start < len) { + if (levels[start] >= lev) { + end = start + 1; + while (end < len && levels[end] >= lev) { + end++; + } + for (lo = start, hi = end - 1; lo < hi; lo++, hi--) { + tmp = _array[lo]; + _array[lo] = _array[hi]; + _array[hi] = tmp; + } + start = end; + } + start++; + } + } + + function _getCharClass(chars, types, classes, ix) { + var cType = types[ix], + wType, + nType, + len, + i; + switch (cType) { + case L: + case R: + lastArabic = false; + case ON: + case AN: + return cType; + case EN: + return lastArabic ? AN : EN; + case AL: + lastArabic = true; + hasUBAT_AL = true; + return R; + case WS: + return ON; + case CS: + if ( + ix < 1 || + ix + 1 >= types.length || + ((wType = classes[ix - 1]) != EN && wType != AN) || + ((nType = types[ix + 1]) != EN && nType != AN) + ) { + return ON; + } + if (lastArabic) { + nType = AN; + } + return nType == wType ? nType : ON; + case ES: + wType = ix > 0 ? classes[ix - 1] : B; + if (wType == EN && ix + 1 < types.length && types[ix + 1] == EN) { + return EN; + } + return ON; + case ET: + if (ix > 0 && classes[ix - 1] == EN) { + return EN; + } + if (lastArabic) { + return ON; + } + i = ix + 1; + len = types.length; + while (i < len && types[i] == ET) { + i++; + } + if (i < len && types[i] == EN) { + return EN; + } + return ON; + case NSM: + len = types.length; + i = ix + 1; + while (i < len && types[i] == NSM) { + i++; + } + if (i < len) { + var c = chars[ix], + rtlCandidate = (c >= 0x0591 && c <= 0x08ff) || c == 0xfb1e; + + wType = types[i]; + if (rtlCandidate && (wType == R || wType == AL)) { + return R; + } + } + + if (ix < 1 || (wType = types[ix - 1]) == B) { + return ON; + } + return classes[ix - 1]; + case B: + lastArabic = false; + hasUBAT_B = true; + return dir; + case S: + hasUBAT_S = true; + return ON; + case LRE: + case RLE: + case LRO: + case RLO: + case PDF: + lastArabic = false; + case BN: + return ON; + } + } + + function _getCharacterType(ch) { + var uc = ch.charCodeAt(0), + hi = uc >> 8; + + if (hi == 0) { + return uc > 0x00bf ? L : UnicodeTBL00[uc]; + } else if (hi == 5) { + return /[\u0591-\u05f4]/.test(ch) ? R : L; + } else if (hi == 6) { + if (/[\u0610-\u061a\u064b-\u065f\u06d6-\u06e4\u06e7-\u06ed]/.test(ch)) + return NSM; + else if (/[\u0660-\u0669\u066b-\u066c]/.test(ch)) return AN; + else if (uc == 0x066a) return ET; + else if (/[\u06f0-\u06f9]/.test(ch)) return EN; + else return AL; + } else if (hi == 0x20 && uc <= 0x205f) { + return UnicodeTBL20[uc & 0xff]; + } else if (hi == 0xfe) { + return uc >= 0xfe70 ? AL : ON; + } + return ON; + } + + function _isArabicDiacritics(ch) { + return ch >= "\u064b" && ch <= "\u0655"; + } + exports.L = L; + exports.R = R; + exports.EN = EN; + exports.ON_R = 3; + exports.AN = 4; + exports.R_H = 5; + exports.B = 6; + exports.RLE = 7; + + exports.DOT = "\xB7"; + exports.doBidiReorder = function(text, textCharTypes, isRtl) { + if (text.length < 2) return {}; + + var chars = text.split(""), + logicalFromVisual = new Array(chars.length), + bidiLevels = new Array(chars.length), + levels = []; + + dir = isRtl ? RTL : LTR; + + _computeLevels(chars, levels, chars.length, textCharTypes); + + for ( + var i = 0; + i < logicalFromVisual.length; + logicalFromVisual[i] = i, i++ + ); + + _invertLevel(2, levels, logicalFromVisual); + _invertLevel(1, levels, logicalFromVisual); + + for (var i = 0; i < logicalFromVisual.length - 1; i++) { + //fix levels to reflect character width + if (textCharTypes[i] === AN) { + levels[i] = exports.AN; + } else if ( + levels[i] === R && + ((textCharTypes[i] > AL && textCharTypes[i] < LRE) || + textCharTypes[i] === ON || + textCharTypes[i] === BN) + ) { + levels[i] = exports.ON_R; + } else if ( + i > 0 && + chars[i - 1] === "\u0644" && + /\u0622|\u0623|\u0625|\u0627/.test(chars[i]) + ) { + levels[i - 1] = levels[i] = exports.R_H; + i++; + } + } + if (chars[chars.length - 1] === exports.DOT) + levels[chars.length - 1] = exports.B; + + if (chars[0] === "\u202B") levels[0] = exports.RLE; + + for (var i = 0; i < logicalFromVisual.length; i++) { + bidiLevels[i] = levels[logicalFromVisual[i]]; + } + + return { logicalFromVisual: logicalFromVisual, bidiLevels: bidiLevels }; + }; + exports.hasBidiCharacters = function(text, textCharTypes) { + var ret = false; + for (var i = 0; i < text.length; i++) { + textCharTypes[i] = _getCharacterType(text.charAt(i)); + if ( + !ret && + (textCharTypes[i] == R || + textCharTypes[i] == AL || + textCharTypes[i] == AN) + ) + ret = true; + } + return ret; + }; + exports.getVisualFromLogicalIdx = function(logIdx, rowMap) { + for (var i = 0; i < rowMap.logicalFromVisual.length; i++) { + if (rowMap.logicalFromVisual[i] == logIdx) return i; + } + return 0; + }; +}); + +ace.define( + "ace/bidihandler", + ["require", "exports", "module", "ace/lib/bidiutil", "ace/lib/lang"], + function(require, exports, module) { + "use strict"; + + var bidiUtil = require("./lib/bidiutil"); + var lang = require("./lib/lang"); + var bidiRE = /[\u0590-\u05f4\u0600-\u06ff\u0700-\u08ac\u202B]/; + var BidiHandler = function(session) { + this.session = session; + this.bidiMap = {}; + this.currentRow = null; + this.bidiUtil = bidiUtil; + this.charWidths = []; + this.EOL = "\xAC"; + this.showInvisibles = true; + this.isRtlDir = false; + this.$isRtl = false; + this.line = ""; + this.wrapIndent = 0; + this.EOF = "\xB6"; + this.RLE = "\u202B"; + this.contentWidth = 0; + this.fontMetrics = null; + this.rtlLineOffset = 0; + this.wrapOffset = 0; + this.isMoveLeftOperation = false; + this.seenBidi = bidiRE.test(session.getValue()); + }; + + (function() { + this.isBidiRow = function(screenRow, docRow, splitIndex) { + if (!this.seenBidi) return false; + if (screenRow !== this.currentRow) { + this.currentRow = screenRow; + this.updateRowLine(docRow, splitIndex); + this.updateBidiMap(); + } + return this.bidiMap.bidiLevels; + }; + + this.onChange = function(delta) { + if (!this.seenBidi) { + if (delta.action == "insert" && bidiRE.test(delta.lines.join("\n"))) { + this.seenBidi = true; + this.currentRow = null; + } + } else { + this.currentRow = null; + } + }; + + this.getDocumentRow = function() { + var docRow = 0; + var rowCache = this.session.$screenRowCache; + if (rowCache.length) { + var index = this.session.$getRowCacheIndex(rowCache, this.currentRow); + if (index >= 0) docRow = this.session.$docRowCache[index]; + } + + return docRow; + }; + + this.getSplitIndex = function() { + var splitIndex = 0; + var rowCache = this.session.$screenRowCache; + if (rowCache.length) { + var currentIndex, + prevIndex = this.session.$getRowCacheIndex( + rowCache, + this.currentRow + ); + while (this.currentRow - splitIndex > 0) { + currentIndex = this.session.$getRowCacheIndex( + rowCache, + this.currentRow - splitIndex - 1 + ); + if (currentIndex !== prevIndex) break; + + prevIndex = currentIndex; + splitIndex++; + } + } else { + splitIndex = this.currentRow; + } + + return splitIndex; + }; + + this.updateRowLine = function(docRow, splitIndex) { + if (docRow === undefined) docRow = this.getDocumentRow(); + + var isLastRow = docRow === this.session.getLength() - 1, + endOfLine = isLastRow ? this.EOF : this.EOL; + + this.wrapIndent = 0; + this.line = this.session.getLine(docRow); + this.isRtlDir = this.$isRtl || this.line.charAt(0) === this.RLE; + if (this.session.$useWrapMode) { + var splits = this.session.$wrapData[docRow]; + if (splits) { + if (splitIndex === undefined) splitIndex = this.getSplitIndex(); + + if (splitIndex > 0 && splits.length) { + this.wrapIndent = splits.indent; + this.wrapOffset = this.wrapIndent * this.charWidths[bidiUtil.L]; + this.line = + splitIndex < splits.length + ? this.line.substring( + splits[splitIndex - 1], + splits[splitIndex] + ) + : this.line.substring(splits[splits.length - 1]); + } else { + this.line = this.line.substring(0, splits[splitIndex]); + } + } + if (splitIndex == splits.length) + this.line += this.showInvisibles ? endOfLine : bidiUtil.DOT; + } else { + this.line += this.showInvisibles ? endOfLine : bidiUtil.DOT; + } + var session = this.session, + shift = 0, + size; + this.line = this.line.replace( + /\t|[\u1100-\u2029, \u202F-\uFFE6]/g, + function(ch, i) { + if (ch === "\t" || session.isFullWidth(ch.charCodeAt(0))) { + size = ch === "\t" ? session.getScreenTabSize(i + shift) : 2; + shift += size - 1; + return lang.stringRepeat(bidiUtil.DOT, size); + } + return ch; + } + ); + + if (this.isRtlDir) { + this.fontMetrics.$main.textContent = + this.line.charAt(this.line.length - 1) == bidiUtil.DOT + ? this.line.substr(0, this.line.length - 1) + : this.line; + this.rtlLineOffset = + this.contentWidth - + this.fontMetrics.$main.getBoundingClientRect().width; + } + }; + + this.updateBidiMap = function() { + var textCharTypes = []; + if ( + bidiUtil.hasBidiCharacters(this.line, textCharTypes) || + this.isRtlDir + ) { + this.bidiMap = bidiUtil.doBidiReorder( + this.line, + textCharTypes, + this.isRtlDir + ); + } else { + this.bidiMap = {}; + } + }; + this.markAsDirty = function() { + this.currentRow = null; + }; + this.updateCharacterWidths = function(fontMetrics) { + if (this.characterWidth === fontMetrics.$characterSize.width) return; + + this.fontMetrics = fontMetrics; + var characterWidth = (this.characterWidth = + fontMetrics.$characterSize.width); + var bidiCharWidth = fontMetrics.$measureCharWidth("\u05d4"); + + this.charWidths[bidiUtil.L] = this.charWidths[ + bidiUtil.EN + ] = this.charWidths[bidiUtil.ON_R] = characterWidth; + this.charWidths[bidiUtil.R] = this.charWidths[ + bidiUtil.AN + ] = bidiCharWidth; + this.charWidths[bidiUtil.R_H] = bidiCharWidth * 0.45; + this.charWidths[bidiUtil.B] = this.charWidths[bidiUtil.RLE] = 0; + + this.currentRow = null; + }; + + this.setShowInvisibles = function(showInvisibles) { + this.showInvisibles = showInvisibles; + this.currentRow = null; + }; + + this.setEolChar = function(eolChar) { + this.EOL = eolChar; + }; + + this.setContentWidth = function(width) { + this.contentWidth = width; + }; + + this.isRtlLine = function(row) { + if (this.$isRtl) return true; + if (row != undefined) + return this.session.getLine(row).charAt(0) == this.RLE; + else return this.isRtlDir; + }; + + this.setRtlDirection = function(editor, isRtlDir) { + var cursor = editor.getCursorPosition(); + for ( + var row = editor.selection.getSelectionAnchor().row; + row <= cursor.row; + row++ + ) { + if ( + !isRtlDir && + editor.session.getLine(row).charAt(0) === + editor.session.$bidiHandler.RLE + ) + editor.session.doc.removeInLine(row, 0, 1); + else if ( + isRtlDir && + editor.session.getLine(row).charAt(0) !== + editor.session.$bidiHandler.RLE + ) + editor.session.doc.insert( + { column: 0, row: row }, + editor.session.$bidiHandler.RLE + ); + } + }; + this.getPosLeft = function(col) { + col -= this.wrapIndent; + var leftBoundary = this.line.charAt(0) === this.RLE ? 1 : 0; + var logicalIdx = + col > leftBoundary + ? this.session.getOverwrite() + ? col + : col - 1 + : leftBoundary; + var visualIdx = bidiUtil.getVisualFromLogicalIdx( + logicalIdx, + this.bidiMap + ), + levels = this.bidiMap.bidiLevels, + left = 0; + + if ( + !this.session.getOverwrite() && + col <= leftBoundary && + levels[visualIdx] % 2 !== 0 + ) + visualIdx++; + + for (var i = 0; i < visualIdx; i++) { + left += this.charWidths[levels[i]]; + } + + if ( + !this.session.getOverwrite() && + col > leftBoundary && + levels[visualIdx] % 2 === 0 + ) + left += this.charWidths[levels[visualIdx]]; + + if (this.wrapIndent) + left += this.isRtlDir ? -1 * this.wrapOffset : this.wrapOffset; + + if (this.isRtlDir) left += this.rtlLineOffset; + + return left; + }; + this.getSelections = function(startCol, endCol) { + var map = this.bidiMap, + levels = map.bidiLevels, + level, + selections = [], + offset = 0, + selColMin = Math.min(startCol, endCol) - this.wrapIndent, + selColMax = Math.max(startCol, endCol) - this.wrapIndent, + isSelected = false, + isSelectedPrev = false, + selectionStart = 0; + + if (this.wrapIndent) + offset += this.isRtlDir ? -1 * this.wrapOffset : this.wrapOffset; + + for (var logIdx, visIdx = 0; visIdx < levels.length; visIdx++) { + logIdx = map.logicalFromVisual[visIdx]; + level = levels[visIdx]; + isSelected = logIdx >= selColMin && logIdx < selColMax; + if (isSelected && !isSelectedPrev) { + selectionStart = offset; + } else if (!isSelected && isSelectedPrev) { + selections.push({ + left: selectionStart, + width: offset - selectionStart + }); + } + offset += this.charWidths[level]; + isSelectedPrev = isSelected; + } + + if (isSelected && visIdx === levels.length) { + selections.push({ + left: selectionStart, + width: offset - selectionStart + }); + } + + if (this.isRtlDir) { + for (var i = 0; i < selections.length; i++) { + selections[i].left += this.rtlLineOffset; + } + } + return selections; + }; + this.offsetToCol = function(posX) { + if (this.isRtlDir) posX -= this.rtlLineOffset; + + var logicalIdx = 0, + posX = Math.max(posX, 0), + offset = 0, + visualIdx = 0, + levels = this.bidiMap.bidiLevels, + charWidth = this.charWidths[levels[visualIdx]]; + + if (this.wrapIndent) + posX -= this.isRtlDir ? -1 * this.wrapOffset : this.wrapOffset; + + while (posX > offset + charWidth / 2) { + offset += charWidth; + if (visualIdx === levels.length - 1) { + charWidth = 0; + break; + } + charWidth = this.charWidths[levels[++visualIdx]]; + } + + if ( + visualIdx > 0 && + levels[visualIdx - 1] % 2 !== 0 && + levels[visualIdx] % 2 === 0 + ) { + if (posX < offset) visualIdx--; + logicalIdx = this.bidiMap.logicalFromVisual[visualIdx]; + } else if ( + visualIdx > 0 && + levels[visualIdx - 1] % 2 === 0 && + levels[visualIdx] % 2 !== 0 + ) { + logicalIdx = + 1 + + (posX > offset + ? this.bidiMap.logicalFromVisual[visualIdx] + : this.bidiMap.logicalFromVisual[visualIdx - 1]); + } else if ( + (this.isRtlDir && + visualIdx === levels.length - 1 && + charWidth === 0 && + levels[visualIdx - 1] % 2 === 0) || + (!this.isRtlDir && visualIdx === 0 && levels[visualIdx] % 2 !== 0) + ) { + logicalIdx = 1 + this.bidiMap.logicalFromVisual[visualIdx]; + } else { + if ( + visualIdx > 0 && + levels[visualIdx - 1] % 2 !== 0 && + charWidth !== 0 + ) + visualIdx--; + logicalIdx = this.bidiMap.logicalFromVisual[visualIdx]; + } + + if (logicalIdx === 0 && this.isRtlDir) logicalIdx++; + + return logicalIdx + this.wrapIndent; + }; + }.call(BidiHandler.prototype)); + + exports.BidiHandler = BidiHandler; + } +); + +ace.define( + "ace/selection", + [ + "require", + "exports", + "module", + "ace/lib/oop", + "ace/lib/lang", + "ace/lib/event_emitter", + "ace/range" + ], + function(require, exports, module) { + "use strict"; + + var oop = require("./lib/oop"); + var lang = require("./lib/lang"); + var EventEmitter = require("./lib/event_emitter").EventEmitter; + var Range = require("./range").Range; + var Selection = function(session) { + this.session = session; + this.doc = session.getDocument(); + + this.clearSelection(); + this.cursor = this.lead = this.doc.createAnchor(0, 0); + this.anchor = this.doc.createAnchor(0, 0); + this.$silent = false; + + var self = this; + this.cursor.on("change", function(e) { + self.$cursorChanged = true; + if (!self.$silent) self._emit("changeCursor"); + if (!self.$isEmpty && !self.$silent) self._emit("changeSelection"); + if (!self.$keepDesiredColumnOnChange && e.old.column != e.value.column) + self.$desiredColumn = null; + }); + + this.anchor.on("change", function() { + self.$anchorChanged = true; + if (!self.$isEmpty && !self.$silent) self._emit("changeSelection"); + }); + }; + + (function() { + oop.implement(this, EventEmitter); + this.isEmpty = function() { + return ( + this.$isEmpty || + (this.anchor.row == this.lead.row && + this.anchor.column == this.lead.column) + ); + }; + this.isMultiLine = function() { + return !this.$isEmpty && this.anchor.row != this.cursor.row; + }; + this.getCursor = function() { + return this.lead.getPosition(); + }; + this.setSelectionAnchor = function(row, column) { + this.$isEmpty = false; + this.anchor.setPosition(row, column); + }; + this.getAnchor = this.getSelectionAnchor = function() { + if (this.$isEmpty) return this.getSelectionLead(); + + return this.anchor.getPosition(); + }; + this.getSelectionLead = function() { + return this.lead.getPosition(); + }; + this.isBackwards = function() { + var anchor = this.anchor; + var lead = this.lead; + return ( + anchor.row > lead.row || + (anchor.row == lead.row && anchor.column > lead.column) + ); + }; + this.getRange = function() { + var anchor = this.anchor; + var lead = this.lead; + + if (this.$isEmpty) return Range.fromPoints(lead, lead); + + return this.isBackwards() + ? Range.fromPoints(lead, anchor) + : Range.fromPoints(anchor, lead); + }; + this.clearSelection = function() { + if (!this.$isEmpty) { + this.$isEmpty = true; + this._emit("changeSelection"); + } + }; + this.selectAll = function() { + this.$setSelection(0, 0, Number.MAX_VALUE, Number.MAX_VALUE); + }; + this.setRange = this.setSelectionRange = function(range, reverse) { + var start = reverse ? range.end : range.start; + var end = reverse ? range.start : range.end; + this.$setSelection(start.row, start.column, end.row, end.column); + }; + + this.$setSelection = function( + anchorRow, + anchorColumn, + cursorRow, + cursorColumn + ) { + if (this.$silent) return; + var wasEmpty = this.$isEmpty; + var wasMultiselect = this.inMultiSelectMode; + this.$silent = true; + this.$cursorChanged = this.$anchorChanged = false; + this.anchor.setPosition(anchorRow, anchorColumn); + this.cursor.setPosition(cursorRow, cursorColumn); + this.$isEmpty = !Range.comparePoints(this.anchor, this.cursor); + this.$silent = false; + if (this.$cursorChanged) this._emit("changeCursor"); + if ( + this.$cursorChanged || + this.$anchorChanged || + wasEmpty != this.$isEmpty || + wasMultiselect + ) + this._emit("changeSelection"); + }; + + this.$moveSelection = function(mover) { + var lead = this.lead; + if (this.$isEmpty) this.setSelectionAnchor(lead.row, lead.column); + + mover.call(this); + }; + this.selectTo = function(row, column) { + this.$moveSelection(function() { + this.moveCursorTo(row, column); + }); + }; + this.selectToPosition = function(pos) { + this.$moveSelection(function() { + this.moveCursorToPosition(pos); + }); + }; + this.moveTo = function(row, column) { + this.clearSelection(); + this.moveCursorTo(row, column); + }; + this.moveToPosition = function(pos) { + this.clearSelection(); + this.moveCursorToPosition(pos); + }; + this.selectUp = function() { + this.$moveSelection(this.moveCursorUp); + }; + this.selectDown = function() { + this.$moveSelection(this.moveCursorDown); + }; + this.selectRight = function() { + this.$moveSelection(this.moveCursorRight); + }; + this.selectLeft = function() { + this.$moveSelection(this.moveCursorLeft); + }; + this.selectLineStart = function() { + this.$moveSelection(this.moveCursorLineStart); + }; + this.selectLineEnd = function() { + this.$moveSelection(this.moveCursorLineEnd); + }; + this.selectFileEnd = function() { + this.$moveSelection(this.moveCursorFileEnd); + }; + this.selectFileStart = function() { + this.$moveSelection(this.moveCursorFileStart); + }; + this.selectWordRight = function() { + this.$moveSelection(this.moveCursorWordRight); + }; + this.selectWordLeft = function() { + this.$moveSelection(this.moveCursorWordLeft); + }; + this.getWordRange = function(row, column) { + if (typeof column == "undefined") { + var cursor = row || this.lead; + row = cursor.row; + column = cursor.column; + } + return this.session.getWordRange(row, column); + }; + this.selectWord = function() { + this.setSelectionRange(this.getWordRange()); + }; + this.selectAWord = function() { + var cursor = this.getCursor(); + var range = this.session.getAWordRange(cursor.row, cursor.column); + this.setSelectionRange(range); + }; + + this.getLineRange = function(row, excludeLastChar) { + var rowStart = typeof row == "number" ? row : this.lead.row; + var rowEnd; + + var foldLine = this.session.getFoldLine(rowStart); + if (foldLine) { + rowStart = foldLine.start.row; + rowEnd = foldLine.end.row; + } else { + rowEnd = rowStart; + } + if (excludeLastChar === true) + return new Range( + rowStart, + 0, + rowEnd, + this.session.getLine(rowEnd).length + ); + else return new Range(rowStart, 0, rowEnd + 1, 0); + }; + this.selectLine = function() { + this.setSelectionRange(this.getLineRange()); + }; + this.moveCursorUp = function() { + this.moveCursorBy(-1, 0); + }; + this.moveCursorDown = function() { + this.moveCursorBy(1, 0); + }; + this.wouldMoveIntoSoftTab = function(cursor, tabSize, direction) { + var start = cursor.column; + var end = cursor.column + tabSize; + + if (direction < 0) { + start = cursor.column - tabSize; + end = cursor.column; + } + return ( + this.session.isTabStop(cursor) && + this.doc + .getLine(cursor.row) + .slice(start, end) + .split(" ").length - + 1 == + tabSize + ); + }; + this.moveCursorLeft = function() { + var cursor = this.lead.getPosition(), + fold; + + if ((fold = this.session.getFoldAt(cursor.row, cursor.column, -1))) { + this.moveCursorTo(fold.start.row, fold.start.column); + } else if (cursor.column === 0) { + if (cursor.row > 0) { + this.moveCursorTo( + cursor.row - 1, + this.doc.getLine(cursor.row - 1).length + ); + } + } else { + var tabSize = this.session.getTabSize(); + if ( + this.wouldMoveIntoSoftTab(cursor, tabSize, -1) && + !this.session.getNavigateWithinSoftTabs() + ) { + this.moveCursorBy(0, -tabSize); + } else { + this.moveCursorBy(0, -1); + } + } + }; + this.moveCursorRight = function() { + var cursor = this.lead.getPosition(), + fold; + if ((fold = this.session.getFoldAt(cursor.row, cursor.column, 1))) { + this.moveCursorTo(fold.end.row, fold.end.column); + } else if (this.lead.column == this.doc.getLine(this.lead.row).length) { + if (this.lead.row < this.doc.getLength() - 1) { + this.moveCursorTo(this.lead.row + 1, 0); + } + } else { + var tabSize = this.session.getTabSize(); + var cursor = this.lead; + if ( + this.wouldMoveIntoSoftTab(cursor, tabSize, 1) && + !this.session.getNavigateWithinSoftTabs() + ) { + this.moveCursorBy(0, tabSize); + } else { + this.moveCursorBy(0, 1); + } + } + }; + this.moveCursorLineStart = function() { + var row = this.lead.row; + var column = this.lead.column; + var screenRow = this.session.documentToScreenRow(row, column); + var firstColumnPosition = this.session.screenToDocumentPosition( + screenRow, + 0 + ); + var beforeCursor = this.session.getDisplayLine( + row, + null, + firstColumnPosition.row, + firstColumnPosition.column + ); + + var leadingSpace = beforeCursor.match(/^\s*/); + if ( + leadingSpace[0].length != column && + !this.session.$useEmacsStyleLineStart + ) + firstColumnPosition.column += leadingSpace[0].length; + this.moveCursorToPosition(firstColumnPosition); + }; + this.moveCursorLineEnd = function() { + var lead = this.lead; + var lineEnd = this.session.getDocumentLastRowColumnPosition( + lead.row, + lead.column + ); + if (this.lead.column == lineEnd.column) { + var line = this.session.getLine(lineEnd.row); + if (lineEnd.column == line.length) { + var textEnd = line.search(/\s+$/); + if (textEnd > 0) lineEnd.column = textEnd; + } + } + + this.moveCursorTo(lineEnd.row, lineEnd.column); + }; + this.moveCursorFileEnd = function() { + var row = this.doc.getLength() - 1; + var column = this.doc.getLine(row).length; + this.moveCursorTo(row, column); + }; + this.moveCursorFileStart = function() { + this.moveCursorTo(0, 0); + }; + this.moveCursorLongWordRight = function() { + var row = this.lead.row; + var column = this.lead.column; + var line = this.doc.getLine(row); + var rightOfCursor = line.substring(column); + + this.session.nonTokenRe.lastIndex = 0; + this.session.tokenRe.lastIndex = 0; + var fold = this.session.getFoldAt(row, column, 1); + if (fold) { + this.moveCursorTo(fold.end.row, fold.end.column); + return; + } + if (this.session.nonTokenRe.exec(rightOfCursor)) { + column += this.session.nonTokenRe.lastIndex; + this.session.nonTokenRe.lastIndex = 0; + rightOfCursor = line.substring(column); + } + if (column >= line.length) { + this.moveCursorTo(row, line.length); + this.moveCursorRight(); + if (row < this.doc.getLength() - 1) this.moveCursorWordRight(); + return; + } + if (this.session.tokenRe.exec(rightOfCursor)) { + column += this.session.tokenRe.lastIndex; + this.session.tokenRe.lastIndex = 0; + } + + this.moveCursorTo(row, column); + }; + this.moveCursorLongWordLeft = function() { + var row = this.lead.row; + var column = this.lead.column; + var fold; + if ((fold = this.session.getFoldAt(row, column, -1))) { + this.moveCursorTo(fold.start.row, fold.start.column); + return; + } + + var str = this.session.getFoldStringAt(row, column, -1); + if (str == null) { + str = this.doc.getLine(row).substring(0, column); + } + + var leftOfCursor = lang.stringReverse(str); + this.session.nonTokenRe.lastIndex = 0; + this.session.tokenRe.lastIndex = 0; + if (this.session.nonTokenRe.exec(leftOfCursor)) { + column -= this.session.nonTokenRe.lastIndex; + leftOfCursor = leftOfCursor.slice(this.session.nonTokenRe.lastIndex); + this.session.nonTokenRe.lastIndex = 0; + } + if (column <= 0) { + this.moveCursorTo(row, 0); + this.moveCursorLeft(); + if (row > 0) this.moveCursorWordLeft(); + return; + } + if (this.session.tokenRe.exec(leftOfCursor)) { + column -= this.session.tokenRe.lastIndex; + this.session.tokenRe.lastIndex = 0; + } + + this.moveCursorTo(row, column); + }; + + this.$shortWordEndIndex = function(rightOfCursor) { + var index = 0, + ch; + var whitespaceRe = /\s/; + var tokenRe = this.session.tokenRe; + + tokenRe.lastIndex = 0; + if (this.session.tokenRe.exec(rightOfCursor)) { + index = this.session.tokenRe.lastIndex; + } else { + while ((ch = rightOfCursor[index]) && whitespaceRe.test(ch)) index++; + + if (index < 1) { + tokenRe.lastIndex = 0; + while ((ch = rightOfCursor[index]) && !tokenRe.test(ch)) { + tokenRe.lastIndex = 0; + index++; + if (whitespaceRe.test(ch)) { + if (index > 2) { + index--; + break; + } else { + while ((ch = rightOfCursor[index]) && whitespaceRe.test(ch)) + index++; + if (index > 2) break; + } + } + } + } + } + tokenRe.lastIndex = 0; + + return index; + }; + + this.moveCursorShortWordRight = function() { + var row = this.lead.row; + var column = this.lead.column; + var line = this.doc.getLine(row); + var rightOfCursor = line.substring(column); + + var fold = this.session.getFoldAt(row, column, 1); + if (fold) return this.moveCursorTo(fold.end.row, fold.end.column); + + if (column == line.length) { + var l = this.doc.getLength(); + do { + row++; + rightOfCursor = this.doc.getLine(row); + } while (row < l && /^\s*$/.test(rightOfCursor)); + + if (!/^\s+/.test(rightOfCursor)) rightOfCursor = ""; + column = 0; + } + + var index = this.$shortWordEndIndex(rightOfCursor); + + this.moveCursorTo(row, column + index); + }; + + this.moveCursorShortWordLeft = function() { + var row = this.lead.row; + var column = this.lead.column; + + var fold; + if ((fold = this.session.getFoldAt(row, column, -1))) + return this.moveCursorTo(fold.start.row, fold.start.column); + + var line = this.session.getLine(row).substring(0, column); + if (column === 0) { + do { + row--; + line = this.doc.getLine(row); + } while (row > 0 && /^\s*$/.test(line)); + + column = line.length; + if (!/\s+$/.test(line)) line = ""; + } + + var leftOfCursor = lang.stringReverse(line); + var index = this.$shortWordEndIndex(leftOfCursor); + + return this.moveCursorTo(row, column - index); + }; + + this.moveCursorWordRight = function() { + if (this.session.$selectLongWords) this.moveCursorLongWordRight(); + else this.moveCursorShortWordRight(); + }; + + this.moveCursorWordLeft = function() { + if (this.session.$selectLongWords) this.moveCursorLongWordLeft(); + else this.moveCursorShortWordLeft(); + }; + this.moveCursorBy = function(rows, chars) { + var screenPos = this.session.documentToScreenPosition( + this.lead.row, + this.lead.column + ); + + var offsetX; + + if (chars === 0) { + if (rows !== 0) { + if ( + this.session.$bidiHandler.isBidiRow(screenPos.row, this.lead.row) + ) { + offsetX = this.session.$bidiHandler.getPosLeft(screenPos.column); + screenPos.column = Math.round( + offsetX / this.session.$bidiHandler.charWidths[0] + ); + } else { + offsetX = + screenPos.column * this.session.$bidiHandler.charWidths[0]; + } + } + + if (this.$desiredColumn) screenPos.column = this.$desiredColumn; + else this.$desiredColumn = screenPos.column; + } + + if ( + rows != 0 && + this.session.lineWidgets && + this.session.lineWidgets[this.lead.row] + ) { + var widget = this.session.lineWidgets[this.lead.row]; + if (rows < 0) rows -= widget.rowsAbove || 0; + else if (rows > 0) rows += widget.rowCount - (widget.rowsAbove || 0); + } + + var docPos = this.session.screenToDocumentPosition( + screenPos.row + rows, + screenPos.column, + offsetX + ); + + if ( + rows !== 0 && + chars === 0 && + docPos.row === this.lead.row && + docPos.column === this.lead.column + ) { + } + this.moveCursorTo(docPos.row, docPos.column + chars, chars === 0); + }; + this.moveCursorToPosition = function(position) { + this.moveCursorTo(position.row, position.column); + }; + this.moveCursorTo = function(row, column, keepDesiredColumn) { + var fold = this.session.getFoldAt(row, column, 1); + if (fold) { + row = fold.start.row; + column = fold.start.column; + } + + this.$keepDesiredColumnOnChange = true; + var line = this.session.getLine(row); + if ( + /[\uDC00-\uDFFF]/.test(line.charAt(column)) && + line.charAt(column - 1) + ) { + if (this.lead.row == row && this.lead.column == column + 1) + column = column - 1; + else column = column + 1; + } + this.lead.setPosition(row, column); + this.$keepDesiredColumnOnChange = false; + + if (!keepDesiredColumn) this.$desiredColumn = null; + }; + this.moveCursorToScreen = function(row, column, keepDesiredColumn) { + var pos = this.session.screenToDocumentPosition(row, column); + this.moveCursorTo(pos.row, pos.column, keepDesiredColumn); + }; + this.detach = function() { + this.lead.detach(); + this.anchor.detach(); + this.session = this.doc = null; + }; + + this.fromOrientedRange = function(range) { + this.setSelectionRange(range, range.cursor == range.start); + this.$desiredColumn = range.desiredColumn || this.$desiredColumn; + }; + + this.toOrientedRange = function(range) { + var r = this.getRange(); + if (range) { + range.start.column = r.start.column; + range.start.row = r.start.row; + range.end.column = r.end.column; + range.end.row = r.end.row; + } else { + range = r; + } + + range.cursor = this.isBackwards() ? range.start : range.end; + range.desiredColumn = this.$desiredColumn; + return range; + }; + this.getRangeOfMovements = function(func) { + var start = this.getCursor(); + try { + func(this); + var end = this.getCursor(); + return Range.fromPoints(start, end); + } catch (e) { + return Range.fromPoints(start, start); + } finally { + this.moveCursorToPosition(start); + } + }; + + this.toJSON = function() { + if (this.rangeCount) { + var data = this.ranges.map(function(r) { + var r1 = r.clone(); + r1.isBackwards = r.cursor == r.start; + return r1; + }); + } else { + var data = this.getRange(); + data.isBackwards = this.isBackwards(); + } + return data; + }; + + this.fromJSON = function(data) { + if (data.start == undefined) { + if (this.rangeList && data.length > 1) { + this.toSingleRange(data[0]); + for (var i = data.length; i--; ) { + var r = Range.fromPoints(data[i].start, data[i].end); + if (data[i].isBackwards) r.cursor = r.start; + this.addRange(r, true); + } + return; + } else { + data = data[0]; + } + } + if (this.rangeList) this.toSingleRange(data); + this.setSelectionRange(data, data.isBackwards); + }; + + this.isEqual = function(data) { + if ((data.length || this.rangeCount) && data.length != this.rangeCount) + return false; + if (!data.length || !this.ranges) return this.getRange().isEqual(data); + + for (var i = this.ranges.length; i--; ) { + if (!this.ranges[i].isEqual(data[i])) return false; + } + return true; + }; + }.call(Selection.prototype)); + + exports.Selection = Selection; + } +); + +ace.define( + "ace/tokenizer", + ["require", "exports", "module", "ace/config"], + function(require, exports, module) { + "use strict"; + + var config = require("./config"); + var MAX_TOKEN_COUNT = 2000; + var Tokenizer = function(rules) { + this.states = rules; + + this.regExps = {}; + this.matchMappings = {}; + for (var key in this.states) { + var state = this.states[key]; + var ruleRegExps = []; + var matchTotal = 0; + var mapping = (this.matchMappings[key] = { defaultToken: "text" }); + var flag = "g"; + + var splitterRurles = []; + for (var i = 0; i < state.length; i++) { + var rule = state[i]; + if (rule.defaultToken) mapping.defaultToken = rule.defaultToken; + if (rule.caseInsensitive) flag = "gi"; + if (rule.regex == null) continue; + + if (rule.regex instanceof RegExp) + rule.regex = rule.regex.toString().slice(1, -1); + var adjustedregex = rule.regex; + var matchcount = + new RegExp("(?:(" + adjustedregex + ")|(.))").exec("a").length - 2; + if (Array.isArray(rule.token)) { + if (rule.token.length == 1 || matchcount == 1) { + rule.token = rule.token[0]; + } else if (matchcount - 1 != rule.token.length) { + this.reportError( + "number of classes and regexp groups doesn't match", + { + rule: rule, + groupCount: matchcount - 1 + } + ); + rule.token = rule.token[0]; + } else { + rule.tokenArray = rule.token; + rule.token = null; + rule.onMatch = this.$arrayTokens; + } + } else if (typeof rule.token == "function" && !rule.onMatch) { + if (matchcount > 1) rule.onMatch = this.$applyToken; + else rule.onMatch = rule.token; + } + + if (matchcount > 1) { + if (/\\\d/.test(rule.regex)) { + adjustedregex = rule.regex.replace(/\\([0-9]+)/g, function( + match, + digit + ) { + return "\\" + (parseInt(digit, 10) + matchTotal + 1); + }); + } else { + matchcount = 1; + adjustedregex = this.removeCapturingGroups(rule.regex); + } + if (!rule.splitRegex && typeof rule.token != "string") + splitterRurles.push(rule); // flag will be known only at the very end + } + + mapping[matchTotal] = i; + matchTotal += matchcount; + + ruleRegExps.push(adjustedregex); + if (!rule.onMatch) rule.onMatch = null; + } + + if (!ruleRegExps.length) { + mapping[0] = 0; + ruleRegExps.push("$"); + } + + splitterRurles.forEach(function(rule) { + rule.splitRegex = this.createSplitterRegexp(rule.regex, flag); + }, this); + + this.regExps[key] = new RegExp( + "(" + ruleRegExps.join(")|(") + ")|($)", + flag + ); + } + }; + + (function() { + this.$setMaxTokenCount = function(m) { + MAX_TOKEN_COUNT = m | 0; + }; + + this.$applyToken = function(str) { + var values = this.splitRegex.exec(str).slice(1); + var types = this.token.apply(this, values); + if (typeof types === "string") return [{ type: types, value: str }]; + + var tokens = []; + for (var i = 0, l = types.length; i < l; i++) { + if (values[i]) + tokens[tokens.length] = { + type: types[i], + value: values[i] + }; + } + return tokens; + }; + + this.$arrayTokens = function(str) { + if (!str) return []; + var values = this.splitRegex.exec(str); + if (!values) return "text"; + var tokens = []; + var types = this.tokenArray; + for (var i = 0, l = types.length; i < l; i++) { + if (values[i + 1]) + tokens[tokens.length] = { + type: types[i], + value: values[i + 1] + }; + } + return tokens; + }; + + this.removeCapturingGroups = function(src) { + var r = src.replace(/\\.|\[(?:\\.|[^\\\]])*|\(\?[:=!]|(\()/g, function( + x, + y + ) { + return y ? "(?:" : x; + }); + return r; + }; + + this.createSplitterRegexp = function(src, flag) { + if (src.indexOf("(?=") != -1) { + var stack = 0; + var inChClass = false; + var lastCapture = {}; + src.replace(/(\\.)|(\((?:\?[=!])?)|(\))|([\[\]])/g, function( + m, + esc, + parenOpen, + parenClose, + square, + index + ) { + if (inChClass) { + inChClass = square != "]"; + } else if (square) { + inChClass = true; + } else if (parenClose) { + if (stack == lastCapture.stack) { + lastCapture.end = index + 1; + lastCapture.stack = -1; + } + stack--; + } else if (parenOpen) { + stack++; + if (parenOpen.length != 1) { + lastCapture.stack = stack; + lastCapture.start = index; + } + } + return m; + }); + + if ( + lastCapture.end != null && + /^\)*$/.test(src.substr(lastCapture.end)) + ) + src = + src.substring(0, lastCapture.start) + src.substr(lastCapture.end); + } + if (src.charAt(0) != "^") src = "^" + src; + if (src.charAt(src.length - 1) != "$") src += "$"; + + return new RegExp(src, (flag || "").replace("g", "")); + }; + this.getLineTokens = function(line, startState) { + if (startState && typeof startState != "string") { + var stack = startState.slice(0); + startState = stack[0]; + if (startState === "#tmp") { + stack.shift(); + startState = stack.shift(); + } + } else var stack = []; + + var currentState = startState || "start"; + var state = this.states[currentState]; + if (!state) { + currentState = "start"; + state = this.states[currentState]; + } + var mapping = this.matchMappings[currentState]; + var re = this.regExps[currentState]; + re.lastIndex = 0; + + var match, + tokens = []; + var lastIndex = 0; + var matchAttempts = 0; + + var token = { type: null, value: "" }; + + while ((match = re.exec(line))) { + var type = mapping.defaultToken; + var rule = null; + var value = match[0]; + var index = re.lastIndex; + + if (index - value.length > lastIndex) { + var skipped = line.substring(lastIndex, index - value.length); + if (token.type == type) { + token.value += skipped; + } else { + if (token.type) tokens.push(token); + token = { type: type, value: skipped }; + } + } + + for (var i = 0; i < match.length - 2; i++) { + if (match[i + 1] === undefined) continue; + + rule = state[mapping[i]]; + + if (rule.onMatch) + type = rule.onMatch(value, currentState, stack, line); + else type = rule.token; + + if (rule.next) { + if (typeof rule.next == "string") { + currentState = rule.next; + } else { + currentState = rule.next(currentState, stack); + } + + state = this.states[currentState]; + if (!state) { + this.reportError("state doesn't exist", currentState); + currentState = "start"; + state = this.states[currentState]; + } + mapping = this.matchMappings[currentState]; + lastIndex = index; + re = this.regExps[currentState]; + re.lastIndex = index; + } + if (rule.consumeLineEnd) lastIndex = index; + break; + } + + if (value) { + if (typeof type === "string") { + if ((!rule || rule.merge !== false) && token.type === type) { + token.value += value; + } else { + if (token.type) tokens.push(token); + token = { type: type, value: value }; + } + } else if (type) { + if (token.type) tokens.push(token); + token = { type: null, value: "" }; + for (var i = 0; i < type.length; i++) tokens.push(type[i]); + } + } + + if (lastIndex == line.length) break; + + lastIndex = index; + + if (matchAttempts++ > MAX_TOKEN_COUNT) { + if (matchAttempts > 2 * line.length) { + this.reportError("infinite loop with in ace tokenizer", { + startState: startState, + line: line + }); + } + while (lastIndex < line.length) { + if (token.type) tokens.push(token); + token = { + value: line.substring(lastIndex, (lastIndex += 500)), + type: "overflow" + }; + } + currentState = "start"; + stack = []; + break; + } + } + + if (token.type) tokens.push(token); + + if (stack.length > 1) { + if (stack[0] !== currentState) stack.unshift("#tmp", currentState); + } + return { + tokens: tokens, + state: stack.length ? stack : currentState + }; + }; + + this.reportError = config.reportError; + }.call(Tokenizer.prototype)); + + exports.Tokenizer = Tokenizer; + } +); + +ace.define( + "ace/mode/text_highlight_rules", + ["require", "exports", "module", "ace/lib/lang"], + function(require, exports, module) { + "use strict"; + + var lang = require("../lib/lang"); + + var TextHighlightRules = function() { + this.$rules = { + start: [ + { + token: "empty_line", + regex: "^$" + }, + { + defaultToken: "text" + } + ] + }; + }; + + (function() { + this.addRules = function(rules, prefix) { + if (!prefix) { + for (var key in rules) this.$rules[key] = rules[key]; + return; + } + for (var key in rules) { + var state = rules[key]; + for (var i = 0; i < state.length; i++) { + var rule = state[i]; + if (rule.next || rule.onMatch) { + if (typeof rule.next == "string") { + if (rule.next.indexOf(prefix) !== 0) + rule.next = prefix + rule.next; + } + if (rule.nextState && rule.nextState.indexOf(prefix) !== 0) + rule.nextState = prefix + rule.nextState; + } + } + this.$rules[prefix + key] = state; + } + }; + + this.getRules = function() { + return this.$rules; + }; + + this.embedRules = function( + HighlightRules, + prefix, + escapeRules, + states, + append + ) { + var embedRules = + typeof HighlightRules == "function" + ? new HighlightRules().getRules() + : HighlightRules; + if (states) { + for (var i = 0; i < states.length; i++) + states[i] = prefix + states[i]; + } else { + states = []; + for (var key in embedRules) states.push(prefix + key); + } + + this.addRules(embedRules, prefix); + + if (escapeRules) { + var addRules = Array.prototype[append ? "push" : "unshift"]; + for (var i = 0; i < states.length; i++) + addRules.apply(this.$rules[states[i]], lang.deepCopy(escapeRules)); + } + + if (!this.$embeds) this.$embeds = []; + this.$embeds.push(prefix); + }; + + this.getEmbeds = function() { + return this.$embeds; + }; + + var pushState = function(currentState, stack) { + if (currentState != "start" || stack.length) + stack.unshift(this.nextState, currentState); + return this.nextState; + }; + var popState = function(currentState, stack) { + stack.shift(); + return stack.shift() || "start"; + }; + + this.normalizeRules = function() { + var id = 0; + var rules = this.$rules; + function processState(key) { + var state = rules[key]; + state.processed = true; + for (var i = 0; i < state.length; i++) { + var rule = state[i]; + var toInsert = null; + if (Array.isArray(rule)) { + toInsert = rule; + rule = {}; + } + if (!rule.regex && rule.start) { + rule.regex = rule.start; + if (!rule.next) rule.next = []; + rule.next.push( + { + defaultToken: rule.token + }, + { + token: rule.token + ".end", + regex: rule.end || rule.start, + next: "pop" + } + ); + rule.token = rule.token + ".start"; + rule.push = true; + } + var next = rule.next || rule.push; + if (next && Array.isArray(next)) { + var stateName = rule.stateName; + if (!stateName) { + stateName = rule.token; + if (typeof stateName != "string") + stateName = stateName[0] || ""; + if (rules[stateName]) stateName += id++; + } + rules[stateName] = next; + rule.next = stateName; + processState(stateName); + } else if (next == "pop") { + rule.next = popState; + } + + if (rule.push) { + rule.nextState = rule.next || rule.push; + rule.next = pushState; + delete rule.push; + } + + if (rule.rules) { + for (var r in rule.rules) { + if (rules[r]) { + if (rules[r].push) + rules[r].push.apply(rules[r], rule.rules[r]); + } else { + rules[r] = rule.rules[r]; + } + } + } + var includeName = typeof rule == "string" ? rule : rule.include; + if (includeName) { + if (Array.isArray(includeName)) + toInsert = includeName.map(function(x) { + return rules[x]; + }); + else toInsert = rules[includeName]; + } + + if (toInsert) { + var args = [i, 1].concat(toInsert); + if (rule.noEscape) + args = args.filter(function(x) { + return !x.next; + }); + state.splice.apply(state, args); + i--; + } + + if (rule.keywordMap) { + rule.token = this.createKeywordMapper( + rule.keywordMap, + rule.defaultToken || "text", + rule.caseInsensitive + ); + delete rule.defaultToken; + } + } + } + Object.keys(rules).forEach(processState, this); + }; + + this.createKeywordMapper = function( + map, + defaultToken, + ignoreCase, + splitChar + ) { + var keywords = Object.create(null); + Object.keys(map).forEach(function(className) { + var a = map[className]; + if (ignoreCase) a = a.toLowerCase(); + var list = a.split(splitChar || "|"); + for (var i = list.length; i--; ) keywords[list[i]] = className; + }); + if (Object.getPrototypeOf(keywords)) { + keywords.__proto__ = null; + } + this.$keywordList = Object.keys(keywords); + map = null; + return ignoreCase + ? function(value) { + return keywords[value.toLowerCase()] || defaultToken; + } + : function(value) { + return keywords[value] || defaultToken; + }; + }; + + this.getKeywords = function() { + return this.$keywords; + }; + }.call(TextHighlightRules.prototype)); + + exports.TextHighlightRules = TextHighlightRules; + } +); + +ace.define("ace/mode/behaviour", ["require", "exports", "module"], function( + require, + exports, + module +) { + "use strict"; + + var Behaviour = function() { + this.$behaviours = {}; + }; + + (function() { + this.add = function(name, action, callback) { + switch (undefined) { + case this.$behaviours: + this.$behaviours = {}; + case this.$behaviours[name]: + this.$behaviours[name] = {}; + } + this.$behaviours[name][action] = callback; + }; + + this.addBehaviours = function(behaviours) { + for (var key in behaviours) { + for (var action in behaviours[key]) { + this.add(key, action, behaviours[key][action]); + } + } + }; + + this.remove = function(name) { + if (this.$behaviours && this.$behaviours[name]) { + delete this.$behaviours[name]; + } + }; + + this.inherit = function(mode, filter) { + if (typeof mode === "function") { + var behaviours = new mode().getBehaviours(filter); + } else { + var behaviours = mode.getBehaviours(filter); + } + this.addBehaviours(behaviours); + }; + + this.getBehaviours = function(filter) { + if (!filter) { + return this.$behaviours; + } else { + var ret = {}; + for (var i = 0; i < filter.length; i++) { + if (this.$behaviours[filter[i]]) { + ret[filter[i]] = this.$behaviours[filter[i]]; + } + } + return ret; + } + }; + }.call(Behaviour.prototype)); + + exports.Behaviour = Behaviour; +}); + +ace.define( + "ace/token_iterator", + ["require", "exports", "module", "ace/range"], + function(require, exports, module) { + "use strict"; + + var Range = require("./range").Range; + var TokenIterator = function(session, initialRow, initialColumn) { + this.$session = session; + this.$row = initialRow; + this.$rowTokens = session.getTokens(initialRow); + + var token = session.getTokenAt(initialRow, initialColumn); + this.$tokenIndex = token ? token.index : -1; + }; + + (function() { + this.stepBackward = function() { + this.$tokenIndex -= 1; + + while (this.$tokenIndex < 0) { + this.$row -= 1; + if (this.$row < 0) { + this.$row = 0; + return null; + } + + this.$rowTokens = this.$session.getTokens(this.$row); + this.$tokenIndex = this.$rowTokens.length - 1; + } + + return this.$rowTokens[this.$tokenIndex]; + }; + this.stepForward = function() { + this.$tokenIndex += 1; + var rowCount; + while (this.$tokenIndex >= this.$rowTokens.length) { + this.$row += 1; + if (!rowCount) rowCount = this.$session.getLength(); + if (this.$row >= rowCount) { + this.$row = rowCount - 1; + return null; + } + + this.$rowTokens = this.$session.getTokens(this.$row); + this.$tokenIndex = 0; + } + + return this.$rowTokens[this.$tokenIndex]; + }; + this.getCurrentToken = function() { + return this.$rowTokens[this.$tokenIndex]; + }; + this.getCurrentTokenRow = function() { + return this.$row; + }; + this.getCurrentTokenColumn = function() { + var rowTokens = this.$rowTokens; + var tokenIndex = this.$tokenIndex; + var column = rowTokens[tokenIndex].start; + if (column !== undefined) return column; + + column = 0; + while (tokenIndex > 0) { + tokenIndex -= 1; + column += rowTokens[tokenIndex].value.length; + } + + return column; + }; + this.getCurrentTokenPosition = function() { + return { row: this.$row, column: this.getCurrentTokenColumn() }; + }; + this.getCurrentTokenRange = function() { + var token = this.$rowTokens[this.$tokenIndex]; + var column = this.getCurrentTokenColumn(); + return new Range( + this.$row, + column, + this.$row, + column + token.value.length + ); + }; + }.call(TokenIterator.prototype)); + + exports.TokenIterator = TokenIterator; + } +); + +ace.define( + "ace/mode/behaviour/cstyle", + [ + "require", + "exports", + "module", + "ace/lib/oop", + "ace/mode/behaviour", + "ace/token_iterator", + "ace/lib/lang" + ], + function(require, exports, module) { + "use strict"; + + var oop = require("../../lib/oop"); + var Behaviour = require("../behaviour").Behaviour; + var TokenIterator = require("../../token_iterator").TokenIterator; + var lang = require("../../lib/lang"); + + var SAFE_INSERT_IN_TOKENS = [ + "text", + "paren.rparen", + "rparen", + "paren", + "punctuation.operator" + ]; + var SAFE_INSERT_BEFORE_TOKENS = [ + "text", + "paren.rparen", + "rparen", + "paren", + "punctuation.operator", + "comment" + ]; + + var context; + var contextCache = {}; + var defaultQuotes = { '"': '"', "'": "'" }; + + var initContext = function(editor) { + var id = -1; + if (editor.multiSelect) { + id = editor.selection.index; + if (contextCache.rangeCount != editor.multiSelect.rangeCount) + contextCache = { rangeCount: editor.multiSelect.rangeCount }; + } + if (contextCache[id]) return (context = contextCache[id]); + context = contextCache[id] = { + autoInsertedBrackets: 0, + autoInsertedRow: -1, + autoInsertedLineEnd: "", + maybeInsertedBrackets: 0, + maybeInsertedRow: -1, + maybeInsertedLineStart: "", + maybeInsertedLineEnd: "" + }; + }; + + var getWrapped = function(selection, selected, opening, closing) { + var rowDiff = selection.end.row - selection.start.row; + return { + text: opening + selected + closing, + selection: [ + 0, + selection.start.column + 1, + rowDiff, + selection.end.column + (rowDiff ? 0 : 1) + ] + }; + }; + + var CstyleBehaviour = function(options) { + this.add("braces", "insertion", function( + state, + action, + editor, + session, + text + ) { + var cursor = editor.getCursorPosition(); + var line = session.doc.getLine(cursor.row); + if (text == "{") { + initContext(editor); + var selection = editor.getSelectionRange(); + var selected = session.doc.getTextRange(selection); + if ( + selected !== "" && + selected !== "{" && + editor.getWrapBehavioursEnabled() + ) { + return getWrapped(selection, selected, "{", "}"); + } else if (CstyleBehaviour.isSaneInsertion(editor, session)) { + if ( + /[\]\}\)]/.test(line[cursor.column]) || + editor.inMultiSelectMode || + (options && options.braces) + ) { + CstyleBehaviour.recordAutoInsert(editor, session, "}"); + return { + text: "{}", + selection: [1, 1] + }; + } else { + CstyleBehaviour.recordMaybeInsert(editor, session, "{"); + return { + text: "{", + selection: [1, 1] + }; + } + } + } else if (text == "}") { + initContext(editor); + var rightChar = line.substring(cursor.column, cursor.column + 1); + if (rightChar == "}") { + var matching = session.$findOpeningBracket("}", { + column: cursor.column + 1, + row: cursor.row + }); + if ( + matching !== null && + CstyleBehaviour.isAutoInsertedClosing(cursor, line, text) + ) { + CstyleBehaviour.popAutoInsertedClosing(); + return { + text: "", + selection: [1, 1] + }; + } + } + } else if (text == "\n" || text == "\r\n") { + initContext(editor); + var closing = ""; + if (CstyleBehaviour.isMaybeInsertedClosing(cursor, line)) { + closing = lang.stringRepeat("}", context.maybeInsertedBrackets); + CstyleBehaviour.clearMaybeInsertedClosing(); + } + var rightChar = line.substring(cursor.column, cursor.column + 1); + if (rightChar === "}") { + var openBracePos = session.findMatchingBracket( + { row: cursor.row, column: cursor.column + 1 }, + "}" + ); + if (!openBracePos) return null; + var next_indent = this.$getIndent( + session.getLine(openBracePos.row) + ); + } else if (closing) { + var next_indent = this.$getIndent(line); + } else { + CstyleBehaviour.clearMaybeInsertedClosing(); + return; + } + var indent = next_indent + session.getTabString(); + + return { + text: "\n" + indent + "\n" + next_indent + closing, + selection: [1, indent.length, 1, indent.length] + }; + } else { + CstyleBehaviour.clearMaybeInsertedClosing(); + } + }); + + this.add("braces", "deletion", function( + state, + action, + editor, + session, + range + ) { + var selected = session.doc.getTextRange(range); + if (!range.isMultiLine() && selected == "{") { + initContext(editor); + var line = session.doc.getLine(range.start.row); + var rightChar = line.substring( + range.end.column, + range.end.column + 1 + ); + if (rightChar == "}") { + range.end.column++; + return range; + } else { + context.maybeInsertedBrackets--; + } + } + }); + + this.add("parens", "insertion", function( + state, + action, + editor, + session, + text + ) { + if (text == "(") { + initContext(editor); + var selection = editor.getSelectionRange(); + var selected = session.doc.getTextRange(selection); + if (selected !== "" && editor.getWrapBehavioursEnabled()) { + return getWrapped(selection, selected, "(", ")"); + } else if (CstyleBehaviour.isSaneInsertion(editor, session)) { + CstyleBehaviour.recordAutoInsert(editor, session, ")"); + return { + text: "()", + selection: [1, 1] + }; + } + } else if (text == ")") { + initContext(editor); + var cursor = editor.getCursorPosition(); + var line = session.doc.getLine(cursor.row); + var rightChar = line.substring(cursor.column, cursor.column + 1); + if (rightChar == ")") { + var matching = session.$findOpeningBracket(")", { + column: cursor.column + 1, + row: cursor.row + }); + if ( + matching !== null && + CstyleBehaviour.isAutoInsertedClosing(cursor, line, text) + ) { + CstyleBehaviour.popAutoInsertedClosing(); + return { + text: "", + selection: [1, 1] + }; + } + } + } + }); + + this.add("parens", "deletion", function( + state, + action, + editor, + session, + range + ) { + var selected = session.doc.getTextRange(range); + if (!range.isMultiLine() && selected == "(") { + initContext(editor); + var line = session.doc.getLine(range.start.row); + var rightChar = line.substring( + range.start.column + 1, + range.start.column + 2 + ); + if (rightChar == ")") { + range.end.column++; + return range; + } + } + }); + + this.add("brackets", "insertion", function( + state, + action, + editor, + session, + text + ) { + if (text == "[") { + initContext(editor); + var selection = editor.getSelectionRange(); + var selected = session.doc.getTextRange(selection); + if (selected !== "" && editor.getWrapBehavioursEnabled()) { + return getWrapped(selection, selected, "[", "]"); + } else if (CstyleBehaviour.isSaneInsertion(editor, session)) { + CstyleBehaviour.recordAutoInsert(editor, session, "]"); + return { + text: "[]", + selection: [1, 1] + }; + } + } else if (text == "]") { + initContext(editor); + var cursor = editor.getCursorPosition(); + var line = session.doc.getLine(cursor.row); + var rightChar = line.substring(cursor.column, cursor.column + 1); + if (rightChar == "]") { + var matching = session.$findOpeningBracket("]", { + column: cursor.column + 1, + row: cursor.row + }); + if ( + matching !== null && + CstyleBehaviour.isAutoInsertedClosing(cursor, line, text) + ) { + CstyleBehaviour.popAutoInsertedClosing(); + return { + text: "", + selection: [1, 1] + }; + } + } + } + }); + + this.add("brackets", "deletion", function( + state, + action, + editor, + session, + range + ) { + var selected = session.doc.getTextRange(range); + if (!range.isMultiLine() && selected == "[") { + initContext(editor); + var line = session.doc.getLine(range.start.row); + var rightChar = line.substring( + range.start.column + 1, + range.start.column + 2 + ); + if (rightChar == "]") { + range.end.column++; + return range; + } + } + }); + + this.add("string_dquotes", "insertion", function( + state, + action, + editor, + session, + text + ) { + var quotes = session.$mode.$quotes || defaultQuotes; + if (text.length == 1 && quotes[text]) { + if ( + this.lineCommentStart && + this.lineCommentStart.indexOf(text) != -1 + ) + return; + initContext(editor); + var quote = text; + var selection = editor.getSelectionRange(); + var selected = session.doc.getTextRange(selection); + if ( + selected !== "" && + (selected.length != 1 || !quotes[selected]) && + editor.getWrapBehavioursEnabled() + ) { + return getWrapped(selection, selected, quote, quote); + } else if (!selected) { + var cursor = editor.getCursorPosition(); + var line = session.doc.getLine(cursor.row); + var leftChar = line.substring(cursor.column - 1, cursor.column); + var rightChar = line.substring(cursor.column, cursor.column + 1); + + var token = session.getTokenAt(cursor.row, cursor.column); + var rightToken = session.getTokenAt(cursor.row, cursor.column + 1); + if (leftChar == "\\" && token && /escape/.test(token.type)) + return null; + + var stringBefore = token && /string|escape/.test(token.type); + var stringAfter = + !rightToken || /string|escape/.test(rightToken.type); + + var pair; + if (rightChar == quote) { + pair = stringBefore !== stringAfter; + if (pair && /string\.end/.test(rightToken.type)) pair = false; + } else { + if (stringBefore && !stringAfter) return null; // wrap string with different quote + if (stringBefore && stringAfter) return null; // do not pair quotes inside strings + var wordRe = session.$mode.tokenRe; + wordRe.lastIndex = 0; + var isWordBefore = wordRe.test(leftChar); + wordRe.lastIndex = 0; + var isWordAfter = wordRe.test(leftChar); + if (isWordBefore || isWordAfter) return null; // before or after alphanumeric + if (rightChar && !/[\s;,.})\]\\]/.test(rightChar)) return null; // there is rightChar and it isn't closing + var charBefore = line[cursor.column - 2]; + if ( + leftChar == quote && + (charBefore == quote || wordRe.test(charBefore)) + ) + return null; + pair = true; + } + return { + text: pair ? quote + quote : "", + selection: [1, 1] + }; + } + } + }); + + this.add("string_dquotes", "deletion", function( + state, + action, + editor, + session, + range + ) { + var quotes = session.$mode.$quotes || defaultQuotes; + + var selected = session.doc.getTextRange(range); + if (!range.isMultiLine() && quotes.hasOwnProperty(selected)) { + initContext(editor); + var line = session.doc.getLine(range.start.row); + var rightChar = line.substring( + range.start.column + 1, + range.start.column + 2 + ); + if (rightChar == selected) { + range.end.column++; + return range; + } + } + }); + }; + + CstyleBehaviour.isSaneInsertion = function(editor, session) { + var cursor = editor.getCursorPosition(); + var iterator = new TokenIterator(session, cursor.row, cursor.column); + if ( + !this.$matchTokenType( + iterator.getCurrentToken() || "text", + SAFE_INSERT_IN_TOKENS + ) + ) { + if (/[)}\]]/.test(editor.session.getLine(cursor.row)[cursor.column])) + return true; + var iterator2 = new TokenIterator( + session, + cursor.row, + cursor.column + 1 + ); + if ( + !this.$matchTokenType( + iterator2.getCurrentToken() || "text", + SAFE_INSERT_IN_TOKENS + ) + ) + return false; + } + iterator.stepForward(); + return ( + iterator.getCurrentTokenRow() !== cursor.row || + this.$matchTokenType( + iterator.getCurrentToken() || "text", + SAFE_INSERT_BEFORE_TOKENS + ) + ); + }; + + CstyleBehaviour.$matchTokenType = function(token, types) { + return types.indexOf(token.type || token) > -1; + }; + + CstyleBehaviour.recordAutoInsert = function(editor, session, bracket) { + var cursor = editor.getCursorPosition(); + var line = session.doc.getLine(cursor.row); + if ( + !this.isAutoInsertedClosing( + cursor, + line, + context.autoInsertedLineEnd[0] + ) + ) + context.autoInsertedBrackets = 0; + context.autoInsertedRow = cursor.row; + context.autoInsertedLineEnd = bracket + line.substr(cursor.column); + context.autoInsertedBrackets++; + }; + + CstyleBehaviour.recordMaybeInsert = function(editor, session, bracket) { + var cursor = editor.getCursorPosition(); + var line = session.doc.getLine(cursor.row); + if (!this.isMaybeInsertedClosing(cursor, line)) + context.maybeInsertedBrackets = 0; + context.maybeInsertedRow = cursor.row; + context.maybeInsertedLineStart = line.substr(0, cursor.column) + bracket; + context.maybeInsertedLineEnd = line.substr(cursor.column); + context.maybeInsertedBrackets++; + }; + + CstyleBehaviour.isAutoInsertedClosing = function(cursor, line, bracket) { + return ( + context.autoInsertedBrackets > 0 && + cursor.row === context.autoInsertedRow && + bracket === context.autoInsertedLineEnd[0] && + line.substr(cursor.column) === context.autoInsertedLineEnd + ); + }; + + CstyleBehaviour.isMaybeInsertedClosing = function(cursor, line) { + return ( + context.maybeInsertedBrackets > 0 && + cursor.row === context.maybeInsertedRow && + line.substr(cursor.column) === context.maybeInsertedLineEnd && + line.substr(0, cursor.column) == context.maybeInsertedLineStart + ); + }; + + CstyleBehaviour.popAutoInsertedClosing = function() { + context.autoInsertedLineEnd = context.autoInsertedLineEnd.substr(1); + context.autoInsertedBrackets--; + }; + + CstyleBehaviour.clearMaybeInsertedClosing = function() { + if (context) { + context.maybeInsertedBrackets = 0; + context.maybeInsertedRow = -1; + } + }; + + oop.inherits(CstyleBehaviour, Behaviour); + + exports.CstyleBehaviour = CstyleBehaviour; + } +); + +ace.define("ace/unicode", ["require", "exports", "module"], function( + require, + exports, + module +) { + "use strict"; + var wordChars = [ + 48, + 9, + 8, + 25, + 5, + 0, + 2, + 25, + 48, + 0, + 11, + 0, + 5, + 0, + 6, + 22, + 2, + 30, + 2, + 457, + 5, + 11, + 15, + 4, + 8, + 0, + 2, + 0, + 18, + 116, + 2, + 1, + 3, + 3, + 9, + 0, + 2, + 2, + 2, + 0, + 2, + 19, + 2, + 82, + 2, + 138, + 2, + 4, + 3, + 155, + 12, + 37, + 3, + 0, + 8, + 38, + 10, + 44, + 2, + 0, + 2, + 1, + 2, + 1, + 2, + 0, + 9, + 26, + 6, + 2, + 30, + 10, + 7, + 61, + 2, + 9, + 5, + 101, + 2, + 7, + 3, + 9, + 2, + 18, + 3, + 0, + 17, + 58, + 3, + 100, + 15, + 53, + 5, + 0, + 6, + 45, + 211, + 57, + 3, + 18, + 2, + 5, + 3, + 11, + 3, + 9, + 2, + 1, + 7, + 6, + 2, + 2, + 2, + 7, + 3, + 1, + 3, + 21, + 2, + 6, + 2, + 0, + 4, + 3, + 3, + 8, + 3, + 1, + 3, + 3, + 9, + 0, + 5, + 1, + 2, + 4, + 3, + 11, + 16, + 2, + 2, + 5, + 5, + 1, + 3, + 21, + 2, + 6, + 2, + 1, + 2, + 1, + 2, + 1, + 3, + 0, + 2, + 4, + 5, + 1, + 3, + 2, + 4, + 0, + 8, + 3, + 2, + 0, + 8, + 15, + 12, + 2, + 2, + 8, + 2, + 2, + 2, + 21, + 2, + 6, + 2, + 1, + 2, + 4, + 3, + 9, + 2, + 2, + 2, + 2, + 3, + 0, + 16, + 3, + 3, + 9, + 18, + 2, + 2, + 7, + 3, + 1, + 3, + 21, + 2, + 6, + 2, + 1, + 2, + 4, + 3, + 8, + 3, + 1, + 3, + 2, + 9, + 1, + 5, + 1, + 2, + 4, + 3, + 9, + 2, + 0, + 17, + 1, + 2, + 5, + 4, + 2, + 2, + 3, + 4, + 1, + 2, + 0, + 2, + 1, + 4, + 1, + 4, + 2, + 4, + 11, + 5, + 4, + 4, + 2, + 2, + 3, + 3, + 0, + 7, + 0, + 15, + 9, + 18, + 2, + 2, + 7, + 2, + 2, + 2, + 22, + 2, + 9, + 2, + 4, + 4, + 7, + 2, + 2, + 2, + 3, + 8, + 1, + 2, + 1, + 7, + 3, + 3, + 9, + 19, + 1, + 2, + 7, + 2, + 2, + 2, + 22, + 2, + 9, + 2, + 4, + 3, + 8, + 2, + 2, + 2, + 3, + 8, + 1, + 8, + 0, + 2, + 3, + 3, + 9, + 19, + 1, + 2, + 7, + 2, + 2, + 2, + 22, + 2, + 15, + 4, + 7, + 2, + 2, + 2, + 3, + 10, + 0, + 9, + 3, + 3, + 9, + 11, + 5, + 3, + 1, + 2, + 17, + 4, + 23, + 2, + 8, + 2, + 0, + 3, + 6, + 4, + 0, + 5, + 5, + 2, + 0, + 2, + 7, + 19, + 1, + 14, + 57, + 6, + 14, + 2, + 9, + 40, + 1, + 2, + 0, + 3, + 1, + 2, + 0, + 3, + 0, + 7, + 3, + 2, + 6, + 2, + 2, + 2, + 0, + 2, + 0, + 3, + 1, + 2, + 12, + 2, + 2, + 3, + 4, + 2, + 0, + 2, + 5, + 3, + 9, + 3, + 1, + 35, + 0, + 24, + 1, + 7, + 9, + 12, + 0, + 2, + 0, + 2, + 0, + 5, + 9, + 2, + 35, + 5, + 19, + 2, + 5, + 5, + 7, + 2, + 35, + 10, + 0, + 58, + 73, + 7, + 77, + 3, + 37, + 11, + 42, + 2, + 0, + 4, + 328, + 2, + 3, + 3, + 6, + 2, + 0, + 2, + 3, + 3, + 40, + 2, + 3, + 3, + 32, + 2, + 3, + 3, + 6, + 2, + 0, + 2, + 3, + 3, + 14, + 2, + 56, + 2, + 3, + 3, + 66, + 5, + 0, + 33, + 15, + 17, + 84, + 13, + 619, + 3, + 16, + 2, + 25, + 6, + 74, + 22, + 12, + 2, + 6, + 12, + 20, + 12, + 19, + 13, + 12, + 2, + 2, + 2, + 1, + 13, + 51, + 3, + 29, + 4, + 0, + 5, + 1, + 3, + 9, + 34, + 2, + 3, + 9, + 7, + 87, + 9, + 42, + 6, + 69, + 11, + 28, + 4, + 11, + 5, + 11, + 11, + 39, + 3, + 4, + 12, + 43, + 5, + 25, + 7, + 10, + 38, + 27, + 5, + 62, + 2, + 28, + 3, + 10, + 7, + 9, + 14, + 0, + 89, + 75, + 5, + 9, + 18, + 8, + 13, + 42, + 4, + 11, + 71, + 55, + 9, + 9, + 4, + 48, + 83, + 2, + 2, + 30, + 14, + 230, + 23, + 280, + 3, + 5, + 3, + 37, + 3, + 5, + 3, + 7, + 2, + 0, + 2, + 0, + 2, + 0, + 2, + 30, + 3, + 52, + 2, + 6, + 2, + 0, + 4, + 2, + 2, + 6, + 4, + 3, + 3, + 5, + 5, + 12, + 6, + 2, + 2, + 6, + 67, + 1, + 20, + 0, + 29, + 0, + 14, + 0, + 17, + 4, + 60, + 12, + 5, + 0, + 4, + 11, + 18, + 0, + 5, + 0, + 3, + 9, + 2, + 0, + 4, + 4, + 7, + 0, + 2, + 0, + 2, + 0, + 2, + 3, + 2, + 10, + 3, + 3, + 6, + 4, + 5, + 0, + 53, + 1, + 2684, + 46, + 2, + 46, + 2, + 132, + 7, + 6, + 15, + 37, + 11, + 53, + 10, + 0, + 17, + 22, + 10, + 6, + 2, + 6, + 2, + 6, + 2, + 6, + 2, + 6, + 2, + 6, + 2, + 6, + 2, + 6, + 2, + 31, + 48, + 0, + 470, + 1, + 36, + 5, + 2, + 4, + 6, + 1, + 5, + 85, + 3, + 1, + 3, + 2, + 2, + 89, + 2, + 3, + 6, + 40, + 4, + 93, + 18, + 23, + 57, + 15, + 513, + 6581, + 75, + 20939, + 53, + 1164, + 68, + 45, + 3, + 268, + 4, + 27, + 21, + 31, + 3, + 13, + 13, + 1, + 2, + 24, + 9, + 69, + 11, + 1, + 38, + 8, + 3, + 102, + 3, + 1, + 111, + 44, + 25, + 51, + 13, + 68, + 12, + 9, + 7, + 23, + 4, + 0, + 5, + 45, + 3, + 35, + 13, + 28, + 4, + 64, + 15, + 10, + 39, + 54, + 10, + 13, + 3, + 9, + 7, + 22, + 4, + 1, + 5, + 66, + 25, + 2, + 227, + 42, + 2, + 1, + 3, + 9, + 7, + 11171, + 13, + 22, + 5, + 48, + 8453, + 301, + 3, + 61, + 3, + 105, + 39, + 6, + 13, + 4, + 6, + 11, + 2, + 12, + 2, + 4, + 2, + 0, + 2, + 1, + 2, + 1, + 2, + 107, + 34, + 362, + 19, + 63, + 3, + 53, + 41, + 11, + 5, + 15, + 17, + 6, + 13, + 1, + 25, + 2, + 33, + 4, + 2, + 134, + 20, + 9, + 8, + 25, + 5, + 0, + 2, + 25, + 12, + 88, + 4, + 5, + 3, + 5, + 3, + 5, + 3, + 2 + ]; + + var code = 0; + var str = []; + for (var i = 0; i < wordChars.length; i += 2) { + str.push((code += wordChars[i])); + if (wordChars[i + 1]) str.push(45, (code += wordChars[i + 1])); + } + + exports.wordChars = String.fromCharCode.apply(null, str); +}); + +ace.define( + "ace/mode/text", + [ + "require", + "exports", + "module", + "ace/config", + "ace/tokenizer", + "ace/mode/text_highlight_rules", + "ace/mode/behaviour/cstyle", + "ace/unicode", + "ace/lib/lang", + "ace/token_iterator", + "ace/range" + ], + function(require, exports, module) { + "use strict"; + var config = require("../config"); + + var Tokenizer = require("../tokenizer").Tokenizer; + var TextHighlightRules = require("./text_highlight_rules") + .TextHighlightRules; + var CstyleBehaviour = require("./behaviour/cstyle").CstyleBehaviour; + var unicode = require("../unicode"); + var lang = require("../lib/lang"); + var TokenIterator = require("../token_iterator").TokenIterator; + var Range = require("../range").Range; + + var Mode = function() { + this.HighlightRules = TextHighlightRules; + }; + + (function() { + this.$defaultBehaviour = new CstyleBehaviour(); + + this.tokenRe = new RegExp("^[" + unicode.wordChars + "\\$_]+", "g"); + + this.nonTokenRe = new RegExp( + "^(?:[^" + unicode.wordChars + "\\$_]|\\s])+", + "g" + ); + + this.getTokenizer = function() { + if (!this.$tokenizer) { + this.$highlightRules = + this.$highlightRules || + new this.HighlightRules(this.$highlightRuleConfig); + this.$tokenizer = new Tokenizer(this.$highlightRules.getRules()); + } + return this.$tokenizer; + }; + + this.lineCommentStart = ""; + this.blockComment = ""; + + this.toggleCommentLines = function(state, session, startRow, endRow) { + var doc = session.doc; + + var ignoreBlankLines = true; + var shouldRemove = true; + var minIndent = Infinity; + var tabSize = session.getTabSize(); + var insertAtTabStop = false; + + if (!this.lineCommentStart) { + if (!this.blockComment) return false; + var lineCommentStart = this.blockComment.start; + var lineCommentEnd = this.blockComment.end; + var regexpStart = new RegExp( + "^(\\s*)(?:" + lang.escapeRegExp(lineCommentStart) + ")" + ); + var regexpEnd = new RegExp( + "(?:" + lang.escapeRegExp(lineCommentEnd) + ")\\s*$" + ); + + var comment = function(line, i) { + if (testRemove(line, i)) return; + if (!ignoreBlankLines || /\S/.test(line)) { + doc.insertInLine({ row: i, column: line.length }, lineCommentEnd); + doc.insertInLine({ row: i, column: minIndent }, lineCommentStart); + } + }; + + var uncomment = function(line, i) { + var m; + if ((m = line.match(regexpEnd))) + doc.removeInLine(i, line.length - m[0].length, line.length); + if ((m = line.match(regexpStart))) + doc.removeInLine(i, m[1].length, m[0].length); + }; + + var testRemove = function(line, row) { + if (regexpStart.test(line)) return true; + var tokens = session.getTokens(row); + for (var i = 0; i < tokens.length; i++) { + if (tokens[i].type === "comment") return true; + } + }; + } else { + if (Array.isArray(this.lineCommentStart)) { + var regexpStart = this.lineCommentStart + .map(lang.escapeRegExp) + .join("|"); + var lineCommentStart = this.lineCommentStart[0]; + } else { + var regexpStart = lang.escapeRegExp(this.lineCommentStart); + var lineCommentStart = this.lineCommentStart; + } + regexpStart = new RegExp("^(\\s*)(?:" + regexpStart + ") ?"); + + insertAtTabStop = session.getUseSoftTabs(); + + var uncomment = function(line, i) { + var m = line.match(regexpStart); + if (!m) return; + var start = m[1].length, + end = m[0].length; + if (!shouldInsertSpace(line, start, end) && m[0][end - 1] == " ") + end--; + doc.removeInLine(i, start, end); + }; + var commentWithSpace = lineCommentStart + " "; + var comment = function(line, i) { + if (!ignoreBlankLines || /\S/.test(line)) { + if (shouldInsertSpace(line, minIndent, minIndent)) + doc.insertInLine( + { row: i, column: minIndent }, + commentWithSpace + ); + else + doc.insertInLine( + { row: i, column: minIndent }, + lineCommentStart + ); + } + }; + var testRemove = function(line, i) { + return regexpStart.test(line); + }; + + var shouldInsertSpace = function(line, before, after) { + var spaces = 0; + while (before-- && line.charAt(before) == " ") spaces++; + if (spaces % tabSize != 0) return false; + var spaces = 0; + while (line.charAt(after++) == " ") spaces++; + if (tabSize > 2) return spaces % tabSize != tabSize - 1; + else return spaces % tabSize == 0; + }; + } + + function iter(fun) { + for (var i = startRow; i <= endRow; i++) fun(doc.getLine(i), i); + } + + var minEmptyLength = Infinity; + iter(function(line, i) { + var indent = line.search(/\S/); + if (indent !== -1) { + if (indent < minIndent) minIndent = indent; + if (shouldRemove && !testRemove(line, i)) shouldRemove = false; + } else if (minEmptyLength > line.length) { + minEmptyLength = line.length; + } + }); + + if (minIndent == Infinity) { + minIndent = minEmptyLength; + ignoreBlankLines = false; + shouldRemove = false; + } + + if (insertAtTabStop && minIndent % tabSize != 0) + minIndent = Math.floor(minIndent / tabSize) * tabSize; + + iter(shouldRemove ? uncomment : comment); + }; + + this.toggleBlockComment = function(state, session, range, cursor) { + var comment = this.blockComment; + if (!comment) return; + if (!comment.start && comment[0]) comment = comment[0]; + + var iterator = new TokenIterator(session, cursor.row, cursor.column); + var token = iterator.getCurrentToken(); + + var sel = session.selection; + var initialRange = session.selection.toOrientedRange(); + var startRow, colDiff; + + if (token && /comment/.test(token.type)) { + var startRange, endRange; + while (token && /comment/.test(token.type)) { + var i = token.value.indexOf(comment.start); + if (i != -1) { + var row = iterator.getCurrentTokenRow(); + var column = iterator.getCurrentTokenColumn() + i; + startRange = new Range( + row, + column, + row, + column + comment.start.length + ); + break; + } + token = iterator.stepBackward(); + } + + var iterator = new TokenIterator(session, cursor.row, cursor.column); + var token = iterator.getCurrentToken(); + while (token && /comment/.test(token.type)) { + var i = token.value.indexOf(comment.end); + if (i != -1) { + var row = iterator.getCurrentTokenRow(); + var column = iterator.getCurrentTokenColumn() + i; + endRange = new Range( + row, + column, + row, + column + comment.end.length + ); + break; + } + token = iterator.stepForward(); + } + if (endRange) session.remove(endRange); + if (startRange) { + session.remove(startRange); + startRow = startRange.start.row; + colDiff = -comment.start.length; + } + } else { + colDiff = comment.start.length; + startRow = range.start.row; + session.insert(range.end, comment.end); + session.insert(range.start, comment.start); + } + if (initialRange.start.row == startRow) + initialRange.start.column += colDiff; + if (initialRange.end.row == startRow) + initialRange.end.column += colDiff; + session.selection.fromOrientedRange(initialRange); + }; + + this.getNextLineIndent = function(state, line, tab) { + return this.$getIndent(line); + }; + + this.checkOutdent = function(state, line, input) { + return false; + }; + + this.autoOutdent = function(state, doc, row) {}; + + this.$getIndent = function(line) { + return line.match(/^\s*/)[0]; + }; + + this.createWorker = function(session) { + return null; + }; + + this.createModeDelegates = function(mapping) { + this.$embeds = []; + this.$modes = {}; + for (var i in mapping) { + if (mapping[i]) { + var Mode = mapping[i]; + var id = Mode.prototype.$id; + var mode = config.$modes[id]; + if (!mode) config.$modes[id] = mode = new Mode(); + if (!config.$modes[i]) config.$modes[i] = mode; + this.$embeds.push(i); + this.$modes[i] = mode; + } + } + + var delegations = [ + "toggleBlockComment", + "toggleCommentLines", + "getNextLineIndent", + "checkOutdent", + "autoOutdent", + "transformAction", + "getCompletions" + ]; + + for (var i = 0; i < delegations.length; i++) { + (function(scope) { + var functionName = delegations[i]; + var defaultHandler = scope[functionName]; + scope[delegations[i]] = function() { + return this.$delegator(functionName, arguments, defaultHandler); + }; + })(this); + } + }; + + this.$delegator = function(method, args, defaultHandler) { + var state = args[0] || "start"; + if (typeof state != "string") { + if (Array.isArray(state[2])) { + var language = state[2][state[2].length - 1]; + var mode = this.$modes[language]; + if (mode) + return mode[method].apply( + mode, + [state[1]].concat([].slice.call(args, 1)) + ); + } + state = state[0] || "start"; + } + + for (var i = 0; i < this.$embeds.length; i++) { + if (!this.$modes[this.$embeds[i]]) continue; + + var split = state.split(this.$embeds[i]); + if (!split[0] && split[1]) { + args[0] = split[1]; + var mode = this.$modes[this.$embeds[i]]; + return mode[method].apply(mode, args); + } + } + var ret = defaultHandler.apply(this, args); + return defaultHandler ? ret : undefined; + }; + + this.transformAction = function(state, action, editor, session, param) { + if (this.$behaviour) { + var behaviours = this.$behaviour.getBehaviours(); + for (var key in behaviours) { + if (behaviours[key][action]) { + var ret = behaviours[key][action].apply(this, arguments); + if (ret) { + return ret; + } + } + } + } + }; + + this.getKeywords = function(append) { + if (!this.completionKeywords) { + var rules = this.$tokenizer.rules; + var completionKeywords = []; + for (var rule in rules) { + var ruleItr = rules[rule]; + for (var r = 0, l = ruleItr.length; r < l; r++) { + if (typeof ruleItr[r].token === "string") { + if (/keyword|support|storage/.test(ruleItr[r].token)) + completionKeywords.push(ruleItr[r].regex); + } else if (typeof ruleItr[r].token === "object") { + for ( + var a = 0, aLength = ruleItr[r].token.length; + a < aLength; + a++ + ) { + if (/keyword|support|storage/.test(ruleItr[r].token[a])) { + var rule = ruleItr[r].regex.match(/\(.+?\)/g)[a]; + completionKeywords.push(rule.substr(1, rule.length - 2)); + } + } + } + } + } + this.completionKeywords = completionKeywords; + } + if (!append) return this.$keywordList; + return completionKeywords.concat(this.$keywordList || []); + }; + + this.$createKeywordList = function() { + if (!this.$highlightRules) this.getTokenizer(); + return (this.$keywordList = this.$highlightRules.$keywordList || []); + }; + + this.getCompletions = function(state, session, pos, prefix) { + var keywords = this.$keywordList || this.$createKeywordList(); + return keywords.map(function(word) { + return { + name: word, + value: word, + score: 0, + meta: "keyword" + }; + }); + }; + + this.$id = "ace/mode/text"; + }.call(Mode.prototype)); + + exports.Mode = Mode; + } +); + +ace.define("ace/apply_delta", ["require", "exports", "module"], function( + require, + exports, + module +) { + "use strict"; + + function throwDeltaError(delta, errorText) { + console.log("Invalid Delta:", delta); + throw "Invalid Delta: " + errorText; + } + + function positionInDocument(docLines, position) { + return ( + position.row >= 0 && + position.row < docLines.length && + position.column >= 0 && + position.column <= docLines[position.row].length + ); + } + + function validateDelta(docLines, delta) { + if (delta.action != "insert" && delta.action != "remove") + throwDeltaError(delta, "delta.action must be 'insert' or 'remove'"); + if (!(delta.lines instanceof Array)) + throwDeltaError(delta, "delta.lines must be an Array"); + if (!delta.start || !delta.end) + throwDeltaError(delta, "delta.start/end must be an present"); + var start = delta.start; + if (!positionInDocument(docLines, delta.start)) + throwDeltaError(delta, "delta.start must be contained in document"); + var end = delta.end; + if (delta.action == "remove" && !positionInDocument(docLines, end)) + throwDeltaError( + delta, + "delta.end must contained in document for 'remove' actions" + ); + var numRangeRows = end.row - start.row; + var numRangeLastLineChars = + end.column - (numRangeRows == 0 ? start.column : 0); + if ( + numRangeRows != delta.lines.length - 1 || + delta.lines[numRangeRows].length != numRangeLastLineChars + ) + throwDeltaError(delta, "delta.range must match delta lines"); + } + + exports.applyDelta = function(docLines, delta, doNotValidate) { + var row = delta.start.row; + var startColumn = delta.start.column; + var line = docLines[row] || ""; + switch (delta.action) { + case "insert": + var lines = delta.lines; + if (lines.length === 1) { + docLines[row] = + line.substring(0, startColumn) + + delta.lines[0] + + line.substring(startColumn); + } else { + var args = [row, 1].concat(delta.lines); + docLines.splice.apply(docLines, args); + docLines[row] = line.substring(0, startColumn) + docLines[row]; + docLines[row + delta.lines.length - 1] += line.substring(startColumn); + } + break; + case "remove": + var endColumn = delta.end.column; + var endRow = delta.end.row; + if (row === endRow) { + docLines[row] = + line.substring(0, startColumn) + line.substring(endColumn); + } else { + docLines.splice( + row, + endRow - row + 1, + line.substring(0, startColumn) + + docLines[endRow].substring(endColumn) + ); + } + break; + } + }; +}); + +ace.define( + "ace/anchor", + ["require", "exports", "module", "ace/lib/oop", "ace/lib/event_emitter"], + function(require, exports, module) { + "use strict"; + + var oop = require("./lib/oop"); + var EventEmitter = require("./lib/event_emitter").EventEmitter; + + var Anchor = (exports.Anchor = function(doc, row, column) { + this.$onChange = this.onChange.bind(this); + this.attach(doc); + + if (typeof column == "undefined") this.setPosition(row.row, row.column); + else this.setPosition(row, column); + }); + + (function() { + oop.implement(this, EventEmitter); + this.getPosition = function() { + return this.$clipPositionToDocument(this.row, this.column); + }; + this.getDocument = function() { + return this.document; + }; + this.$insertRight = false; + this.onChange = function(delta) { + if (delta.start.row == delta.end.row && delta.start.row != this.row) + return; + + if (delta.start.row > this.row) return; + + var point = $getTransformedPoint( + delta, + { row: this.row, column: this.column }, + this.$insertRight + ); + this.setPosition(point.row, point.column, true); + }; + + function $pointsInOrder(point1, point2, equalPointsInOrder) { + var bColIsAfter = equalPointsInOrder + ? point1.column <= point2.column + : point1.column < point2.column; + return ( + point1.row < point2.row || (point1.row == point2.row && bColIsAfter) + ); + } + + function $getTransformedPoint(delta, point, moveIfEqual) { + var deltaIsInsert = delta.action == "insert"; + var deltaRowShift = + (deltaIsInsert ? 1 : -1) * (delta.end.row - delta.start.row); + var deltaColShift = + (deltaIsInsert ? 1 : -1) * (delta.end.column - delta.start.column); + var deltaStart = delta.start; + var deltaEnd = deltaIsInsert ? deltaStart : delta.end; // Collapse insert range. + if ($pointsInOrder(point, deltaStart, moveIfEqual)) { + return { + row: point.row, + column: point.column + }; + } + if ($pointsInOrder(deltaEnd, point, !moveIfEqual)) { + return { + row: point.row + deltaRowShift, + column: + point.column + (point.row == deltaEnd.row ? deltaColShift : 0) + }; + } + + return { + row: deltaStart.row, + column: deltaStart.column + }; + } + this.setPosition = function(row, column, noClip) { + var pos; + if (noClip) { + pos = { + row: row, + column: column + }; + } else { + pos = this.$clipPositionToDocument(row, column); + } + + if (this.row == pos.row && this.column == pos.column) return; + + var old = { + row: this.row, + column: this.column + }; + + this.row = pos.row; + this.column = pos.column; + this._signal("change", { + old: old, + value: pos + }); + }; + this.detach = function() { + this.document.off("change", this.$onChange); + }; + this.attach = function(doc) { + this.document = doc || this.document; + this.document.on("change", this.$onChange); + }; + this.$clipPositionToDocument = function(row, column) { + var pos = {}; + + if (row >= this.document.getLength()) { + pos.row = Math.max(0, this.document.getLength() - 1); + pos.column = this.document.getLine(pos.row).length; + } else if (row < 0) { + pos.row = 0; + pos.column = 0; + } else { + pos.row = row; + pos.column = Math.min( + this.document.getLine(pos.row).length, + Math.max(0, column) + ); + } + + if (column < 0) pos.column = 0; + + return pos; + }; + }.call(Anchor.prototype)); + } +); + +ace.define( + "ace/document", + [ + "require", + "exports", + "module", + "ace/lib/oop", + "ace/apply_delta", + "ace/lib/event_emitter", + "ace/range", + "ace/anchor" + ], + function(require, exports, module) { + "use strict"; + + var oop = require("./lib/oop"); + var applyDelta = require("./apply_delta").applyDelta; + var EventEmitter = require("./lib/event_emitter").EventEmitter; + var Range = require("./range").Range; + var Anchor = require("./anchor").Anchor; + + var Document = function(textOrLines) { + this.$lines = [""]; + if (textOrLines.length === 0) { + this.$lines = [""]; + } else if (Array.isArray(textOrLines)) { + this.insertMergedLines({ row: 0, column: 0 }, textOrLines); + } else { + this.insert({ row: 0, column: 0 }, textOrLines); + } + }; + + (function() { + oop.implement(this, EventEmitter); + this.setValue = function(text) { + var len = this.getLength() - 1; + this.remove(new Range(0, 0, len, this.getLine(len).length)); + this.insert({ row: 0, column: 0 }, text); + }; + this.getValue = function() { + return this.getAllLines().join(this.getNewLineCharacter()); + }; + this.createAnchor = function(row, column) { + return new Anchor(this, row, column); + }; + if ("aaa".split(/a/).length === 0) { + this.$split = function(text) { + return text.replace(/\r\n|\r/g, "\n").split("\n"); + }; + } else { + this.$split = function(text) { + return text.split(/\r\n|\r|\n/); + }; + } + + this.$detectNewLine = function(text) { + var match = text.match(/^.*?(\r\n|\r|\n)/m); + this.$autoNewLine = match ? match[1] : "\n"; + this._signal("changeNewLineMode"); + }; + this.getNewLineCharacter = function() { + switch (this.$newLineMode) { + case "windows": + return "\r\n"; + case "unix": + return "\n"; + default: + return this.$autoNewLine || "\n"; + } + }; + + this.$autoNewLine = ""; + this.$newLineMode = "auto"; + this.setNewLineMode = function(newLineMode) { + if (this.$newLineMode === newLineMode) return; + + this.$newLineMode = newLineMode; + this._signal("changeNewLineMode"); + }; + this.getNewLineMode = function() { + return this.$newLineMode; + }; + this.isNewLine = function(text) { + return text == "\r\n" || text == "\r" || text == "\n"; + }; + this.getLine = function(row) { + return this.$lines[row] || ""; + }; + this.getLines = function(firstRow, lastRow) { + return this.$lines.slice(firstRow, lastRow + 1); + }; + this.getAllLines = function() { + return this.getLines(0, this.getLength()); + }; + this.getLength = function() { + return this.$lines.length; + }; + this.getTextRange = function(range) { + return this.getLinesForRange(range).join(this.getNewLineCharacter()); + }; + this.getLinesForRange = function(range) { + var lines; + if (range.start.row === range.end.row) { + lines = [ + this.getLine(range.start.row).substring( + range.start.column, + range.end.column + ) + ]; + } else { + lines = this.getLines(range.start.row, range.end.row); + lines[0] = (lines[0] || "").substring(range.start.column); + var l = lines.length - 1; + if (range.end.row - range.start.row == l) + lines[l] = lines[l].substring(0, range.end.column); + } + return lines; + }; + this.insertLines = function(row, lines) { + console.warn( + "Use of document.insertLines is deprecated. Use the insertFullLines method instead." + ); + return this.insertFullLines(row, lines); + }; + this.removeLines = function(firstRow, lastRow) { + console.warn( + "Use of document.removeLines is deprecated. Use the removeFullLines method instead." + ); + return this.removeFullLines(firstRow, lastRow); + }; + this.insertNewLine = function(position) { + console.warn( + "Use of document.insertNewLine is deprecated. Use insertMergedLines(position, ['', '']) instead." + ); + return this.insertMergedLines(position, ["", ""]); + }; + this.insert = function(position, text) { + if (this.getLength() <= 1) this.$detectNewLine(text); + + return this.insertMergedLines(position, this.$split(text)); + }; + this.insertInLine = function(position, text) { + var start = this.clippedPos(position.row, position.column); + var end = this.pos(position.row, position.column + text.length); + + this.applyDelta( + { + start: start, + end: end, + action: "insert", + lines: [text] + }, + true + ); + + return this.clonePos(end); + }; + + this.clippedPos = function(row, column) { + var length = this.getLength(); + if (row === undefined) { + row = length; + } else if (row < 0) { + row = 0; + } else if (row >= length) { + row = length - 1; + column = undefined; + } + var line = this.getLine(row); + if (column == undefined) column = line.length; + column = Math.min(Math.max(column, 0), line.length); + return { row: row, column: column }; + }; + + this.clonePos = function(pos) { + return { row: pos.row, column: pos.column }; + }; + + this.pos = function(row, column) { + return { row: row, column: column }; + }; + + this.$clipPosition = function(position) { + var length = this.getLength(); + if (position.row >= length) { + position.row = Math.max(0, length - 1); + position.column = this.getLine(length - 1).length; + } else { + position.row = Math.max(0, position.row); + position.column = Math.min( + Math.max(position.column, 0), + this.getLine(position.row).length + ); + } + return position; + }; + this.insertFullLines = function(row, lines) { + row = Math.min(Math.max(row, 0), this.getLength()); + var column = 0; + if (row < this.getLength()) { + lines = lines.concat([""]); + column = 0; + } else { + lines = [""].concat(lines); + row--; + column = this.$lines[row].length; + } + this.insertMergedLines({ row: row, column: column }, lines); + }; + this.insertMergedLines = function(position, lines) { + var start = this.clippedPos(position.row, position.column); + var end = { + row: start.row + lines.length - 1, + column: + (lines.length == 1 ? start.column : 0) + + lines[lines.length - 1].length + }; + + this.applyDelta({ + start: start, + end: end, + action: "insert", + lines: lines + }); + + return this.clonePos(end); + }; + this.remove = function(range) { + var start = this.clippedPos(range.start.row, range.start.column); + var end = this.clippedPos(range.end.row, range.end.column); + this.applyDelta({ + start: start, + end: end, + action: "remove", + lines: this.getLinesForRange({ start: start, end: end }) + }); + return this.clonePos(start); + }; + this.removeInLine = function(row, startColumn, endColumn) { + var start = this.clippedPos(row, startColumn); + var end = this.clippedPos(row, endColumn); + + this.applyDelta( + { + start: start, + end: end, + action: "remove", + lines: this.getLinesForRange({ start: start, end: end }) + }, + true + ); + + return this.clonePos(start); + }; + this.removeFullLines = function(firstRow, lastRow) { + firstRow = Math.min(Math.max(0, firstRow), this.getLength() - 1); + lastRow = Math.min(Math.max(0, lastRow), this.getLength() - 1); + var deleteFirstNewLine = + lastRow == this.getLength() - 1 && firstRow > 0; + var deleteLastNewLine = lastRow < this.getLength() - 1; + var startRow = deleteFirstNewLine ? firstRow - 1 : firstRow; + var startCol = deleteFirstNewLine ? this.getLine(startRow).length : 0; + var endRow = deleteLastNewLine ? lastRow + 1 : lastRow; + var endCol = deleteLastNewLine ? 0 : this.getLine(endRow).length; + var range = new Range(startRow, startCol, endRow, endCol); + var deletedLines = this.$lines.slice(firstRow, lastRow + 1); + + this.applyDelta({ + start: range.start, + end: range.end, + action: "remove", + lines: this.getLinesForRange(range) + }); + return deletedLines; + }; + this.removeNewLine = function(row) { + if (row < this.getLength() - 1 && row >= 0) { + this.applyDelta({ + start: this.pos(row, this.getLine(row).length), + end: this.pos(row + 1, 0), + action: "remove", + lines: ["", ""] + }); + } + }; + this.replace = function(range, text) { + if (!(range instanceof Range)) + range = Range.fromPoints(range.start, range.end); + if (text.length === 0 && range.isEmpty()) return range.start; + if (text == this.getTextRange(range)) return range.end; + + this.remove(range); + var end; + if (text) { + end = this.insert(range.start, text); + } else { + end = range.start; + } + + return end; + }; + this.applyDeltas = function(deltas) { + for (var i = 0; i < deltas.length; i++) { + this.applyDelta(deltas[i]); + } + }; + this.revertDeltas = function(deltas) { + for (var i = deltas.length - 1; i >= 0; i--) { + this.revertDelta(deltas[i]); + } + }; + this.applyDelta = function(delta, doNotValidate) { + var isInsert = delta.action == "insert"; + if ( + isInsert + ? delta.lines.length <= 1 && !delta.lines[0] + : !Range.comparePoints(delta.start, delta.end) + ) { + return; + } + + if (isInsert && delta.lines.length > 20000) { + this.$splitAndapplyLargeDelta(delta, 20000); + } else { + applyDelta(this.$lines, delta, doNotValidate); + this._signal("change", delta); + } + }; + + this.$safeApplyDelta = function(delta) { + var docLength = this.$lines.length; + if ( + (delta.action == "remove" && + delta.start.row < docLength && + delta.end.row < docLength) || + (delta.action == "insert" && delta.start.row <= docLength) + ) { + this.applyDelta(delta); + } + }; + + this.$splitAndapplyLargeDelta = function(delta, MAX) { + var lines = delta.lines; + var l = lines.length - MAX + 1; + var row = delta.start.row; + var column = delta.start.column; + for (var from = 0, to = 0; from < l; from = to) { + to += MAX - 1; + var chunk = lines.slice(from, to); + chunk.push(""); + this.applyDelta( + { + start: this.pos(row + from, column), + end: this.pos(row + to, (column = 0)), + action: delta.action, + lines: chunk + }, + true + ); + } + delta.lines = lines.slice(from); + delta.start.row = row + from; + delta.start.column = column; + this.applyDelta(delta, true); + }; + this.revertDelta = function(delta) { + this.$safeApplyDelta({ + start: this.clonePos(delta.start), + end: this.clonePos(delta.end), + action: delta.action == "insert" ? "remove" : "insert", + lines: delta.lines.slice() + }); + }; + this.indexToPosition = function(index, startRow) { + var lines = this.$lines || this.getAllLines(); + var newlineLength = this.getNewLineCharacter().length; + for (var i = startRow || 0, l = lines.length; i < l; i++) { + index -= lines[i].length + newlineLength; + if (index < 0) + return { row: i, column: index + lines[i].length + newlineLength }; + } + return { + row: l - 1, + column: index + lines[l - 1].length + newlineLength + }; + }; + this.positionToIndex = function(pos, startRow) { + var lines = this.$lines || this.getAllLines(); + var newlineLength = this.getNewLineCharacter().length; + var index = 0; + var row = Math.min(pos.row, lines.length); + for (var i = startRow || 0; i < row; ++i) + index += lines[i].length + newlineLength; + + return index + pos.column; + }; + }.call(Document.prototype)); + + exports.Document = Document; + } +); + +ace.define( + "ace/background_tokenizer", + ["require", "exports", "module", "ace/lib/oop", "ace/lib/event_emitter"], + function(require, exports, module) { + "use strict"; + + var oop = require("./lib/oop"); + var EventEmitter = require("./lib/event_emitter").EventEmitter; + + var BackgroundTokenizer = function(tokenizer, editor) { + this.running = false; + this.lines = []; + this.states = []; + this.currentLine = 0; + this.tokenizer = tokenizer; + + var self = this; + + this.$worker = function() { + if (!self.running) { + return; + } + + var workerStart = new Date(); + var currentLine = self.currentLine; + var endLine = -1; + var doc = self.doc; + + var startLine = currentLine; + while (self.lines[currentLine]) currentLine++; + + var len = doc.getLength(); + var processedLines = 0; + self.running = false; + while (currentLine < len) { + self.$tokenizeRow(currentLine); + endLine = currentLine; + do { + currentLine++; + } while (self.lines[currentLine]); + processedLines++; + if (processedLines % 5 === 0 && new Date() - workerStart > 20) { + self.running = setTimeout(self.$worker, 20); + break; + } + } + self.currentLine = currentLine; + + if (endLine == -1) endLine = currentLine; + + if (startLine <= endLine) self.fireUpdateEvent(startLine, endLine); + }; + }; + + (function() { + oop.implement(this, EventEmitter); + this.setTokenizer = function(tokenizer) { + this.tokenizer = tokenizer; + this.lines = []; + this.states = []; + + this.start(0); + }; + this.setDocument = function(doc) { + this.doc = doc; + this.lines = []; + this.states = []; + + this.stop(); + }; + this.fireUpdateEvent = function(firstRow, lastRow) { + var data = { + first: firstRow, + last: lastRow + }; + this._signal("update", { data: data }); + }; + this.start = function(startRow) { + this.currentLine = Math.min( + startRow || 0, + this.currentLine, + this.doc.getLength() + ); + this.lines.splice(this.currentLine, this.lines.length); + this.states.splice(this.currentLine, this.states.length); + + this.stop(); + this.running = setTimeout(this.$worker, 700); + }; + + this.scheduleStart = function() { + if (!this.running) this.running = setTimeout(this.$worker, 700); + }; + + this.$updateOnChange = function(delta) { + var startRow = delta.start.row; + var len = delta.end.row - startRow; + + if (len === 0) { + this.lines[startRow] = null; + } else if (delta.action == "remove") { + this.lines.splice(startRow, len + 1, null); + this.states.splice(startRow, len + 1, null); + } else { + var args = Array(len + 1); + args.unshift(startRow, 1); + this.lines.splice.apply(this.lines, args); + this.states.splice.apply(this.states, args); + } + + this.currentLine = Math.min( + startRow, + this.currentLine, + this.doc.getLength() + ); + + this.stop(); + }; + this.stop = function() { + if (this.running) clearTimeout(this.running); + this.running = false; + }; + this.getTokens = function(row) { + return this.lines[row] || this.$tokenizeRow(row); + }; + this.getState = function(row) { + if (this.currentLine == row) this.$tokenizeRow(row); + return this.states[row] || "start"; + }; + + this.$tokenizeRow = function(row) { + var line = this.doc.getLine(row); + var state = this.states[row - 1]; + + var data = this.tokenizer.getLineTokens(line, state, row); + + if (this.states[row] + "" !== data.state + "") { + this.states[row] = data.state; + this.lines[row + 1] = null; + if (this.currentLine > row + 1) this.currentLine = row + 1; + } else if (this.currentLine == row) { + this.currentLine = row + 1; + } + + return (this.lines[row] = data.tokens); + }; + }.call(BackgroundTokenizer.prototype)); + + exports.BackgroundTokenizer = BackgroundTokenizer; + } +); + +ace.define( + "ace/search_highlight", + ["require", "exports", "module", "ace/lib/lang", "ace/lib/oop", "ace/range"], + function(require, exports, module) { + "use strict"; + + var lang = require("./lib/lang"); + var oop = require("./lib/oop"); + var Range = require("./range").Range; + + var SearchHighlight = function(regExp, clazz, type) { + this.setRegexp(regExp); + this.clazz = clazz; + this.type = type || "text"; + }; + + (function() { + this.MAX_RANGES = 500; + + this.setRegexp = function(regExp) { + if (this.regExp + "" == regExp + "") return; + this.regExp = regExp; + this.cache = []; + }; + + this.update = function(html, markerLayer, session, config) { + if (!this.regExp) return; + var start = config.firstRow, + end = config.lastRow; + + for (var i = start; i <= end; i++) { + var ranges = this.cache[i]; + if (ranges == null) { + ranges = lang.getMatchOffsets(session.getLine(i), this.regExp); + if (ranges.length > this.MAX_RANGES) + ranges = ranges.slice(0, this.MAX_RANGES); + ranges = ranges.map(function(match) { + return new Range(i, match.offset, i, match.offset + match.length); + }); + this.cache[i] = ranges.length ? ranges : ""; + } + + for (var j = ranges.length; j--; ) { + markerLayer.drawSingleLineMarker( + html, + ranges[j].toScreenRange(session), + this.clazz, + config + ); + } + } + }; + }.call(SearchHighlight.prototype)); + + exports.SearchHighlight = SearchHighlight; + } +); + +ace.define( + "ace/edit_session/fold_line", + ["require", "exports", "module", "ace/range"], + function(require, exports, module) { + "use strict"; + + var Range = require("../range").Range; + function FoldLine(foldData, folds) { + this.foldData = foldData; + if (Array.isArray(folds)) { + this.folds = folds; + } else { + folds = this.folds = [folds]; + } + + var last = folds[folds.length - 1]; + this.range = new Range( + folds[0].start.row, + folds[0].start.column, + last.end.row, + last.end.column + ); + this.start = this.range.start; + this.end = this.range.end; + + this.folds.forEach(function(fold) { + fold.setFoldLine(this); + }, this); + } + + (function() { + this.shiftRow = function(shift) { + this.start.row += shift; + this.end.row += shift; + this.folds.forEach(function(fold) { + fold.start.row += shift; + fold.end.row += shift; + }); + }; + + this.addFold = function(fold) { + if (fold.sameRow) { + if (fold.start.row < this.startRow || fold.endRow > this.endRow) { + throw new Error( + "Can't add a fold to this FoldLine as it has no connection" + ); + } + this.folds.push(fold); + this.folds.sort(function(a, b) { + return -a.range.compareEnd(b.start.row, b.start.column); + }); + if (this.range.compareEnd(fold.start.row, fold.start.column) > 0) { + this.end.row = fold.end.row; + this.end.column = fold.end.column; + } else if ( + this.range.compareStart(fold.end.row, fold.end.column) < 0 + ) { + this.start.row = fold.start.row; + this.start.column = fold.start.column; + } + } else if (fold.start.row == this.end.row) { + this.folds.push(fold); + this.end.row = fold.end.row; + this.end.column = fold.end.column; + } else if (fold.end.row == this.start.row) { + this.folds.unshift(fold); + this.start.row = fold.start.row; + this.start.column = fold.start.column; + } else { + throw new Error( + "Trying to add fold to FoldRow that doesn't have a matching row" + ); + } + fold.foldLine = this; + }; + + this.containsRow = function(row) { + return row >= this.start.row && row <= this.end.row; + }; + + this.walk = function(callback, endRow, endColumn) { + var lastEnd = 0, + folds = this.folds, + fold, + cmp, + stop, + isNewRow = true; + + if (endRow == null) { + endRow = this.end.row; + endColumn = this.end.column; + } + + for (var i = 0; i < folds.length; i++) { + fold = folds[i]; + + cmp = fold.range.compareStart(endRow, endColumn); + if (cmp == -1) { + callback(null, endRow, endColumn, lastEnd, isNewRow); + return; + } + + stop = callback( + null, + fold.start.row, + fold.start.column, + lastEnd, + isNewRow + ); + stop = + !stop && + callback( + fold.placeholder, + fold.start.row, + fold.start.column, + lastEnd + ); + if (stop || cmp === 0) { + return; + } + isNewRow = !fold.sameRow; + lastEnd = fold.end.column; + } + callback(null, endRow, endColumn, lastEnd, isNewRow); + }; + + this.getNextFoldTo = function(row, column) { + var fold, cmp; + for (var i = 0; i < this.folds.length; i++) { + fold = this.folds[i]; + cmp = fold.range.compareEnd(row, column); + if (cmp == -1) { + return { + fold: fold, + kind: "after" + }; + } else if (cmp === 0) { + return { + fold: fold, + kind: "inside" + }; + } + } + return null; + }; + + this.addRemoveChars = function(row, column, len) { + var ret = this.getNextFoldTo(row, column), + fold, + folds; + if (ret) { + fold = ret.fold; + if ( + ret.kind == "inside" && + fold.start.column != column && + fold.start.row != row + ) { + window.console && window.console.log(row, column, fold); + } else if (fold.start.row == row) { + folds = this.folds; + var i = folds.indexOf(fold); + if (i === 0) { + this.start.column += len; + } + for (i; i < folds.length; i++) { + fold = folds[i]; + fold.start.column += len; + if (!fold.sameRow) { + return; + } + fold.end.column += len; + } + this.end.column += len; + } + } + }; + + this.split = function(row, column) { + var pos = this.getNextFoldTo(row, column); + + if (!pos || pos.kind == "inside") return null; + + var fold = pos.fold; + var folds = this.folds; + var foldData = this.foldData; + + var i = folds.indexOf(fold); + var foldBefore = folds[i - 1]; + this.end.row = foldBefore.end.row; + this.end.column = foldBefore.end.column; + folds = folds.splice(i, folds.length - i); + + var newFoldLine = new FoldLine(foldData, folds); + foldData.splice(foldData.indexOf(this) + 1, 0, newFoldLine); + return newFoldLine; + }; + + this.merge = function(foldLineNext) { + var folds = foldLineNext.folds; + for (var i = 0; i < folds.length; i++) { + this.addFold(folds[i]); + } + var foldData = this.foldData; + foldData.splice(foldData.indexOf(foldLineNext), 1); + }; + + this.toString = function() { + var ret = [this.range.toString() + ": ["]; + + this.folds.forEach(function(fold) { + ret.push(" " + fold.toString()); + }); + ret.push("]"); + return ret.join("\n"); + }; + + this.idxToPosition = function(idx) { + var lastFoldEndColumn = 0; + + for (var i = 0; i < this.folds.length; i++) { + var fold = this.folds[i]; + + idx -= fold.start.column - lastFoldEndColumn; + if (idx < 0) { + return { + row: fold.start.row, + column: fold.start.column + idx + }; + } + + idx -= fold.placeholder.length; + if (idx < 0) { + return fold.start; + } + + lastFoldEndColumn = fold.end.column; + } + + return { + row: this.end.row, + column: this.end.column + idx + }; + }; + }.call(FoldLine.prototype)); + + exports.FoldLine = FoldLine; + } +); + +ace.define( + "ace/range_list", + ["require", "exports", "module", "ace/range"], + function(require, exports, module) { + "use strict"; + var Range = require("./range").Range; + var comparePoints = Range.comparePoints; + + var RangeList = function() { + this.ranges = []; + this.$bias = 1; + }; + + (function() { + this.comparePoints = comparePoints; + + this.pointIndex = function(pos, excludeEdges, startIndex) { + var list = this.ranges; + + for (var i = startIndex || 0; i < list.length; i++) { + var range = list[i]; + var cmpEnd = comparePoints(pos, range.end); + if (cmpEnd > 0) continue; + var cmpStart = comparePoints(pos, range.start); + if (cmpEnd === 0) return excludeEdges && cmpStart !== 0 ? -i - 2 : i; + if (cmpStart > 0 || (cmpStart === 0 && !excludeEdges)) return i; + + return -i - 1; + } + return -i - 1; + }; + + this.add = function(range) { + var excludeEdges = !range.isEmpty(); + var startIndex = this.pointIndex(range.start, excludeEdges); + if (startIndex < 0) startIndex = -startIndex - 1; + + var endIndex = this.pointIndex(range.end, excludeEdges, startIndex); + + if (endIndex < 0) endIndex = -endIndex - 1; + else endIndex++; + return this.ranges.splice(startIndex, endIndex - startIndex, range); + }; + + this.addList = function(list) { + var removed = []; + for (var i = list.length; i--; ) { + removed.push.apply(removed, this.add(list[i])); + } + return removed; + }; + + this.substractPoint = function(pos) { + var i = this.pointIndex(pos); + + if (i >= 0) return this.ranges.splice(i, 1); + }; + this.merge = function() { + var removed = []; + var list = this.ranges; + + list = list.sort(function(a, b) { + return comparePoints(a.start, b.start); + }); + + var next = list[0], + range; + for (var i = 1; i < list.length; i++) { + range = next; + next = list[i]; + var cmp = comparePoints(range.end, next.start); + if (cmp < 0) continue; + + if (cmp == 0 && !range.isEmpty() && !next.isEmpty()) continue; + + if (comparePoints(range.end, next.end) < 0) { + range.end.row = next.end.row; + range.end.column = next.end.column; + } + + list.splice(i, 1); + removed.push(next); + next = range; + i--; + } + + this.ranges = list; + + return removed; + }; + + this.contains = function(row, column) { + return this.pointIndex({ row: row, column: column }) >= 0; + }; + + this.containsPoint = function(pos) { + return this.pointIndex(pos) >= 0; + }; + + this.rangeAtPoint = function(pos) { + var i = this.pointIndex(pos); + if (i >= 0) return this.ranges[i]; + }; + + this.clipRows = function(startRow, endRow) { + var list = this.ranges; + if ( + list[0].start.row > endRow || + list[list.length - 1].start.row < startRow + ) + return []; + + var startIndex = this.pointIndex({ row: startRow, column: 0 }); + if (startIndex < 0) startIndex = -startIndex - 1; + var endIndex = this.pointIndex({ row: endRow, column: 0 }, startIndex); + if (endIndex < 0) endIndex = -endIndex - 1; + + var clipped = []; + for (var i = startIndex; i < endIndex; i++) { + clipped.push(list[i]); + } + return clipped; + }; + + this.removeAll = function() { + return this.ranges.splice(0, this.ranges.length); + }; + + this.attach = function(session) { + if (this.session) this.detach(); + + this.session = session; + this.onChange = this.$onChange.bind(this); + + this.session.on("change", this.onChange); + }; + + this.detach = function() { + if (!this.session) return; + this.session.removeListener("change", this.onChange); + this.session = null; + }; + + this.$onChange = function(delta) { + var start = delta.start; + var end = delta.end; + var startRow = start.row; + var endRow = end.row; + var ranges = this.ranges; + for (var i = 0, n = ranges.length; i < n; i++) { + var r = ranges[i]; + if (r.end.row >= startRow) break; + } + + if (delta.action == "insert") { + var lineDif = endRow - startRow; + var colDiff = -start.column + end.column; + for (; i < n; i++) { + var r = ranges[i]; + if (r.start.row > startRow) break; + + if (r.start.row == startRow && r.start.column >= start.column) { + if (r.start.column == start.column && this.$bias <= 0) { + } else { + r.start.column += colDiff; + r.start.row += lineDif; + } + } + if (r.end.row == startRow && r.end.column >= start.column) { + if (r.end.column == start.column && this.$bias < 0) { + continue; + } + if (r.end.column == start.column && colDiff > 0 && i < n - 1) { + if ( + r.end.column > r.start.column && + r.end.column == ranges[i + 1].start.column + ) + r.end.column -= colDiff; + } + r.end.column += colDiff; + r.end.row += lineDif; + } + } + } else { + var lineDif = startRow - endRow; + var colDiff = start.column - end.column; + for (; i < n; i++) { + var r = ranges[i]; + + if (r.start.row > endRow) break; + + if ( + r.end.row < endRow && + (startRow < r.end.row || + (startRow == r.end.row && start.column < r.end.column)) + ) { + r.end.row = startRow; + r.end.column = start.column; + } else if (r.end.row == endRow) { + if (r.end.column <= end.column) { + if (lineDif || r.end.column > start.column) { + r.end.column = start.column; + r.end.row = start.row; + } + } else { + r.end.column += colDiff; + r.end.row += lineDif; + } + } else if (r.end.row > endRow) { + r.end.row += lineDif; + } + + if ( + r.start.row < endRow && + (startRow < r.start.row || + (startRow == r.start.row && start.column < r.start.column)) + ) { + r.start.row = startRow; + r.start.column = start.column; + } else if (r.start.row == endRow) { + if (r.start.column <= end.column) { + if (lineDif || r.start.column > start.column) { + r.start.column = start.column; + r.start.row = start.row; + } + } else { + r.start.column += colDiff; + r.start.row += lineDif; + } + } else if (r.start.row > endRow) { + r.start.row += lineDif; + } + } + } + + if (lineDif != 0 && i < n) { + for (; i < n; i++) { + var r = ranges[i]; + r.start.row += lineDif; + r.end.row += lineDif; + } + } + }; + }.call(RangeList.prototype)); + + exports.RangeList = RangeList; + } +); + +ace.define( + "ace/edit_session/fold", + ["require", "exports", "module", "ace/range_list", "ace/lib/oop"], + function(require, exports, module) { + "use strict"; + + var RangeList = require("../range_list").RangeList; + var oop = require("../lib/oop"); + var Fold = (exports.Fold = function(range, placeholder) { + this.foldLine = null; + this.placeholder = placeholder; + this.range = range; + this.start = range.start; + this.end = range.end; + + this.sameRow = range.start.row == range.end.row; + this.subFolds = this.ranges = []; + }); + + oop.inherits(Fold, RangeList); + + (function() { + this.toString = function() { + return '"' + this.placeholder + '" ' + this.range.toString(); + }; + + this.setFoldLine = function(foldLine) { + this.foldLine = foldLine; + this.subFolds.forEach(function(fold) { + fold.setFoldLine(foldLine); + }); + }; + + this.clone = function() { + var range = this.range.clone(); + var fold = new Fold(range, this.placeholder); + this.subFolds.forEach(function(subFold) { + fold.subFolds.push(subFold.clone()); + }); + fold.collapseChildren = this.collapseChildren; + return fold; + }; + + this.addSubFold = function(fold) { + if (this.range.isEqual(fold)) return; + consumeRange(fold, this.start); + + var row = fold.start.row, + column = fold.start.column; + for (var i = 0, cmp = -1; i < this.subFolds.length; i++) { + cmp = this.subFolds[i].range.compare(row, column); + if (cmp != 1) break; + } + var afterStart = this.subFolds[i]; + var firstConsumed = 0; + + if (cmp == 0) { + if (afterStart.range.containsRange(fold)) + return afterStart.addSubFold(fold); + else firstConsumed = 1; + } + var row = fold.range.end.row, + column = fold.range.end.column; + for (var j = i, cmp = -1; j < this.subFolds.length; j++) { + cmp = this.subFolds[j].range.compare(row, column); + if (cmp != 1) break; + } + if (cmp == 0) j++; + var consumedFolds = this.subFolds.splice(i, j - i, fold); + var last = cmp == 0 ? consumedFolds.length - 1 : consumedFolds.length; + for (var k = firstConsumed; k < last; k++) { + fold.addSubFold(consumedFolds[k]); + } + fold.setFoldLine(this.foldLine); + + return fold; + }; + + this.restoreRange = function(range) { + return restoreRange(range, this.start); + }; + }.call(Fold.prototype)); + + function consumePoint(point, anchor) { + point.row -= anchor.row; + if (point.row == 0) point.column -= anchor.column; + } + function consumeRange(range, anchor) { + consumePoint(range.start, anchor); + consumePoint(range.end, anchor); + } + function restorePoint(point, anchor) { + if (point.row == 0) point.column += anchor.column; + point.row += anchor.row; + } + function restoreRange(range, anchor) { + restorePoint(range.start, anchor); + restorePoint(range.end, anchor); + } + } +); + +ace.define( + "ace/edit_session/folding", + [ + "require", + "exports", + "module", + "ace/range", + "ace/edit_session/fold_line", + "ace/edit_session/fold", + "ace/token_iterator" + ], + function(require, exports, module) { + "use strict"; + + var Range = require("../range").Range; + var FoldLine = require("./fold_line").FoldLine; + var Fold = require("./fold").Fold; + var TokenIterator = require("../token_iterator").TokenIterator; + + function Folding() { + this.getFoldAt = function(row, column, side) { + var foldLine = this.getFoldLine(row); + if (!foldLine) return null; + + var folds = foldLine.folds; + for (var i = 0; i < folds.length; i++) { + var range = folds[i].range; + if (range.contains(row, column)) { + if (side == 1 && range.isEnd(row, column) && !range.isEmpty()) { + continue; + } else if ( + side == -1 && + range.isStart(row, column) && + !range.isEmpty() + ) { + continue; + } + return folds[i]; + } + } + }; + this.getFoldsInRange = function(range) { + var start = range.start; + var end = range.end; + var foldLines = this.$foldData; + var foundFolds = []; + + start.column += 1; + end.column -= 1; + + for (var i = 0; i < foldLines.length; i++) { + var cmp = foldLines[i].range.compareRange(range); + if (cmp == 2) { + continue; + } else if (cmp == -2) { + break; + } + + var folds = foldLines[i].folds; + for (var j = 0; j < folds.length; j++) { + var fold = folds[j]; + cmp = fold.range.compareRange(range); + if (cmp == -2) { + break; + } else if (cmp == 2) { + continue; + } else if (cmp == 42) { + break; + } + foundFolds.push(fold); + } + } + start.column -= 1; + end.column += 1; + + return foundFolds; + }; + + this.getFoldsInRangeList = function(ranges) { + if (Array.isArray(ranges)) { + var folds = []; + ranges.forEach(function(range) { + folds = folds.concat(this.getFoldsInRange(range)); + }, this); + } else { + var folds = this.getFoldsInRange(ranges); + } + return folds; + }; + this.getAllFolds = function() { + var folds = []; + var foldLines = this.$foldData; + + for (var i = 0; i < foldLines.length; i++) + for (var j = 0; j < foldLines[i].folds.length; j++) + folds.push(foldLines[i].folds[j]); + + return folds; + }; + this.getFoldStringAt = function(row, column, trim, foldLine) { + foldLine = foldLine || this.getFoldLine(row); + if (!foldLine) return null; + + var lastFold = { + end: { column: 0 } + }; + var str, fold; + for (var i = 0; i < foldLine.folds.length; i++) { + fold = foldLine.folds[i]; + var cmp = fold.range.compareEnd(row, column); + if (cmp == -1) { + str = this.getLine(fold.start.row).substring( + lastFold.end.column, + fold.start.column + ); + break; + } else if (cmp === 0) { + return null; + } + lastFold = fold; + } + if (!str) + str = this.getLine(fold.start.row).substring(lastFold.end.column); + + if (trim == -1) return str.substring(0, column - lastFold.end.column); + else if (trim == 1) return str.substring(column - lastFold.end.column); + else return str; + }; + + this.getFoldLine = function(docRow, startFoldLine) { + var foldData = this.$foldData; + var i = 0; + if (startFoldLine) i = foldData.indexOf(startFoldLine); + if (i == -1) i = 0; + for (i; i < foldData.length; i++) { + var foldLine = foldData[i]; + if (foldLine.start.row <= docRow && foldLine.end.row >= docRow) { + return foldLine; + } else if (foldLine.end.row > docRow) { + return null; + } + } + return null; + }; + this.getNextFoldLine = function(docRow, startFoldLine) { + var foldData = this.$foldData; + var i = 0; + if (startFoldLine) i = foldData.indexOf(startFoldLine); + if (i == -1) i = 0; + for (i; i < foldData.length; i++) { + var foldLine = foldData[i]; + if (foldLine.end.row >= docRow) { + return foldLine; + } + } + return null; + }; + + this.getFoldedRowCount = function(first, last) { + var foldData = this.$foldData, + rowCount = last - first + 1; + for (var i = 0; i < foldData.length; i++) { + var foldLine = foldData[i], + end = foldLine.end.row, + start = foldLine.start.row; + if (end >= last) { + if (start < last) { + if (start >= first) rowCount -= last - start; + else rowCount = 0; // in one fold + } + break; + } else if (end >= first) { + if (start >= first) + // fold inside range + rowCount -= end - start; + else rowCount -= end - first + 1; + } + } + return rowCount; + }; + + this.$addFoldLine = function(foldLine) { + this.$foldData.push(foldLine); + this.$foldData.sort(function(a, b) { + return a.start.row - b.start.row; + }); + return foldLine; + }; + this.addFold = function(placeholder, range) { + var foldData = this.$foldData; + var added = false; + var fold; + + if (placeholder instanceof Fold) fold = placeholder; + else { + fold = new Fold(range, placeholder); + fold.collapseChildren = range.collapseChildren; + } + this.$clipRangeToDocument(fold.range); + + var startRow = fold.start.row; + var startColumn = fold.start.column; + var endRow = fold.end.row; + var endColumn = fold.end.column; + + var startFold = this.getFoldAt(startRow, startColumn, 1); + var endFold = this.getFoldAt(endRow, endColumn, -1); + if (startFold && endFold == startFold) + return startFold.addSubFold(fold); + + if (startFold && !startFold.range.isStart(startRow, startColumn)) + this.removeFold(startFold); + + if (endFold && !endFold.range.isEnd(endRow, endColumn)) + this.removeFold(endFold); + var folds = this.getFoldsInRange(fold.range); + if (folds.length > 0) { + this.removeFolds(folds); + folds.forEach(function(subFold) { + fold.addSubFold(subFold); + }); + } + + for (var i = 0; i < foldData.length; i++) { + var foldLine = foldData[i]; + if (endRow == foldLine.start.row) { + foldLine.addFold(fold); + added = true; + break; + } else if (startRow == foldLine.end.row) { + foldLine.addFold(fold); + added = true; + if (!fold.sameRow) { + var foldLineNext = foldData[i + 1]; + if (foldLineNext && foldLineNext.start.row == endRow) { + foldLine.merge(foldLineNext); + break; + } + } + break; + } else if (endRow <= foldLine.start.row) { + break; + } + } + + if (!added) + foldLine = this.$addFoldLine(new FoldLine(this.$foldData, fold)); + + if (this.$useWrapMode) + this.$updateWrapData(foldLine.start.row, foldLine.start.row); + else this.$updateRowLengthCache(foldLine.start.row, foldLine.start.row); + this.$modified = true; + this._signal("changeFold", { data: fold, action: "add" }); + + return fold; + }; + + this.addFolds = function(folds) { + folds.forEach(function(fold) { + this.addFold(fold); + }, this); + }; + + this.removeFold = function(fold) { + var foldLine = fold.foldLine; + var startRow = foldLine.start.row; + var endRow = foldLine.end.row; + + var foldLines = this.$foldData; + var folds = foldLine.folds; + if (folds.length == 1) { + foldLines.splice(foldLines.indexOf(foldLine), 1); + } else if (foldLine.range.isEnd(fold.end.row, fold.end.column)) { + folds.pop(); + foldLine.end.row = folds[folds.length - 1].end.row; + foldLine.end.column = folds[folds.length - 1].end.column; + } else if (foldLine.range.isStart(fold.start.row, fold.start.column)) { + folds.shift(); + foldLine.start.row = folds[0].start.row; + foldLine.start.column = folds[0].start.column; + } else if (fold.sameRow) { + folds.splice(folds.indexOf(fold), 1); + } else { + var newFoldLine = foldLine.split(fold.start.row, fold.start.column); + folds = newFoldLine.folds; + folds.shift(); + newFoldLine.start.row = folds[0].start.row; + newFoldLine.start.column = folds[0].start.column; + } + + if (!this.$updating) { + if (this.$useWrapMode) this.$updateWrapData(startRow, endRow); + else this.$updateRowLengthCache(startRow, endRow); + } + this.$modified = true; + this._signal("changeFold", { data: fold, action: "remove" }); + }; + + this.removeFolds = function(folds) { + var cloneFolds = []; + for (var i = 0; i < folds.length; i++) { + cloneFolds.push(folds[i]); + } + + cloneFolds.forEach(function(fold) { + this.removeFold(fold); + }, this); + this.$modified = true; + }; + + this.expandFold = function(fold) { + this.removeFold(fold); + fold.subFolds.forEach(function(subFold) { + fold.restoreRange(subFold); + this.addFold(subFold); + }, this); + if (fold.collapseChildren > 0) { + this.foldAll( + fold.start.row + 1, + fold.end.row, + fold.collapseChildren - 1 + ); + } + fold.subFolds = []; + }; + + this.expandFolds = function(folds) { + folds.forEach(function(fold) { + this.expandFold(fold); + }, this); + }; + + this.unfold = function(location, expandInner) { + var range, folds; + if (location == null) { + range = new Range(0, 0, this.getLength(), 0); + expandInner = true; + } else if (typeof location == "number") + range = new Range( + location, + 0, + location, + this.getLine(location).length + ); + else if ("row" in location) + range = Range.fromPoints(location, location); + else range = location; + + folds = this.getFoldsInRangeList(range); + if (expandInner) { + this.removeFolds(folds); + } else { + var subFolds = folds; + while (subFolds.length) { + this.expandFolds(subFolds); + subFolds = this.getFoldsInRangeList(range); + } + } + if (folds.length) return folds; + }; + this.isRowFolded = function(docRow, startFoldRow) { + return !!this.getFoldLine(docRow, startFoldRow); + }; + + this.getRowFoldEnd = function(docRow, startFoldRow) { + var foldLine = this.getFoldLine(docRow, startFoldRow); + return foldLine ? foldLine.end.row : docRow; + }; + + this.getRowFoldStart = function(docRow, startFoldRow) { + var foldLine = this.getFoldLine(docRow, startFoldRow); + return foldLine ? foldLine.start.row : docRow; + }; + + this.getFoldDisplayLine = function( + foldLine, + endRow, + endColumn, + startRow, + startColumn + ) { + if (startRow == null) startRow = foldLine.start.row; + if (startColumn == null) startColumn = 0; + if (endRow == null) endRow = foldLine.end.row; + if (endColumn == null) endColumn = this.getLine(endRow).length; + var doc = this.doc; + var textLine = ""; + + foldLine.walk( + function(placeholder, row, column, lastColumn) { + if (row < startRow) return; + if (row == startRow) { + if (column < startColumn) return; + lastColumn = Math.max(startColumn, lastColumn); + } + + if (placeholder != null) { + textLine += placeholder; + } else { + textLine += doc.getLine(row).substring(lastColumn, column); + } + }, + endRow, + endColumn + ); + return textLine; + }; + + this.getDisplayLine = function(row, endColumn, startRow, startColumn) { + var foldLine = this.getFoldLine(row); + + if (!foldLine) { + var line; + line = this.doc.getLine(row); + return line.substring(startColumn || 0, endColumn || line.length); + } else { + return this.getFoldDisplayLine( + foldLine, + row, + endColumn, + startRow, + startColumn + ); + } + }; + + this.$cloneFoldData = function() { + var fd = []; + fd = this.$foldData.map(function(foldLine) { + var folds = foldLine.folds.map(function(fold) { + return fold.clone(); + }); + return new FoldLine(fd, folds); + }); + + return fd; + }; + + this.toggleFold = function(tryToUnfold) { + var selection = this.selection; + var range = selection.getRange(); + var fold; + var bracketPos; + + if (range.isEmpty()) { + var cursor = range.start; + fold = this.getFoldAt(cursor.row, cursor.column); + + if (fold) { + this.expandFold(fold); + return; + } else if ((bracketPos = this.findMatchingBracket(cursor))) { + if (range.comparePoint(bracketPos) == 1) { + range.end = bracketPos; + } else { + range.start = bracketPos; + range.start.column++; + range.end.column--; + } + } else if ( + (bracketPos = this.findMatchingBracket({ + row: cursor.row, + column: cursor.column + 1 + })) + ) { + if (range.comparePoint(bracketPos) == 1) range.end = bracketPos; + else range.start = bracketPos; + + range.start.column++; + } else { + range = + this.getCommentFoldRange(cursor.row, cursor.column) || range; + } + } else { + var folds = this.getFoldsInRange(range); + if (tryToUnfold && folds.length) { + this.expandFolds(folds); + return; + } else if (folds.length == 1) { + fold = folds[0]; + } + } + + if (!fold) fold = this.getFoldAt(range.start.row, range.start.column); + + if (fold && fold.range.toString() == range.toString()) { + this.expandFold(fold); + return; + } + + var placeholder = "..."; + if (!range.isMultiLine()) { + placeholder = this.getTextRange(range); + if (placeholder.length < 4) return; + placeholder = placeholder.trim().substring(0, 2) + ".."; + } + + this.addFold(placeholder, range); + }; + + this.getCommentFoldRange = function(row, column, dir) { + var iterator = new TokenIterator(this, row, column); + var token = iterator.getCurrentToken(); + var type = token.type; + if (token && /^comment|string/.test(type)) { + type = type.match(/comment|string/)[0]; + if (type == "comment") type += "|doc-start"; + var re = new RegExp(type); + var range = new Range(); + if (dir != 1) { + do { + token = iterator.stepBackward(); + } while (token && re.test(token.type)); + iterator.stepForward(); + } + + range.start.row = iterator.getCurrentTokenRow(); + range.start.column = iterator.getCurrentTokenColumn() + 2; + + iterator = new TokenIterator(this, row, column); + + if (dir != -1) { + var lastRow = -1; + do { + token = iterator.stepForward(); + if (lastRow == -1) { + var state = this.getState(iterator.$row); + if (!re.test(state)) lastRow = iterator.$row; + } else if (iterator.$row > lastRow) { + break; + } + } while (token && re.test(token.type)); + token = iterator.stepBackward(); + } else token = iterator.getCurrentToken(); + + range.end.row = iterator.getCurrentTokenRow(); + range.end.column = + iterator.getCurrentTokenColumn() + token.value.length - 2; + return range; + } + }; + + this.foldAll = function(startRow, endRow, depth) { + if (depth == undefined) depth = 100000; // JSON.stringify doesn't hanle Infinity + var foldWidgets = this.foldWidgets; + if (!foldWidgets) return; // mode doesn't support folding + endRow = endRow || this.getLength(); + startRow = startRow || 0; + for (var row = startRow; row < endRow; row++) { + if (foldWidgets[row] == null) + foldWidgets[row] = this.getFoldWidget(row); + if (foldWidgets[row] != "start") continue; + + var range = this.getFoldWidgetRange(row); + if ( + range && + range.isMultiLine() && + range.end.row <= endRow && + range.start.row >= startRow + ) { + row = range.end.row; + try { + var fold = this.addFold("...", range); + if (fold) fold.collapseChildren = depth; + } catch (e) {} + } + } + }; + this.$foldStyles = { + manual: 1, + markbegin: 1, + markbeginend: 1 + }; + this.$foldStyle = "markbegin"; + this.setFoldStyle = function(style) { + if (!this.$foldStyles[style]) + throw new Error( + "invalid fold style: " + + style + + "[" + + Object.keys(this.$foldStyles).join(", ") + + "]" + ); + + if (this.$foldStyle == style) return; + + this.$foldStyle = style; + + if (style == "manual") this.unfold(); + var mode = this.$foldMode; + this.$setFolding(null); + this.$setFolding(mode); + }; + + this.$setFolding = function(foldMode) { + if (this.$foldMode == foldMode) return; + + this.$foldMode = foldMode; + + this.off("change", this.$updateFoldWidgets); + this.off("tokenizerUpdate", this.$tokenizerUpdateFoldWidgets); + this._signal("changeAnnotation"); + + if (!foldMode || this.$foldStyle == "manual") { + this.foldWidgets = null; + return; + } + + this.foldWidgets = []; + this.getFoldWidget = foldMode.getFoldWidget.bind( + foldMode, + this, + this.$foldStyle + ); + this.getFoldWidgetRange = foldMode.getFoldWidgetRange.bind( + foldMode, + this, + this.$foldStyle + ); + + this.$updateFoldWidgets = this.updateFoldWidgets.bind(this); + this.$tokenizerUpdateFoldWidgets = this.tokenizerUpdateFoldWidgets.bind( + this + ); + this.on("change", this.$updateFoldWidgets); + this.on("tokenizerUpdate", this.$tokenizerUpdateFoldWidgets); + }; + + this.getParentFoldRangeData = function(row, ignoreCurrent) { + var fw = this.foldWidgets; + if (!fw || (ignoreCurrent && fw[row])) return {}; + + var i = row - 1, + firstRange; + while (i >= 0) { + var c = fw[i]; + if (c == null) c = fw[i] = this.getFoldWidget(i); + + if (c == "start") { + var range = this.getFoldWidgetRange(i); + if (!firstRange) firstRange = range; + if (range && range.end.row >= row) break; + } + i--; + } + + return { + range: i !== -1 && range, + firstRange: firstRange + }; + }; + + this.onFoldWidgetClick = function(row, e) { + e = e.domEvent; + var options = { + children: e.shiftKey, + all: e.ctrlKey || e.metaKey, + siblings: e.altKey + }; + + var range = this.$toggleFoldWidget(row, options); + if (!range) { + var el = e.target || e.srcElement; + if (el && /ace_fold-widget/.test(el.className)) + el.className += " ace_invalid"; + } + }; + + this.$toggleFoldWidget = function(row, options) { + if (!this.getFoldWidget) return; + var type = this.getFoldWidget(row); + var line = this.getLine(row); + + var dir = type === "end" ? -1 : 1; + var fold = this.getFoldAt(row, dir === -1 ? 0 : line.length, dir); + + if (fold) { + if (options.children || options.all) this.removeFold(fold); + else this.expandFold(fold); + return fold; + } + + var range = this.getFoldWidgetRange(row, true); + if (range && !range.isMultiLine()) { + fold = this.getFoldAt(range.start.row, range.start.column, 1); + if (fold && range.isEqual(fold.range)) { + this.removeFold(fold); + return fold; + } + } + + if (options.siblings) { + var data = this.getParentFoldRangeData(row); + if (data.range) { + var startRow = data.range.start.row + 1; + var endRow = data.range.end.row; + } + this.foldAll(startRow, endRow, options.all ? 10000 : 0); + } else if (options.children) { + endRow = range ? range.end.row : this.getLength(); + this.foldAll(row + 1, endRow, options.all ? 10000 : 0); + } else if (range) { + if (options.all) range.collapseChildren = 10000; + this.addFold("...", range); + } + + return range; + }; + + this.toggleFoldWidget = function(toggleParent) { + var row = this.selection.getCursor().row; + row = this.getRowFoldStart(row); + var range = this.$toggleFoldWidget(row, {}); + + if (range) return; + var data = this.getParentFoldRangeData(row, true); + range = data.range || data.firstRange; + + if (range) { + row = range.start.row; + var fold = this.getFoldAt(row, this.getLine(row).length, 1); + + if (fold) { + this.removeFold(fold); + } else { + this.addFold("...", range); + } + } + }; + + this.updateFoldWidgets = function(delta) { + var firstRow = delta.start.row; + var len = delta.end.row - firstRow; + + if (len === 0) { + this.foldWidgets[firstRow] = null; + } else if (delta.action == "remove") { + this.foldWidgets.splice(firstRow, len + 1, null); + } else { + var args = Array(len + 1); + args.unshift(firstRow, 1); + this.foldWidgets.splice.apply(this.foldWidgets, args); + } + }; + this.tokenizerUpdateFoldWidgets = function(e) { + var rows = e.data; + if (rows.first != rows.last) { + if (this.foldWidgets.length > rows.first) + this.foldWidgets.splice(rows.first, this.foldWidgets.length); + } + }; + } + + exports.Folding = Folding; + } +); + +ace.define( + "ace/edit_session/bracket_match", + ["require", "exports", "module", "ace/token_iterator", "ace/range"], + function(require, exports, module) { + "use strict"; + + var TokenIterator = require("../token_iterator").TokenIterator; + var Range = require("../range").Range; + + function BracketMatch() { + this.findMatchingBracket = function(position, chr) { + if (position.column == 0) return null; + + var charBeforeCursor = + chr || this.getLine(position.row).charAt(position.column - 1); + if (charBeforeCursor == "") return null; + + var match = charBeforeCursor.match(/([\(\[\{])|([\)\]\}])/); + if (!match) return null; + + if (match[1]) return this.$findClosingBracket(match[1], position); + else return this.$findOpeningBracket(match[2], position); + }; + + this.getBracketRange = function(pos) { + var line = this.getLine(pos.row); + var before = true, + range; + + var chr = line.charAt(pos.column - 1); + var match = chr && chr.match(/([\(\[\{])|([\)\]\}])/); + if (!match) { + chr = line.charAt(pos.column); + pos = { row: pos.row, column: pos.column + 1 }; + match = chr && chr.match(/([\(\[\{])|([\)\]\}])/); + before = false; + } + if (!match) return null; + + if (match[1]) { + var bracketPos = this.$findClosingBracket(match[1], pos); + if (!bracketPos) return null; + range = Range.fromPoints(pos, bracketPos); + if (!before) { + range.end.column++; + range.start.column--; + } + range.cursor = range.end; + } else { + var bracketPos = this.$findOpeningBracket(match[2], pos); + if (!bracketPos) return null; + range = Range.fromPoints(bracketPos, pos); + if (!before) { + range.start.column++; + range.end.column--; + } + range.cursor = range.start; + } + + return range; + }; + this.getMatchingBracketRanges = function(pos) { + var line = this.getLine(pos.row); + + var chr = line.charAt(pos.column - 1); + var match = chr && chr.match(/([\(\[\{])|([\)\]\}])/); + if (!match) { + chr = line.charAt(pos.column); + pos = { row: pos.row, column: pos.column + 1 }; + match = chr && chr.match(/([\(\[\{])|([\)\]\}])/); + } + + if (!match) return null; + + var startRange = new Range( + pos.row, + pos.column - 1, + pos.row, + pos.column + ); + var bracketPos = match[1] + ? this.$findClosingBracket(match[1], pos) + : this.$findOpeningBracket(match[2], pos); + if (!bracketPos) return [startRange]; + var endRange = new Range( + bracketPos.row, + bracketPos.column, + bracketPos.row, + bracketPos.column + 1 + ); + + return [startRange, endRange]; + }; + + this.$brackets = { + ")": "(", + "(": ")", + "]": "[", + "[": "]", + "{": "}", + "}": "{", + "<": ">", + ">": "<" + }; + + this.$findOpeningBracket = function(bracket, position, typeRe) { + var openBracket = this.$brackets[bracket]; + var depth = 1; + + var iterator = new TokenIterator(this, position.row, position.column); + var token = iterator.getCurrentToken(); + if (!token) token = iterator.stepForward(); + if (!token) return; + + if (!typeRe) { + typeRe = new RegExp( + "(\\.?" + + token.type + .replace(".", "\\.") + .replace("rparen", ".paren") + .replace(/\b(?:end)\b/, "(?:start|begin|end)") + + ")+" + ); + } + var valueIndex = position.column - iterator.getCurrentTokenColumn() - 2; + var value = token.value; + + while (true) { + while (valueIndex >= 0) { + var chr = value.charAt(valueIndex); + if (chr == openBracket) { + depth -= 1; + if (depth == 0) { + return { + row: iterator.getCurrentTokenRow(), + column: valueIndex + iterator.getCurrentTokenColumn() + }; + } + } else if (chr == bracket) { + depth += 1; + } + valueIndex -= 1; + } + do { + token = iterator.stepBackward(); + } while (token && !typeRe.test(token.type)); + + if (token == null) break; + + value = token.value; + valueIndex = value.length - 1; + } + + return null; + }; + + this.$findClosingBracket = function(bracket, position, typeRe) { + var closingBracket = this.$brackets[bracket]; + var depth = 1; + + var iterator = new TokenIterator(this, position.row, position.column); + var token = iterator.getCurrentToken(); + if (!token) token = iterator.stepForward(); + if (!token) return; + + if (!typeRe) { + typeRe = new RegExp( + "(\\.?" + + token.type + .replace(".", "\\.") + .replace("lparen", ".paren") + .replace(/\b(?:start|begin)\b/, "(?:start|begin|end)") + + ")+" + ); + } + var valueIndex = position.column - iterator.getCurrentTokenColumn(); + + while (true) { + var value = token.value; + var valueLength = value.length; + while (valueIndex < valueLength) { + var chr = value.charAt(valueIndex); + if (chr == closingBracket) { + depth -= 1; + if (depth == 0) { + return { + row: iterator.getCurrentTokenRow(), + column: valueIndex + iterator.getCurrentTokenColumn() + }; + } + } else if (chr == bracket) { + depth += 1; + } + valueIndex += 1; + } + do { + token = iterator.stepForward(); + } while (token && !typeRe.test(token.type)); + + if (token == null) break; + + valueIndex = 0; + } + + return null; + }; + } + exports.BracketMatch = BracketMatch; + } +); + +ace.define( + "ace/edit_session", + [ + "require", + "exports", + "module", + "ace/lib/oop", + "ace/lib/lang", + "ace/bidihandler", + "ace/config", + "ace/lib/event_emitter", + "ace/selection", + "ace/mode/text", + "ace/range", + "ace/document", + "ace/background_tokenizer", + "ace/search_highlight", + "ace/edit_session/folding", + "ace/edit_session/bracket_match" + ], + function(require, exports, module) { + "use strict"; + + var oop = require("./lib/oop"); + var lang = require("./lib/lang"); + var BidiHandler = require("./bidihandler").BidiHandler; + var config = require("./config"); + var EventEmitter = require("./lib/event_emitter").EventEmitter; + var Selection = require("./selection").Selection; + var TextMode = require("./mode/text").Mode; + var Range = require("./range").Range; + var Document = require("./document").Document; + var BackgroundTokenizer = require("./background_tokenizer") + .BackgroundTokenizer; + var SearchHighlight = require("./search_highlight").SearchHighlight; + + var EditSession = function(text, mode) { + this.$breakpoints = []; + this.$decorations = []; + this.$frontMarkers = {}; + this.$backMarkers = {}; + this.$markerId = 1; + this.$undoSelect = true; + + this.$foldData = []; + this.id = "session" + ++EditSession.$uid; + this.$foldData.toString = function() { + return this.join("\n"); + }; + this.on("changeFold", this.onChangeFold.bind(this)); + this.$onChange = this.onChange.bind(this); + + if (typeof text != "object" || !text.getLine) text = new Document(text); + + this.setDocument(text); + this.selection = new Selection(this); + this.$bidiHandler = new BidiHandler(this); + + config.resetOptions(this); + this.setMode(mode); + config._signal("session", this); + }; + + EditSession.$uid = 0; + + (function() { + oop.implement(this, EventEmitter); + this.setDocument = function(doc) { + if (this.doc) this.doc.removeListener("change", this.$onChange); + + this.doc = doc; + doc.on("change", this.$onChange); + + if (this.bgTokenizer) this.bgTokenizer.setDocument(this.getDocument()); + + this.resetCaches(); + }; + this.getDocument = function() { + return this.doc; + }; + this.$resetRowCache = function(docRow) { + if (!docRow) { + this.$docRowCache = []; + this.$screenRowCache = []; + return; + } + var l = this.$docRowCache.length; + var i = this.$getRowCacheIndex(this.$docRowCache, docRow) + 1; + if (l > i) { + this.$docRowCache.splice(i, l); + this.$screenRowCache.splice(i, l); + } + }; + + this.$getRowCacheIndex = function(cacheArray, val) { + var low = 0; + var hi = cacheArray.length - 1; + + while (low <= hi) { + var mid = (low + hi) >> 1; + var c = cacheArray[mid]; + + if (val > c) low = mid + 1; + else if (val < c) hi = mid - 1; + else return mid; + } + + return low - 1; + }; + + this.resetCaches = function() { + this.$modified = true; + this.$wrapData = []; + this.$rowLengthCache = []; + this.$resetRowCache(0); + if (this.bgTokenizer) this.bgTokenizer.start(0); + }; + + this.onChangeFold = function(e) { + var fold = e.data; + this.$resetRowCache(fold.start.row); + }; + + this.onChange = function(delta) { + this.$modified = true; + this.$bidiHandler.onChange(delta); + this.$resetRowCache(delta.start.row); + + var removedFolds = this.$updateInternalDataOnChange(delta); + if (!this.$fromUndo && this.$undoManager) { + if (removedFolds && removedFolds.length) { + this.$undoManager.add( + { + action: "removeFolds", + folds: removedFolds + }, + this.mergeUndoDeltas + ); + this.mergeUndoDeltas = true; + } + this.$undoManager.add(delta, this.mergeUndoDeltas); + this.mergeUndoDeltas = true; + + this.$informUndoManager.schedule(); + } + + this.bgTokenizer && this.bgTokenizer.$updateOnChange(delta); + this._signal("change", delta); + }; + this.setValue = function(text) { + this.doc.setValue(text); + this.selection.moveTo(0, 0); + + this.$resetRowCache(0); + this.setUndoManager(this.$undoManager); + this.getUndoManager().reset(); + }; + this.getValue = this.toString = function() { + return this.doc.getValue(); + }; + this.getSelection = function() { + return this.selection; + }; + this.getState = function(row) { + return this.bgTokenizer.getState(row); + }; + this.getTokens = function(row) { + return this.bgTokenizer.getTokens(row); + }; + this.getTokenAt = function(row, column) { + var tokens = this.bgTokenizer.getTokens(row); + var token, + c = 0; + if (column == null) { + var i = tokens.length - 1; + c = this.getLine(row).length; + } else { + for (var i = 0; i < tokens.length; i++) { + c += tokens[i].value.length; + if (c >= column) break; + } + } + token = tokens[i]; + if (!token) return null; + token.index = i; + token.start = c - token.value.length; + return token; + }; + this.setUndoManager = function(undoManager) { + this.$undoManager = undoManager; + + if (this.$informUndoManager) this.$informUndoManager.cancel(); + + if (undoManager) { + var self = this; + undoManager.addSession(this); + this.$syncInformUndoManager = function() { + self.$informUndoManager.cancel(); + self.mergeUndoDeltas = false; + }; + this.$informUndoManager = lang.delayedCall( + this.$syncInformUndoManager + ); + } else { + this.$syncInformUndoManager = function() {}; + } + }; + this.markUndoGroup = function() { + if (this.$syncInformUndoManager) this.$syncInformUndoManager(); + }; + + this.$defaultUndoManager = { + undo: function() {}, + redo: function() {}, + hasUndo: function() {}, + hasRedo: function() {}, + reset: function() {}, + add: function() {}, + addSelection: function() {}, + startNewGroup: function() {}, + addSession: function() {} + }; + this.getUndoManager = function() { + return this.$undoManager || this.$defaultUndoManager; + }; + this.getTabString = function() { + if (this.getUseSoftTabs()) { + return lang.stringRepeat(" ", this.getTabSize()); + } else { + return "\t"; + } + }; + this.setUseSoftTabs = function(val) { + this.setOption("useSoftTabs", val); + }; + this.getUseSoftTabs = function() { + return this.$useSoftTabs && !this.$mode.$indentWithTabs; + }; + this.setTabSize = function(tabSize) { + this.setOption("tabSize", tabSize); + }; + this.getTabSize = function() { + return this.$tabSize; + }; + this.isTabStop = function(position) { + return this.$useSoftTabs && position.column % this.$tabSize === 0; + }; + this.setNavigateWithinSoftTabs = function(navigateWithinSoftTabs) { + this.setOption("navigateWithinSoftTabs", navigateWithinSoftTabs); + }; + this.getNavigateWithinSoftTabs = function() { + return this.$navigateWithinSoftTabs; + }; + + this.$overwrite = false; + this.setOverwrite = function(overwrite) { + this.setOption("overwrite", overwrite); + }; + this.getOverwrite = function() { + return this.$overwrite; + }; + this.toggleOverwrite = function() { + this.setOverwrite(!this.$overwrite); + }; + this.addGutterDecoration = function(row, className) { + if (!this.$decorations[row]) this.$decorations[row] = ""; + this.$decorations[row] += " " + className; + this._signal("changeBreakpoint", {}); + }; + this.removeGutterDecoration = function(row, className) { + this.$decorations[row] = (this.$decorations[row] || "").replace( + " " + className, + "" + ); + this._signal("changeBreakpoint", {}); + }; + this.getBreakpoints = function() { + return this.$breakpoints; + }; + this.setBreakpoints = function(rows) { + this.$breakpoints = []; + for (var i = 0; i < rows.length; i++) { + this.$breakpoints[rows[i]] = "ace_breakpoint"; + } + this._signal("changeBreakpoint", {}); + }; + this.clearBreakpoints = function() { + this.$breakpoints = []; + this._signal("changeBreakpoint", {}); + }; + this.setBreakpoint = function(row, className) { + if (className === undefined) className = "ace_breakpoint"; + if (className) this.$breakpoints[row] = className; + else delete this.$breakpoints[row]; + this._signal("changeBreakpoint", {}); + }; + this.clearBreakpoint = function(row) { + delete this.$breakpoints[row]; + this._signal("changeBreakpoint", {}); + }; + this.addMarker = function(range, clazz, type, inFront) { + var id = this.$markerId++; + + var marker = { + range: range, + type: type || "line", + renderer: typeof type == "function" ? type : null, + clazz: clazz, + inFront: !!inFront, + id: id + }; + + if (inFront) { + this.$frontMarkers[id] = marker; + this._signal("changeFrontMarker"); + } else { + this.$backMarkers[id] = marker; + this._signal("changeBackMarker"); + } + + return id; + }; + this.addDynamicMarker = function(marker, inFront) { + if (!marker.update) return; + var id = this.$markerId++; + marker.id = id; + marker.inFront = !!inFront; + + if (inFront) { + this.$frontMarkers[id] = marker; + this._signal("changeFrontMarker"); + } else { + this.$backMarkers[id] = marker; + this._signal("changeBackMarker"); + } + + return marker; + }; + this.removeMarker = function(markerId) { + var marker = + this.$frontMarkers[markerId] || this.$backMarkers[markerId]; + if (!marker) return; + + var markers = marker.inFront ? this.$frontMarkers : this.$backMarkers; + delete markers[markerId]; + this._signal(marker.inFront ? "changeFrontMarker" : "changeBackMarker"); + }; + this.getMarkers = function(inFront) { + return inFront ? this.$frontMarkers : this.$backMarkers; + }; + + this.highlight = function(re) { + if (!this.$searchHighlight) { + var highlight = new SearchHighlight( + null, + "ace_selected-word", + "text" + ); + this.$searchHighlight = this.addDynamicMarker(highlight); + } + this.$searchHighlight.setRegexp(re); + }; + this.highlightLines = function(startRow, endRow, clazz, inFront) { + if (typeof endRow != "number") { + clazz = endRow; + endRow = startRow; + } + if (!clazz) clazz = "ace_step"; + + var range = new Range(startRow, 0, endRow, Infinity); + range.id = this.addMarker(range, clazz, "fullLine", inFront); + return range; + }; + this.setAnnotations = function(annotations) { + this.$annotations = annotations; + this._signal("changeAnnotation", {}); + }; + this.getAnnotations = function() { + return this.$annotations || []; + }; + this.clearAnnotations = function() { + this.setAnnotations([]); + }; + this.$detectNewLine = function(text) { + var match = text.match(/^.*?(\r?\n)/m); + if (match) { + this.$autoNewLine = match[1]; + } else { + this.$autoNewLine = "\n"; + } + }; + this.getWordRange = function(row, column) { + var line = this.getLine(row); + + var inToken = false; + if (column > 0) inToken = !!line.charAt(column - 1).match(this.tokenRe); + + if (!inToken) inToken = !!line.charAt(column).match(this.tokenRe); + + if (inToken) var re = this.tokenRe; + else if (/^\s+$/.test(line.slice(column - 1, column + 1))) + var re = /\s/; + else var re = this.nonTokenRe; + + var start = column; + if (start > 0) { + do { + start--; + } while (start >= 0 && line.charAt(start).match(re)); + start++; + } + + var end = column; + while (end < line.length && line.charAt(end).match(re)) { + end++; + } + + return new Range(row, start, row, end); + }; + this.getAWordRange = function(row, column) { + var wordRange = this.getWordRange(row, column); + var line = this.getLine(wordRange.end.row); + + while (line.charAt(wordRange.end.column).match(/[ \t]/)) { + wordRange.end.column += 1; + } + return wordRange; + }; + this.setNewLineMode = function(newLineMode) { + this.doc.setNewLineMode(newLineMode); + }; + this.getNewLineMode = function() { + return this.doc.getNewLineMode(); + }; + this.setUseWorker = function(useWorker) { + this.setOption("useWorker", useWorker); + }; + this.getUseWorker = function() { + return this.$useWorker; + }; + this.onReloadTokenizer = function(e) { + var rows = e.data; + this.bgTokenizer.start(rows.first); + this._signal("tokenizerUpdate", e); + }; + + this.$modes = config.$modes; + this.$mode = null; + this.$modeId = null; + this.setMode = function(mode, cb) { + if (mode && typeof mode === "object") { + if (mode.getTokenizer) return this.$onChangeMode(mode); + var options = mode; + var path = options.path; + } else { + path = mode || "ace/mode/text"; + } + if (!this.$modes["ace/mode/text"]) + this.$modes["ace/mode/text"] = new TextMode(); + + if (this.$modes[path] && !options) { + this.$onChangeMode(this.$modes[path]); + cb && cb(); + return; + } + this.$modeId = path; + config.loadModule( + ["mode", path], + function(m) { + if (this.$modeId !== path) return cb && cb(); + if (this.$modes[path] && !options) { + this.$onChangeMode(this.$modes[path]); + } else if (m && m.Mode) { + m = new m.Mode(options); + if (!options) { + this.$modes[path] = m; + m.$id = path; + } + this.$onChangeMode(m); + } + cb && cb(); + }.bind(this) + ); + if (!this.$mode) this.$onChangeMode(this.$modes["ace/mode/text"], true); + }; + + this.$onChangeMode = function(mode, $isPlaceholder) { + if (!$isPlaceholder) this.$modeId = mode.$id; + if (this.$mode === mode) return; + + var oldMode = this.$mode; + this.$mode = mode; + + this.$stopWorker(); + + if (this.$useWorker) this.$startWorker(); + + var tokenizer = mode.getTokenizer(); + + if (tokenizer.on !== undefined) { + var onReloadTokenizer = this.onReloadTokenizer.bind(this); + tokenizer.on("update", onReloadTokenizer); + } + + if (!this.bgTokenizer) { + this.bgTokenizer = new BackgroundTokenizer(tokenizer); + var _self = this; + this.bgTokenizer.on("update", function(e) { + _self._signal("tokenizerUpdate", e); + }); + } else { + this.bgTokenizer.setTokenizer(tokenizer); + } + + this.bgTokenizer.setDocument(this.getDocument()); + + this.tokenRe = mode.tokenRe; + this.nonTokenRe = mode.nonTokenRe; + + if (!$isPlaceholder) { + if (mode.attachToSession) mode.attachToSession(this); + this.$options.wrapMethod.set.call(this, this.$wrapMethod); + this.$setFolding(mode.foldingRules); + this.bgTokenizer.start(0); + this._emit("changeMode", { oldMode: oldMode, mode: mode }); + } + }; + + this.$stopWorker = function() { + if (this.$worker) { + this.$worker.terminate(); + this.$worker = null; + } + }; + + this.$startWorker = function() { + try { + this.$worker = this.$mode.createWorker(this); + } catch (e) { + config.warn("Could not load worker", e); + this.$worker = null; + } + }; + this.getMode = function() { + return this.$mode; + }; + + this.$scrollTop = 0; + this.setScrollTop = function(scrollTop) { + if (this.$scrollTop === scrollTop || isNaN(scrollTop)) return; + + this.$scrollTop = scrollTop; + this._signal("changeScrollTop", scrollTop); + }; + this.getScrollTop = function() { + return this.$scrollTop; + }; + + this.$scrollLeft = 0; + this.setScrollLeft = function(scrollLeft) { + if (this.$scrollLeft === scrollLeft || isNaN(scrollLeft)) return; + + this.$scrollLeft = scrollLeft; + this._signal("changeScrollLeft", scrollLeft); + }; + this.getScrollLeft = function() { + return this.$scrollLeft; + }; + this.getScreenWidth = function() { + this.$computeWidth(); + if (this.lineWidgets) + return Math.max(this.getLineWidgetMaxWidth(), this.screenWidth); + return this.screenWidth; + }; + + this.getLineWidgetMaxWidth = function() { + if (this.lineWidgetsWidth != null) return this.lineWidgetsWidth; + var width = 0; + this.lineWidgets.forEach(function(w) { + if (w && w.screenWidth > width) width = w.screenWidth; + }); + return (this.lineWidgetWidth = width); + }; + + this.$computeWidth = function(force) { + if (this.$modified || force) { + this.$modified = false; + + if (this.$useWrapMode) return (this.screenWidth = this.$wrapLimit); + + var lines = this.doc.getAllLines(); + var cache = this.$rowLengthCache; + var longestScreenLine = 0; + var foldIndex = 0; + var foldLine = this.$foldData[foldIndex]; + var foldStart = foldLine ? foldLine.start.row : Infinity; + var len = lines.length; + + for (var i = 0; i < len; i++) { + if (i > foldStart) { + i = foldLine.end.row + 1; + if (i >= len) break; + foldLine = this.$foldData[foldIndex++]; + foldStart = foldLine ? foldLine.start.row : Infinity; + } + + if (cache[i] == null) + cache[i] = this.$getStringScreenWidth(lines[i])[0]; + + if (cache[i] > longestScreenLine) longestScreenLine = cache[i]; + } + this.screenWidth = longestScreenLine; + } + }; + this.getLine = function(row) { + return this.doc.getLine(row); + }; + this.getLines = function(firstRow, lastRow) { + return this.doc.getLines(firstRow, lastRow); + }; + this.getLength = function() { + return this.doc.getLength(); + }; + this.getTextRange = function(range) { + return this.doc.getTextRange(range || this.selection.getRange()); + }; + this.insert = function(position, text) { + return this.doc.insert(position, text); + }; + this.remove = function(range) { + return this.doc.remove(range); + }; + this.removeFullLines = function(firstRow, lastRow) { + return this.doc.removeFullLines(firstRow, lastRow); + }; + this.undoChanges = function(deltas, dontSelect) { + if (!deltas.length) return; + + this.$fromUndo = true; + for (var i = deltas.length - 1; i != -1; i--) { + var delta = deltas[i]; + if (delta.action == "insert" || delta.action == "remove") { + this.doc.revertDelta(delta); + } else if (delta.folds) { + this.addFolds(delta.folds); + } + } + if (!dontSelect && this.$undoSelect) { + if (deltas.selectionBefore) + this.selection.fromJSON(deltas.selectionBefore); + else this.selection.setRange(this.$getUndoSelection(deltas, true)); + } + this.$fromUndo = false; + }; + this.redoChanges = function(deltas, dontSelect) { + if (!deltas.length) return; + + this.$fromUndo = true; + for (var i = 0; i < deltas.length; i++) { + var delta = deltas[i]; + if (delta.action == "insert" || delta.action == "remove") { + this.doc.$safeApplyDelta(delta); + } + } + + if (!dontSelect && this.$undoSelect) { + if (deltas.selectionAfter) + this.selection.fromJSON(deltas.selectionAfter); + else this.selection.setRange(this.$getUndoSelection(deltas, false)); + } + this.$fromUndo = false; + }; + this.setUndoSelect = function(enable) { + this.$undoSelect = enable; + }; + + this.$getUndoSelection = function(deltas, isUndo) { + function isInsert(delta) { + return isUndo ? delta.action !== "insert" : delta.action === "insert"; + } + + var range, point; + + for (var i = 0; i < deltas.length; i++) { + var delta = deltas[i]; + if (!delta.start) continue; // skip folds + if (!range) { + if (isInsert(delta)) { + range = Range.fromPoints(delta.start, delta.end); + } else { + range = Range.fromPoints(delta.start, delta.start); + } + continue; + } + + if (isInsert(delta)) { + point = delta.start; + if (range.compare(point.row, point.column) == -1) { + range.setStart(point); + } + point = delta.end; + if (range.compare(point.row, point.column) == 1) { + range.setEnd(point); + } + } else { + point = delta.start; + if (range.compare(point.row, point.column) == -1) { + range = Range.fromPoints(delta.start, delta.start); + } + } + } + return range; + }; + this.replace = function(range, text) { + return this.doc.replace(range, text); + }; + this.moveText = function(fromRange, toPosition, copy) { + var text = this.getTextRange(fromRange); + var folds = this.getFoldsInRange(fromRange); + + var toRange = Range.fromPoints(toPosition, toPosition); + if (!copy) { + this.remove(fromRange); + var rowDiff = fromRange.start.row - fromRange.end.row; + var collDiff = rowDiff + ? -fromRange.end.column + : fromRange.start.column - fromRange.end.column; + if (collDiff) { + if ( + toRange.start.row == fromRange.end.row && + toRange.start.column > fromRange.end.column + ) + toRange.start.column += collDiff; + if ( + toRange.end.row == fromRange.end.row && + toRange.end.column > fromRange.end.column + ) + toRange.end.column += collDiff; + } + if (rowDiff && toRange.start.row >= fromRange.end.row) { + toRange.start.row += rowDiff; + toRange.end.row += rowDiff; + } + } + + toRange.end = this.insert(toRange.start, text); + if (folds.length) { + var oldStart = fromRange.start; + var newStart = toRange.start; + var rowDiff = newStart.row - oldStart.row; + var collDiff = newStart.column - oldStart.column; + this.addFolds( + folds.map(function(x) { + x = x.clone(); + if (x.start.row == oldStart.row) x.start.column += collDiff; + if (x.end.row == oldStart.row) x.end.column += collDiff; + x.start.row += rowDiff; + x.end.row += rowDiff; + return x; + }) + ); + } + + return toRange; + }; + this.indentRows = function(startRow, endRow, indentString) { + indentString = indentString.replace(/\t/g, this.getTabString()); + for (var row = startRow; row <= endRow; row++) + this.doc.insertInLine({ row: row, column: 0 }, indentString); + }; + this.outdentRows = function(range) { + var rowRange = range.collapseRows(); + var deleteRange = new Range(0, 0, 0, 0); + var size = this.getTabSize(); + + for (var i = rowRange.start.row; i <= rowRange.end.row; ++i) { + var line = this.getLine(i); + + deleteRange.start.row = i; + deleteRange.end.row = i; + for (var j = 0; j < size; ++j) if (line.charAt(j) != " ") break; + if (j < size && line.charAt(j) == "\t") { + deleteRange.start.column = j; + deleteRange.end.column = j + 1; + } else { + deleteRange.start.column = 0; + deleteRange.end.column = j; + } + this.remove(deleteRange); + } + }; + + this.$moveLines = function(firstRow, lastRow, dir) { + firstRow = this.getRowFoldStart(firstRow); + lastRow = this.getRowFoldEnd(lastRow); + if (dir < 0) { + var row = this.getRowFoldStart(firstRow + dir); + if (row < 0) return 0; + var diff = row - firstRow; + } else if (dir > 0) { + var row = this.getRowFoldEnd(lastRow + dir); + if (row > this.doc.getLength() - 1) return 0; + var diff = row - lastRow; + } else { + firstRow = this.$clipRowToDocument(firstRow); + lastRow = this.$clipRowToDocument(lastRow); + var diff = lastRow - firstRow + 1; + } + + var range = new Range(firstRow, 0, lastRow, Number.MAX_VALUE); + var folds = this.getFoldsInRange(range).map(function(x) { + x = x.clone(); + x.start.row += diff; + x.end.row += diff; + return x; + }); + + var lines = + dir == 0 + ? this.doc.getLines(firstRow, lastRow) + : this.doc.removeFullLines(firstRow, lastRow); + this.doc.insertFullLines(firstRow + diff, lines); + folds.length && this.addFolds(folds); + return diff; + }; + this.moveLinesUp = function(firstRow, lastRow) { + return this.$moveLines(firstRow, lastRow, -1); + }; + this.moveLinesDown = function(firstRow, lastRow) { + return this.$moveLines(firstRow, lastRow, 1); + }; + this.duplicateLines = function(firstRow, lastRow) { + return this.$moveLines(firstRow, lastRow, 0); + }; + + this.$clipRowToDocument = function(row) { + return Math.max(0, Math.min(row, this.doc.getLength() - 1)); + }; + + this.$clipColumnToRow = function(row, column) { + if (column < 0) return 0; + return Math.min(this.doc.getLine(row).length, column); + }; + + this.$clipPositionToDocument = function(row, column) { + column = Math.max(0, column); + + if (row < 0) { + row = 0; + column = 0; + } else { + var len = this.doc.getLength(); + if (row >= len) { + row = len - 1; + column = this.doc.getLine(len - 1).length; + } else { + column = Math.min(this.doc.getLine(row).length, column); + } + } + + return { + row: row, + column: column + }; + }; + + this.$clipRangeToDocument = function(range) { + if (range.start.row < 0) { + range.start.row = 0; + range.start.column = 0; + } else { + range.start.column = this.$clipColumnToRow( + range.start.row, + range.start.column + ); + } + + var len = this.doc.getLength() - 1; + if (range.end.row > len) { + range.end.row = len; + range.end.column = this.doc.getLine(len).length; + } else { + range.end.column = this.$clipColumnToRow( + range.end.row, + range.end.column + ); + } + return range; + }; + this.$wrapLimit = 80; + this.$useWrapMode = false; + this.$wrapLimitRange = { + min: null, + max: null + }; + this.setUseWrapMode = function(useWrapMode) { + if (useWrapMode != this.$useWrapMode) { + this.$useWrapMode = useWrapMode; + this.$modified = true; + this.$resetRowCache(0); + if (useWrapMode) { + var len = this.getLength(); + this.$wrapData = Array(len); + this.$updateWrapData(0, len - 1); + } + + this._signal("changeWrapMode"); + } + }; + this.getUseWrapMode = function() { + return this.$useWrapMode; + }; + this.setWrapLimitRange = function(min, max) { + if ( + this.$wrapLimitRange.min !== min || + this.$wrapLimitRange.max !== max + ) { + this.$wrapLimitRange = { min: min, max: max }; + this.$modified = true; + this.$bidiHandler.markAsDirty(); + if (this.$useWrapMode) this._signal("changeWrapMode"); + } + }; + this.adjustWrapLimit = function(desiredLimit, $printMargin) { + var limits = this.$wrapLimitRange; + if (limits.max < 0) limits = { min: $printMargin, max: $printMargin }; + var wrapLimit = this.$constrainWrapLimit( + desiredLimit, + limits.min, + limits.max + ); + if (wrapLimit != this.$wrapLimit && wrapLimit > 1) { + this.$wrapLimit = wrapLimit; + this.$modified = true; + if (this.$useWrapMode) { + this.$updateWrapData(0, this.getLength() - 1); + this.$resetRowCache(0); + this._signal("changeWrapLimit"); + } + return true; + } + return false; + }; + + this.$constrainWrapLimit = function(wrapLimit, min, max) { + if (min) wrapLimit = Math.max(min, wrapLimit); + + if (max) wrapLimit = Math.min(max, wrapLimit); + + return wrapLimit; + }; + this.getWrapLimit = function() { + return this.$wrapLimit; + }; + this.setWrapLimit = function(limit) { + this.setWrapLimitRange(limit, limit); + }; + this.getWrapLimitRange = function() { + return { + min: this.$wrapLimitRange.min, + max: this.$wrapLimitRange.max + }; + }; + + this.$updateInternalDataOnChange = function(delta) { + var useWrapMode = this.$useWrapMode; + var action = delta.action; + var start = delta.start; + var end = delta.end; + var firstRow = start.row; + var lastRow = end.row; + var len = lastRow - firstRow; + var removedFolds = null; + + this.$updating = true; + if (len != 0) { + if (action === "remove") { + this[useWrapMode ? "$wrapData" : "$rowLengthCache"].splice( + firstRow, + len + ); + + var foldLines = this.$foldData; + removedFolds = this.getFoldsInRange(delta); + this.removeFolds(removedFolds); + + var foldLine = this.getFoldLine(end.row); + var idx = 0; + if (foldLine) { + foldLine.addRemoveChars( + end.row, + end.column, + start.column - end.column + ); + foldLine.shiftRow(-len); + + var foldLineBefore = this.getFoldLine(firstRow); + if (foldLineBefore && foldLineBefore !== foldLine) { + foldLineBefore.merge(foldLine); + foldLine = foldLineBefore; + } + idx = foldLines.indexOf(foldLine) + 1; + } + + for (idx; idx < foldLines.length; idx++) { + var foldLine = foldLines[idx]; + if (foldLine.start.row >= end.row) { + foldLine.shiftRow(-len); + } + } + + lastRow = firstRow; + } else { + var args = Array(len); + args.unshift(firstRow, 0); + var arr = useWrapMode ? this.$wrapData : this.$rowLengthCache; + arr.splice.apply(arr, args); + var foldLines = this.$foldData; + var foldLine = this.getFoldLine(firstRow); + var idx = 0; + if (foldLine) { + var cmp = foldLine.range.compareInside(start.row, start.column); + if (cmp == 0) { + foldLine = foldLine.split(start.row, start.column); + if (foldLine) { + foldLine.shiftRow(len); + foldLine.addRemoveChars( + lastRow, + 0, + end.column - start.column + ); + } + } else if (cmp == -1) { + foldLine.addRemoveChars(firstRow, 0, end.column - start.column); + foldLine.shiftRow(len); + } + idx = foldLines.indexOf(foldLine) + 1; + } + + for (idx; idx < foldLines.length; idx++) { + var foldLine = foldLines[idx]; + if (foldLine.start.row >= firstRow) { + foldLine.shiftRow(len); + } + } + } + } else { + len = Math.abs(delta.start.column - delta.end.column); + if (action === "remove") { + removedFolds = this.getFoldsInRange(delta); + this.removeFolds(removedFolds); + + len = -len; + } + var foldLine = this.getFoldLine(firstRow); + if (foldLine) { + foldLine.addRemoveChars(firstRow, start.column, len); + } + } + + if (useWrapMode && this.$wrapData.length != this.doc.getLength()) { + console.error( + "doc.getLength() and $wrapData.length have to be the same!" + ); + } + this.$updating = false; + + if (useWrapMode) this.$updateWrapData(firstRow, lastRow); + else this.$updateRowLengthCache(firstRow, lastRow); + + return removedFolds; + }; + + this.$updateRowLengthCache = function(firstRow, lastRow, b) { + this.$rowLengthCache[firstRow] = null; + this.$rowLengthCache[lastRow] = null; + }; + + this.$updateWrapData = function(firstRow, lastRow) { + var lines = this.doc.getAllLines(); + var tabSize = this.getTabSize(); + var wrapData = this.$wrapData; + var wrapLimit = this.$wrapLimit; + var tokens; + var foldLine; + + var row = firstRow; + lastRow = Math.min(lastRow, lines.length - 1); + while (row <= lastRow) { + foldLine = this.getFoldLine(row, foldLine); + if (!foldLine) { + tokens = this.$getDisplayTokens(lines[row]); + wrapData[row] = this.$computeWrapSplits(tokens, wrapLimit, tabSize); + row++; + } else { + tokens = []; + foldLine.walk( + function(placeholder, row, column, lastColumn) { + var walkTokens; + if (placeholder != null) { + walkTokens = this.$getDisplayTokens( + placeholder, + tokens.length + ); + walkTokens[0] = PLACEHOLDER_START; + for (var i = 1; i < walkTokens.length; i++) { + walkTokens[i] = PLACEHOLDER_BODY; + } + } else { + walkTokens = this.$getDisplayTokens( + lines[row].substring(lastColumn, column), + tokens.length + ); + } + tokens = tokens.concat(walkTokens); + }.bind(this), + foldLine.end.row, + lines[foldLine.end.row].length + 1 + ); + + wrapData[foldLine.start.row] = this.$computeWrapSplits( + tokens, + wrapLimit, + tabSize + ); + row = foldLine.end.row + 1; + } + } + }; + var CHAR = 1, + CHAR_EXT = 2, + PLACEHOLDER_START = 3, + PLACEHOLDER_BODY = 4, + PUNCTUATION = 9, + SPACE = 10, + TAB = 11, + TAB_SPACE = 12; + + this.$computeWrapSplits = function(tokens, wrapLimit, tabSize) { + if (tokens.length == 0) { + return []; + } + + var splits = []; + var displayLength = tokens.length; + var lastSplit = 0, + lastDocSplit = 0; + + var isCode = this.$wrapAsCode; + + var indentedSoftWrap = this.$indentedSoftWrap; + var maxIndent = + wrapLimit <= Math.max(2 * tabSize, 8) || indentedSoftWrap === false + ? 0 + : Math.floor(wrapLimit / 2); + + function getWrapIndent() { + var indentation = 0; + if (maxIndent === 0) return indentation; + if (indentedSoftWrap) { + for (var i = 0; i < tokens.length; i++) { + var token = tokens[i]; + if (token == SPACE) indentation += 1; + else if (token == TAB) indentation += tabSize; + else if (token == TAB_SPACE) continue; + else break; + } + } + if (isCode && indentedSoftWrap !== false) indentation += tabSize; + return Math.min(indentation, maxIndent); + } + function addSplit(screenPos) { + var len = screenPos - lastSplit; + for (var i = lastSplit; i < screenPos; i++) { + var ch = tokens[i]; + if (ch === 12 || ch === 2) len -= 1; + } + + if (!splits.length) { + indent = getWrapIndent(); + splits.indent = indent; + } + lastDocSplit += len; + splits.push(lastDocSplit); + lastSplit = screenPos; + } + var indent = 0; + while (displayLength - lastSplit > wrapLimit - indent) { + var split = lastSplit + wrapLimit - indent; + if (tokens[split - 1] >= SPACE && tokens[split] >= SPACE) { + addSplit(split); + continue; + } + if ( + tokens[split] == PLACEHOLDER_START || + tokens[split] == PLACEHOLDER_BODY + ) { + for (split; split != lastSplit - 1; split--) { + if (tokens[split] == PLACEHOLDER_START) { + break; + } + } + if (split > lastSplit) { + addSplit(split); + continue; + } + split = lastSplit + wrapLimit; + for (split; split < tokens.length; split++) { + if (tokens[split] != PLACEHOLDER_BODY) { + break; + } + } + if (split == tokens.length) { + break; // Breaks the while-loop. + } + addSplit(split); + continue; + } + var minSplit = Math.max( + split - (wrapLimit - (wrapLimit >> 2)), + lastSplit - 1 + ); + while (split > minSplit && tokens[split] < PLACEHOLDER_START) { + split--; + } + if (isCode) { + while (split > minSplit && tokens[split] < PLACEHOLDER_START) { + split--; + } + while (split > minSplit && tokens[split] == PUNCTUATION) { + split--; + } + } else { + while (split > minSplit && tokens[split] < SPACE) { + split--; + } + } + if (split > minSplit) { + addSplit(++split); + continue; + } + split = lastSplit + wrapLimit; + if (tokens[split] == CHAR_EXT) split--; + addSplit(split - indent); + } + return splits; + }; + this.$getDisplayTokens = function(str, offset) { + var arr = []; + var tabSize; + offset = offset || 0; + + for (var i = 0; i < str.length; i++) { + var c = str.charCodeAt(i); + if (c == 9) { + tabSize = this.getScreenTabSize(arr.length + offset); + arr.push(TAB); + for (var n = 1; n < tabSize; n++) { + arr.push(TAB_SPACE); + } + } else if (c == 32) { + arr.push(SPACE); + } else if ((c > 39 && c < 48) || (c > 57 && c < 64)) { + arr.push(PUNCTUATION); + } else if (c >= 0x1100 && isFullWidth(c)) { + arr.push(CHAR, CHAR_EXT); + } else { + arr.push(CHAR); + } + } + return arr; + }; + this.$getStringScreenWidth = function( + str, + maxScreenColumn, + screenColumn + ) { + if (maxScreenColumn == 0) return [0, 0]; + if (maxScreenColumn == null) maxScreenColumn = Infinity; + screenColumn = screenColumn || 0; + + var c, column; + for (column = 0; column < str.length; column++) { + c = str.charCodeAt(column); + if (c == 9) { + screenColumn += this.getScreenTabSize(screenColumn); + } else if (c >= 0x1100 && isFullWidth(c)) { + screenColumn += 2; + } else { + screenColumn += 1; + } + if (screenColumn > maxScreenColumn) { + break; + } + } + + return [screenColumn, column]; + }; + + this.lineWidgets = null; + this.getRowLength = function(row) { + var h = 1; + if (this.lineWidgets) + h += (this.lineWidgets[row] && this.lineWidgets[row].rowCount) || 0; + + if (!this.$useWrapMode || !this.$wrapData[row]) return h; + else return this.$wrapData[row].length + h; + }; + this.getRowLineCount = function(row) { + if (!this.$useWrapMode || !this.$wrapData[row]) { + return 1; + } else { + return this.$wrapData[row].length + 1; + } + }; + + this.getRowWrapIndent = function(screenRow) { + if (this.$useWrapMode) { + var pos = this.screenToDocumentPosition(screenRow, Number.MAX_VALUE); + var splits = this.$wrapData[pos.row]; + return splits.length && splits[0] < pos.column ? splits.indent : 0; + } else { + return 0; + } + }; + this.getScreenLastRowColumn = function(screenRow) { + var pos = this.screenToDocumentPosition(screenRow, Number.MAX_VALUE); + return this.documentToScreenColumn(pos.row, pos.column); + }; + this.getDocumentLastRowColumn = function(docRow, docColumn) { + var screenRow = this.documentToScreenRow(docRow, docColumn); + return this.getScreenLastRowColumn(screenRow); + }; + this.getDocumentLastRowColumnPosition = function(docRow, docColumn) { + var screenRow = this.documentToScreenRow(docRow, docColumn); + return this.screenToDocumentPosition(screenRow, Number.MAX_VALUE / 10); + }; + this.getRowSplitData = function(row) { + if (!this.$useWrapMode) { + return undefined; + } else { + return this.$wrapData[row]; + } + }; + this.getScreenTabSize = function(screenColumn) { + return this.$tabSize - (screenColumn % this.$tabSize | 0); + }; + + this.screenToDocumentRow = function(screenRow, screenColumn) { + return this.screenToDocumentPosition(screenRow, screenColumn).row; + }; + + this.screenToDocumentColumn = function(screenRow, screenColumn) { + return this.screenToDocumentPosition(screenRow, screenColumn).column; + }; + this.screenToDocumentPosition = function( + screenRow, + screenColumn, + offsetX + ) { + if (screenRow < 0) return { row: 0, column: 0 }; + + var line; + var docRow = 0; + var docColumn = 0; + var column; + var row = 0; + var rowLength = 0; + + var rowCache = this.$screenRowCache; + var i = this.$getRowCacheIndex(rowCache, screenRow); + var l = rowCache.length; + if (l && i >= 0) { + var row = rowCache[i]; + var docRow = this.$docRowCache[i]; + var doCache = screenRow > rowCache[l - 1]; + } else { + var doCache = !l; + } + + var maxRow = this.getLength() - 1; + var foldLine = this.getNextFoldLine(docRow); + var foldStart = foldLine ? foldLine.start.row : Infinity; + + while (row <= screenRow) { + rowLength = this.getRowLength(docRow); + if (row + rowLength > screenRow || docRow >= maxRow) { + break; + } else { + row += rowLength; + docRow++; + if (docRow > foldStart) { + docRow = foldLine.end.row + 1; + foldLine = this.getNextFoldLine(docRow, foldLine); + foldStart = foldLine ? foldLine.start.row : Infinity; + } + } + + if (doCache) { + this.$docRowCache.push(docRow); + this.$screenRowCache.push(row); + } + } + + if (foldLine && foldLine.start.row <= docRow) { + line = this.getFoldDisplayLine(foldLine); + docRow = foldLine.start.row; + } else if (row + rowLength <= screenRow || docRow > maxRow) { + return { + row: maxRow, + column: this.getLine(maxRow).length + }; + } else { + line = this.getLine(docRow); + foldLine = null; + } + var wrapIndent = 0, + splitIndex = Math.floor(screenRow - row); + if (this.$useWrapMode) { + var splits = this.$wrapData[docRow]; + if (splits) { + column = splits[splitIndex]; + if (splitIndex > 0 && splits.length) { + wrapIndent = splits.indent; + docColumn = splits[splitIndex - 1] || splits[splits.length - 1]; + line = line.substring(docColumn); + } + } + } + + if ( + offsetX !== undefined && + this.$bidiHandler.isBidiRow(row + splitIndex, docRow, splitIndex) + ) + screenColumn = this.$bidiHandler.offsetToCol(offsetX); + + docColumn += this.$getStringScreenWidth( + line, + screenColumn - wrapIndent + )[1]; + if (this.$useWrapMode && docColumn >= column) docColumn = column - 1; + + if (foldLine) return foldLine.idxToPosition(docColumn); + + return { row: docRow, column: docColumn }; + }; + this.documentToScreenPosition = function(docRow, docColumn) { + if (typeof docColumn === "undefined") + var pos = this.$clipPositionToDocument(docRow.row, docRow.column); + else pos = this.$clipPositionToDocument(docRow, docColumn); + + docRow = pos.row; + docColumn = pos.column; + + var screenRow = 0; + var foldStartRow = null; + var fold = null; + fold = this.getFoldAt(docRow, docColumn, 1); + if (fold) { + docRow = fold.start.row; + docColumn = fold.start.column; + } + + var rowEnd, + row = 0; + + var rowCache = this.$docRowCache; + var i = this.$getRowCacheIndex(rowCache, docRow); + var l = rowCache.length; + if (l && i >= 0) { + var row = rowCache[i]; + var screenRow = this.$screenRowCache[i]; + var doCache = docRow > rowCache[l - 1]; + } else { + var doCache = !l; + } + + var foldLine = this.getNextFoldLine(row); + var foldStart = foldLine ? foldLine.start.row : Infinity; + + while (row < docRow) { + if (row >= foldStart) { + rowEnd = foldLine.end.row + 1; + if (rowEnd > docRow) break; + foldLine = this.getNextFoldLine(rowEnd, foldLine); + foldStart = foldLine ? foldLine.start.row : Infinity; + } else { + rowEnd = row + 1; + } + + screenRow += this.getRowLength(row); + row = rowEnd; + + if (doCache) { + this.$docRowCache.push(row); + this.$screenRowCache.push(screenRow); + } + } + var textLine = ""; + if (foldLine && row >= foldStart) { + textLine = this.getFoldDisplayLine(foldLine, docRow, docColumn); + foldStartRow = foldLine.start.row; + } else { + textLine = this.getLine(docRow).substring(0, docColumn); + foldStartRow = docRow; + } + var wrapIndent = 0; + if (this.$useWrapMode) { + var wrapRow = this.$wrapData[foldStartRow]; + if (wrapRow) { + var screenRowOffset = 0; + while (textLine.length >= wrapRow[screenRowOffset]) { + screenRow++; + screenRowOffset++; + } + textLine = textLine.substring( + wrapRow[screenRowOffset - 1] || 0, + textLine.length + ); + wrapIndent = screenRowOffset > 0 ? wrapRow.indent : 0; + } + } + + if ( + this.lineWidgets && + this.lineWidgets[row] && + this.lineWidgets[row].rowsAbove + ) + screenRow += this.lineWidgets[row].rowsAbove; + + return { + row: screenRow, + column: wrapIndent + this.$getStringScreenWidth(textLine)[0] + }; + }; + this.documentToScreenColumn = function(row, docColumn) { + return this.documentToScreenPosition(row, docColumn).column; + }; + this.documentToScreenRow = function(docRow, docColumn) { + return this.documentToScreenPosition(docRow, docColumn).row; + }; + this.getScreenLength = function() { + var screenRows = 0; + var fold = null; + if (!this.$useWrapMode) { + screenRows = this.getLength(); + var foldData = this.$foldData; + for (var i = 0; i < foldData.length; i++) { + fold = foldData[i]; + screenRows -= fold.end.row - fold.start.row; + } + } else { + var lastRow = this.$wrapData.length; + var row = 0, + i = 0; + var fold = this.$foldData[i++]; + var foldStart = fold ? fold.start.row : Infinity; + + while (row < lastRow) { + var splits = this.$wrapData[row]; + screenRows += splits ? splits.length + 1 : 1; + row++; + if (row > foldStart) { + row = fold.end.row + 1; + fold = this.$foldData[i++]; + foldStart = fold ? fold.start.row : Infinity; + } + } + } + if (this.lineWidgets) screenRows += this.$getWidgetScreenLength(); + + return screenRows; + }; + this.$setFontMetrics = function(fm) { + if (!this.$enableVarChar) return; + this.$getStringScreenWidth = function( + str, + maxScreenColumn, + screenColumn + ) { + if (maxScreenColumn === 0) return [0, 0]; + if (!maxScreenColumn) maxScreenColumn = Infinity; + screenColumn = screenColumn || 0; + + var c, column; + for (column = 0; column < str.length; column++) { + c = str.charAt(column); + if (c === "\t") { + screenColumn += this.getScreenTabSize(screenColumn); + } else { + screenColumn += fm.getCharacterWidth(c); + } + if (screenColumn > maxScreenColumn) { + break; + } + } + + return [screenColumn, column]; + }; + }; + + this.destroy = function() { + if (this.bgTokenizer) { + this.bgTokenizer.setDocument(null); + this.bgTokenizer = null; + } + this.$stopWorker(); + this.removeAllListeners(); + this.selection.detach(); + }; + + this.isFullWidth = isFullWidth; + function isFullWidth(c) { + if (c < 0x1100) return false; + return ( + (c >= 0x1100 && c <= 0x115f) || + (c >= 0x11a3 && c <= 0x11a7) || + (c >= 0x11fa && c <= 0x11ff) || + (c >= 0x2329 && c <= 0x232a) || + (c >= 0x2e80 && c <= 0x2e99) || + (c >= 0x2e9b && c <= 0x2ef3) || + (c >= 0x2f00 && c <= 0x2fd5) || + (c >= 0x2ff0 && c <= 0x2ffb) || + (c >= 0x3000 && c <= 0x303e) || + (c >= 0x3041 && c <= 0x3096) || + (c >= 0x3099 && c <= 0x30ff) || + (c >= 0x3105 && c <= 0x312d) || + (c >= 0x3131 && c <= 0x318e) || + (c >= 0x3190 && c <= 0x31ba) || + (c >= 0x31c0 && c <= 0x31e3) || + (c >= 0x31f0 && c <= 0x321e) || + (c >= 0x3220 && c <= 0x3247) || + (c >= 0x3250 && c <= 0x32fe) || + (c >= 0x3300 && c <= 0x4dbf) || + (c >= 0x4e00 && c <= 0xa48c) || + (c >= 0xa490 && c <= 0xa4c6) || + (c >= 0xa960 && c <= 0xa97c) || + (c >= 0xac00 && c <= 0xd7a3) || + (c >= 0xd7b0 && c <= 0xd7c6) || + (c >= 0xd7cb && c <= 0xd7fb) || + (c >= 0xf900 && c <= 0xfaff) || + (c >= 0xfe10 && c <= 0xfe19) || + (c >= 0xfe30 && c <= 0xfe52) || + (c >= 0xfe54 && c <= 0xfe66) || + (c >= 0xfe68 && c <= 0xfe6b) || + (c >= 0xff01 && c <= 0xff60) || + (c >= 0xffe0 && c <= 0xffe6) + ); + } + }.call(EditSession.prototype)); + + require("./edit_session/folding").Folding.call(EditSession.prototype); + require("./edit_session/bracket_match").BracketMatch.call( + EditSession.prototype + ); + + config.defineOptions(EditSession.prototype, "session", { + wrap: { + set: function(value) { + if (!value || value == "off") value = false; + else if (value == "free") value = true; + else if (value == "printMargin") value = -1; + else if (typeof value == "string") + value = parseInt(value, 10) || false; + + if (this.$wrap == value) return; + this.$wrap = value; + if (!value) { + this.setUseWrapMode(false); + } else { + var col = typeof value == "number" ? value : null; + this.setWrapLimitRange(col, col); + this.setUseWrapMode(true); + } + }, + get: function() { + if (this.getUseWrapMode()) { + if (this.$wrap == -1) return "printMargin"; + if (!this.getWrapLimitRange().min) return "free"; + return this.$wrap; + } + return "off"; + }, + handlesSet: true + }, + wrapMethod: { + set: function(val) { + val = val == "auto" ? this.$mode.type != "text" : val != "text"; + if (val != this.$wrapAsCode) { + this.$wrapAsCode = val; + if (this.$useWrapMode) { + this.$useWrapMode = false; + this.setUseWrapMode(true); + } + } + }, + initialValue: "auto" + }, + indentedSoftWrap: { + set: function() { + if (this.$useWrapMode) { + this.$useWrapMode = false; + this.setUseWrapMode(true); + } + }, + initialValue: true + }, + firstLineNumber: { + set: function() { + this._signal("changeBreakpoint"); + }, + initialValue: 1 + }, + useWorker: { + set: function(useWorker) { + this.$useWorker = useWorker; + + this.$stopWorker(); + if (useWorker) this.$startWorker(); + }, + initialValue: true + }, + useSoftTabs: { initialValue: true }, + tabSize: { + set: function(tabSize) { + tabSize = parseInt(tabSize); + if (tabSize > 0 && this.$tabSize !== tabSize) { + this.$modified = true; + this.$rowLengthCache = []; + this.$tabSize = tabSize; + this._signal("changeTabSize"); + } + }, + initialValue: 4, + handlesSet: true + }, + navigateWithinSoftTabs: { initialValue: false }, + foldStyle: { + set: function(val) { + this.setFoldStyle(val); + }, + handlesSet: true + }, + overwrite: { + set: function(val) { + this._signal("changeOverwrite"); + }, + initialValue: false + }, + newLineMode: { + set: function(val) { + this.doc.setNewLineMode(val); + }, + get: function() { + return this.doc.getNewLineMode(); + }, + handlesSet: true + }, + mode: { + set: function(val) { + this.setMode(val); + }, + get: function() { + return this.$modeId; + }, + handlesSet: true + } + }); + + exports.EditSession = EditSession; + } +); + +ace.define( + "ace/search", + ["require", "exports", "module", "ace/lib/lang", "ace/lib/oop", "ace/range"], + function(require, exports, module) { + "use strict"; + + var lang = require("./lib/lang"); + var oop = require("./lib/oop"); + var Range = require("./range").Range; + + var Search = function() { + this.$options = {}; + }; + + (function() { + this.set = function(options) { + oop.mixin(this.$options, options); + return this; + }; + this.getOptions = function() { + return lang.copyObject(this.$options); + }; + this.setOptions = function(options) { + this.$options = options; + }; + this.find = function(session) { + var options = this.$options; + var iterator = this.$matchIterator(session, options); + if (!iterator) return false; + + var firstRange = null; + iterator.forEach(function(sr, sc, er, ec) { + firstRange = new Range(sr, sc, er, ec); + if ( + sc == ec && + options.start && + options.start.start && + options.skipCurrent != false && + firstRange.isEqual(options.start) + ) { + firstRange = null; + return false; + } + + return true; + }); + + return firstRange; + }; + this.findAll = function(session) { + var options = this.$options; + if (!options.needle) return []; + this.$assembleRegExp(options); + + var range = options.range; + var lines = range + ? session.getLines(range.start.row, range.end.row) + : session.doc.getAllLines(); + + var ranges = []; + var re = options.re; + if (options.$isMultiLine) { + var len = re.length; + var maxRow = lines.length - len; + var prevRange; + outer: for (var row = re.offset || 0; row <= maxRow; row++) { + for (var j = 0; j < len; j++) + if (lines[row + j].search(re[j]) == -1) continue outer; + + var startLine = lines[row]; + var line = lines[row + len - 1]; + var startIndex = + startLine.length - startLine.match(re[0])[0].length; + var endIndex = line.match(re[len - 1])[0].length; + + if ( + prevRange && + prevRange.end.row === row && + prevRange.end.column > startIndex + ) { + continue; + } + ranges.push( + (prevRange = new Range(row, startIndex, row + len - 1, endIndex)) + ); + if (len > 2) row = row + len - 2; + } + } else { + for (var i = 0; i < lines.length; i++) { + var matches = lang.getMatchOffsets(lines[i], re); + for (var j = 0; j < matches.length; j++) { + var match = matches[j]; + ranges.push( + new Range(i, match.offset, i, match.offset + match.length) + ); + } + } + } + + if (range) { + var startColumn = range.start.column; + var endColumn = range.start.column; + var i = 0, + j = ranges.length - 1; + while ( + i < j && + ranges[i].start.column < startColumn && + ranges[i].start.row == range.start.row + ) + i++; + + while ( + i < j && + ranges[j].end.column > endColumn && + ranges[j].end.row == range.end.row + ) + j--; + + ranges = ranges.slice(i, j + 1); + for (i = 0, j = ranges.length; i < j; i++) { + ranges[i].start.row += range.start.row; + ranges[i].end.row += range.start.row; + } + } + + return ranges; + }; + this.replace = function(input, replacement) { + var options = this.$options; + + var re = this.$assembleRegExp(options); + if (options.$isMultiLine) return replacement; + + if (!re) return; + + var match = re.exec(input); + if (!match || match[0].length != input.length) return null; + + replacement = input.replace(re, replacement); + if (options.preserveCase) { + replacement = replacement.split(""); + for (var i = Math.min(input.length, input.length); i--; ) { + var ch = input[i]; + if (ch && ch.toLowerCase() != ch) + replacement[i] = replacement[i].toUpperCase(); + else replacement[i] = replacement[i].toLowerCase(); + } + replacement = replacement.join(""); + } + + return replacement; + }; + + this.$assembleRegExp = function(options, $disableFakeMultiline) { + if (options.needle instanceof RegExp) + return (options.re = options.needle); + + var needle = options.needle; + + if (!options.needle) return (options.re = false); + + if (!options.regExp) needle = lang.escapeRegExp(needle); + + if (options.wholeWord) needle = addWordBoundary(needle, options); + + var modifier = options.caseSensitive ? "gm" : "gmi"; + + options.$isMultiLine = !$disableFakeMultiline && /[\n\r]/.test(needle); + if (options.$isMultiLine) + return (options.re = this.$assembleMultilineRegExp(needle, modifier)); + + try { + var re = new RegExp(needle, modifier); + } catch (e) { + re = false; + } + return (options.re = re); + }; + + this.$assembleMultilineRegExp = function(needle, modifier) { + var parts = needle.replace(/\r\n|\r|\n/g, "$\n^").split("\n"); + var re = []; + for (var i = 0; i < parts.length; i++) + try { + re.push(new RegExp(parts[i], modifier)); + } catch (e) { + return false; + } + return re; + }; + + this.$matchIterator = function(session, options) { + var re = this.$assembleRegExp(options); + if (!re) return false; + var backwards = options.backwards == true; + var skipCurrent = options.skipCurrent != false; + + var range = options.range; + var start = options.start; + if (!start) + start = range + ? range[backwards ? "end" : "start"] + : session.selection.getRange(); + + if (start.start) + start = start[skipCurrent != backwards ? "end" : "start"]; + + var firstRow = range ? range.start.row : 0; + var lastRow = range ? range.end.row : session.getLength() - 1; + + if (backwards) { + var forEach = function(callback) { + var row = start.row; + if (forEachInLine(row, start.column, callback)) return; + for (row--; row >= firstRow; row--) + if (forEachInLine(row, Number.MAX_VALUE, callback)) return; + if (options.wrap == false) return; + for (row = lastRow, firstRow = start.row; row >= firstRow; row--) + if (forEachInLine(row, Number.MAX_VALUE, callback)) return; + }; + } else { + var forEach = function(callback) { + var row = start.row; + if (forEachInLine(row, start.column, callback)) return; + for (row = row + 1; row <= lastRow; row++) + if (forEachInLine(row, 0, callback)) return; + if (options.wrap == false) return; + for (row = firstRow, lastRow = start.row; row <= lastRow; row++) + if (forEachInLine(row, 0, callback)) return; + }; + } + + if (options.$isMultiLine) { + var len = re.length; + var forEachInLine = function(row, offset, callback) { + var startRow = backwards ? row - len + 1 : row; + if (startRow < 0) return; + var line = session.getLine(startRow); + var startIndex = line.search(re[0]); + if ((!backwards && startIndex < offset) || startIndex === -1) + return; + for (var i = 1; i < len; i++) { + line = session.getLine(startRow + i); + if (line.search(re[i]) == -1) return; + } + var endIndex = line.match(re[len - 1])[0].length; + if (backwards && endIndex > offset) return; + if (callback(startRow, startIndex, startRow + len - 1, endIndex)) + return true; + }; + } else if (backwards) { + var forEachInLine = function(row, endIndex, callback) { + var line = session.getLine(row); + var matches = []; + var m, + last = 0; + re.lastIndex = 0; + while ((m = re.exec(line))) { + var length = m[0].length; + last = m.index; + if (!length) { + if (last >= line.length) break; + re.lastIndex = last += 1; + } + if (m.index + length > endIndex) break; + matches.push(m.index, length); + } + for (var i = matches.length - 1; i >= 0; i -= 2) { + var column = matches[i - 1]; + var length = matches[i]; + if (callback(row, column, row, column + length)) return true; + } + }; + } else { + var forEachInLine = function(row, startIndex, callback) { + var line = session.getLine(row); + var last; + var m; + re.lastIndex = startIndex; + while ((m = re.exec(line))) { + var length = m[0].length; + last = m.index; + if (callback(row, last, row, last + length)) return true; + if (!length) { + re.lastIndex = last += 1; + if (last >= line.length) return false; + } + } + }; + } + return { forEach: forEach }; + }; + }.call(Search.prototype)); + + function addWordBoundary(needle, options) { + function wordBoundary(c) { + if (/\w/.test(c) || options.regExp) return "\\b"; + return ""; + } + return ( + wordBoundary(needle[0]) + + needle + + wordBoundary(needle[needle.length - 1]) + ); + } + + exports.Search = Search; + } +); + +ace.define( + "ace/keyboard/hash_handler", + ["require", "exports", "module", "ace/lib/keys", "ace/lib/useragent"], + function(require, exports, module) { + "use strict"; + + var keyUtil = require("../lib/keys"); + var useragent = require("../lib/useragent"); + var KEY_MODS = keyUtil.KEY_MODS; + + function HashHandler(config, platform) { + this.platform = platform || (useragent.isMac ? "mac" : "win"); + this.commands = {}; + this.commandKeyBinding = {}; + this.addCommands(config); + this.$singleCommand = true; + } + + function MultiHashHandler(config, platform) { + HashHandler.call(this, config, platform); + this.$singleCommand = false; + } + + MultiHashHandler.prototype = HashHandler.prototype; + + (function() { + this.addCommand = function(command) { + if (this.commands[command.name]) this.removeCommand(command); + + this.commands[command.name] = command; + + if (command.bindKey) this._buildKeyHash(command); + }; + + this.removeCommand = function(command, keepCommand) { + var name = + command && (typeof command === "string" ? command : command.name); + command = this.commands[name]; + if (!keepCommand) delete this.commands[name]; + var ckb = this.commandKeyBinding; + for (var keyId in ckb) { + var cmdGroup = ckb[keyId]; + if (cmdGroup == command) { + delete ckb[keyId]; + } else if (Array.isArray(cmdGroup)) { + var i = cmdGroup.indexOf(command); + if (i != -1) { + cmdGroup.splice(i, 1); + if (cmdGroup.length == 1) ckb[keyId] = cmdGroup[0]; + } + } + } + }; + + this.bindKey = function(key, command, position) { + if (typeof key == "object" && key) { + if (position == undefined) position = key.position; + key = key[this.platform]; + } + if (!key) return; + if (typeof command == "function") + return this.addCommand({ + exec: command, + bindKey: key, + name: command.name || key + }); + + key.split("|").forEach(function(keyPart) { + var chain = ""; + if (keyPart.indexOf(" ") != -1) { + var parts = keyPart.split(/\s+/); + keyPart = parts.pop(); + parts.forEach(function(keyPart) { + var binding = this.parseKeys(keyPart); + var id = KEY_MODS[binding.hashId] + binding.key; + chain += (chain ? " " : "") + id; + this._addCommandToBinding(chain, "chainKeys"); + }, this); + chain += " "; + } + var binding = this.parseKeys(keyPart); + var id = KEY_MODS[binding.hashId] + binding.key; + this._addCommandToBinding(chain + id, command, position); + }, this); + }; + + function getPosition(command) { + return ( + (typeof command == "object" && + command.bindKey && + command.bindKey.position) || + (command.isDefault ? -100 : 0) + ); + } + this._addCommandToBinding = function(keyId, command, position) { + var ckb = this.commandKeyBinding, + i; + if (!command) { + delete ckb[keyId]; + } else if (!ckb[keyId] || this.$singleCommand) { + ckb[keyId] = command; + } else { + if (!Array.isArray(ckb[keyId])) { + ckb[keyId] = [ckb[keyId]]; + } else if ((i = ckb[keyId].indexOf(command)) != -1) { + ckb[keyId].splice(i, 1); + } + + if (typeof position != "number") { + position = getPosition(command); + } + + var commands = ckb[keyId]; + for (i = 0; i < commands.length; i++) { + var other = commands[i]; + var otherPos = getPosition(other); + if (otherPos > position) break; + } + commands.splice(i, 0, command); + } + }; + + this.addCommands = function(commands) { + commands && + Object.keys(commands).forEach(function(name) { + var command = commands[name]; + if (!command) return; + + if (typeof command === "string") return this.bindKey(command, name); + + if (typeof command === "function") command = { exec: command }; + + if (typeof command !== "object") return; + + if (!command.name) command.name = name; + + this.addCommand(command); + }, this); + }; + + this.removeCommands = function(commands) { + Object.keys(commands).forEach(function(name) { + this.removeCommand(commands[name]); + }, this); + }; + + this.bindKeys = function(keyList) { + Object.keys(keyList).forEach(function(key) { + this.bindKey(key, keyList[key]); + }, this); + }; + + this._buildKeyHash = function(command) { + this.bindKey(command.bindKey, command); + }; + this.parseKeys = function(keys) { + var parts = keys + .toLowerCase() + .split(/[\-\+]([\-\+])?/) + .filter(function(x) { + return x; + }); + var key = parts.pop(); + + var keyCode = keyUtil[key]; + if (keyUtil.FUNCTION_KEYS[keyCode]) + key = keyUtil.FUNCTION_KEYS[keyCode].toLowerCase(); + else if (!parts.length) return { key: key, hashId: -1 }; + else if (parts.length == 1 && parts[0] == "shift") + return { key: key.toUpperCase(), hashId: -1 }; + + var hashId = 0; + for (var i = parts.length; i--; ) { + var modifier = keyUtil.KEY_MODS[parts[i]]; + if (modifier == null) { + if (typeof console != "undefined") + console.error("invalid modifier " + parts[i] + " in " + keys); + return false; + } + hashId |= modifier; + } + return { key: key, hashId: hashId }; + }; + + this.findKeyCommand = function findKeyCommand(hashId, keyString) { + var key = KEY_MODS[hashId] + keyString; + return this.commandKeyBinding[key]; + }; + + this.handleKeyboard = function(data, hashId, keyString, keyCode) { + if (keyCode < 0) return; + var key = KEY_MODS[hashId] + keyString; + var command = this.commandKeyBinding[key]; + if (data.$keyChain) { + data.$keyChain += " " + key; + command = this.commandKeyBinding[data.$keyChain] || command; + } + + if (command) { + if ( + command == "chainKeys" || + command[command.length - 1] == "chainKeys" + ) { + data.$keyChain = data.$keyChain || key; + return { command: "null" }; + } + } + + if (data.$keyChain) { + if ((!hashId || hashId == 4) && keyString.length == 1) + data.$keyChain = data.$keyChain.slice(0, -key.length - 1); + // wait for input + else if (hashId == -1 || keyCode > 0) data.$keyChain = ""; // reset keyChain + } + return { command: command }; + }; + + this.getStatusText = function(editor, data) { + return data.$keyChain || ""; + }; + }.call(HashHandler.prototype)); + + exports.HashHandler = HashHandler; + exports.MultiHashHandler = MultiHashHandler; + } +); + +ace.define( + "ace/commands/command_manager", + [ + "require", + "exports", + "module", + "ace/lib/oop", + "ace/keyboard/hash_handler", + "ace/lib/event_emitter" + ], + function(require, exports, module) { + "use strict"; + + var oop = require("../lib/oop"); + var MultiHashHandler = require("../keyboard/hash_handler").MultiHashHandler; + var EventEmitter = require("../lib/event_emitter").EventEmitter; + + var CommandManager = function(platform, commands) { + MultiHashHandler.call(this, commands, platform); + this.byName = this.commands; + this.setDefaultHandler("exec", function(e) { + return e.command.exec(e.editor, e.args || {}); + }); + }; + + oop.inherits(CommandManager, MultiHashHandler); + + (function() { + oop.implement(this, EventEmitter); + + this.exec = function(command, editor, args) { + if (Array.isArray(command)) { + for (var i = command.length; i--; ) { + if (this.exec(command[i], editor, args)) return true; + } + return false; + } + + if (typeof command === "string") command = this.commands[command]; + + if (!command) return false; + + if (editor && editor.$readOnly && !command.readOnly) return false; + + if ( + this.$checkCommandState != false && + command.isAvailable && + !command.isAvailable(editor) + ) + return false; + + var e = { editor: editor, command: command, args: args }; + e.returnValue = this._emit("exec", e); + this._signal("afterExec", e); + + return e.returnValue === false ? false : true; + }; + + this.toggleRecording = function(editor) { + if (this.$inReplay) return; + + editor && editor._emit("changeStatus"); + if (this.recording) { + this.macro.pop(); + this.off("exec", this.$addCommandToMacro); + + if (!this.macro.length) this.macro = this.oldMacro; + + return (this.recording = false); + } + if (!this.$addCommandToMacro) { + this.$addCommandToMacro = function(e) { + this.macro.push([e.command, e.args]); + }.bind(this); + } + + this.oldMacro = this.macro; + this.macro = []; + this.on("exec", this.$addCommandToMacro); + return (this.recording = true); + }; + + this.replay = function(editor) { + if (this.$inReplay || !this.macro) return; + + if (this.recording) return this.toggleRecording(editor); + + try { + this.$inReplay = true; + this.macro.forEach(function(x) { + if (typeof x == "string") this.exec(x, editor); + else this.exec(x[0], editor, x[1]); + }, this); + } finally { + this.$inReplay = false; + } + }; + + this.trimMacro = function(m) { + return m.map(function(x) { + if (typeof x[0] != "string") x[0] = x[0].name; + if (!x[1]) x = x[0]; + return x; + }); + }; + }.call(CommandManager.prototype)); + + exports.CommandManager = CommandManager; + } +); + +ace.define( + "ace/commands/default_commands", + ["require", "exports", "module", "ace/lib/lang", "ace/config", "ace/range"], + function(require, exports, module) { + "use strict"; + + var lang = require("../lib/lang"); + var config = require("../config"); + var Range = require("../range").Range; + + function bindKey(win, mac) { + return { win: win, mac: mac }; + } + exports.commands = [ + { + name: "showSettingsMenu", + bindKey: bindKey("Ctrl-,", "Command-,"), + exec: function(editor) { + config.loadModule("ace/ext/settings_menu", function(module) { + module.init(editor); + editor.showSettingsMenu(); + }); + }, + readOnly: true + }, + { + name: "goToNextError", + bindKey: bindKey("Alt-E", "F4"), + exec: function(editor) { + config.loadModule("./ext/error_marker", function(module) { + module.showErrorMarker(editor, 1); + }); + }, + scrollIntoView: "animate", + readOnly: true + }, + { + name: "goToPreviousError", + bindKey: bindKey("Alt-Shift-E", "Shift-F4"), + exec: function(editor) { + config.loadModule("./ext/error_marker", function(module) { + module.showErrorMarker(editor, -1); + }); + }, + scrollIntoView: "animate", + readOnly: true + }, + { + name: "selectall", + description: "Select all", + bindKey: bindKey("Ctrl-A", "Command-A"), + exec: function(editor) { + editor.selectAll(); + }, + readOnly: true + }, + { + name: "centerselection", + description: "Center selection", + bindKey: bindKey(null, "Ctrl-L"), + exec: function(editor) { + editor.centerSelection(); + }, + readOnly: true + }, + { + name: "gotoline", + description: "Go to line...", + bindKey: bindKey("Ctrl-L", "Command-L"), + exec: function(editor, line) { + if (typeof line === "number" && !isNaN(line)) editor.gotoLine(line); + editor.prompt({ $type: "gotoLine" }); + }, + readOnly: true + }, + { + name: "fold", + bindKey: bindKey("Alt-L|Ctrl-F1", "Command-Alt-L|Command-F1"), + exec: function(editor) { + editor.session.toggleFold(false); + }, + multiSelectAction: "forEach", + scrollIntoView: "center", + readOnly: true + }, + { + name: "unfold", + bindKey: bindKey( + "Alt-Shift-L|Ctrl-Shift-F1", + "Command-Alt-Shift-L|Command-Shift-F1" + ), + exec: function(editor) { + editor.session.toggleFold(true); + }, + multiSelectAction: "forEach", + scrollIntoView: "center", + readOnly: true + }, + { + name: "toggleFoldWidget", + bindKey: bindKey("F2", "F2"), + exec: function(editor) { + editor.session.toggleFoldWidget(); + }, + multiSelectAction: "forEach", + scrollIntoView: "center", + readOnly: true + }, + { + name: "toggleParentFoldWidget", + bindKey: bindKey("Alt-F2", "Alt-F2"), + exec: function(editor) { + editor.session.toggleFoldWidget(true); + }, + multiSelectAction: "forEach", + scrollIntoView: "center", + readOnly: true + }, + { + name: "foldall", + description: "Fold all", + bindKey: bindKey(null, "Ctrl-Command-Option-0"), + exec: function(editor) { + editor.session.foldAll(); + }, + scrollIntoView: "center", + readOnly: true + }, + { + name: "foldOther", + description: "Fold other", + bindKey: bindKey("Alt-0", "Command-Option-0"), + exec: function(editor) { + editor.session.foldAll(); + editor.session.unfold(editor.selection.getAllRanges()); + }, + scrollIntoView: "center", + readOnly: true + }, + { + name: "unfoldall", + description: "Unfold all", + bindKey: bindKey("Alt-Shift-0", "Command-Option-Shift-0"), + exec: function(editor) { + editor.session.unfold(); + }, + scrollIntoView: "center", + readOnly: true + }, + { + name: "findnext", + description: "Find next", + bindKey: bindKey("Ctrl-K", "Command-G"), + exec: function(editor) { + editor.findNext(); + }, + multiSelectAction: "forEach", + scrollIntoView: "center", + readOnly: true + }, + { + name: "findprevious", + description: "Find previous", + bindKey: bindKey("Ctrl-Shift-K", "Command-Shift-G"), + exec: function(editor) { + editor.findPrevious(); + }, + multiSelectAction: "forEach", + scrollIntoView: "center", + readOnly: true + }, + { + name: "selectOrFindNext", + description: "Select or find next", + bindKey: bindKey("Alt-K", "Ctrl-G"), + exec: function(editor) { + if (editor.selection.isEmpty()) editor.selection.selectWord(); + else editor.findNext(); + }, + readOnly: true + }, + { + name: "selectOrFindPrevious", + description: "Select or find previous", + bindKey: bindKey("Alt-Shift-K", "Ctrl-Shift-G"), + exec: function(editor) { + if (editor.selection.isEmpty()) editor.selection.selectWord(); + else editor.findPrevious(); + }, + readOnly: true + }, + { + name: "find", + description: "Find", + bindKey: bindKey("Ctrl-F", "Command-F"), + exec: function(editor) { + config.loadModule("ace/ext/searchbox", function(e) { + e.Search(editor); + }); + }, + readOnly: true + }, + { + name: "overwrite", + description: "Overwrite", + bindKey: "Insert", + exec: function(editor) { + editor.toggleOverwrite(); + }, + readOnly: true + }, + { + name: "selecttostart", + description: "Select to start", + bindKey: bindKey( + "Ctrl-Shift-Home", + "Command-Shift-Home|Command-Shift-Up" + ), + exec: function(editor) { + editor.getSelection().selectFileStart(); + }, + multiSelectAction: "forEach", + readOnly: true, + scrollIntoView: "animate", + aceCommandGroup: "fileJump" + }, + { + name: "gotostart", + description: "Go to start", + bindKey: bindKey("Ctrl-Home", "Command-Home|Command-Up"), + exec: function(editor) { + editor.navigateFileStart(); + }, + multiSelectAction: "forEach", + readOnly: true, + scrollIntoView: "animate", + aceCommandGroup: "fileJump" + }, + { + name: "selectup", + description: "Select up", + bindKey: bindKey("Shift-Up", "Shift-Up|Ctrl-Shift-P"), + exec: function(editor) { + editor.getSelection().selectUp(); + }, + multiSelectAction: "forEach", + scrollIntoView: "cursor", + readOnly: true + }, + { + name: "golineup", + description: "Go line up", + bindKey: bindKey("Up", "Up|Ctrl-P"), + exec: function(editor, args) { + editor.navigateUp(args.times); + }, + multiSelectAction: "forEach", + scrollIntoView: "cursor", + readOnly: true + }, + { + name: "selecttoend", + description: "Select to end", + bindKey: bindKey( + "Ctrl-Shift-End", + "Command-Shift-End|Command-Shift-Down" + ), + exec: function(editor) { + editor.getSelection().selectFileEnd(); + }, + multiSelectAction: "forEach", + readOnly: true, + scrollIntoView: "animate", + aceCommandGroup: "fileJump" + }, + { + name: "gotoend", + description: "Go to end", + bindKey: bindKey("Ctrl-End", "Command-End|Command-Down"), + exec: function(editor) { + editor.navigateFileEnd(); + }, + multiSelectAction: "forEach", + readOnly: true, + scrollIntoView: "animate", + aceCommandGroup: "fileJump" + }, + { + name: "selectdown", + description: "Select down", + bindKey: bindKey("Shift-Down", "Shift-Down|Ctrl-Shift-N"), + exec: function(editor) { + editor.getSelection().selectDown(); + }, + multiSelectAction: "forEach", + scrollIntoView: "cursor", + readOnly: true + }, + { + name: "golinedown", + description: "Go line down", + bindKey: bindKey("Down", "Down|Ctrl-N"), + exec: function(editor, args) { + editor.navigateDown(args.times); + }, + multiSelectAction: "forEach", + scrollIntoView: "cursor", + readOnly: true + }, + { + name: "selectwordleft", + description: "Select word left", + bindKey: bindKey("Ctrl-Shift-Left", "Option-Shift-Left"), + exec: function(editor) { + editor.getSelection().selectWordLeft(); + }, + multiSelectAction: "forEach", + scrollIntoView: "cursor", + readOnly: true + }, + { + name: "gotowordleft", + description: "Go to word left", + bindKey: bindKey("Ctrl-Left", "Option-Left"), + exec: function(editor) { + editor.navigateWordLeft(); + }, + multiSelectAction: "forEach", + scrollIntoView: "cursor", + readOnly: true + }, + { + name: "selecttolinestart", + description: "Select to line start", + bindKey: bindKey("Alt-Shift-Left", "Command-Shift-Left|Ctrl-Shift-A"), + exec: function(editor) { + editor.getSelection().selectLineStart(); + }, + multiSelectAction: "forEach", + scrollIntoView: "cursor", + readOnly: true + }, + { + name: "gotolinestart", + description: "Go to line start", + bindKey: bindKey("Alt-Left|Home", "Command-Left|Home|Ctrl-A"), + exec: function(editor) { + editor.navigateLineStart(); + }, + multiSelectAction: "forEach", + scrollIntoView: "cursor", + readOnly: true + }, + { + name: "selectleft", + description: "Select left", + bindKey: bindKey("Shift-Left", "Shift-Left|Ctrl-Shift-B"), + exec: function(editor) { + editor.getSelection().selectLeft(); + }, + multiSelectAction: "forEach", + scrollIntoView: "cursor", + readOnly: true + }, + { + name: "gotoleft", + description: "Go to left", + bindKey: bindKey("Left", "Left|Ctrl-B"), + exec: function(editor, args) { + editor.navigateLeft(args.times); + }, + multiSelectAction: "forEach", + scrollIntoView: "cursor", + readOnly: true + }, + { + name: "selectwordright", + description: "Select word right", + bindKey: bindKey("Ctrl-Shift-Right", "Option-Shift-Right"), + exec: function(editor) { + editor.getSelection().selectWordRight(); + }, + multiSelectAction: "forEach", + scrollIntoView: "cursor", + readOnly: true + }, + { + name: "gotowordright", + description: "Go to word right", + bindKey: bindKey("Ctrl-Right", "Option-Right"), + exec: function(editor) { + editor.navigateWordRight(); + }, + multiSelectAction: "forEach", + scrollIntoView: "cursor", + readOnly: true + }, + { + name: "selecttolineend", + description: "Select to line end", + bindKey: bindKey( + "Alt-Shift-Right", + "Command-Shift-Right|Shift-End|Ctrl-Shift-E" + ), + exec: function(editor) { + editor.getSelection().selectLineEnd(); + }, + multiSelectAction: "forEach", + scrollIntoView: "cursor", + readOnly: true + }, + { + name: "gotolineend", + description: "Go to line end", + bindKey: bindKey("Alt-Right|End", "Command-Right|End|Ctrl-E"), + exec: function(editor) { + editor.navigateLineEnd(); + }, + multiSelectAction: "forEach", + scrollIntoView: "cursor", + readOnly: true + }, + { + name: "selectright", + description: "Select right", + bindKey: bindKey("Shift-Right", "Shift-Right"), + exec: function(editor) { + editor.getSelection().selectRight(); + }, + multiSelectAction: "forEach", + scrollIntoView: "cursor", + readOnly: true + }, + { + name: "gotoright", + description: "Go to right", + bindKey: bindKey("Right", "Right|Ctrl-F"), + exec: function(editor, args) { + editor.navigateRight(args.times); + }, + multiSelectAction: "forEach", + scrollIntoView: "cursor", + readOnly: true + }, + { + name: "selectpagedown", + description: "Select page down", + bindKey: "Shift-PageDown", + exec: function(editor) { + editor.selectPageDown(); + }, + readOnly: true + }, + { + name: "pagedown", + description: "Page down", + bindKey: bindKey(null, "Option-PageDown"), + exec: function(editor) { + editor.scrollPageDown(); + }, + readOnly: true + }, + { + name: "gotopagedown", + description: "Go to page down", + bindKey: bindKey("PageDown", "PageDown|Ctrl-V"), + exec: function(editor) { + editor.gotoPageDown(); + }, + readOnly: true + }, + { + name: "selectpageup", + description: "Select page up", + bindKey: "Shift-PageUp", + exec: function(editor) { + editor.selectPageUp(); + }, + readOnly: true + }, + { + name: "pageup", + description: "Page up", + bindKey: bindKey(null, "Option-PageUp"), + exec: function(editor) { + editor.scrollPageUp(); + }, + readOnly: true + }, + { + name: "gotopageup", + description: "Go to page up", + bindKey: "PageUp", + exec: function(editor) { + editor.gotoPageUp(); + }, + readOnly: true + }, + { + name: "scrollup", + description: "Scroll up", + bindKey: bindKey("Ctrl-Up", null), + exec: function(e) { + e.renderer.scrollBy(0, -2 * e.renderer.layerConfig.lineHeight); + }, + readOnly: true + }, + { + name: "scrolldown", + description: "Scroll down", + bindKey: bindKey("Ctrl-Down", null), + exec: function(e) { + e.renderer.scrollBy(0, 2 * e.renderer.layerConfig.lineHeight); + }, + readOnly: true + }, + { + name: "selectlinestart", + description: "Select line start", + bindKey: "Shift-Home", + exec: function(editor) { + editor.getSelection().selectLineStart(); + }, + multiSelectAction: "forEach", + scrollIntoView: "cursor", + readOnly: true + }, + { + name: "selectlineend", + description: "Select line end", + bindKey: "Shift-End", + exec: function(editor) { + editor.getSelection().selectLineEnd(); + }, + multiSelectAction: "forEach", + scrollIntoView: "cursor", + readOnly: true + }, + { + name: "togglerecording", + description: "Toggle recording", + bindKey: bindKey("Ctrl-Alt-E", "Command-Option-E"), + exec: function(editor) { + editor.commands.toggleRecording(editor); + }, + readOnly: true + }, + { + name: "replaymacro", + description: "Replay macro", + bindKey: bindKey("Ctrl-Shift-E", "Command-Shift-E"), + exec: function(editor) { + editor.commands.replay(editor); + }, + readOnly: true + }, + { + name: "jumptomatching", + description: "Jump to matching", + bindKey: bindKey("Ctrl-\\|Ctrl-P", "Command-\\"), + exec: function(editor) { + editor.jumpToMatching(); + }, + multiSelectAction: "forEach", + scrollIntoView: "animate", + readOnly: true + }, + { + name: "selecttomatching", + description: "Select to matching", + bindKey: bindKey("Ctrl-Shift-\\|Ctrl-Shift-P", "Command-Shift-\\"), + exec: function(editor) { + editor.jumpToMatching(true); + }, + multiSelectAction: "forEach", + scrollIntoView: "animate", + readOnly: true + }, + { + name: "expandToMatching", + description: "Expand to matching", + bindKey: bindKey("Ctrl-Shift-M", "Ctrl-Shift-M"), + exec: function(editor) { + editor.jumpToMatching(true, true); + }, + multiSelectAction: "forEach", + scrollIntoView: "animate", + readOnly: true + }, + { + name: "passKeysToBrowser", + description: "Pass keys to browser", + bindKey: bindKey(null, null), + exec: function() {}, + passEvent: true, + readOnly: true + }, + { + name: "copy", + description: "Copy", + exec: function(editor) {}, + readOnly: true + }, + { + name: "cut", + description: "Cut", + exec: function(editor) { + var cutLine = + editor.$copyWithEmptySelection && editor.selection.isEmpty(); + var range = cutLine + ? editor.selection.getLineRange() + : editor.selection.getRange(); + editor._emit("cut", range); + + if (!range.isEmpty()) editor.session.remove(range); + editor.clearSelection(); + }, + scrollIntoView: "cursor", + multiSelectAction: "forEach" + }, + { + name: "paste", + description: "Paste", + exec: function(editor, args) { + editor.$handlePaste(args); + }, + scrollIntoView: "cursor" + }, + { + name: "removeline", + description: "Remove line", + bindKey: bindKey("Ctrl-D", "Command-D"), + exec: function(editor) { + editor.removeLines(); + }, + scrollIntoView: "cursor", + multiSelectAction: "forEachLine" + }, + { + name: "duplicateSelection", + description: "Duplicate selection", + bindKey: bindKey("Ctrl-Shift-D", "Command-Shift-D"), + exec: function(editor) { + editor.duplicateSelection(); + }, + scrollIntoView: "cursor", + multiSelectAction: "forEach" + }, + { + name: "sortlines", + description: "Sort lines", + bindKey: bindKey("Ctrl-Alt-S", "Command-Alt-S"), + exec: function(editor) { + editor.sortLines(); + }, + scrollIntoView: "selection", + multiSelectAction: "forEachLine" + }, + { + name: "togglecomment", + description: "Toggle comment", + bindKey: bindKey("Ctrl-/", "Command-/"), + exec: function(editor) { + editor.toggleCommentLines(); + }, + multiSelectAction: "forEachLine", + scrollIntoView: "selectionPart" + }, + { + name: "toggleBlockComment", + description: "Toggle block comment", + bindKey: bindKey("Ctrl-Shift-/", "Command-Shift-/"), + exec: function(editor) { + editor.toggleBlockComment(); + }, + multiSelectAction: "forEach", + scrollIntoView: "selectionPart" + }, + { + name: "modifyNumberUp", + description: "Modify number up", + bindKey: bindKey("Ctrl-Shift-Up", "Alt-Shift-Up"), + exec: function(editor) { + editor.modifyNumber(1); + }, + scrollIntoView: "cursor", + multiSelectAction: "forEach" + }, + { + name: "modifyNumberDown", + description: "Modify number down", + bindKey: bindKey("Ctrl-Shift-Down", "Alt-Shift-Down"), + exec: function(editor) { + editor.modifyNumber(-1); + }, + scrollIntoView: "cursor", + multiSelectAction: "forEach" + }, + { + name: "replace", + description: "Replace", + bindKey: bindKey("Ctrl-H", "Command-Option-F"), + exec: function(editor) { + config.loadModule("ace/ext/searchbox", function(e) { + e.Search(editor, true); + }); + } + }, + { + name: "undo", + description: "Undo", + bindKey: bindKey("Ctrl-Z", "Command-Z"), + exec: function(editor) { + editor.undo(); + } + }, + { + name: "redo", + description: "Redo", + bindKey: bindKey("Ctrl-Shift-Z|Ctrl-Y", "Command-Shift-Z|Command-Y"), + exec: function(editor) { + editor.redo(); + } + }, + { + name: "copylinesup", + description: "Copy lines up", + bindKey: bindKey("Alt-Shift-Up", "Command-Option-Up"), + exec: function(editor) { + editor.copyLinesUp(); + }, + scrollIntoView: "cursor" + }, + { + name: "movelinesup", + description: "Move lines up", + bindKey: bindKey("Alt-Up", "Option-Up"), + exec: function(editor) { + editor.moveLinesUp(); + }, + scrollIntoView: "cursor" + }, + { + name: "copylinesdown", + description: "Copy lines down", + bindKey: bindKey("Alt-Shift-Down", "Command-Option-Down"), + exec: function(editor) { + editor.copyLinesDown(); + }, + scrollIntoView: "cursor" + }, + { + name: "movelinesdown", + description: "Move lines down", + bindKey: bindKey("Alt-Down", "Option-Down"), + exec: function(editor) { + editor.moveLinesDown(); + }, + scrollIntoView: "cursor" + }, + { + name: "del", + description: "Delete", + bindKey: bindKey("Delete", "Delete|Ctrl-D|Shift-Delete"), + exec: function(editor) { + editor.remove("right"); + }, + multiSelectAction: "forEach", + scrollIntoView: "cursor" + }, + { + name: "backspace", + description: "Backspace", + bindKey: bindKey( + "Shift-Backspace|Backspace", + "Ctrl-Backspace|Shift-Backspace|Backspace|Ctrl-H" + ), + exec: function(editor) { + editor.remove("left"); + }, + multiSelectAction: "forEach", + scrollIntoView: "cursor" + }, + { + name: "cut_or_delete", + description: "Cut or delete", + bindKey: bindKey("Shift-Delete", null), + exec: function(editor) { + if (editor.selection.isEmpty()) { + editor.remove("left"); + } else { + return false; + } + }, + multiSelectAction: "forEach", + scrollIntoView: "cursor" + }, + { + name: "removetolinestart", + description: "Remove to line start", + bindKey: bindKey("Alt-Backspace", "Command-Backspace"), + exec: function(editor) { + editor.removeToLineStart(); + }, + multiSelectAction: "forEach", + scrollIntoView: "cursor" + }, + { + name: "removetolineend", + description: "Remove to line end", + bindKey: bindKey("Alt-Delete", "Ctrl-K|Command-Delete"), + exec: function(editor) { + editor.removeToLineEnd(); + }, + multiSelectAction: "forEach", + scrollIntoView: "cursor" + }, + { + name: "removetolinestarthard", + description: "Remove to line start hard", + bindKey: bindKey("Ctrl-Shift-Backspace", null), + exec: function(editor) { + var range = editor.selection.getRange(); + range.start.column = 0; + editor.session.remove(range); + }, + multiSelectAction: "forEach", + scrollIntoView: "cursor" + }, + { + name: "removetolineendhard", + description: "Remove to line end hard", + bindKey: bindKey("Ctrl-Shift-Delete", null), + exec: function(editor) { + var range = editor.selection.getRange(); + range.end.column = Number.MAX_VALUE; + editor.session.remove(range); + }, + multiSelectAction: "forEach", + scrollIntoView: "cursor" + }, + { + name: "removewordleft", + description: "Remove word left", + bindKey: bindKey("Ctrl-Backspace", "Alt-Backspace|Ctrl-Alt-Backspace"), + exec: function(editor) { + editor.removeWordLeft(); + }, + multiSelectAction: "forEach", + scrollIntoView: "cursor" + }, + { + name: "removewordright", + description: "Remove word right", + bindKey: bindKey("Ctrl-Delete", "Alt-Delete"), + exec: function(editor) { + editor.removeWordRight(); + }, + multiSelectAction: "forEach", + scrollIntoView: "cursor" + }, + { + name: "outdent", + description: "Outdent", + bindKey: bindKey("Shift-Tab", "Shift-Tab"), + exec: function(editor) { + editor.blockOutdent(); + }, + multiSelectAction: "forEach", + scrollIntoView: "selectionPart" + }, + { + name: "indent", + description: "Indent", + bindKey: bindKey("Tab", "Tab"), + exec: function(editor) { + editor.indent(); + }, + multiSelectAction: "forEach", + scrollIntoView: "selectionPart" + }, + { + name: "blockoutdent", + description: "Block outdent", + bindKey: bindKey("Ctrl-[", "Ctrl-["), + exec: function(editor) { + editor.blockOutdent(); + }, + multiSelectAction: "forEachLine", + scrollIntoView: "selectionPart" + }, + { + name: "blockindent", + description: "Block indent", + bindKey: bindKey("Ctrl-]", "Ctrl-]"), + exec: function(editor) { + editor.blockIndent(); + }, + multiSelectAction: "forEachLine", + scrollIntoView: "selectionPart" + }, + { + name: "insertstring", + description: "Insert string", + exec: function(editor, str) { + editor.insert(str); + }, + multiSelectAction: "forEach", + scrollIntoView: "cursor" + }, + { + name: "inserttext", + description: "Insert text", + exec: function(editor, args) { + editor.insert(lang.stringRepeat(args.text || "", args.times || 1)); + }, + multiSelectAction: "forEach", + scrollIntoView: "cursor" + }, + { + name: "splitline", + description: "Split line", + bindKey: bindKey(null, "Ctrl-O"), + exec: function(editor) { + editor.splitLine(); + }, + multiSelectAction: "forEach", + scrollIntoView: "cursor" + }, + { + name: "transposeletters", + description: "Transpose letters", + bindKey: bindKey("Alt-Shift-X", "Ctrl-T"), + exec: function(editor) { + editor.transposeLetters(); + }, + multiSelectAction: function(editor) { + editor.transposeSelections(1); + }, + scrollIntoView: "cursor" + }, + { + name: "touppercase", + description: "To uppercase", + bindKey: bindKey("Ctrl-U", "Ctrl-U"), + exec: function(editor) { + editor.toUpperCase(); + }, + multiSelectAction: "forEach", + scrollIntoView: "cursor" + }, + { + name: "tolowercase", + description: "To lowercase", + bindKey: bindKey("Ctrl-Shift-U", "Ctrl-Shift-U"), + exec: function(editor) { + editor.toLowerCase(); + }, + multiSelectAction: "forEach", + scrollIntoView: "cursor" + }, + { + name: "autoindent", + description: "Auto Indent", + bindKey: bindKey(null, null), + exec: function(editor) { + editor.autoIndent(); + }, + multiSelectAction: "forEachLine", + scrollIntoView: "animate" + }, + { + name: "expandtoline", + description: "Expand to line", + bindKey: bindKey("Ctrl-Shift-L", "Command-Shift-L"), + exec: function(editor) { + var range = editor.selection.getRange(); + + range.start.column = range.end.column = 0; + range.end.row++; + editor.selection.setRange(range, false); + }, + multiSelectAction: "forEach", + scrollIntoView: "cursor", + readOnly: true + }, + { + name: "joinlines", + description: "Join lines", + bindKey: bindKey(null, null), + exec: function(editor) { + var isBackwards = editor.selection.isBackwards(); + var selectionStart = isBackwards + ? editor.selection.getSelectionLead() + : editor.selection.getSelectionAnchor(); + var selectionEnd = isBackwards + ? editor.selection.getSelectionAnchor() + : editor.selection.getSelectionLead(); + var firstLineEndCol = editor.session.doc.getLine(selectionStart.row) + .length; + var selectedText = editor.session.doc.getTextRange( + editor.selection.getRange() + ); + var selectedCount = selectedText.replace(/\n\s*/, " ").length; + var insertLine = editor.session.doc.getLine(selectionStart.row); + + for (var i = selectionStart.row + 1; i <= selectionEnd.row + 1; i++) { + var curLine = lang.stringTrimLeft( + lang.stringTrimRight(editor.session.doc.getLine(i)) + ); + if (curLine.length !== 0) { + curLine = " " + curLine; + } + insertLine += curLine; + } + + if (selectionEnd.row + 1 < editor.session.doc.getLength() - 1) { + insertLine += editor.session.doc.getNewLineCharacter(); + } + + editor.clearSelection(); + editor.session.doc.replace( + new Range(selectionStart.row, 0, selectionEnd.row + 2, 0), + insertLine + ); + + if (selectedCount > 0) { + editor.selection.moveCursorTo( + selectionStart.row, + selectionStart.column + ); + editor.selection.selectTo( + selectionStart.row, + selectionStart.column + selectedCount + ); + } else { + firstLineEndCol = + editor.session.doc.getLine(selectionStart.row).length > + firstLineEndCol + ? firstLineEndCol + 1 + : firstLineEndCol; + editor.selection.moveCursorTo(selectionStart.row, firstLineEndCol); + } + }, + multiSelectAction: "forEach", + readOnly: true + }, + { + name: "invertSelection", + description: "Invert selection", + bindKey: bindKey(null, null), + exec: function(editor) { + var endRow = editor.session.doc.getLength() - 1; + var endCol = editor.session.doc.getLine(endRow).length; + var ranges = editor.selection.rangeList.ranges; + var newRanges = []; + if (ranges.length < 1) { + ranges = [editor.selection.getRange()]; + } + + for (var i = 0; i < ranges.length; i++) { + if (i == ranges.length - 1) { + if ( + !( + ranges[i].end.row === endRow && + ranges[i].end.column === endCol + ) + ) { + newRanges.push( + new Range( + ranges[i].end.row, + ranges[i].end.column, + endRow, + endCol + ) + ); + } + } + + if (i === 0) { + if ( + !(ranges[i].start.row === 0 && ranges[i].start.column === 0) + ) { + newRanges.push( + new Range(0, 0, ranges[i].start.row, ranges[i].start.column) + ); + } + } else { + newRanges.push( + new Range( + ranges[i - 1].end.row, + ranges[i - 1].end.column, + ranges[i].start.row, + ranges[i].start.column + ) + ); + } + } + + editor.exitMultiSelectMode(); + editor.clearSelection(); + + for (var i = 0; i < newRanges.length; i++) { + editor.selection.addRange(newRanges[i], false); + } + }, + readOnly: true, + scrollIntoView: "none" + }, + { + name: "addLineAfter", + exec: function(editor) { + editor.selection.clearSelection(); + editor.navigateLineEnd(); + editor.insert("\n"); + }, + multiSelectAction: "forEach", + scrollIntoView: "cursor" + }, + { + name: "addLineBefore", + exec: function(editor) { + editor.selection.clearSelection(); + var cursor = editor.getCursorPosition(); + editor.selection.moveTo(cursor.row - 1, Number.MAX_VALUE); + editor.insert("\n"); + if (cursor.row === 0) editor.navigateUp(); + }, + multiSelectAction: "forEach", + scrollIntoView: "cursor" + }, + { + name: "openCommandPallete", + description: "Open command pallete", + bindKey: bindKey("F1", "F1"), + exec: function(editor) { + editor.prompt({ $type: "commands" }); + }, + readOnly: true + }, + { + name: "modeSelect", + description: "Change language mode...", + bindKey: bindKey(null, null), + exec: function(editor) { + editor.prompt({ $type: "modes" }); + }, + readOnly: true + } + ]; + } +); + +ace.define( + "ace/editor", + [ + "require", + "exports", + "module", + "ace/lib/fixoldbrowsers", + "ace/lib/oop", + "ace/lib/dom", + "ace/lib/lang", + "ace/lib/useragent", + "ace/keyboard/textinput", + "ace/mouse/mouse_handler", + "ace/mouse/fold_handler", + "ace/keyboard/keybinding", + "ace/edit_session", + "ace/search", + "ace/range", + "ace/lib/event_emitter", + "ace/commands/command_manager", + "ace/commands/default_commands", + "ace/config", + "ace/token_iterator", + "ace/clipboard" + ], + function(require, exports, module) { + "use strict"; + + require("./lib/fixoldbrowsers"); + + var oop = require("./lib/oop"); + var dom = require("./lib/dom"); + var lang = require("./lib/lang"); + var useragent = require("./lib/useragent"); + var TextInput = require("./keyboard/textinput").TextInput; + var MouseHandler = require("./mouse/mouse_handler").MouseHandler; + var FoldHandler = require("./mouse/fold_handler").FoldHandler; + var KeyBinding = require("./keyboard/keybinding").KeyBinding; + var EditSession = require("./edit_session").EditSession; + var Search = require("./search").Search; + var Range = require("./range").Range; + var EventEmitter = require("./lib/event_emitter").EventEmitter; + var CommandManager = require("./commands/command_manager").CommandManager; + var defaultCommands = require("./commands/default_commands").commands; + var config = require("./config"); + var TokenIterator = require("./token_iterator").TokenIterator; + + var clipboard = require("./clipboard"); + var Editor = function(renderer, session, options) { + this.$toDestroy = []; + var container = renderer.getContainerElement(); + this.container = container; + this.renderer = renderer; + this.id = "editor" + ++Editor.$uid; + + this.commands = new CommandManager( + useragent.isMac ? "mac" : "win", + defaultCommands + ); + if (typeof document == "object") { + this.textInput = new TextInput(renderer.getTextAreaContainer(), this); + this.renderer.textarea = this.textInput.getElement(); + this.$mouseHandler = new MouseHandler(this); + new FoldHandler(this); + } + + this.keyBinding = new KeyBinding(this); + + this.$search = new Search().set({ + wrap: true + }); + + this.$historyTracker = this.$historyTracker.bind(this); + this.commands.on("exec", this.$historyTracker); + + this.$initOperationListeners(); + + this._$emitInputEvent = lang.delayedCall( + function() { + this._signal("input", {}); + if (this.session && this.session.bgTokenizer) + this.session.bgTokenizer.scheduleStart(); + }.bind(this) + ); + + this.on("change", function(_, _self) { + _self._$emitInputEvent.schedule(31); + }); + + this.setSession( + session || (options && options.session) || new EditSession("") + ); + config.resetOptions(this); + if (options) this.setOptions(options); + config._signal("editor", this); + }; + + Editor.$uid = 0; + + (function() { + oop.implement(this, EventEmitter); + + this.$initOperationListeners = function() { + this.commands.on("exec", this.startOperation.bind(this), true); + this.commands.on("afterExec", this.endOperation.bind(this), true); + + this.$opResetTimer = lang.delayedCall( + this.endOperation.bind(this, true) + ); + this.on( + "change", + function() { + if (!this.curOp) { + this.startOperation(); + this.curOp.selectionBefore = this.$lastSel; + } + this.curOp.docChanged = true; + }.bind(this), + true + ); + + this.on( + "changeSelection", + function() { + if (!this.curOp) { + this.startOperation(); + this.curOp.selectionBefore = this.$lastSel; + } + this.curOp.selectionChanged = true; + }.bind(this), + true + ); + }; + + this.curOp = null; + this.prevOp = {}; + this.startOperation = function(commandEvent) { + if (this.curOp) { + if (!commandEvent || this.curOp.command) return; + this.prevOp = this.curOp; + } + if (!commandEvent) { + this.previousCommand = null; + commandEvent = {}; + } + + this.$opResetTimer.schedule(); + this.curOp = this.session.curOp = { + command: commandEvent.command || {}, + args: commandEvent.args, + scrollTop: this.renderer.scrollTop + }; + this.curOp.selectionBefore = this.selection.toJSON(); + }; + + this.endOperation = function(e) { + if (this.curOp && this.session) { + if ((e && e.returnValue === false) || !this.session) + return (this.curOp = null); + if ( + e == true && + this.curOp.command && + this.curOp.command.name == "mouse" + ) + return; + this._signal("beforeEndOperation"); + if (!this.curOp) return; + var command = this.curOp.command; + var scrollIntoView = command && command.scrollIntoView; + if (scrollIntoView) { + switch (scrollIntoView) { + case "center-animate": + scrollIntoView = "animate"; + case "center": + this.renderer.scrollCursorIntoView(null, 0.5); + break; + case "animate": + case "cursor": + this.renderer.scrollCursorIntoView(); + break; + case "selectionPart": + var range = this.selection.getRange(); + var config = this.renderer.layerConfig; + if ( + range.start.row >= config.lastRow || + range.end.row <= config.firstRow + ) { + this.renderer.scrollSelectionIntoView( + this.selection.anchor, + this.selection.lead + ); + } + break; + default: + break; + } + if (scrollIntoView == "animate") + this.renderer.animateScrolling(this.curOp.scrollTop); + } + var sel = this.selection.toJSON(); + this.curOp.selectionAfter = sel; + this.$lastSel = this.selection.toJSON(); + this.session.getUndoManager().addSelection(sel); + this.prevOp = this.curOp; + this.curOp = null; + } + }; + this.$mergeableCommands = ["backspace", "del", "insertstring"]; + this.$historyTracker = function(e) { + if (!this.$mergeUndoDeltas) return; + + var prev = this.prevOp; + var mergeableCommands = this.$mergeableCommands; + var shouldMerge = prev.command && e.command.name == prev.command.name; + if (e.command.name == "insertstring") { + var text = e.args; + if (this.mergeNextCommand === undefined) this.mergeNextCommand = true; + + shouldMerge = + shouldMerge && + this.mergeNextCommand && // previous command allows to coalesce with + (!/\s/.test(text) || /\s/.test(prev.args)); // previous insertion was of same type + + this.mergeNextCommand = true; + } else { + shouldMerge = + shouldMerge && mergeableCommands.indexOf(e.command.name) !== -1; // the command is mergeable + } + + if ( + this.$mergeUndoDeltas != "always" && + Date.now() - this.sequenceStartTime > 2000 + ) { + shouldMerge = false; // the sequence is too long + } + + if (shouldMerge) this.session.mergeUndoDeltas = true; + else if (mergeableCommands.indexOf(e.command.name) !== -1) + this.sequenceStartTime = Date.now(); + }; + this.setKeyboardHandler = function(keyboardHandler, cb) { + if ( + keyboardHandler && + typeof keyboardHandler === "string" && + keyboardHandler != "ace" + ) { + this.$keybindingId = keyboardHandler; + var _self = this; + config.loadModule(["keybinding", keyboardHandler], function(module) { + if (_self.$keybindingId == keyboardHandler) + _self.keyBinding.setKeyboardHandler(module && module.handler); + cb && cb(); + }); + } else { + this.$keybindingId = null; + this.keyBinding.setKeyboardHandler(keyboardHandler); + cb && cb(); + } + }; + this.getKeyboardHandler = function() { + return this.keyBinding.getKeyboardHandler(); + }; + this.setSession = function(session) { + if (this.session == session) return; + if (this.curOp) this.endOperation(); + this.curOp = {}; + + var oldSession = this.session; + if (oldSession) { + this.session.off("change", this.$onDocumentChange); + this.session.off("changeMode", this.$onChangeMode); + this.session.off("tokenizerUpdate", this.$onTokenizerUpdate); + this.session.off("changeTabSize", this.$onChangeTabSize); + this.session.off("changeWrapLimit", this.$onChangeWrapLimit); + this.session.off("changeWrapMode", this.$onChangeWrapMode); + this.session.off("changeFold", this.$onChangeFold); + this.session.off("changeFrontMarker", this.$onChangeFrontMarker); + this.session.off("changeBackMarker", this.$onChangeBackMarker); + this.session.off("changeBreakpoint", this.$onChangeBreakpoint); + this.session.off("changeAnnotation", this.$onChangeAnnotation); + this.session.off("changeOverwrite", this.$onCursorChange); + this.session.off("changeScrollTop", this.$onScrollTopChange); + this.session.off("changeScrollLeft", this.$onScrollLeftChange); + + var selection = this.session.getSelection(); + selection.off("changeCursor", this.$onCursorChange); + selection.off("changeSelection", this.$onSelectionChange); + } + + this.session = session; + if (session) { + this.$onDocumentChange = this.onDocumentChange.bind(this); + session.on("change", this.$onDocumentChange); + this.renderer.setSession(session); + + this.$onChangeMode = this.onChangeMode.bind(this); + session.on("changeMode", this.$onChangeMode); + + this.$onTokenizerUpdate = this.onTokenizerUpdate.bind(this); + session.on("tokenizerUpdate", this.$onTokenizerUpdate); + + this.$onChangeTabSize = this.renderer.onChangeTabSize.bind( + this.renderer + ); + session.on("changeTabSize", this.$onChangeTabSize); + + this.$onChangeWrapLimit = this.onChangeWrapLimit.bind(this); + session.on("changeWrapLimit", this.$onChangeWrapLimit); + + this.$onChangeWrapMode = this.onChangeWrapMode.bind(this); + session.on("changeWrapMode", this.$onChangeWrapMode); + + this.$onChangeFold = this.onChangeFold.bind(this); + session.on("changeFold", this.$onChangeFold); + + this.$onChangeFrontMarker = this.onChangeFrontMarker.bind(this); + this.session.on("changeFrontMarker", this.$onChangeFrontMarker); + + this.$onChangeBackMarker = this.onChangeBackMarker.bind(this); + this.session.on("changeBackMarker", this.$onChangeBackMarker); + + this.$onChangeBreakpoint = this.onChangeBreakpoint.bind(this); + this.session.on("changeBreakpoint", this.$onChangeBreakpoint); + + this.$onChangeAnnotation = this.onChangeAnnotation.bind(this); + this.session.on("changeAnnotation", this.$onChangeAnnotation); + + this.$onCursorChange = this.onCursorChange.bind(this); + this.session.on("changeOverwrite", this.$onCursorChange); + + this.$onScrollTopChange = this.onScrollTopChange.bind(this); + this.session.on("changeScrollTop", this.$onScrollTopChange); + + this.$onScrollLeftChange = this.onScrollLeftChange.bind(this); + this.session.on("changeScrollLeft", this.$onScrollLeftChange); + + this.selection = session.getSelection(); + this.selection.on("changeCursor", this.$onCursorChange); + + this.$onSelectionChange = this.onSelectionChange.bind(this); + this.selection.on("changeSelection", this.$onSelectionChange); + + this.onChangeMode(); + + this.onCursorChange(); + + this.onScrollTopChange(); + this.onScrollLeftChange(); + this.onSelectionChange(); + this.onChangeFrontMarker(); + this.onChangeBackMarker(); + this.onChangeBreakpoint(); + this.onChangeAnnotation(); + this.session.getUseWrapMode() && this.renderer.adjustWrapLimit(); + this.renderer.updateFull(); + } else { + this.selection = null; + this.renderer.setSession(session); + } + + this._signal("changeSession", { + session: session, + oldSession: oldSession + }); + + this.curOp = null; + + oldSession && oldSession._signal("changeEditor", { oldEditor: this }); + session && session._signal("changeEditor", { editor: this }); + + if (session && session.bgTokenizer) session.bgTokenizer.scheduleStart(); + }; + this.getSession = function() { + return this.session; + }; + this.setValue = function(val, cursorPos) { + this.session.doc.setValue(val); + + if (!cursorPos) this.selectAll(); + else if (cursorPos == 1) this.navigateFileEnd(); + else if (cursorPos == -1) this.navigateFileStart(); + + return val; + }; + this.getValue = function() { + return this.session.getValue(); + }; + this.getSelection = function() { + return this.selection; + }; + this.resize = function(force) { + this.renderer.onResize(force); + }; + this.setTheme = function(theme, cb) { + this.renderer.setTheme(theme, cb); + }; + this.getTheme = function() { + return this.renderer.getTheme(); + }; + this.setStyle = function(style) { + this.renderer.setStyle(style); + }; + this.unsetStyle = function(style) { + this.renderer.unsetStyle(style); + }; + this.getFontSize = function() { + return ( + this.getOption("fontSize") || + dom.computedStyle(this.container).fontSize + ); + }; + this.setFontSize = function(size) { + this.setOption("fontSize", size); + }; + + this.$highlightBrackets = function() { + if (this.$highlightPending) { + return; + } + var self = this; + this.$highlightPending = true; + setTimeout(function() { + self.$highlightPending = false; + var session = self.session; + if (!session || !session.bgTokenizer) return; + if (session.$bracketHighlight) { + session.$bracketHighlight.markerIds.forEach(function(id) { + session.removeMarker(id); + }); + session.$bracketHighlight = null; + } + var ranges = session.getMatchingBracketRanges( + self.getCursorPosition() + ); + if (!ranges && session.$mode.getMatching) + ranges = session.$mode.getMatching(self.session); + if (!ranges) return; + + var markerType = "ace_bracket"; + if (!Array.isArray(ranges)) { + ranges = [ranges]; + } else if (ranges.length == 1) { + markerType = "ace_error_bracket"; + } + if (ranges.length == 2) { + if (Range.comparePoints(ranges[0].end, ranges[1].start) == 0) + ranges = [Range.fromPoints(ranges[0].start, ranges[1].end)]; + else if (Range.comparePoints(ranges[0].start, ranges[1].end) == 0) + ranges = [Range.fromPoints(ranges[1].start, ranges[0].end)]; + } + + session.$bracketHighlight = { + ranges: ranges, + markerIds: ranges.map(function(range) { + return session.addMarker(range, markerType, "text"); + }) + }; + }, 50); + }; + this.$highlightTags = function() { + if (this.$highlightTagPending) return; + var self = this; + this.$highlightTagPending = true; + setTimeout(function() { + self.$highlightTagPending = false; + + var session = self.session; + if (!session || !session.bgTokenizer) return; + + var pos = self.getCursorPosition(); + var iterator = new TokenIterator(self.session, pos.row, pos.column); + var token = iterator.getCurrentToken(); + + if (!token || !/\b(?:tag-open|tag-name)/.test(token.type)) { + session.removeMarker(session.$tagHighlight); + session.$tagHighlight = null; + return; + } + + if (token.type.indexOf("tag-open") != -1) { + token = iterator.stepForward(); + if (!token) return; + } + + var tag = token.value; + var depth = 0; + var prevToken = iterator.stepBackward(); + + if (prevToken.value == "<") { + do { + prevToken = token; + token = iterator.stepForward(); + + if ( + token && + token.value === tag && + token.type.indexOf("tag-name") !== -1 + ) { + if (prevToken.value === "<") { + depth++; + } else if (prevToken.value === "= 0); + } else { + do { + token = prevToken; + prevToken = iterator.stepBackward(); + + if ( + token && + token.value === tag && + token.type.indexOf("tag-name") !== -1 + ) { + if (prevToken.value === "<") { + depth++; + } else if (prevToken.value === " 1) + ) + highlight = false; + } + + if (session.$highlightLineMarker && !highlight) { + session.removeMarker(session.$highlightLineMarker.id); + session.$highlightLineMarker = null; + } else if (!session.$highlightLineMarker && highlight) { + var range = new Range( + highlight.row, + highlight.column, + highlight.row, + Infinity + ); + range.id = session.addMarker(range, "ace_active-line", "screenLine"); + session.$highlightLineMarker = range; + } else if (highlight) { + session.$highlightLineMarker.start.row = highlight.row; + session.$highlightLineMarker.end.row = highlight.row; + session.$highlightLineMarker.start.column = highlight.column; + session._signal("changeBackMarker"); + } + }; + + this.onSelectionChange = function(e) { + var session = this.session; + + if (session.$selectionMarker) { + session.removeMarker(session.$selectionMarker); + } + session.$selectionMarker = null; + + if (!this.selection.isEmpty()) { + var range = this.selection.getRange(); + var style = this.getSelectionStyle(); + session.$selectionMarker = session.addMarker( + range, + "ace_selection", + style + ); + } else { + this.$updateHighlightActiveLine(); + } + + var re = + this.$highlightSelectedWord && this.$getSelectionHighLightRegexp(); + this.session.highlight(re); + + this._signal("changeSelection"); + }; + + this.$getSelectionHighLightRegexp = function() { + var session = this.session; + + var selection = this.getSelectionRange(); + if (selection.isEmpty() || selection.isMultiLine()) return; + + var startColumn = selection.start.column; + var endColumn = selection.end.column; + var line = session.getLine(selection.start.row); + + var needle = line.substring(startColumn, endColumn); + if (needle.length > 5000 || !/[\w\d]/.test(needle)) return; + + var re = this.$search.$assembleRegExp({ + wholeWord: true, + caseSensitive: true, + needle: needle + }); + + var wordWithBoundary = line.substring(startColumn - 1, endColumn + 1); + if (!re.test(wordWithBoundary)) return; + + return re; + }; + + this.onChangeFrontMarker = function() { + this.renderer.updateFrontMarkers(); + }; + + this.onChangeBackMarker = function() { + this.renderer.updateBackMarkers(); + }; + + this.onChangeBreakpoint = function() { + this.renderer.updateBreakpoints(); + }; + + this.onChangeAnnotation = function() { + this.renderer.setAnnotations(this.session.getAnnotations()); + }; + + this.onChangeMode = function(e) { + this.renderer.updateText(); + this._emit("changeMode", e); + }; + + this.onChangeWrapLimit = function() { + this.renderer.updateFull(); + }; + + this.onChangeWrapMode = function() { + this.renderer.onResize(true); + }; + + this.onChangeFold = function() { + this.$updateHighlightActiveLine(); + this.renderer.updateFull(); + }; + this.getSelectedText = function() { + return this.session.getTextRange(this.getSelectionRange()); + }; + this.getCopyText = function() { + var text = this.getSelectedText(); + var nl = this.session.doc.getNewLineCharacter(); + var copyLine = false; + if (!text && this.$copyWithEmptySelection) { + copyLine = true; + var ranges = this.selection.getAllRanges(); + for (var i = 0; i < ranges.length; i++) { + var range = ranges[i]; + if (i && ranges[i - 1].start.row == range.start.row) continue; + text += this.session.getLine(range.start.row) + nl; + } + } + var e = { text: text }; + this._signal("copy", e); + clipboard.lineMode = copyLine ? e.text : ""; + return e.text; + }; + this.onCopy = function() { + this.commands.exec("copy", this); + }; + this.onCut = function() { + this.commands.exec("cut", this); + }; + this.onPaste = function(text, event) { + var e = { text: text, event: event }; + this.commands.exec("paste", this, e); + }; + + this.$handlePaste = function(e) { + if (typeof e == "string") e = { text: e }; + this._signal("paste", e); + var text = e.text; + + var lineMode = text == clipboard.lineMode; + var session = this.session; + if (!this.inMultiSelectMode || this.inVirtualSelectionMode) { + if (lineMode) + session.insert({ row: this.selection.lead.row, column: 0 }, text); + else this.insert(text); + } else if (lineMode) { + this.selection.rangeList.ranges.forEach(function(range) { + session.insert({ row: range.start.row, column: 0 }, text); + }); + } else { + var lines = text.split(/\r\n|\r|\n/); + var ranges = this.selection.rangeList.ranges; + + var isFullLine = lines.length == 2 && (!lines[0] || !lines[1]); + if (lines.length != ranges.length || isFullLine) + return this.commands.exec("insertstring", this, text); + + for (var i = ranges.length; i--; ) { + var range = ranges[i]; + if (!range.isEmpty()) session.remove(range); + + session.insert(range.start, lines[i]); + } + } + }; + + this.execCommand = function(command, args) { + return this.commands.exec(command, this, args); + }; + this.insert = function(text, pasted) { + var session = this.session; + var mode = session.getMode(); + var cursor = this.getCursorPosition(); + + if (this.getBehavioursEnabled() && !pasted) { + var transform = mode.transformAction( + session.getState(cursor.row), + "insertion", + this, + session, + text + ); + if (transform) { + if (text !== transform.text) { + if (!this.inVirtualSelectionMode) { + this.session.mergeUndoDeltas = false; + this.mergeNextCommand = false; + } + } + text = transform.text; + } + } + + if (text == "\t") text = this.session.getTabString(); + if (!this.selection.isEmpty()) { + var range = this.getSelectionRange(); + cursor = this.session.remove(range); + this.clearSelection(); + } else if (this.session.getOverwrite() && text.indexOf("\n") == -1) { + var range = new Range.fromPoints(cursor, cursor); + range.end.column += text.length; + this.session.remove(range); + } + + if (text == "\n" || text == "\r\n") { + var line = session.getLine(cursor.row); + if (cursor.column > line.search(/\S|$/)) { + var d = line.substr(cursor.column).search(/\S|$/); + session.doc.removeInLine( + cursor.row, + cursor.column, + cursor.column + d + ); + } + } + this.clearSelection(); + + var start = cursor.column; + var lineState = session.getState(cursor.row); + var line = session.getLine(cursor.row); + var shouldOutdent = mode.checkOutdent(lineState, line, text); + session.insert(cursor, text); + + if (transform && transform.selection) { + if (transform.selection.length == 2) { + // Transform relative to the current column + this.selection.setSelectionRange( + new Range( + cursor.row, + start + transform.selection[0], + cursor.row, + start + transform.selection[1] + ) + ); + } else { + // Transform relative to the current row. + this.selection.setSelectionRange( + new Range( + cursor.row + transform.selection[0], + transform.selection[1], + cursor.row + transform.selection[2], + transform.selection[3] + ) + ); + } + } + if (this.$enableAutoIndent) { + if (session.getDocument().isNewLine(text)) { + var lineIndent = mode.getNextLineIndent( + lineState, + line.slice(0, cursor.column), + session.getTabString() + ); + + session.insert({ row: cursor.row + 1, column: 0 }, lineIndent); + } + if (shouldOutdent) mode.autoOutdent(lineState, session, cursor.row); + } + }; + + this.autoIndent = function() { + var session = this.session; + var mode = session.getMode(); + + var startRow, endRow; + if (this.selection.isEmpty()) { + startRow = 0; + endRow = session.doc.getLength() - 1; + } else { + var selectedRange = this.getSelectionRange(); + + startRow = selectedRange.start.row; + endRow = selectedRange.end.row; + } + + var prevLineState = ""; + var prevLine = ""; + var lineIndent = ""; + var line, currIndent, range; + var tab = session.getTabString(); + + for (var row = startRow; row <= endRow; row++) { + if (row > 0) { + prevLineState = session.getState(row - 1); + prevLine = session.getLine(row - 1); + lineIndent = mode.getNextLineIndent(prevLineState, prevLine, tab); + } + + line = session.getLine(row); + currIndent = mode.$getIndent(line); + if (lineIndent !== currIndent) { + if (currIndent.length > 0) { + range = new Range(row, 0, row, currIndent.length); + session.remove(range); + } + if (lineIndent.length > 0) { + session.insert({ row: row, column: 0 }, lineIndent); + } + } + + mode.autoOutdent(prevLineState, session, row); + } + }; + + this.onTextInput = function(text, composition) { + if (!composition) return this.keyBinding.onTextInput(text); + + this.startOperation({ command: { name: "insertstring" } }); + var applyComposition = this.applyComposition.bind( + this, + text, + composition + ); + if (this.selection.rangeCount) this.forEachSelection(applyComposition); + else applyComposition(); + this.endOperation(); + }; + + this.applyComposition = function(text, composition) { + if (composition.extendLeft || composition.extendRight) { + var r = this.selection.getRange(); + r.start.column -= composition.extendLeft; + r.end.column += composition.extendRight; + if (r.start.column < 0) { + r.start.row--; + r.start.column += this.session.getLine(r.start.row).length + 1; + } + this.selection.setRange(r); + if (!text && !r.isEmpty()) this.remove(); + } + if (text || !this.selection.isEmpty()) this.insert(text, true); + if (composition.restoreStart || composition.restoreEnd) { + var r = this.selection.getRange(); + r.start.column -= composition.restoreStart; + r.end.column -= composition.restoreEnd; + this.selection.setRange(r); + } + }; + + this.onCommandKey = function(e, hashId, keyCode) { + return this.keyBinding.onCommandKey(e, hashId, keyCode); + }; + this.setOverwrite = function(overwrite) { + this.session.setOverwrite(overwrite); + }; + this.getOverwrite = function() { + return this.session.getOverwrite(); + }; + this.toggleOverwrite = function() { + this.session.toggleOverwrite(); + }; + this.setScrollSpeed = function(speed) { + this.setOption("scrollSpeed", speed); + }; + this.getScrollSpeed = function() { + return this.getOption("scrollSpeed"); + }; + this.setDragDelay = function(dragDelay) { + this.setOption("dragDelay", dragDelay); + }; + this.getDragDelay = function() { + return this.getOption("dragDelay"); + }; + this.setSelectionStyle = function(val) { + this.setOption("selectionStyle", val); + }; + this.getSelectionStyle = function() { + return this.getOption("selectionStyle"); + }; + this.setHighlightActiveLine = function(shouldHighlight) { + this.setOption("highlightActiveLine", shouldHighlight); + }; + this.getHighlightActiveLine = function() { + return this.getOption("highlightActiveLine"); + }; + this.setHighlightGutterLine = function(shouldHighlight) { + this.setOption("highlightGutterLine", shouldHighlight); + }; + + this.getHighlightGutterLine = function() { + return this.getOption("highlightGutterLine"); + }; + this.setHighlightSelectedWord = function(shouldHighlight) { + this.setOption("highlightSelectedWord", shouldHighlight); + }; + this.getHighlightSelectedWord = function() { + return this.$highlightSelectedWord; + }; + + this.setAnimatedScroll = function(shouldAnimate) { + this.renderer.setAnimatedScroll(shouldAnimate); + }; + + this.getAnimatedScroll = function() { + return this.renderer.getAnimatedScroll(); + }; + this.setShowInvisibles = function(showInvisibles) { + this.renderer.setShowInvisibles(showInvisibles); + }; + this.getShowInvisibles = function() { + return this.renderer.getShowInvisibles(); + }; + + this.setDisplayIndentGuides = function(display) { + this.renderer.setDisplayIndentGuides(display); + }; + + this.getDisplayIndentGuides = function() { + return this.renderer.getDisplayIndentGuides(); + }; + this.setShowPrintMargin = function(showPrintMargin) { + this.renderer.setShowPrintMargin(showPrintMargin); + }; + this.getShowPrintMargin = function() { + return this.renderer.getShowPrintMargin(); + }; + this.setPrintMarginColumn = function(showPrintMargin) { + this.renderer.setPrintMarginColumn(showPrintMargin); + }; + this.getPrintMarginColumn = function() { + return this.renderer.getPrintMarginColumn(); + }; + this.setReadOnly = function(readOnly) { + this.setOption("readOnly", readOnly); + }; + this.getReadOnly = function() { + return this.getOption("readOnly"); + }; + this.setBehavioursEnabled = function(enabled) { + this.setOption("behavioursEnabled", enabled); + }; + this.getBehavioursEnabled = function() { + return this.getOption("behavioursEnabled"); + }; + this.setWrapBehavioursEnabled = function(enabled) { + this.setOption("wrapBehavioursEnabled", enabled); + }; + this.getWrapBehavioursEnabled = function() { + return this.getOption("wrapBehavioursEnabled"); + }; + this.setShowFoldWidgets = function(show) { + this.setOption("showFoldWidgets", show); + }; + this.getShowFoldWidgets = function() { + return this.getOption("showFoldWidgets"); + }; + + this.setFadeFoldWidgets = function(fade) { + this.setOption("fadeFoldWidgets", fade); + }; + + this.getFadeFoldWidgets = function() { + return this.getOption("fadeFoldWidgets"); + }; + this.remove = function(dir) { + if (this.selection.isEmpty()) { + if (dir == "left") this.selection.selectLeft(); + else this.selection.selectRight(); + } + + var range = this.getSelectionRange(); + if (this.getBehavioursEnabled()) { + var session = this.session; + var state = session.getState(range.start.row); + var new_range = session + .getMode() + .transformAction(state, "deletion", this, session, range); + + if (range.end.column === 0) { + var text = session.getTextRange(range); + if (text[text.length - 1] == "\n") { + var line = session.getLine(range.end.row); + if (/^\s+$/.test(line)) { + range.end.column = line.length; + } + } + } + if (new_range) range = new_range; + } + + this.session.remove(range); + this.clearSelection(); + }; + this.removeWordRight = function() { + if (this.selection.isEmpty()) this.selection.selectWordRight(); + + this.session.remove(this.getSelectionRange()); + this.clearSelection(); + }; + this.removeWordLeft = function() { + if (this.selection.isEmpty()) this.selection.selectWordLeft(); + + this.session.remove(this.getSelectionRange()); + this.clearSelection(); + }; + this.removeToLineStart = function() { + if (this.selection.isEmpty()) this.selection.selectLineStart(); + if (this.selection.isEmpty()) this.selection.selectLeft(); + this.session.remove(this.getSelectionRange()); + this.clearSelection(); + }; + this.removeToLineEnd = function() { + if (this.selection.isEmpty()) this.selection.selectLineEnd(); + + var range = this.getSelectionRange(); + if ( + range.start.column == range.end.column && + range.start.row == range.end.row + ) { + range.end.column = 0; + range.end.row++; + } + + this.session.remove(range); + this.clearSelection(); + }; + this.splitLine = function() { + if (!this.selection.isEmpty()) { + this.session.remove(this.getSelectionRange()); + this.clearSelection(); + } + + var cursor = this.getCursorPosition(); + this.insert("\n"); + this.moveCursorToPosition(cursor); + }; + this.transposeLetters = function() { + if (!this.selection.isEmpty()) { + return; + } + + var cursor = this.getCursorPosition(); + var column = cursor.column; + if (column === 0) return; + + var line = this.session.getLine(cursor.row); + var swap, range; + if (column < line.length) { + swap = line.charAt(column) + line.charAt(column - 1); + range = new Range(cursor.row, column - 1, cursor.row, column + 1); + } else { + swap = line.charAt(column - 1) + line.charAt(column - 2); + range = new Range(cursor.row, column - 2, cursor.row, column); + } + this.session.replace(range, swap); + this.session.selection.moveToPosition(range.end); + }; + this.toLowerCase = function() { + var originalRange = this.getSelectionRange(); + if (this.selection.isEmpty()) { + this.selection.selectWord(); + } + + var range = this.getSelectionRange(); + var text = this.session.getTextRange(range); + this.session.replace(range, text.toLowerCase()); + this.selection.setSelectionRange(originalRange); + }; + this.toUpperCase = function() { + var originalRange = this.getSelectionRange(); + if (this.selection.isEmpty()) { + this.selection.selectWord(); + } + + var range = this.getSelectionRange(); + var text = this.session.getTextRange(range); + this.session.replace(range, text.toUpperCase()); + this.selection.setSelectionRange(originalRange); + }; + this.indent = function() { + var session = this.session; + var range = this.getSelectionRange(); + + if (range.start.row < range.end.row) { + var rows = this.$getSelectedRows(); + session.indentRows(rows.first, rows.last, "\t"); + return; + } else if (range.start.column < range.end.column) { + var text = session.getTextRange(range); + if (!/^\s+$/.test(text)) { + var rows = this.$getSelectedRows(); + session.indentRows(rows.first, rows.last, "\t"); + return; + } + } + + var line = session.getLine(range.start.row); + var position = range.start; + var size = session.getTabSize(); + var column = session.documentToScreenColumn( + position.row, + position.column + ); + + if (this.session.getUseSoftTabs()) { + var count = size - (column % size); + var indentString = lang.stringRepeat(" ", count); + } else { + var count = column % size; + while (line[range.start.column - 1] == " " && count) { + range.start.column--; + count--; + } + this.selection.setSelectionRange(range); + indentString = "\t"; + } + return this.insert(indentString); + }; + this.blockIndent = function() { + var rows = this.$getSelectedRows(); + this.session.indentRows(rows.first, rows.last, "\t"); + }; + this.blockOutdent = function() { + var selection = this.session.getSelection(); + this.session.outdentRows(selection.getRange()); + }; + this.sortLines = function() { + var rows = this.$getSelectedRows(); + var session = this.session; + + var lines = []; + for (var i = rows.first; i <= rows.last; i++) + lines.push(session.getLine(i)); + + lines.sort(function(a, b) { + if (a.toLowerCase() < b.toLowerCase()) return -1; + if (a.toLowerCase() > b.toLowerCase()) return 1; + return 0; + }); + + var deleteRange = new Range(0, 0, 0, 0); + for (var i = rows.first; i <= rows.last; i++) { + var line = session.getLine(i); + deleteRange.start.row = i; + deleteRange.end.row = i; + deleteRange.end.column = line.length; + session.replace(deleteRange, lines[i - rows.first]); + } + }; + this.toggleCommentLines = function() { + var state = this.session.getState(this.getCursorPosition().row); + var rows = this.$getSelectedRows(); + this.session + .getMode() + .toggleCommentLines(state, this.session, rows.first, rows.last); + }; + + this.toggleBlockComment = function() { + var cursor = this.getCursorPosition(); + var state = this.session.getState(cursor.row); + var range = this.getSelectionRange(); + this.session + .getMode() + .toggleBlockComment(state, this.session, range, cursor); + }; + this.getNumberAt = function(row, column) { + var _numberRx = /[\-]?[0-9]+(?:\.[0-9]+)?/g; + _numberRx.lastIndex = 0; + + var s = this.session.getLine(row); + while (_numberRx.lastIndex < column) { + var m = _numberRx.exec(s); + if (m.index <= column && m.index + m[0].length >= column) { + var number = { + value: m[0], + start: m.index, + end: m.index + m[0].length + }; + return number; + } + } + return null; + }; + this.modifyNumber = function(amount) { + var row = this.selection.getCursor().row; + var column = this.selection.getCursor().column; + var charRange = new Range(row, column - 1, row, column); + + var c = this.session.getTextRange(charRange); + if (!isNaN(parseFloat(c)) && isFinite(c)) { + var nr = this.getNumberAt(row, column); + if (nr) { + var fp = + nr.value.indexOf(".") >= 0 + ? nr.start + nr.value.indexOf(".") + 1 + : nr.end; + var decimals = nr.start + nr.value.length - fp; + + var t = parseFloat(nr.value); + t *= Math.pow(10, decimals); + + if (fp !== nr.end && column < fp) { + amount *= Math.pow(10, nr.end - column - 1); + } else { + amount *= Math.pow(10, nr.end - column); + } + + t += amount; + t /= Math.pow(10, decimals); + var nnr = t.toFixed(decimals); + var replaceRange = new Range(row, nr.start, row, nr.end); + this.session.replace(replaceRange, nnr); + this.moveCursorTo( + row, + Math.max(nr.start + 1, column + nnr.length - nr.value.length) + ); + } + } else { + this.toggleWord(); + } + }; + + this.$toggleWordPairs = [ + ["first", "last"], + ["true", "false"], + ["yes", "no"], + ["width", "height"], + ["top", "bottom"], + ["right", "left"], + ["on", "off"], + ["x", "y"], + ["get", "set"], + ["max", "min"], + ["horizontal", "vertical"], + ["show", "hide"], + ["add", "remove"], + ["up", "down"], + ["before", "after"], + ["even", "odd"], + ["in", "out"], + ["inside", "outside"], + ["next", "previous"], + ["increase", "decrease"], + ["attach", "detach"], + ["&&", "||"], + ["==", "!="] + ]; + + this.toggleWord = function() { + var row = this.selection.getCursor().row; + var column = this.selection.getCursor().column; + this.selection.selectWord(); + var currentState = this.getSelectedText(); + var currWordStart = this.selection.getWordRange().start.column; + var wordParts = currentState + .replace(/([a-z]+|[A-Z]+)(?=[A-Z_]|$)/g, "$1 ") + .split(/\s/); + var delta = column - currWordStart - 1; + if (delta < 0) delta = 0; + var curLength = 0, + itLength = 0; + var that = this; + if (currentState.match(/[A-Za-z0-9_]+/)) { + wordParts.forEach(function(item, i) { + itLength = curLength + item.length; + if (delta >= curLength && delta <= itLength) { + currentState = item; + that.selection.clearSelection(); + that.moveCursorTo(row, curLength + currWordStart); + that.selection.selectTo(row, itLength + currWordStart); + } + curLength = itLength; + }); + } + + var wordPairs = this.$toggleWordPairs; + var reg; + for (var i = 0; i < wordPairs.length; i++) { + var item = wordPairs[i]; + for (var j = 0; j <= 1; j++) { + var negate = +!j; + var firstCondition = currentState.match( + new RegExp( + "^\\s?_?(" + lang.escapeRegExp(item[j]) + ")\\s?$", + "i" + ) + ); + if (firstCondition) { + var secondCondition = currentState.match( + new RegExp( + "([_]|^|\\s)(" + + lang.escapeRegExp(firstCondition[1]) + + ")($|\\s)", + "g" + ) + ); + if (secondCondition) { + reg = currentState.replace( + new RegExp(lang.escapeRegExp(item[j]), "i"), + function(result) { + var res = item[negate]; + if (result.toUpperCase() == result) { + res = res.toUpperCase(); + } else if ( + result.charAt(0).toUpperCase() == result.charAt(0) + ) { + res = + res.substr(0, 0) + + item[negate].charAt(0).toUpperCase() + + res.substr(1); + } + return res; + } + ); + this.insert(reg); + reg = ""; + } + } + } + } + }; + this.removeLines = function() { + var rows = this.$getSelectedRows(); + this.session.removeFullLines(rows.first, rows.last); + this.clearSelection(); + }; + + this.duplicateSelection = function() { + var sel = this.selection; + var doc = this.session; + var range = sel.getRange(); + var reverse = sel.isBackwards(); + if (range.isEmpty()) { + var row = range.start.row; + doc.duplicateLines(row, row); + } else { + var point = reverse ? range.start : range.end; + var endPoint = doc.insert(point, doc.getTextRange(range), false); + range.start = point; + range.end = endPoint; + + sel.setSelectionRange(range, reverse); + } + }; + this.moveLinesDown = function() { + this.$moveLines(1, false); + }; + this.moveLinesUp = function() { + this.$moveLines(-1, false); + }; + this.moveText = function(range, toPosition, copy) { + return this.session.moveText(range, toPosition, copy); + }; + this.copyLinesUp = function() { + this.$moveLines(-1, true); + }; + this.copyLinesDown = function() { + this.$moveLines(1, true); + }; + this.$moveLines = function(dir, copy) { + var rows, moved; + var selection = this.selection; + if (!selection.inMultiSelectMode || this.inVirtualSelectionMode) { + var range = selection.toOrientedRange(); + rows = this.$getSelectedRows(range); + moved = this.session.$moveLines( + rows.first, + rows.last, + copy ? 0 : dir + ); + if (copy && dir == -1) moved = 0; + range.moveBy(moved, 0); + selection.fromOrientedRange(range); + } else { + var ranges = selection.rangeList.ranges; + selection.rangeList.detach(this.session); + this.inVirtualSelectionMode = true; + + var diff = 0; + var totalDiff = 0; + var l = ranges.length; + for (var i = 0; i < l; i++) { + var rangeIndex = i; + ranges[i].moveBy(diff, 0); + rows = this.$getSelectedRows(ranges[i]); + var first = rows.first; + var last = rows.last; + while (++i < l) { + if (totalDiff) ranges[i].moveBy(totalDiff, 0); + var subRows = this.$getSelectedRows(ranges[i]); + if (copy && subRows.first != last) break; + else if (!copy && subRows.first > last + 1) break; + last = subRows.last; + } + i--; + diff = this.session.$moveLines(first, last, copy ? 0 : dir); + if (copy && dir == -1) rangeIndex = i + 1; + while (rangeIndex <= i) { + ranges[rangeIndex].moveBy(diff, 0); + rangeIndex++; + } + if (!copy) diff = 0; + totalDiff += diff; + } + + selection.fromOrientedRange(selection.ranges[0]); + selection.rangeList.attach(this.session); + this.inVirtualSelectionMode = false; + } + }; + this.$getSelectedRows = function(range) { + range = (range || this.getSelectionRange()).collapseRows(); + + return { + first: this.session.getRowFoldStart(range.start.row), + last: this.session.getRowFoldEnd(range.end.row) + }; + }; + + this.onCompositionStart = function(compositionState) { + this.renderer.showComposition(compositionState); + }; + + this.onCompositionUpdate = function(text) { + this.renderer.setCompositionText(text); + }; + + this.onCompositionEnd = function() { + this.renderer.hideComposition(); + }; + this.getFirstVisibleRow = function() { + return this.renderer.getFirstVisibleRow(); + }; + this.getLastVisibleRow = function() { + return this.renderer.getLastVisibleRow(); + }; + this.isRowVisible = function(row) { + return ( + row >= this.getFirstVisibleRow() && row <= this.getLastVisibleRow() + ); + }; + this.isRowFullyVisible = function(row) { + return ( + row >= this.renderer.getFirstFullyVisibleRow() && + row <= this.renderer.getLastFullyVisibleRow() + ); + }; + this.$getVisibleRowCount = function() { + return ( + this.renderer.getScrollBottomRow() - + this.renderer.getScrollTopRow() + + 1 + ); + }; + + this.$moveByPage = function(dir, select) { + var renderer = this.renderer; + var config = this.renderer.layerConfig; + var rows = dir * Math.floor(config.height / config.lineHeight); + + if (select === true) { + this.selection.$moveSelection(function() { + this.moveCursorBy(rows, 0); + }); + } else if (select === false) { + this.selection.moveCursorBy(rows, 0); + this.selection.clearSelection(); + } + + var scrollTop = renderer.scrollTop; + + renderer.scrollBy(0, rows * config.lineHeight); + if (select != null) renderer.scrollCursorIntoView(null, 0.5); + + renderer.animateScrolling(scrollTop); + }; + this.selectPageDown = function() { + this.$moveByPage(1, true); + }; + this.selectPageUp = function() { + this.$moveByPage(-1, true); + }; + this.gotoPageDown = function() { + this.$moveByPage(1, false); + }; + this.gotoPageUp = function() { + this.$moveByPage(-1, false); + }; + this.scrollPageDown = function() { + this.$moveByPage(1); + }; + this.scrollPageUp = function() { + this.$moveByPage(-1); + }; + this.scrollToRow = function(row) { + this.renderer.scrollToRow(row); + }; + this.scrollToLine = function(line, center, animate, callback) { + this.renderer.scrollToLine(line, center, animate, callback); + }; + this.centerSelection = function() { + var range = this.getSelectionRange(); + var pos = { + row: Math.floor( + range.start.row + (range.end.row - range.start.row) / 2 + ), + column: Math.floor( + range.start.column + (range.end.column - range.start.column) / 2 + ) + }; + this.renderer.alignCursor(pos, 0.5); + }; + this.getCursorPosition = function() { + return this.selection.getCursor(); + }; + this.getCursorPositionScreen = function() { + return this.session.documentToScreenPosition(this.getCursorPosition()); + }; + this.getSelectionRange = function() { + return this.selection.getRange(); + }; + this.selectAll = function() { + this.selection.selectAll(); + }; + this.clearSelection = function() { + this.selection.clearSelection(); + }; + this.moveCursorTo = function(row, column) { + this.selection.moveCursorTo(row, column); + }; + this.moveCursorToPosition = function(pos) { + this.selection.moveCursorToPosition(pos); + }; + this.jumpToMatching = function(select, expand) { + var cursor = this.getCursorPosition(); + var iterator = new TokenIterator( + this.session, + cursor.row, + cursor.column + ); + var prevToken = iterator.getCurrentToken(); + var token = prevToken || iterator.stepForward(); + + if (!token) return; + var matchType; + var found = false; + var depth = {}; + var i = cursor.column - token.start; + var bracketType; + var brackets = { + ")": "(", + "(": "(", + "]": "[", + "[": "[", + "{": "{", + "}": "{" + }; + + do { + if (token.value.match(/[{}()\[\]]/g)) { + for (; i < token.value.length && !found; i++) { + if (!brackets[token.value[i]]) { + continue; + } + + bracketType = + brackets[token.value[i]] + + "." + + token.type.replace("rparen", "lparen"); + + if (isNaN(depth[bracketType])) { + depth[bracketType] = 0; + } + + switch (token.value[i]) { + case "(": + case "[": + case "{": + depth[bracketType]++; + break; + case ")": + case "]": + case "}": + depth[bracketType]--; + + if (depth[bracketType] === -1) { + matchType = "bracket"; + found = true; + } + break; + } + } + } else if (token.type.indexOf("tag-name") !== -1) { + if (isNaN(depth[token.value])) { + depth[token.value] = 0; + } + + if (prevToken.value === "<") { + depth[token.value]++; + } else if (prevToken.value === "= 0; --i) { + if (this.$tryReplace(ranges[i], replacement)) { + replaced++; + } + } + + this.selection.setSelectionRange(selection); + + return replaced; + }; + + this.$tryReplace = function(range, replacement) { + var input = this.session.getTextRange(range); + replacement = this.$search.replace(input, replacement); + if (replacement !== null) { + range.end = this.session.replace(range, replacement); + return range; + } else { + return null; + } + }; + this.getLastSearchOptions = function() { + return this.$search.getOptions(); + }; + this.find = function(needle, options, animate) { + if (!options) options = {}; + + if (typeof needle == "string" || needle instanceof RegExp) + options.needle = needle; + else if (typeof needle == "object") oop.mixin(options, needle); + + var range = this.selection.getRange(); + if (options.needle == null) { + needle = + this.session.getTextRange(range) || this.$search.$options.needle; + if (!needle) { + range = this.session.getWordRange( + range.start.row, + range.start.column + ); + needle = this.session.getTextRange(range); + } + this.$search.set({ needle: needle }); + } + + this.$search.set(options); + if (!options.start) this.$search.set({ start: range }); + + var newRange = this.$search.find(this.session); + if (options.preventScroll) return newRange; + if (newRange) { + this.revealRange(newRange, animate); + return newRange; + } + if (options.backwards) range.start = range.end; + else range.end = range.start; + this.selection.setRange(range); + }; + this.findNext = function(options, animate) { + this.find({ skipCurrent: true, backwards: false }, options, animate); + }; + this.findPrevious = function(options, animate) { + this.find(options, { skipCurrent: true, backwards: true }, animate); + }; + + this.revealRange = function(range, animate) { + this.session.unfold(range); + this.selection.setSelectionRange(range); + + var scrollTop = this.renderer.scrollTop; + this.renderer.scrollSelectionIntoView(range.start, range.end, 0.5); + if (animate !== false) this.renderer.animateScrolling(scrollTop); + }; + this.undo = function() { + this.session.getUndoManager().undo(this.session); + this.renderer.scrollCursorIntoView(null, 0.5); + }; + this.redo = function() { + this.session.getUndoManager().redo(this.session); + this.renderer.scrollCursorIntoView(null, 0.5); + }; + this.destroy = function() { + if (this.$toDestroy) { + this.$toDestroy.forEach(function(el) { + el.destroy(); + }); + this.$toDestroy = null; + } + this.renderer.destroy(); + this._signal("destroy", this); + if (this.session) this.session.destroy(); + if (this._$emitInputEvent) this._$emitInputEvent.cancel(); + this.removeAllListeners(); + }; + this.setAutoScrollEditorIntoView = function(enable) { + if (!enable) return; + var rect; + var self = this; + var shouldScroll = false; + if (!this.$scrollAnchor) + this.$scrollAnchor = document.createElement("div"); + var scrollAnchor = this.$scrollAnchor; + scrollAnchor.style.cssText = "position:absolute"; + this.container.insertBefore(scrollAnchor, this.container.firstChild); + var onChangeSelection = this.on("changeSelection", function() { + shouldScroll = true; + }); + var onBeforeRender = this.renderer.on("beforeRender", function() { + if (shouldScroll) + rect = self.renderer.container.getBoundingClientRect(); + }); + var onAfterRender = this.renderer.on("afterRender", function() { + if ( + shouldScroll && + rect && + (self.isFocused() || (self.searchBox && self.searchBox.isFocused())) + ) { + var renderer = self.renderer; + var pos = renderer.$cursorLayer.$pixelPos; + var config = renderer.layerConfig; + var top = pos.top - config.offset; + if (pos.top >= 0 && top + rect.top < 0) { + shouldScroll = true; + } else if ( + pos.top < config.height && + pos.top + rect.top + config.lineHeight > window.innerHeight + ) { + shouldScroll = false; + } else { + shouldScroll = null; + } + if (shouldScroll != null) { + scrollAnchor.style.top = top + "px"; + scrollAnchor.style.left = pos.left + "px"; + scrollAnchor.style.height = config.lineHeight + "px"; + scrollAnchor.scrollIntoView(shouldScroll); + } + shouldScroll = rect = null; + } + }); + this.setAutoScrollEditorIntoView = function(enable) { + if (enable) return; + delete this.setAutoScrollEditorIntoView; + this.off("changeSelection", onChangeSelection); + this.renderer.off("afterRender", onAfterRender); + this.renderer.off("beforeRender", onBeforeRender); + }; + }; + + this.$resetCursorStyle = function() { + var style = this.$cursorStyle || "ace"; + var cursorLayer = this.renderer.$cursorLayer; + if (!cursorLayer) return; + cursorLayer.setSmoothBlinking(/smooth/.test(style)); + cursorLayer.isBlinking = !this.$readOnly && style != "wide"; + dom.setCssClass( + cursorLayer.element, + "ace_slim-cursors", + /slim/.test(style) + ); + }; + this.prompt = function(message, options, callback) { + var editor = this; + config.loadModule("./ext/prompt", function(module) { + module.prompt(editor, message, options, callback); + }); + }; + }.call(Editor.prototype)); + + config.defineOptions(Editor.prototype, "editor", { + selectionStyle: { + set: function(style) { + this.onSelectionChange(); + this._signal("changeSelectionStyle", { data: style }); + }, + initialValue: "line" + }, + highlightActiveLine: { + set: function() { + this.$updateHighlightActiveLine(); + }, + initialValue: true + }, + highlightSelectedWord: { + set: function(shouldHighlight) { + this.$onSelectionChange(); + }, + initialValue: true + }, + readOnly: { + set: function(readOnly) { + this.textInput.setReadOnly(readOnly); + this.$resetCursorStyle(); + }, + initialValue: false + }, + copyWithEmptySelection: { + set: function(value) { + this.textInput.setCopyWithEmptySelection(value); + }, + initialValue: false + }, + cursorStyle: { + set: function(val) { + this.$resetCursorStyle(); + }, + values: ["ace", "slim", "smooth", "wide"], + initialValue: "ace" + }, + mergeUndoDeltas: { + values: [false, true, "always"], + initialValue: true + }, + behavioursEnabled: { initialValue: true }, + wrapBehavioursEnabled: { initialValue: true }, + enableAutoIndent: { initialValue: true }, + autoScrollEditorIntoView: { + set: function(val) { + this.setAutoScrollEditorIntoView(val); + } + }, + keyboardHandler: { + set: function(val) { + this.setKeyboardHandler(val); + }, + get: function() { + return this.$keybindingId; + }, + handlesSet: true + }, + value: { + set: function(val) { + this.session.setValue(val); + }, + get: function() { + return this.getValue(); + }, + handlesSet: true, + hidden: true + }, + session: { + set: function(val) { + this.setSession(val); + }, + get: function() { + return this.session; + }, + handlesSet: true, + hidden: true + }, + + showLineNumbers: { + set: function(show) { + this.renderer.$gutterLayer.setShowLineNumbers(show); + this.renderer.$loop.schedule(this.renderer.CHANGE_GUTTER); + if (show && this.$relativeLineNumbers) + relativeNumberRenderer.attach(this); + else relativeNumberRenderer.detach(this); + }, + initialValue: true + }, + relativeLineNumbers: { + set: function(value) { + if (this.$showLineNumbers && value) + relativeNumberRenderer.attach(this); + else relativeNumberRenderer.detach(this); + } + }, + placeholder: { + set: function(message) { + if (!this.$updatePlaceholder) { + this.$updatePlaceholder = function() { + var value = + this.session && (this.renderer.$composition || this.getValue()); + if (value && this.renderer.placeholderNode) { + this.renderer.off("afterRender", this.$updatePlaceholder); + dom.removeCssClass(this.container, "ace_hasPlaceholder"); + this.renderer.placeholderNode.remove(); + this.renderer.placeholderNode = null; + } else if (!value && !this.renderer.placeholderNode) { + this.renderer.on("afterRender", this.$updatePlaceholder); + dom.addCssClass(this.container, "ace_hasPlaceholder"); + var el = dom.createElement("div"); + el.className = "ace_placeholder"; + el.textContent = this.$placeholder || ""; + this.renderer.placeholderNode = el; + this.renderer.content.appendChild( + this.renderer.placeholderNode + ); + } else if (!value && this.renderer.placeholderNode) { + this.renderer.placeholderNode.textContent = + this.$placeholder || ""; + } + }.bind(this); + this.on("input", this.$updatePlaceholder); + } + this.$updatePlaceholder(); + } + }, + + hScrollBarAlwaysVisible: "renderer", + vScrollBarAlwaysVisible: "renderer", + highlightGutterLine: "renderer", + animatedScroll: "renderer", + showInvisibles: "renderer", + showPrintMargin: "renderer", + printMarginColumn: "renderer", + printMargin: "renderer", + fadeFoldWidgets: "renderer", + showFoldWidgets: "renderer", + displayIndentGuides: "renderer", + showGutter: "renderer", + fontSize: "renderer", + fontFamily: "renderer", + maxLines: "renderer", + minLines: "renderer", + scrollPastEnd: "renderer", + fixedWidthGutter: "renderer", + theme: "renderer", + hasCssTransforms: "renderer", + maxPixelHeight: "renderer", + useTextareaForIME: "renderer", + + scrollSpeed: "$mouseHandler", + dragDelay: "$mouseHandler", + dragEnabled: "$mouseHandler", + focusTimeout: "$mouseHandler", + tooltipFollowsMouse: "$mouseHandler", + + firstLineNumber: "session", + overwrite: "session", + newLineMode: "session", + useWorker: "session", + useSoftTabs: "session", + navigateWithinSoftTabs: "session", + tabSize: "session", + wrap: "session", + indentedSoftWrap: "session", + foldStyle: "session", + mode: "session" + }); + + var relativeNumberRenderer = { + getText: function(session, row) { + return ( + (Math.abs(session.selection.lead.row - row) || + row + 1 + (row < 9 ? "\xb7" : "")) + "" + ); + }, + getWidth: function(session, lastLineNumber, config) { + return ( + Math.max( + lastLineNumber.toString().length, + (config.lastRow + 1).toString().length, + 2 + ) * config.characterWidth + ); + }, + update: function(e, editor) { + editor.renderer.$loop.schedule(editor.renderer.CHANGE_GUTTER); + }, + attach: function(editor) { + editor.renderer.$gutterLayer.$renderer = this; + editor.on("changeSelection", this.update); + this.update(null, editor); + }, + detach: function(editor) { + if (editor.renderer.$gutterLayer.$renderer == this) + editor.renderer.$gutterLayer.$renderer = null; + editor.off("changeSelection", this.update); + this.update(null, editor); + } + }; + + exports.Editor = Editor; + } +); + +ace.define( + "ace/undomanager", + ["require", "exports", "module", "ace/range"], + function(require, exports, module) { + "use strict"; + var UndoManager = function() { + this.$maxRev = 0; + this.$fromUndo = false; + this.reset(); + }; + + (function() { + this.addSession = function(session) { + this.$session = session; + }; + this.add = function(delta, allowMerge, session) { + if (this.$fromUndo) return; + if (delta == this.$lastDelta) return; + if (!this.$keepRedoStack) this.$redoStack.length = 0; + if (allowMerge === false || !this.lastDeltas) { + this.lastDeltas = []; + this.$undoStack.push(this.lastDeltas); + delta.id = this.$rev = ++this.$maxRev; + } + if (delta.action == "remove" || delta.action == "insert") + this.$lastDelta = delta; + this.lastDeltas.push(delta); + }; + + this.addSelection = function(selection, rev) { + this.selections.push({ + value: selection, + rev: rev || this.$rev + }); + }; + + this.startNewGroup = function() { + this.lastDeltas = null; + return this.$rev; + }; + + this.markIgnored = function(from, to) { + if (to == null) to = this.$rev + 1; + var stack = this.$undoStack; + for (var i = stack.length; i--; ) { + var delta = stack[i][0]; + if (delta.id <= from) break; + if (delta.id < to) delta.ignore = true; + } + this.lastDeltas = null; + }; + + this.getSelection = function(rev, after) { + var stack = this.selections; + for (var i = stack.length; i--; ) { + var selection = stack[i]; + if (selection.rev < rev) { + if (after) selection = stack[i + 1]; + return selection; + } + } + }; + + this.getRevision = function() { + return this.$rev; + }; + + this.getDeltas = function(from, to) { + if (to == null) to = this.$rev + 1; + var stack = this.$undoStack; + var end = null, + start = 0; + for (var i = stack.length; i--; ) { + var delta = stack[i][0]; + if (delta.id < to && !end) end = i + 1; + if (delta.id <= from) { + start = i + 1; + break; + } + } + return stack.slice(start, end); + }; + + this.getChangedRanges = function(from, to) { + if (to == null) to = this.$rev + 1; + }; + + this.getChangedLines = function(from, to) { + if (to == null) to = this.$rev + 1; + }; + this.undo = function(session, dontSelect) { + this.lastDeltas = null; + var stack = this.$undoStack; + + if (!rearrangeUndoStack(stack, stack.length)) return; + + if (!session) session = this.$session; + + if (this.$redoStackBaseRev !== this.$rev && this.$redoStack.length) + this.$redoStack = []; + + this.$fromUndo = true; + + var deltaSet = stack.pop(); + var undoSelectionRange = null; + if (deltaSet) { + undoSelectionRange = session.undoChanges(deltaSet, dontSelect); + this.$redoStack.push(deltaSet); + this.$syncRev(); + } + + this.$fromUndo = false; + + return undoSelectionRange; + }; + this.redo = function(session, dontSelect) { + this.lastDeltas = null; + + if (!session) session = this.$session; + + this.$fromUndo = true; + if (this.$redoStackBaseRev != this.$rev) { + var diff = this.getDeltas(this.$redoStackBaseRev, this.$rev + 1); + rebaseRedoStack(this.$redoStack, diff); + this.$redoStackBaseRev = this.$rev; + this.$redoStack.forEach(function(x) { + x[0].id = ++this.$maxRev; + }, this); + } + var deltaSet = this.$redoStack.pop(); + var redoSelectionRange = null; + + if (deltaSet) { + redoSelectionRange = session.redoChanges(deltaSet, dontSelect); + this.$undoStack.push(deltaSet); + this.$syncRev(); + } + this.$fromUndo = false; + + return redoSelectionRange; + }; + + this.$syncRev = function() { + var stack = this.$undoStack; + var nextDelta = stack[stack.length - 1]; + var id = (nextDelta && nextDelta[0].id) || 0; + this.$redoStackBaseRev = id; + this.$rev = id; + }; + this.reset = function() { + this.lastDeltas = null; + this.$lastDelta = null; + this.$undoStack = []; + this.$redoStack = []; + this.$rev = 0; + this.mark = 0; + this.$redoStackBaseRev = this.$rev; + this.selections = []; + }; + this.canUndo = function() { + return this.$undoStack.length > 0; + }; + this.canRedo = function() { + return this.$redoStack.length > 0; + }; + this.bookmark = function(rev) { + if (rev == undefined) rev = this.$rev; + this.mark = rev; + }; + this.isAtBookmark = function() { + return this.$rev === this.mark; + }; + + this.toJSON = function() {}; + + this.fromJSON = function() {}; + + this.hasUndo = this.canUndo; + this.hasRedo = this.canRedo; + this.isClean = this.isAtBookmark; + this.markClean = this.bookmark; + + this.$prettyPrint = function(delta) { + if (delta) return stringifyDelta(delta); + return ( + stringifyDelta(this.$undoStack) + + "\n---\n" + + stringifyDelta(this.$redoStack) + ); + }; + }.call(UndoManager.prototype)); + + function rearrangeUndoStack(stack, pos) { + for (var i = pos; i--; ) { + var deltaSet = stack[i]; + if (deltaSet && !deltaSet[0].ignore) { + while (i < pos - 1) { + var swapped = swapGroups(stack[i], stack[i + 1]); + stack[i] = swapped[0]; + stack[i + 1] = swapped[1]; + i++; + } + return true; + } + } + } + + var Range = require("./range").Range; + var cmp = Range.comparePoints; + var comparePoints = Range.comparePoints; + + function $updateMarkers(delta) { + var isInsert = delta.action == "insert"; + var start = delta.start; + var end = delta.end; + var rowShift = (end.row - start.row) * (isInsert ? 1 : -1); + var colShift = (end.column - start.column) * (isInsert ? 1 : -1); + if (isInsert) end = start; + + for (var i in this.marks) { + var point = this.marks[i]; + var cmp = comparePoints(point, start); + if (cmp < 0) { + continue; // delta starts after the range + } + if (cmp === 0) { + if (isInsert) { + if (point.bias == 1) { + cmp = 1; + } else { + point.bias == -1; + continue; + } + } + } + var cmp2 = isInsert ? cmp : comparePoints(point, end); + if (cmp2 > 0) { + point.row += rowShift; + point.column += point.row == end.row ? colShift : 0; + continue; + } + if (!isInsert && cmp2 <= 0) { + point.row = start.row; + point.column = start.column; + if (cmp2 === 0) point.bias = 1; + } + } + } + + function clonePos(pos) { + return { row: pos.row, column: pos.column }; + } + function cloneDelta(d) { + return { + start: clonePos(d.start), + end: clonePos(d.end), + action: d.action, + lines: d.lines.slice() + }; + } + function stringifyDelta(d) { + d = d || this; + if (Array.isArray(d)) { + return d.map(stringifyDelta).join("\n"); + } + var type = ""; + if (d.action) { + type = d.action == "insert" ? "+" : "-"; + type += "[" + d.lines + "]"; + } else if (d.value) { + if (Array.isArray(d.value)) { + type = d.value.map(stringifyRange).join("\n"); + } else { + type = stringifyRange(d.value); + } + } + if (d.start) { + type += stringifyRange(d); + } + if (d.id || d.rev) { + type += "\t(" + (d.id || d.rev) + ")"; + } + return type; + } + function stringifyRange(r) { + return ( + r.start.row + + ":" + + r.start.column + + "=>" + + r.end.row + + ":" + + r.end.column + ); + } + + function swap(d1, d2) { + var i1 = d1.action == "insert"; + var i2 = d2.action == "insert"; + + if (i1 && i2) { + if (cmp(d2.start, d1.end) >= 0) { + shift(d2, d1, -1); + } else if (cmp(d2.start, d1.start) <= 0) { + shift(d1, d2, +1); + } else { + return null; + } + } else if (i1 && !i2) { + if (cmp(d2.start, d1.end) >= 0) { + shift(d2, d1, -1); + } else if (cmp(d2.end, d1.start) <= 0) { + shift(d1, d2, -1); + } else { + return null; + } + } else if (!i1 && i2) { + if (cmp(d2.start, d1.start) >= 0) { + shift(d2, d1, +1); + } else if (cmp(d2.start, d1.start) <= 0) { + shift(d1, d2, +1); + } else { + return null; + } + } else if (!i1 && !i2) { + if (cmp(d2.start, d1.start) >= 0) { + shift(d2, d1, +1); + } else if (cmp(d2.end, d1.start) <= 0) { + shift(d1, d2, -1); + } else { + return null; + } + } + return [d2, d1]; + } + function swapGroups(ds1, ds2) { + for (var i = ds1.length; i--; ) { + for (var j = 0; j < ds2.length; j++) { + if (!swap(ds1[i], ds2[j])) { + while (i < ds1.length) { + while (j--) { + swap(ds2[j], ds1[i]); + } + j = ds2.length; + i++; + } + return [ds1, ds2]; + } + } + } + ds1.selectionBefore = ds2.selectionBefore = ds1.selectionAfter = ds2.selectionAfter = null; + return [ds2, ds1]; + } + function xform(d1, c1) { + var i1 = d1.action == "insert"; + var i2 = c1.action == "insert"; + + if (i1 && i2) { + if (cmp(d1.start, c1.start) < 0) { + shift(c1, d1, 1); + } else { + shift(d1, c1, 1); + } + } else if (i1 && !i2) { + if (cmp(d1.start, c1.end) >= 0) { + shift(d1, c1, -1); + } else if (cmp(d1.start, c1.start) <= 0) { + shift(c1, d1, +1); + } else { + shift(d1, Range.fromPoints(c1.start, d1.start), -1); + shift(c1, d1, +1); + } + } else if (!i1 && i2) { + if (cmp(c1.start, d1.end) >= 0) { + shift(c1, d1, -1); + } else if (cmp(c1.start, d1.start) <= 0) { + shift(d1, c1, +1); + } else { + shift(c1, Range.fromPoints(d1.start, c1.start), -1); + shift(d1, c1, +1); + } + } else if (!i1 && !i2) { + if (cmp(c1.start, d1.end) >= 0) { + shift(c1, d1, -1); + } else if (cmp(c1.end, d1.start) <= 0) { + shift(d1, c1, -1); + } else { + var before, after; + if (cmp(d1.start, c1.start) < 0) { + before = d1; + d1 = splitDelta(d1, c1.start); + } + if (cmp(d1.end, c1.end) > 0) { + after = splitDelta(d1, c1.end); + } + + shiftPos(c1.end, d1.start, d1.end, -1); + if (after && !before) { + d1.lines = after.lines; + d1.start = after.start; + d1.end = after.end; + after = d1; + } + + return [c1, before, after].filter(Boolean); + } + } + return [c1, d1]; + } + + function shift(d1, d2, dir) { + shiftPos(d1.start, d2.start, d2.end, dir); + shiftPos(d1.end, d2.start, d2.end, dir); + } + function shiftPos(pos, start, end, dir) { + if (pos.row == (dir == 1 ? start : end).row) { + pos.column += dir * (end.column - start.column); + } + pos.row += dir * (end.row - start.row); + } + function splitDelta(c, pos) { + var lines = c.lines; + var end = c.end; + c.end = clonePos(pos); + var rowsBefore = c.end.row - c.start.row; + var otherLines = lines.splice(rowsBefore, lines.length); + + var col = rowsBefore ? pos.column : pos.column - c.start.column; + lines.push(otherLines[0].substring(0, col)); + otherLines[0] = otherLines[0].substr(col); + var rest = { + start: clonePos(pos), + end: end, + lines: otherLines, + action: c.action + }; + return rest; + } + + function moveDeltasByOne(redoStack, d) { + d = cloneDelta(d); + for (var j = redoStack.length; j--; ) { + var deltaSet = redoStack[j]; + for (var i = 0; i < deltaSet.length; i++) { + var x = deltaSet[i]; + var xformed = xform(x, d); + d = xformed[0]; + if (xformed.length != 2) { + if (xformed[2]) { + deltaSet.splice(i + 1, 1, xformed[1], xformed[2]); + i++; + } else if (!xformed[1]) { + deltaSet.splice(i, 1); + i--; + } + } + } + if (!deltaSet.length) { + redoStack.splice(j, 1); + } + } + return redoStack; + } + function rebaseRedoStack(redoStack, deltaSets) { + for (var i = 0; i < deltaSets.length; i++) { + var deltas = deltaSets[i]; + for (var j = 0; j < deltas.length; j++) { + moveDeltasByOne(redoStack, deltas[j]); + } + } + } + + exports.UndoManager = UndoManager; + } +); + +ace.define( + "ace/layer/lines", + ["require", "exports", "module", "ace/lib/dom"], + function(require, exports, module) { + "use strict"; + + var dom = require("../lib/dom"); + + var Lines = function(element, canvasHeight) { + this.element = element; + this.canvasHeight = canvasHeight || 500000; + this.element.style.height = this.canvasHeight * 2 + "px"; + + this.cells = []; + this.cellCache = []; + this.$offsetCoefficient = 0; + }; + + (function() { + this.moveContainer = function(config) { + dom.translate( + this.element, + 0, + -((config.firstRowScreen * config.lineHeight) % this.canvasHeight) - + config.offset * this.$offsetCoefficient + ); + }; + + this.pageChanged = function(oldConfig, newConfig) { + return ( + Math.floor( + (oldConfig.firstRowScreen * oldConfig.lineHeight) / + this.canvasHeight + ) !== + Math.floor( + (newConfig.firstRowScreen * newConfig.lineHeight) / + this.canvasHeight + ) + ); + }; + + this.computeLineTop = function(row, config, session) { + var screenTop = config.firstRowScreen * config.lineHeight; + var screenPage = Math.floor(screenTop / this.canvasHeight); + var lineTop = session.documentToScreenRow(row, 0) * config.lineHeight; + return lineTop - screenPage * this.canvasHeight; + }; + + this.computeLineHeight = function(row, config, session) { + return config.lineHeight * session.getRowLineCount(row); + }; + + this.getLength = function() { + return this.cells.length; + }; + + this.get = function(index) { + return this.cells[index]; + }; + + this.shift = function() { + this.$cacheCell(this.cells.shift()); + }; + + this.pop = function() { + this.$cacheCell(this.cells.pop()); + }; + + this.push = function(cell) { + if (Array.isArray(cell)) { + this.cells.push.apply(this.cells, cell); + var fragment = dom.createFragment(this.element); + for (var i = 0; i < cell.length; i++) { + fragment.appendChild(cell[i].element); + } + this.element.appendChild(fragment); + } else { + this.cells.push(cell); + this.element.appendChild(cell.element); + } + }; + + this.unshift = function(cell) { + if (Array.isArray(cell)) { + this.cells.unshift.apply(this.cells, cell); + var fragment = dom.createFragment(this.element); + for (var i = 0; i < cell.length; i++) { + fragment.appendChild(cell[i].element); + } + if (this.element.firstChild) + this.element.insertBefore(fragment, this.element.firstChild); + else this.element.appendChild(fragment); + } else { + this.cells.unshift(cell); + this.element.insertAdjacentElement("afterbegin", cell.element); + } + }; + + this.last = function() { + if (this.cells.length) return this.cells[this.cells.length - 1]; + else return null; + }; + + this.$cacheCell = function(cell) { + if (!cell) return; + + cell.element.remove(); + this.cellCache.push(cell); + }; + + this.createCell = function(row, config, session, initElement) { + var cell = this.cellCache.pop(); + if (!cell) { + var element = dom.createElement("div"); + if (initElement) initElement(element); + + this.element.appendChild(element); + + cell = { + element: element, + text: "", + row: row + }; + } + cell.row = row; + + return cell; + }; + }.call(Lines.prototype)); + + exports.Lines = Lines; + } +); + +ace.define( + "ace/layer/gutter", + [ + "require", + "exports", + "module", + "ace/lib/dom", + "ace/lib/oop", + "ace/lib/lang", + "ace/lib/event_emitter", + "ace/layer/lines" + ], + function(require, exports, module) { + "use strict"; + + var dom = require("../lib/dom"); + var oop = require("../lib/oop"); + var lang = require("../lib/lang"); + var EventEmitter = require("../lib/event_emitter").EventEmitter; + var Lines = require("./lines").Lines; + + var Gutter = function(parentEl) { + this.element = dom.createElement("div"); + this.element.className = "ace_layer ace_gutter-layer"; + parentEl.appendChild(this.element); + this.setShowFoldWidgets(this.$showFoldWidgets); + + this.gutterWidth = 0; + + this.$annotations = []; + this.$updateAnnotations = this.$updateAnnotations.bind(this); + + this.$lines = new Lines(this.element); + this.$lines.$offsetCoefficient = 1; + }; + + (function() { + oop.implement(this, EventEmitter); + + this.setSession = function(session) { + if (this.session) this.session.off("change", this.$updateAnnotations); + this.session = session; + if (session) session.on("change", this.$updateAnnotations); + }; + + this.addGutterDecoration = function(row, className) { + if (window.console) + console.warn && + console.warn("deprecated use session.addGutterDecoration"); + this.session.addGutterDecoration(row, className); + }; + + this.removeGutterDecoration = function(row, className) { + if (window.console) + console.warn && + console.warn("deprecated use session.removeGutterDecoration"); + this.session.removeGutterDecoration(row, className); + }; + + this.setAnnotations = function(annotations) { + this.$annotations = []; + for (var i = 0; i < annotations.length; i++) { + var annotation = annotations[i]; + var row = annotation.row; + var rowInfo = this.$annotations[row]; + if (!rowInfo) rowInfo = this.$annotations[row] = { text: [] }; + + var annoText = annotation.text; + annoText = annoText + ? lang.escapeHTML(annoText) + : annotation.html || ""; + + if (rowInfo.text.indexOf(annoText) === -1) + rowInfo.text.push(annoText); + + var type = annotation.type; + if (type == "error") rowInfo.className = " ace_error"; + else if (type == "warning" && rowInfo.className != " ace_error") + rowInfo.className = " ace_warning"; + else if (type == "info" && !rowInfo.className) + rowInfo.className = " ace_info"; + } + }; + + this.$updateAnnotations = function(delta) { + if (!this.$annotations.length) return; + var firstRow = delta.start.row; + var len = delta.end.row - firstRow; + if (len === 0) { + } else if (delta.action == "remove") { + this.$annotations.splice(firstRow, len + 1, null); + } else { + var args = new Array(len + 1); + args.unshift(firstRow, 1); + this.$annotations.splice.apply(this.$annotations, args); + } + }; + + this.update = function(config) { + this.config = config; + + var session = this.session; + var firstRow = config.firstRow; + var lastRow = Math.min( + config.lastRow + config.gutterOffset, // needed to compensate for hor scollbar + session.getLength() - 1 + ); + + this.oldLastRow = lastRow; + this.config = config; + + this.$lines.moveContainer(config); + this.$updateCursorRow(); + + var fold = session.getNextFoldLine(firstRow); + var foldStart = fold ? fold.start.row : Infinity; + + var cell = null; + var index = -1; + var row = firstRow; + + while (true) { + if (row > foldStart) { + row = fold.end.row + 1; + fold = session.getNextFoldLine(row, fold); + foldStart = fold ? fold.start.row : Infinity; + } + if (row > lastRow) { + while (this.$lines.getLength() > index + 1) this.$lines.pop(); + + break; + } + + cell = this.$lines.get(++index); + if (cell) { + cell.row = row; + } else { + cell = this.$lines.createCell( + row, + config, + this.session, + onCreateCell + ); + this.$lines.push(cell); + } + + this.$renderCell(cell, config, fold, row); + row++; + } + + this._signal("afterRender"); + this.$updateGutterWidth(config); + }; + + this.$updateGutterWidth = function(config) { + var session = this.session; + + var gutterRenderer = session.gutterRenderer || this.$renderer; + + var firstLineNumber = session.$firstLineNumber; + var lastLineText = this.$lines.last() ? this.$lines.last().text : ""; + + if (this.$fixedWidth || session.$useWrapMode) + lastLineText = session.getLength() + firstLineNumber - 1; + + var gutterWidth = gutterRenderer + ? gutterRenderer.getWidth(session, lastLineText, config) + : lastLineText.toString().length * config.characterWidth; + + var padding = this.$padding || this.$computePadding(); + gutterWidth += padding.left + padding.right; + if (gutterWidth !== this.gutterWidth && !isNaN(gutterWidth)) { + this.gutterWidth = gutterWidth; + this.element.parentNode.style.width = this.element.style.width = + Math.ceil(this.gutterWidth) + "px"; + this._signal("changeGutterWidth", gutterWidth); + } + }; + + this.$updateCursorRow = function() { + if (!this.$highlightGutterLine) return; + + var position = this.session.selection.getCursor(); + if (this.$cursorRow === position.row) return; + + this.$cursorRow = position.row; + }; + + this.updateLineHighlight = function() { + if (!this.$highlightGutterLine) return; + var row = this.session.selection.cursor.row; + this.$cursorRow = row; + + if (this.$cursorCell && this.$cursorCell.row == row) return; + if (this.$cursorCell) + this.$cursorCell.element.className = this.$cursorCell.element.className.replace( + "ace_gutter-active-line ", + "" + ); + var cells = this.$lines.cells; + this.$cursorCell = null; + for (var i = 0; i < cells.length; i++) { + var cell = cells[i]; + if (cell.row >= this.$cursorRow) { + if (cell.row > this.$cursorRow) { + var fold = this.session.getFoldLine(this.$cursorRow); + if (i > 0 && fold && fold.start.row == cells[i - 1].row) + cell = cells[i - 1]; + else break; + } + cell.element.className = + "ace_gutter-active-line " + cell.element.className; + this.$cursorCell = cell; + break; + } + } + }; + + this.scrollLines = function(config) { + var oldConfig = this.config; + this.config = config; + + this.$updateCursorRow(); + if (this.$lines.pageChanged(oldConfig, config)) + return this.update(config); + + this.$lines.moveContainer(config); + + var lastRow = Math.min( + config.lastRow + config.gutterOffset, // needed to compensate for hor scollbar + this.session.getLength() - 1 + ); + var oldLastRow = this.oldLastRow; + this.oldLastRow = lastRow; + + if (!oldConfig || oldLastRow < config.firstRow) + return this.update(config); + + if (lastRow < oldConfig.firstRow) return this.update(config); + + if (oldConfig.firstRow < config.firstRow) + for ( + var row = this.session.getFoldedRowCount( + oldConfig.firstRow, + config.firstRow - 1 + ); + row > 0; + row-- + ) + this.$lines.shift(); + + if (oldLastRow > lastRow) + for ( + var row = this.session.getFoldedRowCount(lastRow + 1, oldLastRow); + row > 0; + row-- + ) + this.$lines.pop(); + + if (config.firstRow < oldConfig.firstRow) { + this.$lines.unshift( + this.$renderLines(config, config.firstRow, oldConfig.firstRow - 1) + ); + } + + if (lastRow > oldLastRow) { + this.$lines.push(this.$renderLines(config, oldLastRow + 1, lastRow)); + } + + this.updateLineHighlight(); + + this._signal("afterRender"); + this.$updateGutterWidth(config); + }; + + this.$renderLines = function(config, firstRow, lastRow) { + var fragment = []; + var row = firstRow; + var foldLine = this.session.getNextFoldLine(row); + var foldStart = foldLine ? foldLine.start.row : Infinity; + + while (true) { + if (row > foldStart) { + row = foldLine.end.row + 1; + foldLine = this.session.getNextFoldLine(row, foldLine); + foldStart = foldLine ? foldLine.start.row : Infinity; + } + if (row > lastRow) break; + + var cell = this.$lines.createCell( + row, + config, + this.session, + onCreateCell + ); + this.$renderCell(cell, config, foldLine, row); + fragment.push(cell); + + row++; + } + return fragment; + }; + + this.$renderCell = function(cell, config, fold, row) { + var element = cell.element; + + var session = this.session; + + var textNode = element.childNodes[0]; + var foldWidget = element.childNodes[1]; + + var firstLineNumber = session.$firstLineNumber; + + var breakpoints = session.$breakpoints; + var decorations = session.$decorations; + var gutterRenderer = session.gutterRenderer || this.$renderer; + var foldWidgets = this.$showFoldWidgets && session.foldWidgets; + var foldStart = fold ? fold.start.row : Number.MAX_VALUE; + + var className = "ace_gutter-cell "; + if (this.$highlightGutterLine) { + if ( + row == this.$cursorRow || + (fold && + row < this.$cursorRow && + row >= foldStart && + this.$cursorRow <= fold.end.row) + ) { + className += "ace_gutter-active-line "; + if (this.$cursorCell != cell) { + if (this.$cursorCell) + this.$cursorCell.element.className = this.$cursorCell.element.className.replace( + "ace_gutter-active-line ", + "" + ); + this.$cursorCell = cell; + } + } + } + + if (breakpoints[row]) className += breakpoints[row]; + if (decorations[row]) className += decorations[row]; + if (this.$annotations[row]) + className += this.$annotations[row].className; + if (element.className != className) element.className = className; + + if (foldWidgets) { + var c = foldWidgets[row]; + if (c == null) c = foldWidgets[row] = session.getFoldWidget(row); + } + + if (c) { + var className = "ace_fold-widget ace_" + c; + if (c == "start" && row == foldStart && row < fold.end.row) + className += " ace_closed"; + else className += " ace_open"; + if (foldWidget.className != className) + foldWidget.className = className; + + var foldHeight = config.lineHeight + "px"; + dom.setStyle(foldWidget.style, "height", foldHeight); + dom.setStyle(foldWidget.style, "display", "inline-block"); + } else { + if (foldWidget) { + dom.setStyle(foldWidget.style, "display", "none"); + } + } + + var text = (gutterRenderer + ? gutterRenderer.getText(session, row) + : row + firstLineNumber + ).toString(); + + if (text !== textNode.data) { + textNode.data = text; + } + + dom.setStyle( + cell.element.style, + "height", + this.$lines.computeLineHeight(row, config, session) + "px" + ); + dom.setStyle( + cell.element.style, + "top", + this.$lines.computeLineTop(row, config, session) + "px" + ); + + cell.text = text; + return cell; + }; + + this.$fixedWidth = false; + + this.$highlightGutterLine = true; + this.$renderer = ""; + this.setHighlightGutterLine = function(highlightGutterLine) { + this.$highlightGutterLine = highlightGutterLine; + }; + + this.$showLineNumbers = true; + this.$renderer = ""; + this.setShowLineNumbers = function(show) { + this.$renderer = !show && { + getWidth: function() { + return 0; + }, + getText: function() { + return ""; + } + }; + }; + + this.getShowLineNumbers = function() { + return this.$showLineNumbers; + }; + + this.$showFoldWidgets = true; + this.setShowFoldWidgets = function(show) { + if (show) dom.addCssClass(this.element, "ace_folding-enabled"); + else dom.removeCssClass(this.element, "ace_folding-enabled"); + + this.$showFoldWidgets = show; + this.$padding = null; + }; + + this.getShowFoldWidgets = function() { + return this.$showFoldWidgets; + }; + + this.$computePadding = function() { + if (!this.element.firstChild) return { left: 0, right: 0 }; + var style = dom.computedStyle(this.element.firstChild); + this.$padding = {}; + this.$padding.left = + (parseInt(style.borderLeftWidth) || 0) + + (parseInt(style.paddingLeft) || 0) + + 1; + this.$padding.right = + (parseInt(style.borderRightWidth) || 0) + + (parseInt(style.paddingRight) || 0); + return this.$padding; + }; + + this.getRegion = function(point) { + var padding = this.$padding || this.$computePadding(); + var rect = this.element.getBoundingClientRect(); + if (point.x < padding.left + rect.left) return "markers"; + if (this.$showFoldWidgets && point.x > rect.right - padding.right) + return "foldWidgets"; + }; + }.call(Gutter.prototype)); + + function onCreateCell(element) { + var textNode = document.createTextNode(""); + element.appendChild(textNode); + + var foldWidget = dom.createElement("span"); + element.appendChild(foldWidget); + + return element; + } + + exports.Gutter = Gutter; + } +); + +ace.define( + "ace/layer/marker", + ["require", "exports", "module", "ace/range", "ace/lib/dom"], + function(require, exports, module) { + "use strict"; + + var Range = require("../range").Range; + var dom = require("../lib/dom"); + + var Marker = function(parentEl) { + this.element = dom.createElement("div"); + this.element.className = "ace_layer ace_marker-layer"; + parentEl.appendChild(this.element); + }; + + (function() { + this.$padding = 0; + + this.setPadding = function(padding) { + this.$padding = padding; + }; + this.setSession = function(session) { + this.session = session; + }; + + this.setMarkers = function(markers) { + this.markers = markers; + }; + + this.elt = function(className, css) { + var x = this.i != -1 && this.element.childNodes[this.i]; + if (!x) { + x = document.createElement("div"); + this.element.appendChild(x); + this.i = -1; + } else { + this.i++; + } + x.style.cssText = css; + x.className = className; + }; + + this.update = function(config) { + if (!config) return; + + this.config = config; + + this.i = 0; + var html; + for (var key in this.markers) { + var marker = this.markers[key]; + + if (!marker.range) { + marker.update(html, this, this.session, config); + continue; + } + + var range = marker.range.clipRows(config.firstRow, config.lastRow); + if (range.isEmpty()) continue; + + range = range.toScreenRange(this.session); + if (marker.renderer) { + var top = this.$getTop(range.start.row, config); + var left = + this.$padding + range.start.column * config.characterWidth; + marker.renderer(html, range, left, top, config); + } else if (marker.type == "fullLine") { + this.drawFullLineMarker(html, range, marker.clazz, config); + } else if (marker.type == "screenLine") { + this.drawScreenLineMarker(html, range, marker.clazz, config); + } else if (range.isMultiLine()) { + if (marker.type == "text") + this.drawTextMarker(html, range, marker.clazz, config); + else this.drawMultiLineMarker(html, range, marker.clazz, config); + } else { + this.drawSingleLineMarker( + html, + range, + marker.clazz + " ace_start" + " ace_br15", + config + ); + } + } + if (this.i != -1) { + while (this.i < this.element.childElementCount) + this.element.removeChild(this.element.lastChild); + } + }; + + this.$getTop = function(row, layerConfig) { + return (row - layerConfig.firstRowScreen) * layerConfig.lineHeight; + }; + + function getBorderClass(tl, tr, br, bl) { + return (tl ? 1 : 0) | (tr ? 2 : 0) | (br ? 4 : 0) | (bl ? 8 : 0); + } + this.drawTextMarker = function( + stringBuilder, + range, + clazz, + layerConfig, + extraStyle + ) { + var session = this.session; + var start = range.start.row; + var end = range.end.row; + var row = start; + var prev = 0; + var curr = 0; + var next = session.getScreenLastRowColumn(row); + var lineRange = new Range(row, range.start.column, row, curr); + for (; row <= end; row++) { + lineRange.start.row = lineRange.end.row = row; + lineRange.start.column = + row == start ? range.start.column : session.getRowWrapIndent(row); + lineRange.end.column = next; + prev = curr; + curr = next; + next = + row + 1 < end + ? session.getScreenLastRowColumn(row + 1) + : row == end + ? 0 + : range.end.column; + this.drawSingleLineMarker( + stringBuilder, + lineRange, + clazz + + (row == start ? " ace_start" : "") + + " ace_br" + + getBorderClass( + row == start || (row == start + 1 && range.start.column), + prev < curr, + curr > next, + row == end + ), + layerConfig, + row == end ? 0 : 1, + extraStyle + ); + } + }; + this.drawMultiLineMarker = function( + stringBuilder, + range, + clazz, + config, + extraStyle + ) { + var padding = this.$padding; + var height = config.lineHeight; + var top = this.$getTop(range.start.row, config); + var left = padding + range.start.column * config.characterWidth; + extraStyle = extraStyle || ""; + + if (this.session.$bidiHandler.isBidiRow(range.start.row)) { + var range1 = range.clone(); + range1.end.row = range1.start.row; + range1.end.column = this.session.getLine(range1.start.row).length; + this.drawBidiSingleLineMarker( + stringBuilder, + range1, + clazz + " ace_br1 ace_start", + config, + null, + extraStyle + ); + } else { + this.elt( + clazz + " ace_br1 ace_start", + "height:" + + height + + "px;" + + "right:0;" + + "top:" + + top + + "px;left:" + + left + + "px;" + + (extraStyle || "") + ); + } + if (this.session.$bidiHandler.isBidiRow(range.end.row)) { + var range1 = range.clone(); + range1.start.row = range1.end.row; + range1.start.column = 0; + this.drawBidiSingleLineMarker( + stringBuilder, + range1, + clazz + " ace_br12", + config, + null, + extraStyle + ); + } else { + top = this.$getTop(range.end.row, config); + var width = range.end.column * config.characterWidth; + + this.elt( + clazz + " ace_br12", + "height:" + + height + + "px;" + + "width:" + + width + + "px;" + + "top:" + + top + + "px;" + + "left:" + + padding + + "px;" + + (extraStyle || "") + ); + } + height = (range.end.row - range.start.row - 1) * config.lineHeight; + if (height <= 0) return; + top = this.$getTop(range.start.row + 1, config); + + var radiusClass = + (range.start.column ? 1 : 0) | (range.end.column ? 0 : 8); + + this.elt( + clazz + (radiusClass ? " ace_br" + radiusClass : ""), + "height:" + + height + + "px;" + + "right:0;" + + "top:" + + top + + "px;" + + "left:" + + padding + + "px;" + + (extraStyle || "") + ); + }; + this.drawSingleLineMarker = function( + stringBuilder, + range, + clazz, + config, + extraLength, + extraStyle + ) { + if (this.session.$bidiHandler.isBidiRow(range.start.row)) + return this.drawBidiSingleLineMarker( + stringBuilder, + range, + clazz, + config, + extraLength, + extraStyle + ); + var height = config.lineHeight; + var width = + (range.end.column + (extraLength || 0) - range.start.column) * + config.characterWidth; + + var top = this.$getTop(range.start.row, config); + var left = this.$padding + range.start.column * config.characterWidth; + + this.elt( + clazz, + "height:" + + height + + "px;" + + "width:" + + width + + "px;" + + "top:" + + top + + "px;" + + "left:" + + left + + "px;" + + (extraStyle || "") + ); + }; + this.drawBidiSingleLineMarker = function( + stringBuilder, + range, + clazz, + config, + extraLength, + extraStyle + ) { + var height = config.lineHeight, + top = this.$getTop(range.start.row, config), + padding = this.$padding; + var selections = this.session.$bidiHandler.getSelections( + range.start.column, + range.end.column + ); + + selections.forEach(function(selection) { + this.elt( + clazz, + "height:" + + height + + "px;" + + "width:" + + selection.width + + (extraLength || 0) + + "px;" + + "top:" + + top + + "px;" + + "left:" + + (padding + selection.left) + + "px;" + + (extraStyle || "") + ); + }, this); + }; + + this.drawFullLineMarker = function( + stringBuilder, + range, + clazz, + config, + extraStyle + ) { + var top = this.$getTop(range.start.row, config); + var height = config.lineHeight; + if (range.start.row != range.end.row) + height += this.$getTop(range.end.row, config) - top; + + this.elt( + clazz, + "height:" + + height + + "px;" + + "top:" + + top + + "px;" + + "left:0;right:0;" + + (extraStyle || "") + ); + }; + + this.drawScreenLineMarker = function( + stringBuilder, + range, + clazz, + config, + extraStyle + ) { + var top = this.$getTop(range.start.row, config); + var height = config.lineHeight; + + this.elt( + clazz, + "height:" + + height + + "px;" + + "top:" + + top + + "px;" + + "left:0;right:0;" + + (extraStyle || "") + ); + }; + }.call(Marker.prototype)); + + exports.Marker = Marker; + } +); + +ace.define( + "ace/layer/text", + [ + "require", + "exports", + "module", + "ace/lib/oop", + "ace/lib/dom", + "ace/lib/lang", + "ace/layer/lines", + "ace/lib/event_emitter" + ], + function(require, exports, module) { + "use strict"; + + var oop = require("../lib/oop"); + var dom = require("../lib/dom"); + var lang = require("../lib/lang"); + var Lines = require("./lines").Lines; + var EventEmitter = require("../lib/event_emitter").EventEmitter; + + var Text = function(parentEl) { + this.dom = dom; + this.element = this.dom.createElement("div"); + this.element.className = "ace_layer ace_text-layer"; + parentEl.appendChild(this.element); + this.$updateEolChar = this.$updateEolChar.bind(this); + this.$lines = new Lines(this.element); + }; + + (function() { + oop.implement(this, EventEmitter); + + this.EOF_CHAR = "\xB6"; + this.EOL_CHAR_LF = "\xAC"; + this.EOL_CHAR_CRLF = "\xa4"; + this.EOL_CHAR = this.EOL_CHAR_LF; + this.TAB_CHAR = "\u2014"; //"\u21E5"; + this.SPACE_CHAR = "\xB7"; + this.$padding = 0; + this.MAX_LINE_LENGTH = 10000; + + this.$updateEolChar = function() { + var doc = this.session.doc; + var unixMode = + doc.getNewLineCharacter() == "\n" && + doc.getNewLineMode() != "windows"; + var EOL_CHAR = unixMode ? this.EOL_CHAR_LF : this.EOL_CHAR_CRLF; + if (this.EOL_CHAR != EOL_CHAR) { + this.EOL_CHAR = EOL_CHAR; + return true; + } + }; + + this.setPadding = function(padding) { + this.$padding = padding; + this.element.style.margin = "0 " + padding + "px"; + }; + + this.getLineHeight = function() { + return this.$fontMetrics.$characterSize.height || 0; + }; + + this.getCharacterWidth = function() { + return this.$fontMetrics.$characterSize.width || 0; + }; + + this.$setFontMetrics = function(measure) { + this.$fontMetrics = measure; + this.$fontMetrics.on( + "changeCharacterSize", + function(e) { + this._signal("changeCharacterSize", e); + }.bind(this) + ); + this.$pollSizeChanges(); + }; + + this.checkForSizeChanges = function() { + this.$fontMetrics.checkForSizeChanges(); + }; + this.$pollSizeChanges = function() { + return (this.$pollSizeChangesTimer = this.$fontMetrics.$pollSizeChanges()); + }; + this.setSession = function(session) { + this.session = session; + if (session) this.$computeTabString(); + }; + + this.showInvisibles = false; + this.showSpaces = false; + this.showTabs = false; + this.showEOL = false; + this.setShowInvisibles = function(showInvisibles) { + if (this.showInvisibles == showInvisibles) return false; + + this.showInvisibles = showInvisibles; + if (typeof showInvisibles == "string") { + this.showSpaces = /tab/i.test(showInvisibles); + this.showTabs = /space/i.test(showInvisibles); + this.showEOL = /eol/i.test(showInvisibles); + } else { + this.showSpaces = this.showTabs = this.showEOL = showInvisibles; + } + this.$computeTabString(); + return true; + }; + + this.displayIndentGuides = true; + this.setDisplayIndentGuides = function(display) { + if (this.displayIndentGuides == display) return false; + + this.displayIndentGuides = display; + this.$computeTabString(); + return true; + }; + + this.$tabStrings = []; + this.onChangeTabSize = this.$computeTabString = function() { + var tabSize = this.session.getTabSize(); + this.tabSize = tabSize; + var tabStr = (this.$tabStrings = [0]); + for (var i = 1; i < tabSize + 1; i++) { + if (this.showTabs) { + var span = this.dom.createElement("span"); + span.className = "ace_invisible ace_invisible_tab"; + span.textContent = lang.stringRepeat(this.TAB_CHAR, i); + tabStr.push(span); + } else { + tabStr.push( + this.dom.createTextNode(lang.stringRepeat(" ", i), this.element) + ); + } + } + if (this.displayIndentGuides) { + this.$indentGuideRe = /\s\S| \t|\t |\s$/; + var className = "ace_indent-guide"; + var spaceClass = this.showSpaces + ? " ace_invisible ace_invisible_space" + : ""; + var spaceContent = this.showSpaces + ? lang.stringRepeat(this.SPACE_CHAR, this.tabSize) + : lang.stringRepeat(" ", this.tabSize); + + var tabClass = this.showTabs + ? " ace_invisible ace_invisible_tab" + : ""; + var tabContent = this.showTabs + ? lang.stringRepeat(this.TAB_CHAR, this.tabSize) + : spaceContent; + + var span = this.dom.createElement("span"); + span.className = className + spaceClass; + span.textContent = spaceContent; + this.$tabStrings[" "] = span; + + var span = this.dom.createElement("span"); + span.className = className + tabClass; + span.textContent = tabContent; + this.$tabStrings["\t"] = span; + } + }; + + this.updateLines = function(config, firstRow, lastRow) { + if ( + this.config.lastRow != config.lastRow || + this.config.firstRow != config.firstRow + ) { + return this.update(config); + } + + this.config = config; + + var first = Math.max(firstRow, config.firstRow); + var last = Math.min(lastRow, config.lastRow); + + var lineElements = this.element.childNodes; + var lineElementsIdx = 0; + + for (var row = config.firstRow; row < first; row++) { + var foldLine = this.session.getFoldLine(row); + if (foldLine) { + if (foldLine.containsRow(first)) { + first = foldLine.start.row; + break; + } else { + row = foldLine.end.row; + } + } + lineElementsIdx++; + } + + var heightChanged = false; + var row = first; + var foldLine = this.session.getNextFoldLine(row); + var foldStart = foldLine ? foldLine.start.row : Infinity; + + while (true) { + if (row > foldStart) { + row = foldLine.end.row + 1; + foldLine = this.session.getNextFoldLine(row, foldLine); + foldStart = foldLine ? foldLine.start.row : Infinity; + } + if (row > last) break; + + var lineElement = lineElements[lineElementsIdx++]; + if (lineElement) { + this.dom.removeChildren(lineElement); + this.$renderLine( + lineElement, + row, + row == foldStart ? foldLine : false + ); + + if (heightChanged) + lineElement.style.top = + this.$lines.computeLineTop(row, config, this.session) + "px"; + + var height = + config.lineHeight * this.session.getRowLength(row) + "px"; + if (lineElement.style.height != height) { + heightChanged = true; + lineElement.style.height = height; + } + } + row++; + } + if (heightChanged) { + while (lineElementsIdx < this.$lines.cells.length) { + var cell = this.$lines.cells[lineElementsIdx++]; + cell.element.style.top = + this.$lines.computeLineTop(cell.row, config, this.session) + "px"; + } + } + }; + + this.scrollLines = function(config) { + var oldConfig = this.config; + this.config = config; + + if (this.$lines.pageChanged(oldConfig, config)) + return this.update(config); + + this.$lines.moveContainer(config); + + var lastRow = config.lastRow; + var oldLastRow = oldConfig ? oldConfig.lastRow : -1; + + if (!oldConfig || oldLastRow < config.firstRow) + return this.update(config); + + if (lastRow < oldConfig.firstRow) return this.update(config); + + if (!oldConfig || oldConfig.lastRow < config.firstRow) + return this.update(config); + + if (config.lastRow < oldConfig.firstRow) return this.update(config); + + if (oldConfig.firstRow < config.firstRow) + for ( + var row = this.session.getFoldedRowCount( + oldConfig.firstRow, + config.firstRow - 1 + ); + row > 0; + row-- + ) + this.$lines.shift(); + + if (oldConfig.lastRow > config.lastRow) + for ( + var row = this.session.getFoldedRowCount( + config.lastRow + 1, + oldConfig.lastRow + ); + row > 0; + row-- + ) + this.$lines.pop(); + + if (config.firstRow < oldConfig.firstRow) { + this.$lines.unshift( + this.$renderLinesFragment( + config, + config.firstRow, + oldConfig.firstRow - 1 + ) + ); + } + + if (config.lastRow > oldConfig.lastRow) { + this.$lines.push( + this.$renderLinesFragment( + config, + oldConfig.lastRow + 1, + config.lastRow + ) + ); + } + }; + + this.$renderLinesFragment = function(config, firstRow, lastRow) { + var fragment = []; + var row = firstRow; + var foldLine = this.session.getNextFoldLine(row); + var foldStart = foldLine ? foldLine.start.row : Infinity; + + while (true) { + if (row > foldStart) { + row = foldLine.end.row + 1; + foldLine = this.session.getNextFoldLine(row, foldLine); + foldStart = foldLine ? foldLine.start.row : Infinity; + } + if (row > lastRow) break; + + var line = this.$lines.createCell(row, config, this.session); + + var lineEl = line.element; + this.dom.removeChildren(lineEl); + dom.setStyle( + lineEl.style, + "height", + this.$lines.computeLineHeight(row, config, this.session) + "px" + ); + dom.setStyle( + lineEl.style, + "top", + this.$lines.computeLineTop(row, config, this.session) + "px" + ); + this.$renderLine(lineEl, row, row == foldStart ? foldLine : false); + + if (this.$useLineGroups()) { + lineEl.className = "ace_line_group"; + } else { + lineEl.className = "ace_line"; + } + fragment.push(line); + + row++; + } + return fragment; + }; + + this.update = function(config) { + this.$lines.moveContainer(config); + + this.config = config; + + var firstRow = config.firstRow; + var lastRow = config.lastRow; + + var lines = this.$lines; + while (lines.getLength()) lines.pop(); + + lines.push(this.$renderLinesFragment(config, firstRow, lastRow)); + }; + + this.$textToken = { + text: true, + rparen: true, + lparen: true + }; + + this.$renderToken = function(parent, screenColumn, token, value) { + var self = this; + var re = /(\t)|( +)|([\x00-\x1f\x80-\xa0\xad\u1680\u180E\u2000-\u200f\u2028\u2029\u202F\u205F\uFEFF\uFFF9-\uFFFC]+)|(\u3000)|([\u1100-\u115F\u11A3-\u11A7\u11FA-\u11FF\u2329-\u232A\u2E80-\u2E99\u2E9B-\u2EF3\u2F00-\u2FD5\u2FF0-\u2FFB\u3001-\u303E\u3041-\u3096\u3099-\u30FF\u3105-\u312D\u3131-\u318E\u3190-\u31BA\u31C0-\u31E3\u31F0-\u321E\u3220-\u3247\u3250-\u32FE\u3300-\u4DBF\u4E00-\uA48C\uA490-\uA4C6\uA960-\uA97C\uAC00-\uD7A3\uD7B0-\uD7C6\uD7CB-\uD7FB\uF900-\uFAFF\uFE10-\uFE19\uFE30-\uFE52\uFE54-\uFE66\uFE68-\uFE6B\uFF01-\uFF60\uFFE0-\uFFE6]|[\uD800-\uDBFF][\uDC00-\uDFFF])/g; + + var valueFragment = this.dom.createFragment(this.element); + + var m; + var i = 0; + while ((m = re.exec(value))) { + var tab = m[1]; + var simpleSpace = m[2]; + var controlCharacter = m[3]; + var cjkSpace = m[4]; + var cjk = m[5]; + + if (!self.showSpaces && simpleSpace) continue; + + var before = i != m.index ? value.slice(i, m.index) : ""; + + i = m.index + m[0].length; + + if (before) { + valueFragment.appendChild( + this.dom.createTextNode(before, this.element) + ); + } + + if (tab) { + var tabSize = self.session.getScreenTabSize(screenColumn + m.index); + valueFragment.appendChild( + self.$tabStrings[tabSize].cloneNode(true) + ); + screenColumn += tabSize - 1; + } else if (simpleSpace) { + if (self.showSpaces) { + var span = this.dom.createElement("span"); + span.className = "ace_invisible ace_invisible_space"; + span.textContent = lang.stringRepeat( + self.SPACE_CHAR, + simpleSpace.length + ); + valueFragment.appendChild(span); + } else { + valueFragment.appendChild( + this.com.createTextNode(simpleSpace, this.element) + ); + } + } else if (controlCharacter) { + var span = this.dom.createElement("span"); + span.className = "ace_invisible ace_invisible_space ace_invalid"; + span.textContent = lang.stringRepeat( + self.SPACE_CHAR, + controlCharacter.length + ); + valueFragment.appendChild(span); + } else if (cjkSpace) { + screenColumn += 1; + + var span = this.dom.createElement("span"); + span.style.width = self.config.characterWidth * 2 + "px"; + span.className = self.showSpaces + ? "ace_cjk ace_invisible ace_invisible_space" + : "ace_cjk"; + span.textContent = self.showSpaces ? self.SPACE_CHAR : cjkSpace; + valueFragment.appendChild(span); + } else if (cjk) { + screenColumn += 1; + var span = this.dom.createElement("span"); + span.style.width = self.config.characterWidth * 2 + "px"; + span.className = "ace_cjk"; + span.textContent = cjk; + valueFragment.appendChild(span); + } + } + + valueFragment.appendChild( + this.dom.createTextNode(i ? value.slice(i) : value, this.element) + ); + + if (!this.$textToken[token.type]) { + var classes = "ace_" + token.type.replace(/\./g, " ace_"); + var span = this.dom.createElement("span"); + if (token.type == "fold") + span.style.width = + token.value.length * this.config.characterWidth + "px"; + + span.className = classes; + span.appendChild(valueFragment); + + parent.appendChild(span); + } else { + parent.appendChild(valueFragment); + } + + return screenColumn + value.length; + }; + + this.renderIndentGuide = function(parent, value, max) { + var cols = value.search(this.$indentGuideRe); + if (cols <= 0 || cols >= max) return value; + if (value[0] == " ") { + cols -= cols % this.tabSize; + var count = cols / this.tabSize; + for (var i = 0; i < count; i++) { + parent.appendChild(this.$tabStrings[" "].cloneNode(true)); + } + return value.substr(cols); + } else if (value[0] == "\t") { + for (var i = 0; i < cols; i++) { + parent.appendChild(this.$tabStrings["\t"].cloneNode(true)); + } + return value.substr(cols); + } + return value; + }; + + this.$createLineElement = function(parent) { + var lineEl = this.dom.createElement("div"); + lineEl.className = "ace_line"; + lineEl.style.height = this.config.lineHeight + "px"; + + return lineEl; + }; + + this.$renderWrappedLine = function(parent, tokens, splits) { + var chars = 0; + var split = 0; + var splitChars = splits[0]; + var screenColumn = 0; + + var lineEl = this.$createLineElement(); + parent.appendChild(lineEl); + + for (var i = 0; i < tokens.length; i++) { + var token = tokens[i]; + var value = token.value; + if (i == 0 && this.displayIndentGuides) { + chars = value.length; + value = this.renderIndentGuide(lineEl, value, splitChars); + if (!value) continue; + chars -= value.length; + } + + if (chars + value.length < splitChars) { + screenColumn = this.$renderToken( + lineEl, + screenColumn, + token, + value + ); + chars += value.length; + } else { + while (chars + value.length >= splitChars) { + screenColumn = this.$renderToken( + lineEl, + screenColumn, + token, + value.substring(0, splitChars - chars) + ); + value = value.substring(splitChars - chars); + chars = splitChars; + + lineEl = this.$createLineElement(); + parent.appendChild(lineEl); + + lineEl.appendChild( + this.dom.createTextNode( + lang.stringRepeat("\xa0", splits.indent), + this.element + ) + ); + + split++; + screenColumn = 0; + splitChars = splits[split] || Number.MAX_VALUE; + } + if (value.length != 0) { + chars += value.length; + screenColumn = this.$renderToken( + lineEl, + screenColumn, + token, + value + ); + } + } + } + + if (splits[splits.length - 1] > this.MAX_LINE_LENGTH) + this.$renderOverflowMessage(lineEl, screenColumn, null, "", true); + }; + + this.$renderSimpleLine = function(parent, tokens) { + var screenColumn = 0; + var token = tokens[0]; + var value = token.value; + if (this.displayIndentGuides) + value = this.renderIndentGuide(parent, value); + if (value) + screenColumn = this.$renderToken(parent, screenColumn, token, value); + for (var i = 1; i < tokens.length; i++) { + token = tokens[i]; + value = token.value; + if (screenColumn + value.length > this.MAX_LINE_LENGTH) + return this.$renderOverflowMessage( + parent, + screenColumn, + token, + value + ); + screenColumn = this.$renderToken(parent, screenColumn, token, value); + } + }; + + this.$renderOverflowMessage = function( + parent, + screenColumn, + token, + value, + hide + ) { + token && + this.$renderToken( + parent, + screenColumn, + token, + value.slice(0, this.MAX_LINE_LENGTH - screenColumn) + ); + + var overflowEl = this.dom.createElement("span"); + overflowEl.className = "ace_inline_button ace_keyword ace_toggle_wrap"; + overflowEl.textContent = hide ? "" : ""; + + parent.appendChild(overflowEl); + }; + this.$renderLine = function(parent, row, foldLine) { + if (!foldLine && foldLine != false) + foldLine = this.session.getFoldLine(row); + + if (foldLine) var tokens = this.$getFoldLineTokens(row, foldLine); + else var tokens = this.session.getTokens(row); + + var lastLineEl = parent; + if (tokens.length) { + var splits = this.session.getRowSplitData(row); + if (splits && splits.length) { + this.$renderWrappedLine(parent, tokens, splits); + var lastLineEl = parent.lastChild; + } else { + var lastLineEl = parent; + if (this.$useLineGroups()) { + lastLineEl = this.$createLineElement(); + parent.appendChild(lastLineEl); + } + this.$renderSimpleLine(lastLineEl, tokens); + } + } else if (this.$useLineGroups()) { + lastLineEl = this.$createLineElement(); + parent.appendChild(lastLineEl); + } + + if (this.showEOL && lastLineEl) { + if (foldLine) row = foldLine.end.row; + + var invisibleEl = this.dom.createElement("span"); + invisibleEl.className = "ace_invisible ace_invisible_eol"; + invisibleEl.textContent = + row == this.session.getLength() - 1 ? this.EOF_CHAR : this.EOL_CHAR; + + lastLineEl.appendChild(invisibleEl); + } + }; + + this.$getFoldLineTokens = function(row, foldLine) { + var session = this.session; + var renderTokens = []; + + function addTokens(tokens, from, to) { + var idx = 0, + col = 0; + while (col + tokens[idx].value.length < from) { + col += tokens[idx].value.length; + idx++; + + if (idx == tokens.length) return; + } + if (col != from) { + var value = tokens[idx].value.substring(from - col); + if (value.length > to - from) value = value.substring(0, to - from); + + renderTokens.push({ + type: tokens[idx].type, + value: value + }); + + col = from + value.length; + idx += 1; + } + + while (col < to && idx < tokens.length) { + var value = tokens[idx].value; + if (value.length + col > to) { + renderTokens.push({ + type: tokens[idx].type, + value: value.substring(0, to - col) + }); + } else renderTokens.push(tokens[idx]); + col += value.length; + idx += 1; + } + } + + var tokens = session.getTokens(row); + foldLine.walk( + function(placeholder, row, column, lastColumn, isNewRow) { + if (placeholder != null) { + renderTokens.push({ + type: "fold", + value: placeholder + }); + } else { + if (isNewRow) tokens = session.getTokens(row); + + if (tokens.length) addTokens(tokens, lastColumn, column); + } + }, + foldLine.end.row, + this.session.getLine(foldLine.end.row).length + ); + + return renderTokens; + }; + + this.$useLineGroups = function() { + return this.session.getUseWrapMode(); + }; + + this.destroy = function() {}; + }.call(Text.prototype)); + + exports.Text = Text; + } +); + +ace.define( + "ace/layer/cursor", + ["require", "exports", "module", "ace/lib/dom"], + function(require, exports, module) { + "use strict"; + + var dom = require("../lib/dom"); + + var Cursor = function(parentEl) { + this.element = dom.createElement("div"); + this.element.className = "ace_layer ace_cursor-layer"; + parentEl.appendChild(this.element); + + this.isVisible = false; + this.isBlinking = true; + this.blinkInterval = 1000; + this.smoothBlinking = false; + + this.cursors = []; + this.cursor = this.addCursor(); + dom.addCssClass(this.element, "ace_hidden-cursors"); + this.$updateCursors = this.$updateOpacity.bind(this); + }; + + (function() { + this.$updateOpacity = function(val) { + var cursors = this.cursors; + for (var i = cursors.length; i--; ) + dom.setStyle(cursors[i].style, "opacity", val ? "" : "0"); + }; + + this.$startCssAnimation = function() { + var cursors = this.cursors; + for (var i = cursors.length; i--; ) + cursors[i].style.animationDuration = this.blinkInterval + "ms"; + + setTimeout( + function() { + dom.addCssClass(this.element, "ace_animate-blinking"); + }.bind(this) + ); + }; + + this.$stopCssAnimation = function() { + dom.removeCssClass(this.element, "ace_animate-blinking"); + }; + + this.$padding = 0; + this.setPadding = function(padding) { + this.$padding = padding; + }; + + this.setSession = function(session) { + this.session = session; + }; + + this.setBlinking = function(blinking) { + if (blinking != this.isBlinking) { + this.isBlinking = blinking; + this.restartTimer(); + } + }; + + this.setBlinkInterval = function(blinkInterval) { + if (blinkInterval != this.blinkInterval) { + this.blinkInterval = blinkInterval; + this.restartTimer(); + } + }; + + this.setSmoothBlinking = function(smoothBlinking) { + if (smoothBlinking != this.smoothBlinking) { + this.smoothBlinking = smoothBlinking; + dom.setCssClass(this.element, "ace_smooth-blinking", smoothBlinking); + this.$updateCursors(true); + this.restartTimer(); + } + }; + + this.addCursor = function() { + var el = dom.createElement("div"); + el.className = "ace_cursor"; + this.element.appendChild(el); + this.cursors.push(el); + return el; + }; + + this.removeCursor = function() { + if (this.cursors.length > 1) { + var el = this.cursors.pop(); + el.parentNode.removeChild(el); + return el; + } + }; + + this.hideCursor = function() { + this.isVisible = false; + dom.addCssClass(this.element, "ace_hidden-cursors"); + this.restartTimer(); + }; + + this.showCursor = function() { + this.isVisible = true; + dom.removeCssClass(this.element, "ace_hidden-cursors"); + this.restartTimer(); + }; + + this.restartTimer = function() { + var update = this.$updateCursors; + clearInterval(this.intervalId); + clearTimeout(this.timeoutId); + this.$stopCssAnimation(); + + if (this.smoothBlinking) { + dom.removeCssClass(this.element, "ace_smooth-blinking"); + } + + update(true); + + if (!this.isBlinking || !this.blinkInterval || !this.isVisible) { + this.$stopCssAnimation(); + return; + } + + if (this.smoothBlinking) { + setTimeout( + function() { + dom.addCssClass(this.element, "ace_smooth-blinking"); + }.bind(this) + ); + } + + if (dom.HAS_CSS_ANIMATION) { + this.$startCssAnimation(); + } else { + var blink = function() { + this.timeoutId = setTimeout(function() { + update(false); + }, 0.6 * this.blinkInterval); + }.bind(this); + + this.intervalId = setInterval(function() { + update(true); + blink(); + }, this.blinkInterval); + blink(); + } + }; + + this.getPixelPosition = function(position, onScreen) { + if (!this.config || !this.session) return { left: 0, top: 0 }; + + if (!position) position = this.session.selection.getCursor(); + var pos = this.session.documentToScreenPosition(position); + var cursorLeft = + this.$padding + + (this.session.$bidiHandler.isBidiRow(pos.row, position.row) + ? this.session.$bidiHandler.getPosLeft(pos.column) + : pos.column * this.config.characterWidth); + + var cursorTop = + (pos.row - (onScreen ? this.config.firstRowScreen : 0)) * + this.config.lineHeight; + + return { left: cursorLeft, top: cursorTop }; + }; + + this.isCursorInView = function(pixelPos, config) { + return pixelPos.top >= 0 && pixelPos.top < config.maxHeight; + }; + + this.update = function(config) { + this.config = config; + + var selections = this.session.$selectionMarkers; + var i = 0, + cursorIndex = 0; + + if (selections === undefined || selections.length === 0) { + selections = [{ cursor: null }]; + } + + for (var i = 0, n = selections.length; i < n; i++) { + var pixelPos = this.getPixelPosition(selections[i].cursor, true); + if ( + (pixelPos.top > config.height + config.offset || + pixelPos.top < 0) && + i > 1 + ) { + continue; + } + + var element = this.cursors[cursorIndex++] || this.addCursor(); + var style = element.style; + + if (!this.drawCursor) { + if (!this.isCursorInView(pixelPos, config)) { + dom.setStyle(style, "display", "none"); + } else { + dom.setStyle(style, "display", "block"); + dom.translate(element, pixelPos.left, pixelPos.top); + dom.setStyle( + style, + "width", + Math.round(config.characterWidth) + "px" + ); + dom.setStyle(style, "height", config.lineHeight + "px"); + } + } else { + this.drawCursor( + element, + pixelPos, + config, + selections[i], + this.session + ); + } + } + while (this.cursors.length > cursorIndex) this.removeCursor(); + + var overwrite = this.session.getOverwrite(); + this.$setOverwrite(overwrite); + this.$pixelPos = pixelPos; + this.restartTimer(); + }; + + this.drawCursor = null; + + this.$setOverwrite = function(overwrite) { + if (overwrite != this.overwrite) { + this.overwrite = overwrite; + if (overwrite) dom.addCssClass(this.element, "ace_overwrite-cursors"); + else dom.removeCssClass(this.element, "ace_overwrite-cursors"); + } + }; + + this.destroy = function() { + clearInterval(this.intervalId); + clearTimeout(this.timeoutId); + }; + }.call(Cursor.prototype)); + + exports.Cursor = Cursor; + } +); + +ace.define( + "ace/scrollbar", + [ + "require", + "exports", + "module", + "ace/lib/oop", + "ace/lib/dom", + "ace/lib/event", + "ace/lib/event_emitter" + ], + function(require, exports, module) { + "use strict"; + + var oop = require("./lib/oop"); + var dom = require("./lib/dom"); + var event = require("./lib/event"); + var EventEmitter = require("./lib/event_emitter").EventEmitter; + var MAX_SCROLL_H = 0x8000; + var ScrollBar = function(parent) { + this.element = dom.createElement("div"); + this.element.className = "ace_scrollbar ace_scrollbar" + this.classSuffix; + + this.inner = dom.createElement("div"); + this.inner.className = "ace_scrollbar-inner"; + this.inner.textContent = "\xa0"; + this.element.appendChild(this.inner); + + parent.appendChild(this.element); + + this.setVisible(false); + this.skipEvent = false; + + event.addListener(this.element, "scroll", this.onScroll.bind(this)); + event.addListener(this.element, "mousedown", event.preventDefault); + }; + + (function() { + oop.implement(this, EventEmitter); + + this.setVisible = function(isVisible) { + this.element.style.display = isVisible ? "" : "none"; + this.isVisible = isVisible; + this.coeff = 1; + }; + }.call(ScrollBar.prototype)); + var VScrollBar = function(parent, renderer) { + ScrollBar.call(this, parent); + this.scrollTop = 0; + this.scrollHeight = 0; + renderer.$scrollbarWidth = this.width = dom.scrollbarWidth( + parent.ownerDocument + ); + this.inner.style.width = this.element.style.width = + (this.width || 15) + 5 + "px"; + this.$minWidth = 0; + }; + + oop.inherits(VScrollBar, ScrollBar); + + (function() { + this.classSuffix = "-v"; + this.onScroll = function() { + if (!this.skipEvent) { + this.scrollTop = this.element.scrollTop; + if (this.coeff != 1) { + var h = this.element.clientHeight / this.scrollHeight; + this.scrollTop = (this.scrollTop * (1 - h)) / (this.coeff - h); + } + this._emit("scroll", { data: this.scrollTop }); + } + this.skipEvent = false; + }; + this.getWidth = function() { + return Math.max(this.isVisible ? this.width : 0, this.$minWidth || 0); + }; + this.setHeight = function(height) { + this.element.style.height = height + "px"; + }; + this.setInnerHeight = this.setScrollHeight = function(height) { + this.scrollHeight = height; + if (height > MAX_SCROLL_H) { + this.coeff = MAX_SCROLL_H / height; + height = MAX_SCROLL_H; + } else if (this.coeff != 1) { + this.coeff = 1; + } + this.inner.style.height = height + "px"; + }; + this.setScrollTop = function(scrollTop) { + if (this.scrollTop != scrollTop) { + this.skipEvent = true; + this.scrollTop = scrollTop; + this.element.scrollTop = scrollTop * this.coeff; + } + }; + }.call(VScrollBar.prototype)); + var HScrollBar = function(parent, renderer) { + ScrollBar.call(this, parent); + this.scrollLeft = 0; + this.height = renderer.$scrollbarWidth; + this.inner.style.height = this.element.style.height = + (this.height || 15) + 5 + "px"; + }; + + oop.inherits(HScrollBar, ScrollBar); + + (function() { + this.classSuffix = "-h"; + this.onScroll = function() { + if (!this.skipEvent) { + this.scrollLeft = this.element.scrollLeft; + this._emit("scroll", { data: this.scrollLeft }); + } + this.skipEvent = false; + }; + this.getHeight = function() { + return this.isVisible ? this.height : 0; + }; + this.setWidth = function(width) { + this.element.style.width = width + "px"; + }; + this.setInnerWidth = function(width) { + this.inner.style.width = width + "px"; + }; + this.setScrollWidth = function(width) { + this.inner.style.width = width + "px"; + }; + this.setScrollLeft = function(scrollLeft) { + if (this.scrollLeft != scrollLeft) { + this.skipEvent = true; + this.scrollLeft = this.element.scrollLeft = scrollLeft; + } + }; + }.call(HScrollBar.prototype)); + + exports.ScrollBar = VScrollBar; // backward compatibility + exports.ScrollBarV = VScrollBar; // backward compatibility + exports.ScrollBarH = HScrollBar; // backward compatibility + + exports.VScrollBar = VScrollBar; + exports.HScrollBar = HScrollBar; + } +); + +ace.define( + "ace/renderloop", + ["require", "exports", "module", "ace/lib/event"], + function(require, exports, module) { + "use strict"; + + var event = require("./lib/event"); + + var RenderLoop = function(onRender, win) { + this.onRender = onRender; + this.pending = false; + this.changes = 0; + this.$recursionLimit = 2; + this.window = win || window; + var _self = this; + this._flush = function(ts) { + _self.pending = false; + var changes = _self.changes; + + if (changes) { + event.blockIdle(100); + _self.changes = 0; + _self.onRender(changes); + } + + if (_self.changes) { + if (_self.$recursionLimit-- < 0) return; + _self.schedule(); + } else { + _self.$recursionLimit = 2; + } + }; + }; + + (function() { + this.schedule = function(change) { + this.changes = this.changes | change; + if (this.changes && !this.pending) { + event.nextFrame(this._flush); + this.pending = true; + } + }; + + this.clear = function(change) { + var changes = this.changes; + this.changes = 0; + return changes; + }; + }.call(RenderLoop.prototype)); + + exports.RenderLoop = RenderLoop; + } +); + +ace.define( + "ace/layer/font_metrics", + [ + "require", + "exports", + "module", + "ace/lib/oop", + "ace/lib/dom", + "ace/lib/lang", + "ace/lib/event", + "ace/lib/useragent", + "ace/lib/event_emitter" + ], + function(require, exports, module) { + var oop = require("../lib/oop"); + var dom = require("../lib/dom"); + var lang = require("../lib/lang"); + var event = require("../lib/event"); + var useragent = require("../lib/useragent"); + var EventEmitter = require("../lib/event_emitter").EventEmitter; + + var CHAR_COUNT = 256; + var USE_OBSERVER = typeof ResizeObserver == "function"; + var L = 200; + + var FontMetrics = (exports.FontMetrics = function(parentEl) { + this.el = dom.createElement("div"); + this.$setMeasureNodeStyles(this.el.style, true); + + this.$main = dom.createElement("div"); + this.$setMeasureNodeStyles(this.$main.style); + + this.$measureNode = dom.createElement("div"); + this.$setMeasureNodeStyles(this.$measureNode.style); + + this.el.appendChild(this.$main); + this.el.appendChild(this.$measureNode); + parentEl.appendChild(this.el); + + this.$measureNode.textContent = lang.stringRepeat("X", CHAR_COUNT); + + this.$characterSize = { width: 0, height: 0 }; + + if (USE_OBSERVER) this.$addObserver(); + else this.checkForSizeChanges(); + }); + + (function() { + oop.implement(this, EventEmitter); + + this.$characterSize = { width: 0, height: 0 }; + + this.$setMeasureNodeStyles = function(style, isRoot) { + style.width = style.height = "auto"; + style.left = style.top = "0px"; + style.visibility = "hidden"; + style.position = "absolute"; + style.whiteSpace = "pre"; + + if (useragent.isIE < 8) { + style["font-family"] = "inherit"; + } else { + style.font = "inherit"; + } + style.overflow = isRoot ? "hidden" : "visible"; + }; + + this.checkForSizeChanges = function(size) { + if (size === undefined) size = this.$measureSizes(); + if ( + size && + (this.$characterSize.width !== size.width || + this.$characterSize.height !== size.height) + ) { + this.$measureNode.style.fontWeight = "bold"; + var boldSize = this.$measureSizes(); + this.$measureNode.style.fontWeight = ""; + this.$characterSize = size; + this.charSizes = Object.create(null); + this.allowBoldFonts = + boldSize && + boldSize.width === size.width && + boldSize.height === size.height; + this._emit("changeCharacterSize", { data: size }); + } + }; + + this.$addObserver = function() { + var self = this; + this.$observer = new window.ResizeObserver(function(e) { + self.checkForSizeChanges(); + }); + this.$observer.observe(this.$measureNode); + }; + + this.$pollSizeChanges = function() { + if (this.$pollSizeChangesTimer || this.$observer) + return this.$pollSizeChangesTimer; + var self = this; + + return (this.$pollSizeChangesTimer = event.onIdle(function cb() { + self.checkForSizeChanges(); + event.onIdle(cb, 500); + }, 500)); + }; + + this.setPolling = function(val) { + if (val) { + this.$pollSizeChanges(); + } else if (this.$pollSizeChangesTimer) { + clearInterval(this.$pollSizeChangesTimer); + this.$pollSizeChangesTimer = 0; + } + }; + + this.$measureSizes = function(node) { + var size = { + height: (node || this.$measureNode).clientHeight, + width: (node || this.$measureNode).clientWidth / CHAR_COUNT + }; + if (size.width === 0 || size.height === 0) return null; + return size; + }; + + this.$measureCharWidth = function(ch) { + this.$main.textContent = lang.stringRepeat(ch, CHAR_COUNT); + var rect = this.$main.getBoundingClientRect(); + return rect.width / CHAR_COUNT; + }; + + this.getCharacterWidth = function(ch) { + var w = this.charSizes[ch]; + if (w === undefined) { + w = this.charSizes[ch] = + this.$measureCharWidth(ch) / this.$characterSize.width; + } + return w; + }; + + this.destroy = function() { + clearInterval(this.$pollSizeChangesTimer); + if (this.$observer) this.$observer.disconnect(); + if (this.el && this.el.parentNode) + this.el.parentNode.removeChild(this.el); + }; + + this.$getZoom = function getZoom(element) { + if (!element) return 1; + return ( + (window.getComputedStyle(element).zoom || 1) * + getZoom(element.parentElement) + ); + }; + this.$initTransformMeasureNodes = function() { + var t = function(t, l) { + return [ + "div", + { + style: "position: absolute;top:" + t + "px;left:" + l + "px;" + } + ]; + }; + this.els = dom.buildDom([t(0, 0), t(L, 0), t(0, L), t(L, L)], this.el); + }; + this.transformCoordinates = function(clientPos, elPos) { + if (clientPos) { + var zoom = this.$getZoom(this.el); + clientPos = mul(1 / zoom, clientPos); + } + function solve(l1, l2, r) { + var det = l1[1] * l2[0] - l1[0] * l2[1]; + return [ + (-l2[1] * r[0] + l2[0] * r[1]) / det, + (+l1[1] * r[0] - l1[0] * r[1]) / det + ]; + } + function sub(a, b) { + return [a[0] - b[0], a[1] - b[1]]; + } + function add(a, b) { + return [a[0] + b[0], a[1] + b[1]]; + } + function mul(a, b) { + return [a * b[0], a * b[1]]; + } + + if (!this.els) this.$initTransformMeasureNodes(); + + function p(el) { + var r = el.getBoundingClientRect(); + return [r.left, r.top]; + } + + var a = p(this.els[0]); + var b = p(this.els[1]); + var c = p(this.els[2]); + var d = p(this.els[3]); + + var h = solve(sub(d, b), sub(d, c), sub(add(b, c), add(d, a))); + + var m1 = mul(1 + h[0], sub(b, a)); + var m2 = mul(1 + h[1], sub(c, a)); + + if (elPos) { + var x = elPos; + var k = (h[0] * x[0]) / L + (h[1] * x[1]) / L + 1; + var ut = add(mul(x[0], m1), mul(x[1], m2)); + return add(mul(1 / k / L, ut), a); + } + var u = sub(clientPos, a); + var f = solve(sub(m1, mul(h[0], u)), sub(m2, mul(h[1], u)), u); + return mul(L, f); + }; + }.call(FontMetrics.prototype)); + } +); + +ace.define( + "ace/virtual_renderer", + [ + "require", + "exports", + "module", + "ace/lib/oop", + "ace/lib/dom", + "ace/config", + "ace/layer/gutter", + "ace/layer/marker", + "ace/layer/text", + "ace/layer/cursor", + "ace/scrollbar", + "ace/scrollbar", + "ace/renderloop", + "ace/layer/font_metrics", + "ace/lib/event_emitter", + "ace/lib/useragent" + ], + function(require, exports, module) { + "use strict"; + + var oop = require("./lib/oop"); + var dom = require("./lib/dom"); + var config = require("./config"); + var GutterLayer = require("./layer/gutter").Gutter; + var MarkerLayer = require("./layer/marker").Marker; + var TextLayer = require("./layer/text").Text; + var CursorLayer = require("./layer/cursor").Cursor; + var HScrollBar = require("./scrollbar").HScrollBar; + var VScrollBar = require("./scrollbar").VScrollBar; + var RenderLoop = require("./renderloop").RenderLoop; + var FontMetrics = require("./layer/font_metrics").FontMetrics; + var EventEmitter = require("./lib/event_emitter").EventEmitter; + var editorCss = + '\ + .ace_br1 {border-top-left-radius : 3px;}\ + .ace_br2 {border-top-right-radius : 3px;}\ + .ace_br3 {border-top-left-radius : 3px; border-top-right-radius: 3px;}\ + .ace_br4 {border-bottom-right-radius: 3px;}\ + .ace_br5 {border-top-left-radius : 3px; border-bottom-right-radius: 3px;}\ + .ace_br6 {border-top-right-radius : 3px; border-bottom-right-radius: 3px;}\ + .ace_br7 {border-top-left-radius : 3px; border-top-right-radius: 3px; border-bottom-right-radius: 3px;}\ + .ace_br8 {border-bottom-left-radius : 3px;}\ + .ace_br9 {border-top-left-radius : 3px; border-bottom-left-radius: 3px;}\ + .ace_br10{border-top-right-radius : 3px; border-bottom-left-radius: 3px;}\ + .ace_br11{border-top-left-radius : 3px; border-top-right-radius: 3px; border-bottom-left-radius: 3px;}\ + .ace_br12{border-bottom-right-radius: 3px; border-bottom-left-radius: 3px;}\ + .ace_br13{border-top-left-radius : 3px; border-bottom-right-radius: 3px; border-bottom-left-radius: 3px;}\ + .ace_br14{border-top-right-radius : 3px; border-bottom-right-radius: 3px; border-bottom-left-radius: 3px;}\ + .ace_br15{border-top-left-radius : 3px; border-top-right-radius: 3px; border-bottom-right-radius: 3px; border-bottom-left-radius: 3px;}\ + .ace_editor {\ + position: relative;\ + overflow: hidden;\ + padding: 0;\ + direction: ltr;\ + text-align: left;\ + -webkit-tap-highlight-color: rgba(0, 0, 0, 0);\ + }\ + .ace_editor div {\ + font-family: monospace !important;\ + }\ + .ace_scroller {\ + position: absolute;\ + overflow: hidden;\ + top: 0;\ + bottom: 0;\ + background-color: inherit;\ + -ms-user-select: none;\ + -moz-user-select: none;\ + -webkit-user-select: none;\ + user-select: none;\ + cursor: text;\ + }\ + .ace_content {\ + position: absolute;\ + box-sizing: border-box;\ + min-width: 100%;\ + contain: style size layout;\ + font-variant-ligatures: no-common-ligatures;\ + }\ + .ace_dragging .ace_scroller:before{\ + position: absolute;\ + top: 0;\ + left: 0;\ + right: 0;\ + bottom: 0;\ + content: \'\';\ + background: rgba(250, 250, 250, 0.01);\ + z-index: 1000;\ + }\ + .ace_dragging.ace_dark .ace_scroller:before{\ + background: rgba(0, 0, 0, 0.01);\ + }\ + .ace_selecting, .ace_selecting * {\ + cursor: text !important;\ + }\ + .ace_gutter {\ + position: absolute;\ + overflow : hidden;\ + width: auto;\ + top: 0;\ + bottom: 0;\ + left: 0;\ + cursor: default;\ + z-index: 4;\ + -ms-user-select: none;\ + -moz-user-select: none;\ + -webkit-user-select: none;\ + user-select: none;\ + contain: style size layout;\ + }\ + .ace_gutter-active-line {\ + position: absolute;\ + left: 0;\ + right: 0;\ + }\ + .ace_scroller.ace_scroll-left {\ + box-shadow: 17px 0 16px -16px rgba(0, 0, 0, 0.4) inset;\ + }\ + .ace_gutter-cell {\ + position: absolute;\ + top: 0;\ + left: 0;\ + right: 0;\ + padding-left: 19px;\ + padding-right: 6px;\ + background-repeat: no-repeat;\ + }\ + .ace_gutter-cell.ace_error {\ + background-image: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAMAAAAoLQ9TAAABOFBMVEX/////////QRswFAb/Ui4wFAYwFAYwFAaWGAfDRymzOSH/PxswFAb/SiUwFAYwFAbUPRvjQiDllog5HhHdRybsTi3/Tyv9Tir+Syj/UC3////XurebMBIwFAb/RSHbPx/gUzfdwL3kzMivKBAwFAbbvbnhPx66NhowFAYwFAaZJg8wFAaxKBDZurf/RB6mMxb/SCMwFAYwFAbxQB3+RB4wFAb/Qhy4Oh+4QifbNRcwFAYwFAYwFAb/QRzdNhgwFAYwFAbav7v/Uy7oaE68MBK5LxLewr/r2NXewLswFAaxJw4wFAbkPRy2PyYwFAaxKhLm1tMwFAazPiQwFAaUGAb/QBrfOx3bvrv/VC/maE4wFAbRPBq6MRO8Qynew8Dp2tjfwb0wFAbx6eju5+by6uns4uH9/f36+vr/GkHjAAAAYnRSTlMAGt+64rnWu/bo8eAA4InH3+DwoN7j4eLi4xP99Nfg4+b+/u9B/eDs1MD1mO7+4PHg2MXa347g7vDizMLN4eG+Pv7i5evs/v79yu7S3/DV7/498Yv24eH+4ufQ3Ozu/v7+y13sRqwAAADLSURBVHjaZc/XDsFgGIBhtDrshlitmk2IrbHFqL2pvXf/+78DPokj7+Fz9qpU/9UXJIlhmPaTaQ6QPaz0mm+5gwkgovcV6GZzd5JtCQwgsxoHOvJO15kleRLAnMgHFIESUEPmawB9ngmelTtipwwfASilxOLyiV5UVUyVAfbG0cCPHig+GBkzAENHS0AstVF6bacZIOzgLmxsHbt2OecNgJC83JERmePUYq8ARGkJx6XtFsdddBQgZE2nPR6CICZhawjA4Fb/chv+399kfR+MMMDGOQAAAABJRU5ErkJggg==");\ + background-repeat: no-repeat;\ + background-position: 2px center;\ + }\ + .ace_gutter-cell.ace_warning {\ + background-image: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAMAAAAoLQ9TAAAAmVBMVEX///8AAAD///8AAAAAAABPSzb/5sAAAAB/blH/73z/ulkAAAAAAAD85pkAAAAAAAACAgP/vGz/rkDerGbGrV7/pkQICAf////e0IsAAAD/oED/qTvhrnUAAAD/yHD/njcAAADuv2r/nz//oTj/p064oGf/zHAAAAA9Nir/tFIAAAD/tlTiuWf/tkIAAACynXEAAAAAAAAtIRW7zBpBAAAAM3RSTlMAABR1m7RXO8Ln31Z36zT+neXe5OzooRDfn+TZ4p3h2hTf4t3k3ucyrN1K5+Xaks52Sfs9CXgrAAAAjklEQVR42o3PbQ+CIBQFYEwboPhSYgoYunIqqLn6/z8uYdH8Vmdnu9vz4WwXgN/xTPRD2+sgOcZjsge/whXZgUaYYvT8QnuJaUrjrHUQreGczuEafQCO/SJTufTbroWsPgsllVhq3wJEk2jUSzX3CUEDJC84707djRc5MTAQxoLgupWRwW6UB5fS++NV8AbOZgnsC7BpEAAAAABJRU5ErkJggg==");\ + background-position: 2px center;\ + }\ + .ace_gutter-cell.ace_info {\ + background-image: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAAAAAA6mKC9AAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAAAJ0Uk5TAAB2k804AAAAPklEQVQY02NgIB68QuO3tiLznjAwpKTgNyDbMegwisCHZUETUZV0ZqOquBpXj2rtnpSJT1AEnnRmL2OgGgAAIKkRQap2htgAAAAASUVORK5CYII=");\ + background-position: 2px center;\ + }\ + .ace_dark .ace_gutter-cell.ace_info {\ + background-image: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQBAMAAADt3eJSAAAAJFBMVEUAAAChoaGAgIAqKiq+vr6tra1ZWVmUlJSbm5s8PDxubm56enrdgzg3AAAAAXRSTlMAQObYZgAAAClJREFUeNpjYMAPdsMYHegyJZFQBlsUlMFVCWUYKkAZMxZAGdxlDMQBAG+TBP4B6RyJAAAAAElFTkSuQmCC");\ + }\ + .ace_scrollbar {\ + contain: strict;\ + position: absolute;\ + right: 0;\ + bottom: 0;\ + z-index: 6;\ + }\ + .ace_scrollbar-inner {\ + position: absolute;\ + cursor: text;\ + left: 0;\ + top: 0;\ + }\ + .ace_scrollbar-v{\ + overflow-x: hidden;\ + overflow-y: scroll;\ + top: 0;\ + }\ + .ace_scrollbar-h {\ + overflow-x: scroll;\ + overflow-y: hidden;\ + left: 0;\ + }\ + .ace_print-margin {\ + position: absolute;\ + height: 100%;\ + }\ + .ace_text-input {\ + position: absolute;\ + z-index: 0;\ + width: 0.5em;\ + height: 1em;\ + opacity: 0;\ + background: transparent;\ + -moz-appearance: none;\ + appearance: none;\ + border: none;\ + resize: none;\ + outline: none;\ + overflow: hidden;\ + padding: 0 1px;\ + margin: 0 -1px;\ + contain: strict;\ + -ms-user-select: text;\ + -moz-user-select: text;\ + -webkit-user-select: text;\ + user-select: text;\ + white-space: pre!important;\ + }\ + .ace_text-input.ace_composition {\ + background: transparent;\ + color: inherit;\ + z-index: 1000;\ + opacity: 1;\ + }\ + .ace_composition_placeholder { color: transparent }\ + .ace_composition_marker { \ + border-bottom: 1px solid;\ + position: absolute;\ + border-radius: 0;\ + margin-top: 1px;\ + }\ + [ace_nocontext=true] {\ + transform: none!important;\ + filter: none!important;\ + clip-path: none!important;\ + mask : none!important;\ + contain: none!important;\ + perspective: none!important;\ + mix-blend-mode: initial!important;\ + z-index: auto;\ + }\ + .ace_layer {\ + z-index: 1;\ + position: absolute;\ + overflow: hidden;\ + word-wrap: normal;\ + white-space: pre;\ + height: 100%;\ + width: 100%;\ + box-sizing: border-box;\ + pointer-events: none;\ + }\ + .ace_gutter-layer {\ + position: relative;\ + width: auto;\ + text-align: right;\ + pointer-events: auto;\ + height: 1000000px;\ + contain: style size layout;\ + }\ + .ace_text-layer {\ + font: inherit !important;\ + position: absolute;\ + height: 1000000px;\ + width: 1000000px;\ + contain: style size layout;\ + }\ + .ace_text-layer > .ace_line, .ace_text-layer > .ace_line_group {\ + contain: style size layout;\ + position: absolute;\ + top: 0;\ + left: 0;\ + right: 0;\ + }\ + .ace_hidpi .ace_text-layer,\ + .ace_hidpi .ace_gutter-layer,\ + .ace_hidpi .ace_content,\ + .ace_hidpi .ace_gutter {\ + contain: strict;\ + will-change: transform;\ + }\ + .ace_hidpi .ace_text-layer > .ace_line, \ + .ace_hidpi .ace_text-layer > .ace_line_group {\ + contain: strict;\ + }\ + .ace_cjk {\ + display: inline-block;\ + text-align: center;\ + }\ + .ace_cursor-layer {\ + z-index: 4;\ + }\ + .ace_cursor {\ + z-index: 4;\ + position: absolute;\ + box-sizing: border-box;\ + border-left: 2px solid;\ + transform: translatez(0);\ + }\ + .ace_multiselect .ace_cursor {\ + border-left-width: 1px;\ + }\ + .ace_slim-cursors .ace_cursor {\ + border-left-width: 1px;\ + }\ + .ace_overwrite-cursors .ace_cursor {\ + border-left-width: 0;\ + border-bottom: 1px solid;\ + }\ + .ace_hidden-cursors .ace_cursor {\ + opacity: 0.2;\ + }\ + .ace_hasPlaceholder .ace_hidden-cursors .ace_cursor {\ + opacity: 0;\ + }\ + .ace_smooth-blinking .ace_cursor {\ + transition: opacity 0.18s;\ + }\ + .ace_animate-blinking .ace_cursor {\ + animation-duration: 1000ms;\ + animation-timing-function: step-end;\ + animation-name: blink-ace-animate;\ + animation-iteration-count: infinite;\ + }\ + .ace_animate-blinking.ace_smooth-blinking .ace_cursor {\ + animation-duration: 1000ms;\ + animation-timing-function: ease-in-out;\ + animation-name: blink-ace-animate-smooth;\ + }\ + @keyframes blink-ace-animate {\ + from, to { opacity: 1; }\ + 60% { opacity: 0; }\ + }\ + @keyframes blink-ace-animate-smooth {\ + from, to { opacity: 1; }\ + 45% { opacity: 1; }\ + 60% { opacity: 0; }\ + 85% { opacity: 0; }\ + }\ + .ace_marker-layer .ace_step, .ace_marker-layer .ace_stack {\ + position: absolute;\ + z-index: 3;\ + }\ + .ace_marker-layer .ace_selection {\ + position: absolute;\ + z-index: 5;\ + }\ + .ace_marker-layer .ace_bracket {\ + position: absolute;\ + z-index: 6;\ + }\ + .ace_marker-layer .ace_error_bracket {\ + position: absolute;\ + border-bottom: 1px solid #DE5555;\ + border-radius: 0;\ + }\ + .ace_marker-layer .ace_active-line {\ + position: absolute;\ + z-index: 2;\ + }\ + .ace_marker-layer .ace_selected-word {\ + position: absolute;\ + z-index: 4;\ + box-sizing: border-box;\ + }\ + .ace_line .ace_fold {\ + box-sizing: border-box;\ + display: inline-block;\ + height: 11px;\ + margin-top: -2px;\ + vertical-align: middle;\ + background-image:\ + url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABEAAAAJCAYAAADU6McMAAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAAJpJREFUeNpi/P//PwOlgAXGYGRklAVSokD8GmjwY1wasKljQpYACtpCFeADcHVQfQyMQAwzwAZI3wJKvCLkfKBaMSClBlR7BOQikCFGQEErIH0VqkabiGCAqwUadAzZJRxQr/0gwiXIal8zQQPnNVTgJ1TdawL0T5gBIP1MUJNhBv2HKoQHHjqNrA4WO4zY0glyNKLT2KIfIMAAQsdgGiXvgnYAAAAASUVORK5CYII="),\ + url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAA3CAYAAADNNiA5AAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAACJJREFUeNpi+P//fxgTAwPDBxDxD078RSX+YeEyDFMCIMAAI3INmXiwf2YAAAAASUVORK5CYII=");\ + background-repeat: no-repeat, repeat-x;\ + background-position: center center, top left;\ + color: transparent;\ + border: 1px solid black;\ + border-radius: 2px;\ + cursor: pointer;\ + pointer-events: auto;\ + }\ + .ace_dark .ace_fold {\ + }\ + .ace_fold:hover{\ + background-image:\ + url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABEAAAAJCAYAAADU6McMAAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAAJpJREFUeNpi/P//PwOlgAXGYGRklAVSokD8GmjwY1wasKljQpYACtpCFeADcHVQfQyMQAwzwAZI3wJKvCLkfKBaMSClBlR7BOQikCFGQEErIH0VqkabiGCAqwUadAzZJRxQr/0gwiXIal8zQQPnNVTgJ1TdawL0T5gBIP1MUJNhBv2HKoQHHjqNrA4WO4zY0glyNKLT2KIfIMAAQsdgGiXvgnYAAAAASUVORK5CYII="),\ + url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAA3CAYAAADNNiA5AAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAACBJREFUeNpi+P//fz4TAwPDZxDxD5X4i5fLMEwJgAADAEPVDbjNw87ZAAAAAElFTkSuQmCC");\ + }\ + .ace_tooltip {\ + background-color: #FFF;\ + background-image: linear-gradient(to bottom, transparent, rgba(0, 0, 0, 0.1));\ + border: 1px solid gray;\ + border-radius: 1px;\ + box-shadow: 0 1px 2px rgba(0, 0, 0, 0.3);\ + color: black;\ + max-width: 100%;\ + padding: 3px 4px;\ + position: fixed;\ + z-index: 999999;\ + box-sizing: border-box;\ + cursor: default;\ + white-space: pre;\ + word-wrap: break-word;\ + line-height: normal;\ + font-style: normal;\ + font-weight: normal;\ + letter-spacing: normal;\ + pointer-events: none;\ + }\ + .ace_folding-enabled > .ace_gutter-cell {\ + padding-right: 13px;\ + }\ + .ace_fold-widget {\ + box-sizing: border-box;\ + margin: 0 -12px 0 1px;\ + display: none;\ + width: 11px;\ + vertical-align: top;\ + background-image: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAUAAAAFCAYAAACNbyblAAAANElEQVR42mWKsQ0AMAzC8ixLlrzQjzmBiEjp0A6WwBCSPgKAXoLkqSot7nN3yMwR7pZ32NzpKkVoDBUxKAAAAABJRU5ErkJggg==");\ + background-repeat: no-repeat;\ + background-position: center;\ + border-radius: 3px;\ + border: 1px solid transparent;\ + cursor: pointer;\ + }\ + .ace_folding-enabled .ace_fold-widget {\ + display: inline-block; \ + }\ + .ace_fold-widget.ace_end {\ + background-image: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAUAAAAFCAYAAACNbyblAAAANElEQVR42m3HwQkAMAhD0YzsRchFKI7sAikeWkrxwScEB0nh5e7KTPWimZki4tYfVbX+MNl4pyZXejUO1QAAAABJRU5ErkJggg==");\ + }\ + .ace_fold-widget.ace_closed {\ + background-image: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAMAAAAGCAYAAAAG5SQMAAAAOUlEQVR42jXKwQkAMAgDwKwqKD4EwQ26sSOkVWjgIIHAzPiCgaqiqnJHZnKICBERHN194O5b9vbLuAVRL+l0YWnZAAAAAElFTkSuQmCCXA==");\ + }\ + .ace_fold-widget:hover {\ + border: 1px solid rgba(0, 0, 0, 0.3);\ + background-color: rgba(255, 255, 255, 0.2);\ + box-shadow: 0 1px 1px rgba(255, 255, 255, 0.7);\ + }\ + .ace_fold-widget:active {\ + border: 1px solid rgba(0, 0, 0, 0.4);\ + background-color: rgba(0, 0, 0, 0.05);\ + box-shadow: 0 1px 1px rgba(255, 255, 255, 0.8);\ + }\ + .ace_dark .ace_fold-widget {\ + background-image: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAUAAAAFCAYAAACNbyblAAAAHklEQVQIW2P4//8/AzoGEQ7oGCaLLAhWiSwB146BAQCSTPYocqT0AAAAAElFTkSuQmCC");\ + }\ + .ace_dark .ace_fold-widget.ace_end {\ + background-image: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAUAAAAFCAYAAACNbyblAAAAH0lEQVQIW2P4//8/AxQ7wNjIAjDMgC4AxjCVKBirIAAF0kz2rlhxpAAAAABJRU5ErkJggg==");\ + }\ + .ace_dark .ace_fold-widget.ace_closed {\ + background-image: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAMAAAAFCAYAAACAcVaiAAAAHElEQVQIW2P4//+/AxAzgDADlOOAznHAKgPWAwARji8UIDTfQQAAAABJRU5ErkJggg==");\ + }\ + .ace_dark .ace_fold-widget:hover {\ + box-shadow: 0 1px 1px rgba(255, 255, 255, 0.2);\ + background-color: rgba(255, 255, 255, 0.1);\ + }\ + .ace_dark .ace_fold-widget:active {\ + box-shadow: 0 1px 1px rgba(255, 255, 255, 0.2);\ + }\ + .ace_inline_button {\ + border: 1px solid lightgray;\ + display: inline-block;\ + margin: -1px 8px;\ + padding: 0 5px;\ + pointer-events: auto;\ + cursor: pointer;\ + }\ + .ace_inline_button:hover {\ + border-color: gray;\ + background: rgba(200,200,200,0.2);\ + display: inline-block;\ + pointer-events: auto;\ + }\ + .ace_fold-widget.ace_invalid {\ + background-color: #FFB4B4;\ + border-color: #DE5555;\ + }\ + .ace_fade-fold-widgets .ace_fold-widget {\ + transition: opacity 0.4s ease 0.05s;\ + opacity: 0;\ + }\ + .ace_fade-fold-widgets:hover .ace_fold-widget {\ + transition: opacity 0.05s ease 0.05s;\ + opacity:1;\ + }\ + .ace_underline {\ + text-decoration: underline;\ + }\ + .ace_bold {\ + font-weight: bold;\ + }\ + .ace_nobold .ace_bold {\ + font-weight: normal;\ + }\ + .ace_italic {\ + font-style: italic;\ + }\ + .ace_error-marker {\ + background-color: rgba(255, 0, 0,0.2);\ + position: absolute;\ + z-index: 9;\ + }\ + .ace_highlight-marker {\ + background-color: rgba(255, 255, 0,0.2);\ + position: absolute;\ + z-index: 8;\ + }\ + .ace_mobile-menu {\ + position: absolute;\ + line-height: 1.5;\ + border-radius: 4px;\ + -ms-user-select: none;\ + -moz-user-select: none;\ + -webkit-user-select: none;\ + user-select: none;\ + background: white;\ + box-shadow: 1px 3px 2px grey;\ + border: 1px solid #dcdcdc;\ + color: black;\ + }\ + .ace_dark > .ace_mobile-menu {\ + background: #333;\ + color: #ccc;\ + box-shadow: 1px 3px 2px grey;\ + border: 1px solid #444;\ + }\ + .ace_mobile-button {\ + padding: 2px;\ + cursor: pointer;\ + overflow: hidden;\ + }\ + .ace_mobile-button:hover {\ + background-color: #eee;\ + opacity:1;\ + }\ + .ace_mobile-button:active {\ + background-color: #ddd;\ + }\ + .ace_placeholder {\ + transform: scale(0.9);\ + transform-origin: left;\ + white-space: pre;\ + opacity: 0.7;\ + margin: 0 10px;\ + }'; + + var useragent = require("./lib/useragent"); + var HIDE_TEXTAREA = useragent.isIE; + + dom.importCssString(editorCss, "ace_editor.css"); + + var VirtualRenderer = function(container, theme) { + var _self = this; + + this.container = container || dom.createElement("div"); + + dom.addCssClass(this.container, "ace_editor"); + if (dom.HI_DPI) dom.addCssClass(this.container, "ace_hidpi"); + + this.setTheme(theme); + + this.$gutter = dom.createElement("div"); + this.$gutter.className = "ace_gutter"; + this.container.appendChild(this.$gutter); + this.$gutter.setAttribute("aria-hidden", true); + + this.scroller = dom.createElement("div"); + this.scroller.className = "ace_scroller"; + + this.container.appendChild(this.scroller); + + this.content = dom.createElement("div"); + this.content.className = "ace_content"; + this.scroller.appendChild(this.content); + + this.$gutterLayer = new GutterLayer(this.$gutter); + this.$gutterLayer.on("changeGutterWidth", this.onGutterResize.bind(this)); + + this.$markerBack = new MarkerLayer(this.content); + + var textLayer = (this.$textLayer = new TextLayer(this.content)); + this.canvas = textLayer.element; + + this.$markerFront = new MarkerLayer(this.content); + + this.$cursorLayer = new CursorLayer(this.content); + this.$horizScroll = false; + this.$vScroll = false; + + this.scrollBar = this.scrollBarV = new VScrollBar(this.container, this); + this.scrollBarH = new HScrollBar(this.container, this); + this.scrollBarV.on("scroll", function(e) { + if (!_self.$scrollAnimation) + _self.session.setScrollTop(e.data - _self.scrollMargin.top); + }); + this.scrollBarH.on("scroll", function(e) { + if (!_self.$scrollAnimation) + _self.session.setScrollLeft(e.data - _self.scrollMargin.left); + }); + + this.scrollTop = 0; + this.scrollLeft = 0; + + this.cursorPos = { + row: 0, + column: 0 + }; + + this.$fontMetrics = new FontMetrics(this.container); + this.$textLayer.$setFontMetrics(this.$fontMetrics); + this.$textLayer.on("changeCharacterSize", function(e) { + _self.updateCharacterSize(); + _self.onResize( + true, + _self.gutterWidth, + _self.$size.width, + _self.$size.height + ); + _self._signal("changeCharacterSize", e); + }); + + this.$size = { + width: 0, + height: 0, + scrollerHeight: 0, + scrollerWidth: 0, + $dirty: true + }; + + this.layerConfig = { + width: 1, + padding: 0, + firstRow: 0, + firstRowScreen: 0, + lastRow: 0, + lineHeight: 0, + characterWidth: 0, + minHeight: 1, + maxHeight: 1, + offset: 0, + height: 1, + gutterOffset: 1 + }; + + this.scrollMargin = { + left: 0, + right: 0, + top: 0, + bottom: 0, + v: 0, + h: 0 + }; + + this.margin = { + left: 0, + right: 0, + top: 0, + bottom: 0, + v: 0, + h: 0 + }; + + this.$keepTextAreaAtCursor = !useragent.isIOS; + + this.$loop = new RenderLoop( + this.$renderChanges.bind(this), + this.container.ownerDocument.defaultView + ); + this.$loop.schedule(this.CHANGE_FULL); + + this.updateCharacterSize(); + this.setPadding(4); + config.resetOptions(this); + config._signal("renderer", this); + }; + + (function() { + this.CHANGE_CURSOR = 1; + this.CHANGE_MARKER = 2; + this.CHANGE_GUTTER = 4; + this.CHANGE_SCROLL = 8; + this.CHANGE_LINES = 16; + this.CHANGE_TEXT = 32; + this.CHANGE_SIZE = 64; + this.CHANGE_MARKER_BACK = 128; + this.CHANGE_MARKER_FRONT = 256; + this.CHANGE_FULL = 512; + this.CHANGE_H_SCROLL = 1024; + + oop.implement(this, EventEmitter); + + this.updateCharacterSize = function() { + if (this.$textLayer.allowBoldFonts != this.$allowBoldFonts) { + this.$allowBoldFonts = this.$textLayer.allowBoldFonts; + this.setStyle("ace_nobold", !this.$allowBoldFonts); + } + + this.layerConfig.characterWidth = this.characterWidth = this.$textLayer.getCharacterWidth(); + this.layerConfig.lineHeight = this.lineHeight = this.$textLayer.getLineHeight(); + this.$updatePrintMargin(); + dom.setStyle( + this.scroller.style, + "line-height", + this.lineHeight + "px" + ); + }; + this.setSession = function(session) { + if (this.session) + this.session.doc.off("changeNewLineMode", this.onChangeNewLineMode); + + this.session = session; + if (session && this.scrollMargin.top && session.getScrollTop() <= 0) + session.setScrollTop(-this.scrollMargin.top); + + this.$cursorLayer.setSession(session); + this.$markerBack.setSession(session); + this.$markerFront.setSession(session); + this.$gutterLayer.setSession(session); + this.$textLayer.setSession(session); + if (!session) return; + + this.$loop.schedule(this.CHANGE_FULL); + this.session.$setFontMetrics(this.$fontMetrics); + this.scrollBarH.scrollLeft = this.scrollBarV.scrollTop = null; + + this.onChangeNewLineMode = this.onChangeNewLineMode.bind(this); + this.onChangeNewLineMode(); + this.session.doc.on("changeNewLineMode", this.onChangeNewLineMode); + }; + this.updateLines = function(firstRow, lastRow, force) { + if (lastRow === undefined) lastRow = Infinity; + + if (!this.$changedLines) { + this.$changedLines = { + firstRow: firstRow, + lastRow: lastRow + }; + } else { + if (this.$changedLines.firstRow > firstRow) + this.$changedLines.firstRow = firstRow; + + if (this.$changedLines.lastRow < lastRow) + this.$changedLines.lastRow = lastRow; + } + if (this.$changedLines.lastRow < this.layerConfig.firstRow) { + if (force) this.$changedLines.lastRow = this.layerConfig.lastRow; + else return; + } + if (this.$changedLines.firstRow > this.layerConfig.lastRow) return; + this.$loop.schedule(this.CHANGE_LINES); + }; + + this.onChangeNewLineMode = function() { + this.$loop.schedule(this.CHANGE_TEXT); + this.$textLayer.$updateEolChar(); + this.session.$bidiHandler.setEolChar(this.$textLayer.EOL_CHAR); + }; + + this.onChangeTabSize = function() { + this.$loop.schedule(this.CHANGE_TEXT | this.CHANGE_MARKER); + this.$textLayer.onChangeTabSize(); + }; + this.updateText = function() { + this.$loop.schedule(this.CHANGE_TEXT); + }; + this.updateFull = function(force) { + if (force) this.$renderChanges(this.CHANGE_FULL, true); + else this.$loop.schedule(this.CHANGE_FULL); + }; + this.updateFontSize = function() { + this.$textLayer.checkForSizeChanges(); + }; + + this.$changes = 0; + this.$updateSizeAsync = function() { + if (this.$loop.pending) this.$size.$dirty = true; + else this.onResize(); + }; + this.onResize = function(force, gutterWidth, width, height) { + if (this.resizing > 2) return; + else if (this.resizing > 0) this.resizing++; + else this.resizing = force ? 1 : 0; + var el = this.container; + if (!height) height = el.clientHeight || el.scrollHeight; + if (!width) width = el.clientWidth || el.scrollWidth; + var changes = this.$updateCachedSize(force, gutterWidth, width, height); + + if (!this.$size.scrollerHeight || (!width && !height)) + return (this.resizing = 0); + + if (force) this.$gutterLayer.$padding = null; + + if (force) this.$renderChanges(changes | this.$changes, true); + else this.$loop.schedule(changes | this.$changes); + + if (this.resizing) this.resizing = 0; + this.scrollBarV.scrollLeft = this.scrollBarV.scrollTop = null; + }; + + this.$updateCachedSize = function(force, gutterWidth, width, height) { + height -= this.$extraHeight || 0; + var changes = 0; + var size = this.$size; + var oldSize = { + width: size.width, + height: size.height, + scrollerHeight: size.scrollerHeight, + scrollerWidth: size.scrollerWidth + }; + if (height && (force || size.height != height)) { + size.height = height; + changes |= this.CHANGE_SIZE; + + size.scrollerHeight = size.height; + if (this.$horizScroll) + size.scrollerHeight -= this.scrollBarH.getHeight(); + this.scrollBarV.element.style.bottom = + this.scrollBarH.getHeight() + "px"; + + changes = changes | this.CHANGE_SCROLL; + } + + if (width && (force || size.width != width)) { + changes |= this.CHANGE_SIZE; + size.width = width; + + if (gutterWidth == null) + gutterWidth = this.$showGutter ? this.$gutter.offsetWidth : 0; + + this.gutterWidth = gutterWidth; + + dom.setStyle( + this.scrollBarH.element.style, + "left", + gutterWidth + "px" + ); + dom.setStyle( + this.scroller.style, + "left", + gutterWidth + this.margin.left + "px" + ); + size.scrollerWidth = Math.max( + 0, + width - gutterWidth - this.scrollBarV.getWidth() - this.margin.h + ); + dom.setStyle(this.$gutter.style, "left", this.margin.left + "px"); + + var right = this.scrollBarV.getWidth() + "px"; + dom.setStyle(this.scrollBarH.element.style, "right", right); + dom.setStyle(this.scroller.style, "right", right); + dom.setStyle( + this.scroller.style, + "bottom", + this.scrollBarH.getHeight() + ); + + if ( + (this.session && + this.session.getUseWrapMode() && + this.adjustWrapLimit()) || + force + ) { + changes |= this.CHANGE_FULL; + } + } + + size.$dirty = !width || !height; + + if (changes) this._signal("resize", oldSize); + + return changes; + }; + + this.onGutterResize = function(width) { + var gutterWidth = this.$showGutter ? width : 0; + if (gutterWidth != this.gutterWidth) + this.$changes |= this.$updateCachedSize( + true, + gutterWidth, + this.$size.width, + this.$size.height + ); + + if (this.session.getUseWrapMode() && this.adjustWrapLimit()) { + this.$loop.schedule(this.CHANGE_FULL); + } else if (this.$size.$dirty) { + this.$loop.schedule(this.CHANGE_FULL); + } else { + this.$computeLayerConfig(); + } + }; + this.adjustWrapLimit = function() { + var availableWidth = this.$size.scrollerWidth - this.$padding * 2; + var limit = Math.floor(availableWidth / this.characterWidth); + return this.session.adjustWrapLimit( + limit, + this.$showPrintMargin && this.$printMarginColumn + ); + }; + this.setAnimatedScroll = function(shouldAnimate) { + this.setOption("animatedScroll", shouldAnimate); + }; + this.getAnimatedScroll = function() { + return this.$animatedScroll; + }; + this.setShowInvisibles = function(showInvisibles) { + this.setOption("showInvisibles", showInvisibles); + this.session.$bidiHandler.setShowInvisibles(showInvisibles); + }; + this.getShowInvisibles = function() { + return this.getOption("showInvisibles"); + }; + this.getDisplayIndentGuides = function() { + return this.getOption("displayIndentGuides"); + }; + + this.setDisplayIndentGuides = function(display) { + this.setOption("displayIndentGuides", display); + }; + this.setShowPrintMargin = function(showPrintMargin) { + this.setOption("showPrintMargin", showPrintMargin); + }; + this.getShowPrintMargin = function() { + return this.getOption("showPrintMargin"); + }; + this.setPrintMarginColumn = function(showPrintMargin) { + this.setOption("printMarginColumn", showPrintMargin); + }; + this.getPrintMarginColumn = function() { + return this.getOption("printMarginColumn"); + }; + this.getShowGutter = function() { + return this.getOption("showGutter"); + }; + this.setShowGutter = function(show) { + return this.setOption("showGutter", show); + }; + + this.getFadeFoldWidgets = function() { + return this.getOption("fadeFoldWidgets"); + }; + + this.setFadeFoldWidgets = function(show) { + this.setOption("fadeFoldWidgets", show); + }; + + this.setHighlightGutterLine = function(shouldHighlight) { + this.setOption("highlightGutterLine", shouldHighlight); + }; + + this.getHighlightGutterLine = function() { + return this.getOption("highlightGutterLine"); + }; + + this.$updatePrintMargin = function() { + if (!this.$showPrintMargin && !this.$printMarginEl) return; + + if (!this.$printMarginEl) { + var containerEl = dom.createElement("div"); + containerEl.className = "ace_layer ace_print-margin-layer"; + this.$printMarginEl = dom.createElement("div"); + this.$printMarginEl.className = "ace_print-margin"; + containerEl.appendChild(this.$printMarginEl); + this.content.insertBefore(containerEl, this.content.firstChild); + } + + var style = this.$printMarginEl.style; + style.left = + Math.round( + this.characterWidth * this.$printMarginColumn + this.$padding + ) + "px"; + style.visibility = this.$showPrintMargin ? "visible" : "hidden"; + + if (this.session && this.session.$wrap == -1) this.adjustWrapLimit(); + }; + this.getContainerElement = function() { + return this.container; + }; + this.getMouseEventTarget = function() { + return this.scroller; + }; + this.getTextAreaContainer = function() { + return this.container; + }; + this.$moveTextAreaToCursor = function() { + if (this.$isMousePressed) return; + var style = this.textarea.style; + var composition = this.$composition; + if (!this.$keepTextAreaAtCursor && !composition) { + dom.translate(this.textarea, -100, 0); + return; + } + var pixelPos = this.$cursorLayer.$pixelPos; + if (!pixelPos) return; + if (composition && composition.markerRange) + pixelPos = this.$cursorLayer.getPixelPosition( + composition.markerRange.start, + true + ); + + var config = this.layerConfig; + var posTop = pixelPos.top; + var posLeft = pixelPos.left; + posTop -= config.offset; + + var h = + composition && composition.useTextareaForIME + ? this.lineHeight + : HIDE_TEXTAREA + ? 0 + : 1; + if (posTop < 0 || posTop > config.height - h) { + dom.translate(this.textarea, 0, 0); + return; + } + + var w = 1; + var maxTop = this.$size.height - h; + if (!composition) { + posTop += this.lineHeight; + } else { + if (composition.useTextareaForIME) { + var val = this.textarea.value; + w = + this.characterWidth * this.session.$getStringScreenWidth(val)[0]; + } else { + posTop += this.lineHeight + 2; + } + } + + posLeft -= this.scrollLeft; + if (posLeft > this.$size.scrollerWidth - w) + posLeft = this.$size.scrollerWidth - w; + + posLeft += this.gutterWidth + this.margin.left; + + dom.setStyle(style, "height", h + "px"); + dom.setStyle(style, "width", w + "px"); + dom.translate( + this.textarea, + Math.min(posLeft, this.$size.scrollerWidth - w), + Math.min(posTop, maxTop) + ); + }; + this.getFirstVisibleRow = function() { + return this.layerConfig.firstRow; + }; + this.getFirstFullyVisibleRow = function() { + return ( + this.layerConfig.firstRow + (this.layerConfig.offset === 0 ? 0 : 1) + ); + }; + this.getLastFullyVisibleRow = function() { + var config = this.layerConfig; + var lastRow = config.lastRow; + var top = + this.session.documentToScreenRow(lastRow, 0) * config.lineHeight; + if ( + top - this.session.getScrollTop() > + config.height - config.lineHeight + ) + return lastRow - 1; + return lastRow; + }; + this.getLastVisibleRow = function() { + return this.layerConfig.lastRow; + }; + + this.$padding = null; + this.setPadding = function(padding) { + this.$padding = padding; + this.$textLayer.setPadding(padding); + this.$cursorLayer.setPadding(padding); + this.$markerFront.setPadding(padding); + this.$markerBack.setPadding(padding); + this.$loop.schedule(this.CHANGE_FULL); + this.$updatePrintMargin(); + }; + + this.setScrollMargin = function(top, bottom, left, right) { + var sm = this.scrollMargin; + sm.top = top | 0; + sm.bottom = bottom | 0; + sm.right = right | 0; + sm.left = left | 0; + sm.v = sm.top + sm.bottom; + sm.h = sm.left + sm.right; + if (sm.top && this.scrollTop <= 0 && this.session) + this.session.setScrollTop(-sm.top); + this.updateFull(); + }; + + this.setMargin = function(top, bottom, left, right) { + var sm = this.margin; + sm.top = top | 0; + sm.bottom = bottom | 0; + sm.right = right | 0; + sm.left = left | 0; + sm.v = sm.top + sm.bottom; + sm.h = sm.left + sm.right; + this.$updateCachedSize( + true, + this.gutterWidth, + this.$size.width, + this.$size.height + ); + this.updateFull(); + }; + this.getHScrollBarAlwaysVisible = function() { + return this.$hScrollBarAlwaysVisible; + }; + this.setHScrollBarAlwaysVisible = function(alwaysVisible) { + this.setOption("hScrollBarAlwaysVisible", alwaysVisible); + }; + this.getVScrollBarAlwaysVisible = function() { + return this.$vScrollBarAlwaysVisible; + }; + this.setVScrollBarAlwaysVisible = function(alwaysVisible) { + this.setOption("vScrollBarAlwaysVisible", alwaysVisible); + }; + + this.$updateScrollBarV = function() { + var scrollHeight = this.layerConfig.maxHeight; + var scrollerHeight = this.$size.scrollerHeight; + if (!this.$maxLines && this.$scrollPastEnd) { + scrollHeight -= + (scrollerHeight - this.lineHeight) * this.$scrollPastEnd; + if (this.scrollTop > scrollHeight - scrollerHeight) { + scrollHeight = this.scrollTop + scrollerHeight; + this.scrollBarV.scrollTop = null; + } + } + this.scrollBarV.setScrollHeight(scrollHeight + this.scrollMargin.v); + this.scrollBarV.setScrollTop(this.scrollTop + this.scrollMargin.top); + }; + this.$updateScrollBarH = function() { + this.scrollBarH.setScrollWidth( + this.layerConfig.width + 2 * this.$padding + this.scrollMargin.h + ); + this.scrollBarH.setScrollLeft(this.scrollLeft + this.scrollMargin.left); + }; + + this.$frozen = false; + this.freeze = function() { + this.$frozen = true; + }; + + this.unfreeze = function() { + this.$frozen = false; + }; + + this.$renderChanges = function(changes, force) { + if (this.$changes) { + changes |= this.$changes; + this.$changes = 0; + } + if ( + !this.session || + !this.container.offsetWidth || + this.$frozen || + (!changes && !force) + ) { + this.$changes |= changes; + return; + } + if (this.$size.$dirty) { + this.$changes |= changes; + return this.onResize(true); + } + if (!this.lineHeight) { + this.$textLayer.checkForSizeChanges(); + } + + this._signal("beforeRender", changes); + + if (this.session && this.session.$bidiHandler) + this.session.$bidiHandler.updateCharacterWidths(this.$fontMetrics); + + var config = this.layerConfig; + if ( + changes & this.CHANGE_FULL || + changes & this.CHANGE_SIZE || + changes & this.CHANGE_TEXT || + changes & this.CHANGE_LINES || + changes & this.CHANGE_SCROLL || + changes & this.CHANGE_H_SCROLL + ) { + changes |= this.$computeLayerConfig() | this.$loop.clear(); + if ( + config.firstRow != this.layerConfig.firstRow && + config.firstRowScreen == this.layerConfig.firstRowScreen + ) { + var st = + this.scrollTop + + (config.firstRow - this.layerConfig.firstRow) * this.lineHeight; + if (st > 0) { + this.scrollTop = st; + changes = changes | this.CHANGE_SCROLL; + changes |= this.$computeLayerConfig() | this.$loop.clear(); + } + } + config = this.layerConfig; + this.$updateScrollBarV(); + if (changes & this.CHANGE_H_SCROLL) this.$updateScrollBarH(); + + dom.translate(this.content, -this.scrollLeft, -config.offset); + + var width = config.width + 2 * this.$padding + "px"; + var height = config.minHeight + "px"; + + dom.setStyle(this.content.style, "width", width); + dom.setStyle(this.content.style, "height", height); + } + if (changes & this.CHANGE_H_SCROLL) { + dom.translate(this.content, -this.scrollLeft, -config.offset); + this.scroller.className = + this.scrollLeft <= 0 + ? "ace_scroller" + : "ace_scroller ace_scroll-left"; + } + if (changes & this.CHANGE_FULL) { + this.$changedLines = null; + this.$textLayer.update(config); + if (this.$showGutter) this.$gutterLayer.update(config); + this.$markerBack.update(config); + this.$markerFront.update(config); + this.$cursorLayer.update(config); + this.$moveTextAreaToCursor(); + this._signal("afterRender", changes); + return; + } + if (changes & this.CHANGE_SCROLL) { + this.$changedLines = null; + if (changes & this.CHANGE_TEXT || changes & this.CHANGE_LINES) + this.$textLayer.update(config); + else this.$textLayer.scrollLines(config); + + if (this.$showGutter) { + if (changes & this.CHANGE_GUTTER || changes & this.CHANGE_LINES) + this.$gutterLayer.update(config); + else this.$gutterLayer.scrollLines(config); + } + this.$markerBack.update(config); + this.$markerFront.update(config); + this.$cursorLayer.update(config); + this.$moveTextAreaToCursor(); + this._signal("afterRender", changes); + return; + } + + if (changes & this.CHANGE_TEXT) { + this.$changedLines = null; + this.$textLayer.update(config); + if (this.$showGutter) this.$gutterLayer.update(config); + } else if (changes & this.CHANGE_LINES) { + if ( + this.$updateLines() || + (changes & this.CHANGE_GUTTER && this.$showGutter) + ) + this.$gutterLayer.update(config); + } else if (changes & this.CHANGE_TEXT || changes & this.CHANGE_GUTTER) { + if (this.$showGutter) this.$gutterLayer.update(config); + } else if (changes & this.CHANGE_CURSOR) { + if (this.$highlightGutterLine) + this.$gutterLayer.updateLineHighlight(config); + } + + if (changes & this.CHANGE_CURSOR) { + this.$cursorLayer.update(config); + this.$moveTextAreaToCursor(); + } + + if (changes & (this.CHANGE_MARKER | this.CHANGE_MARKER_FRONT)) { + this.$markerFront.update(config); + } + + if (changes & (this.CHANGE_MARKER | this.CHANGE_MARKER_BACK)) { + this.$markerBack.update(config); + } + + this._signal("afterRender", changes); + }; + + this.$autosize = function() { + var height = this.session.getScreenLength() * this.lineHeight; + var maxHeight = this.$maxLines * this.lineHeight; + var desiredHeight = + Math.min( + maxHeight, + Math.max((this.$minLines || 1) * this.lineHeight, height) + ) + + this.scrollMargin.v + + (this.$extraHeight || 0); + if (this.$horizScroll) desiredHeight += this.scrollBarH.getHeight(); + if (this.$maxPixelHeight && desiredHeight > this.$maxPixelHeight) + desiredHeight = this.$maxPixelHeight; + + var hideScrollbars = desiredHeight <= 2 * this.lineHeight; + var vScroll = !hideScrollbars && height > maxHeight; + + if ( + desiredHeight != this.desiredHeight || + this.$size.height != this.desiredHeight || + vScroll != this.$vScroll + ) { + if (vScroll != this.$vScroll) { + this.$vScroll = vScroll; + this.scrollBarV.setVisible(vScroll); + } + + var w = this.container.clientWidth; + this.container.style.height = desiredHeight + "px"; + this.$updateCachedSize(true, this.$gutterWidth, w, desiredHeight); + this.desiredHeight = desiredHeight; + + this._signal("autosize"); + } + }; + + this.$computeLayerConfig = function() { + var session = this.session; + var size = this.$size; + + var hideScrollbars = size.height <= 2 * this.lineHeight; + var screenLines = this.session.getScreenLength(); + var maxHeight = screenLines * this.lineHeight; + + var longestLine = this.$getLongestLine(); + + var horizScroll = + !hideScrollbars && + (this.$hScrollBarAlwaysVisible || + size.scrollerWidth - longestLine - 2 * this.$padding < 0); + + var hScrollChanged = this.$horizScroll !== horizScroll; + if (hScrollChanged) { + this.$horizScroll = horizScroll; + this.scrollBarH.setVisible(horizScroll); + } + var vScrollBefore = this.$vScroll; // autosize can change vscroll value in which case we need to update longestLine + if (this.$maxLines && this.lineHeight > 1) this.$autosize(); + + var minHeight = size.scrollerHeight + this.lineHeight; + + var scrollPastEnd = + !this.$maxLines && this.$scrollPastEnd + ? (size.scrollerHeight - this.lineHeight) * this.$scrollPastEnd + : 0; + maxHeight += scrollPastEnd; + + var sm = this.scrollMargin; + this.session.setScrollTop( + Math.max( + -sm.top, + Math.min( + this.scrollTop, + maxHeight - size.scrollerHeight + sm.bottom + ) + ) + ); + + this.session.setScrollLeft( + Math.max( + -sm.left, + Math.min( + this.scrollLeft, + longestLine + 2 * this.$padding - size.scrollerWidth + sm.right + ) + ) + ); + + var vScroll = + !hideScrollbars && + (this.$vScrollBarAlwaysVisible || + size.scrollerHeight - maxHeight + scrollPastEnd < 0 || + this.scrollTop > sm.top); + var vScrollChanged = vScrollBefore !== vScroll; + if (vScrollChanged) { + this.$vScroll = vScroll; + this.scrollBarV.setVisible(vScroll); + } + + var offset = this.scrollTop % this.lineHeight; + var lineCount = Math.ceil(minHeight / this.lineHeight) - 1; + var firstRow = Math.max( + 0, + Math.round((this.scrollTop - offset) / this.lineHeight) + ); + var lastRow = firstRow + lineCount; + var firstRowScreen, firstRowHeight; + var lineHeight = this.lineHeight; + firstRow = session.screenToDocumentRow(firstRow, 0); + var foldLine = session.getFoldLine(firstRow); + if (foldLine) { + firstRow = foldLine.start.row; + } + + firstRowScreen = session.documentToScreenRow(firstRow, 0); + firstRowHeight = session.getRowLength(firstRow) * lineHeight; + + lastRow = Math.min( + session.screenToDocumentRow(lastRow, 0), + session.getLength() - 1 + ); + minHeight = + size.scrollerHeight + + session.getRowLength(lastRow) * lineHeight + + firstRowHeight; + + offset = this.scrollTop - firstRowScreen * lineHeight; + + var changes = 0; + if (this.layerConfig.width != longestLine || hScrollChanged) + changes = this.CHANGE_H_SCROLL; + if (hScrollChanged || vScrollChanged) { + changes |= this.$updateCachedSize( + true, + this.gutterWidth, + size.width, + size.height + ); + this._signal("scrollbarVisibilityChanged"); + if (vScrollChanged) longestLine = this.$getLongestLine(); + } + + this.layerConfig = { + width: longestLine, + padding: this.$padding, + firstRow: firstRow, + firstRowScreen: firstRowScreen, + lastRow: lastRow, + lineHeight: lineHeight, + characterWidth: this.characterWidth, + minHeight: minHeight, + maxHeight: maxHeight, + offset: offset, + gutterOffset: lineHeight + ? Math.max( + 0, + Math.ceil( + (offset + size.height - size.scrollerHeight) / lineHeight + ) + ) + : 0, + height: this.$size.scrollerHeight + }; + + if (this.session.$bidiHandler) + this.session.$bidiHandler.setContentWidth( + longestLine - this.$padding + ); + + return changes; + }; + + this.$updateLines = function() { + if (!this.$changedLines) return; + var firstRow = this.$changedLines.firstRow; + var lastRow = this.$changedLines.lastRow; + this.$changedLines = null; + + var layerConfig = this.layerConfig; + + if (firstRow > layerConfig.lastRow + 1) { + return; + } + if (lastRow < layerConfig.firstRow) { + return; + } + if (lastRow === Infinity) { + if (this.$showGutter) this.$gutterLayer.update(layerConfig); + this.$textLayer.update(layerConfig); + return; + } + this.$textLayer.updateLines(layerConfig, firstRow, lastRow); + return true; + }; + + this.$getLongestLine = function() { + var charCount = this.session.getScreenWidth(); + if (this.showInvisibles && !this.session.$useWrapMode) charCount += 1; + + if (this.$textLayer && charCount > this.$textLayer.MAX_LINE_LENGTH) + charCount = this.$textLayer.MAX_LINE_LENGTH + 30; + + return Math.max( + this.$size.scrollerWidth - 2 * this.$padding, + Math.round(charCount * this.characterWidth) + ); + }; + this.updateFrontMarkers = function() { + this.$markerFront.setMarkers(this.session.getMarkers(true)); + this.$loop.schedule(this.CHANGE_MARKER_FRONT); + }; + this.updateBackMarkers = function() { + this.$markerBack.setMarkers(this.session.getMarkers()); + this.$loop.schedule(this.CHANGE_MARKER_BACK); + }; + this.addGutterDecoration = function(row, className) { + this.$gutterLayer.addGutterDecoration(row, className); + }; + this.removeGutterDecoration = function(row, className) { + this.$gutterLayer.removeGutterDecoration(row, className); + }; + this.updateBreakpoints = function(rows) { + this.$loop.schedule(this.CHANGE_GUTTER); + }; + this.setAnnotations = function(annotations) { + this.$gutterLayer.setAnnotations(annotations); + this.$loop.schedule(this.CHANGE_GUTTER); + }; + this.updateCursor = function() { + this.$loop.schedule(this.CHANGE_CURSOR); + }; + this.hideCursor = function() { + this.$cursorLayer.hideCursor(); + }; + this.showCursor = function() { + this.$cursorLayer.showCursor(); + }; + + this.scrollSelectionIntoView = function(anchor, lead, offset) { + this.scrollCursorIntoView(anchor, offset); + this.scrollCursorIntoView(lead, offset); + }; + this.scrollCursorIntoView = function(cursor, offset, $viewMargin) { + if (this.$size.scrollerHeight === 0) return; + + var pos = this.$cursorLayer.getPixelPosition(cursor); + + var left = pos.left; + var top = pos.top; + + var topMargin = ($viewMargin && $viewMargin.top) || 0; + var bottomMargin = ($viewMargin && $viewMargin.bottom) || 0; + + var scrollTop = this.$scrollAnimation + ? this.session.getScrollTop() + : this.scrollTop; + + if (scrollTop + topMargin > top) { + if (offset && scrollTop + topMargin > top + this.lineHeight) + top -= offset * this.$size.scrollerHeight; + if (top === 0) top = -this.scrollMargin.top; + this.session.setScrollTop(top); + } else if ( + scrollTop + this.$size.scrollerHeight - bottomMargin < + top + this.lineHeight + ) { + if ( + offset && + scrollTop + this.$size.scrollerHeight - bottomMargin < + top - this.lineHeight + ) + top += offset * this.$size.scrollerHeight; + this.session.setScrollTop( + top + this.lineHeight + bottomMargin - this.$size.scrollerHeight + ); + } + + var scrollLeft = this.scrollLeft; + + if (scrollLeft > left) { + if (left < this.$padding + 2 * this.layerConfig.characterWidth) + left = -this.scrollMargin.left; + this.session.setScrollLeft(left); + } else if ( + scrollLeft + this.$size.scrollerWidth < + left + this.characterWidth + ) { + this.session.setScrollLeft( + Math.round(left + this.characterWidth - this.$size.scrollerWidth) + ); + } else if ( + scrollLeft <= this.$padding && + left - scrollLeft < this.characterWidth + ) { + this.session.setScrollLeft(0); + } + }; + this.getScrollTop = function() { + return this.session.getScrollTop(); + }; + this.getScrollLeft = function() { + return this.session.getScrollLeft(); + }; + this.getScrollTopRow = function() { + return this.scrollTop / this.lineHeight; + }; + this.getScrollBottomRow = function() { + return Math.max( + 0, + Math.floor( + (this.scrollTop + this.$size.scrollerHeight) / this.lineHeight + ) - 1 + ); + }; + this.scrollToRow = function(row) { + this.session.setScrollTop(row * this.lineHeight); + }; + + this.alignCursor = function(cursor, alignment) { + if (typeof cursor == "number") cursor = { row: cursor, column: 0 }; + + var pos = this.$cursorLayer.getPixelPosition(cursor); + var h = this.$size.scrollerHeight - this.lineHeight; + var offset = pos.top - h * (alignment || 0); + + this.session.setScrollTop(offset); + return offset; + }; + + this.STEPS = 8; + this.$calcSteps = function(fromValue, toValue) { + var i = 0; + var l = this.STEPS; + var steps = []; + + var func = function(t, x_min, dx) { + return dx * (Math.pow(t - 1, 3) + 1) + x_min; + }; + + for (i = 0; i < l; ++i) + steps.push(func(i / this.STEPS, fromValue, toValue - fromValue)); + + return steps; + }; + this.scrollToLine = function(line, center, animate, callback) { + var pos = this.$cursorLayer.getPixelPosition({ row: line, column: 0 }); + var offset = pos.top; + if (center) offset -= this.$size.scrollerHeight / 2; + + var initialScroll = this.scrollTop; + this.session.setScrollTop(offset); + if (animate !== false) this.animateScrolling(initialScroll, callback); + }; + + this.animateScrolling = function(fromValue, callback) { + var toValue = this.scrollTop; + if (!this.$animatedScroll) return; + var _self = this; + + if (fromValue == toValue) return; + + if (this.$scrollAnimation) { + var oldSteps = this.$scrollAnimation.steps; + if (oldSteps.length) { + fromValue = oldSteps[0]; + if (fromValue == toValue) return; + } + } + + var steps = _self.$calcSteps(fromValue, toValue); + this.$scrollAnimation = { from: fromValue, to: toValue, steps: steps }; + + clearInterval(this.$timer); + + _self.session.setScrollTop(steps.shift()); + _self.session.$scrollTop = toValue; + this.$timer = setInterval(function() { + if (!_self.session) return clearInterval(_self.$timer); + if (steps.length) { + _self.session.setScrollTop(steps.shift()); + _self.session.$scrollTop = toValue; + } else if (toValue != null) { + _self.session.$scrollTop = -1; + _self.session.setScrollTop(toValue); + toValue = null; + } else { + _self.$timer = clearInterval(_self.$timer); + _self.$scrollAnimation = null; + callback && callback(); + } + }, 10); + }; + this.scrollToY = function(scrollTop) { + if (this.scrollTop !== scrollTop) { + this.$loop.schedule(this.CHANGE_SCROLL); + this.scrollTop = scrollTop; + } + }; + this.scrollToX = function(scrollLeft) { + if (this.scrollLeft !== scrollLeft) this.scrollLeft = scrollLeft; + this.$loop.schedule(this.CHANGE_H_SCROLL); + }; + this.scrollTo = function(x, y) { + this.session.setScrollTop(y); + this.session.setScrollLeft(y); + }; + this.scrollBy = function(deltaX, deltaY) { + deltaY && + this.session.setScrollTop(this.session.getScrollTop() + deltaY); + deltaX && + this.session.setScrollLeft(this.session.getScrollLeft() + deltaX); + }; + this.isScrollableBy = function(deltaX, deltaY) { + if ( + deltaY < 0 && + this.session.getScrollTop() >= 1 - this.scrollMargin.top + ) + return true; + if ( + deltaY > 0 && + this.session.getScrollTop() + + this.$size.scrollerHeight - + this.layerConfig.maxHeight < + -1 + this.scrollMargin.bottom + ) + return true; + if ( + deltaX < 0 && + this.session.getScrollLeft() >= 1 - this.scrollMargin.left + ) + return true; + if ( + deltaX > 0 && + this.session.getScrollLeft() + + this.$size.scrollerWidth - + this.layerConfig.width < + -1 + this.scrollMargin.right + ) + return true; + }; + + this.pixelToScreenCoordinates = function(x, y) { + var canvasPos; + if (this.$hasCssTransforms) { + canvasPos = { top: 0, left: 0 }; + var p = this.$fontMetrics.transformCoordinates([x, y]); + x = p[1] - this.gutterWidth - this.margin.left; + y = p[0]; + } else { + canvasPos = this.scroller.getBoundingClientRect(); + } + + var offsetX = x + this.scrollLeft - canvasPos.left - this.$padding; + var offset = offsetX / this.characterWidth; + var row = Math.floor( + (y + this.scrollTop - canvasPos.top) / this.lineHeight + ); + var col = this.$blockCursor ? Math.floor(offset) : Math.round(offset); + + return { + row: row, + column: col, + side: offset - col > 0 ? 1 : -1, + offsetX: offsetX + }; + }; + + this.screenToTextCoordinates = function(x, y) { + var canvasPos; + if (this.$hasCssTransforms) { + canvasPos = { top: 0, left: 0 }; + var p = this.$fontMetrics.transformCoordinates([x, y]); + x = p[1] - this.gutterWidth - this.margin.left; + y = p[0]; + } else { + canvasPos = this.scroller.getBoundingClientRect(); + } + + var offsetX = x + this.scrollLeft - canvasPos.left - this.$padding; + var offset = offsetX / this.characterWidth; + var col = this.$blockCursor ? Math.floor(offset) : Math.round(offset); + + var row = Math.floor( + (y + this.scrollTop - canvasPos.top) / this.lineHeight + ); + + return this.session.screenToDocumentPosition( + row, + Math.max(col, 0), + offsetX + ); + }; + this.textToScreenCoordinates = function(row, column) { + var canvasPos = this.scroller.getBoundingClientRect(); + var pos = this.session.documentToScreenPosition(row, column); + + var x = + this.$padding + + (this.session.$bidiHandler.isBidiRow(pos.row, row) + ? this.session.$bidiHandler.getPosLeft(pos.column) + : Math.round(pos.column * this.characterWidth)); + + var y = pos.row * this.lineHeight; + + return { + pageX: canvasPos.left + x - this.scrollLeft, + pageY: canvasPos.top + y - this.scrollTop + }; + }; + this.visualizeFocus = function() { + dom.addCssClass(this.container, "ace_focus"); + }; + this.visualizeBlur = function() { + dom.removeCssClass(this.container, "ace_focus"); + }; + this.showComposition = function(composition) { + this.$composition = composition; + if (!composition.cssText) { + composition.cssText = this.textarea.style.cssText; + } + if (composition.useTextareaForIME == undefined) + composition.useTextareaForIME = this.$useTextareaForIME; + + if (this.$useTextareaForIME) { + dom.addCssClass(this.textarea, "ace_composition"); + this.textarea.style.cssText = ""; + this.$moveTextAreaToCursor(); + this.$cursorLayer.element.style.display = "none"; + } else { + composition.markerId = this.session.addMarker( + composition.markerRange, + "ace_composition_marker", + "text" + ); + } + }; + this.setCompositionText = function(text) { + var cursor = this.session.selection.cursor; + this.addToken( + text, + "composition_placeholder", + cursor.row, + cursor.column + ); + this.$moveTextAreaToCursor(); + }; + this.hideComposition = function() { + if (!this.$composition) return; + + if (this.$composition.markerId) + this.session.removeMarker(this.$composition.markerId); + + dom.removeCssClass(this.textarea, "ace_composition"); + this.textarea.style.cssText = this.$composition.cssText; + var cursor = this.session.selection.cursor; + this.removeExtraToken(cursor.row, cursor.column); + this.$composition = null; + this.$cursorLayer.element.style.display = ""; + }; + + this.addToken = function(text, type, row, column) { + var session = this.session; + session.bgTokenizer.lines[row] = null; + var newToken = { type: type, value: text }; + var tokens = session.getTokens(row); + if (column == null) { + tokens.push(newToken); + } else { + var l = 0; + for (var i = 0; i < tokens.length; i++) { + var token = tokens[i]; + l += token.value.length; + if (column <= l) { + var diff = token.value.length - (l - column); + var before = token.value.slice(0, diff); + var after = token.value.slice(diff); + + tokens.splice( + i, + 1, + { type: token.type, value: before }, + newToken, + { type: token.type, value: after } + ); + break; + } + } + } + this.updateLines(row, row); + }; + + this.removeExtraToken = function(row, column) { + this.updateLines(row, row); + }; + this.setTheme = function(theme, cb) { + var _self = this; + this.$themeId = theme; + _self._dispatchEvent("themeChange", { theme: theme }); + + if (!theme || typeof theme == "string") { + var moduleName = theme || this.$options.theme.initialValue; + config.loadModule(["theme", moduleName], afterLoad); + } else { + afterLoad(theme); + } + + function afterLoad(module) { + if (_self.$themeId != theme) return cb && cb(); + if (!module || !module.cssClass) + throw new Error( + "couldn't load module " + theme + " or it didn't call define" + ); + if (module.$id) _self.$themeId = module.$id; + dom.importCssString(module.cssText, module.cssClass, _self.container); + + if (_self.theme) + dom.removeCssClass(_self.container, _self.theme.cssClass); + + var padding = + "padding" in module + ? module.padding + : "padding" in (_self.theme || {}) + ? 4 + : _self.$padding; + if (_self.$padding && padding != _self.$padding) + _self.setPadding(padding); + _self.$theme = module.cssClass; + + _self.theme = module; + dom.addCssClass(_self.container, module.cssClass); + dom.setCssClass(_self.container, "ace_dark", module.isDark); + if (_self.$size) { + _self.$size.width = 0; + _self.$updateSizeAsync(); + } + + _self._dispatchEvent("themeLoaded", { theme: module }); + cb && cb(); + } + }; + this.getTheme = function() { + return this.$themeId; + }; + this.setStyle = function(style, include) { + dom.setCssClass(this.container, style, include !== false); + }; + this.unsetStyle = function(style) { + dom.removeCssClass(this.container, style); + }; + + this.setCursorStyle = function(style) { + dom.setStyle(this.scroller.style, "cursor", style); + }; + this.setMouseCursor = function(cursorStyle) { + dom.setStyle(this.scroller.style, "cursor", cursorStyle); + }; + + this.attachToShadowRoot = function() { + dom.importCssString(editorCss, "ace_editor.css", this.container); + }; + this.destroy = function() { + this.freeze(); + this.$fontMetrics.destroy(); + this.$cursorLayer.destroy(); + this.removeAllListeners(); + this.container.textContent = ""; + }; + }.call(VirtualRenderer.prototype)); + + config.defineOptions(VirtualRenderer.prototype, "renderer", { + animatedScroll: { initialValue: false }, + showInvisibles: { + set: function(value) { + if (this.$textLayer.setShowInvisibles(value)) + this.$loop.schedule(this.CHANGE_TEXT); + }, + initialValue: false + }, + showPrintMargin: { + set: function() { + this.$updatePrintMargin(); + }, + initialValue: true + }, + printMarginColumn: { + set: function() { + this.$updatePrintMargin(); + }, + initialValue: 80 + }, + printMargin: { + set: function(val) { + if (typeof val == "number") this.$printMarginColumn = val; + this.$showPrintMargin = !!val; + this.$updatePrintMargin(); + }, + get: function() { + return this.$showPrintMargin && this.$printMarginColumn; + } + }, + showGutter: { + set: function(show) { + this.$gutter.style.display = show ? "block" : "none"; + this.$loop.schedule(this.CHANGE_FULL); + this.onGutterResize(); + }, + initialValue: true + }, + fadeFoldWidgets: { + set: function(show) { + dom.setCssClass(this.$gutter, "ace_fade-fold-widgets", show); + }, + initialValue: false + }, + showFoldWidgets: { + set: function(show) { + this.$gutterLayer.setShowFoldWidgets(show); + this.$loop.schedule(this.CHANGE_GUTTER); + }, + initialValue: true + }, + displayIndentGuides: { + set: function(show) { + if (this.$textLayer.setDisplayIndentGuides(show)) + this.$loop.schedule(this.CHANGE_TEXT); + }, + initialValue: true + }, + highlightGutterLine: { + set: function(shouldHighlight) { + this.$gutterLayer.setHighlightGutterLine(shouldHighlight); + this.$loop.schedule(this.CHANGE_GUTTER); + }, + initialValue: true + }, + hScrollBarAlwaysVisible: { + set: function(val) { + if (!this.$hScrollBarAlwaysVisible || !this.$horizScroll) + this.$loop.schedule(this.CHANGE_SCROLL); + }, + initialValue: false + }, + vScrollBarAlwaysVisible: { + set: function(val) { + if (!this.$vScrollBarAlwaysVisible || !this.$vScroll) + this.$loop.schedule(this.CHANGE_SCROLL); + }, + initialValue: false + }, + fontSize: { + set: function(size) { + if (typeof size == "number") size = size + "px"; + this.container.style.fontSize = size; + this.updateFontSize(); + }, + initialValue: 12 + }, + fontFamily: { + set: function(name) { + this.container.style.fontFamily = name; + this.updateFontSize(); + } + }, + maxLines: { + set: function(val) { + this.updateFull(); + } + }, + minLines: { + set: function(val) { + if (!(this.$minLines < 0x1ffffffffffff)) this.$minLines = 0; + this.updateFull(); + } + }, + maxPixelHeight: { + set: function(val) { + this.updateFull(); + }, + initialValue: 0 + }, + scrollPastEnd: { + set: function(val) { + val = +val || 0; + if (this.$scrollPastEnd == val) return; + this.$scrollPastEnd = val; + this.$loop.schedule(this.CHANGE_SCROLL); + }, + initialValue: 0, + handlesSet: true + }, + fixedWidthGutter: { + set: function(val) { + this.$gutterLayer.$fixedWidth = !!val; + this.$loop.schedule(this.CHANGE_GUTTER); + } + }, + theme: { + set: function(val) { + this.setTheme(val); + }, + get: function() { + return this.$themeId || this.theme; + }, + initialValue: "./theme/textmate", + handlesSet: true + }, + hasCssTransforms: {}, + useTextareaForIME: { + initialValue: !useragent.isMobile && !useragent.isIE + } + }); + + exports.VirtualRenderer = VirtualRenderer; + } +); + +ace.define( + "ace/worker/worker_client", + [ + "require", + "exports", + "module", + "ace/lib/oop", + "ace/lib/net", + "ace/lib/event_emitter", + "ace/config" + ], + function(require, exports, module) { + "use strict"; + + var oop = require("../lib/oop"); + var net = require("../lib/net"); + var EventEmitter = require("../lib/event_emitter").EventEmitter; + var config = require("../config"); + + function $workerBlob(workerUrl) { + var script = "importScripts('" + net.qualifyURL(workerUrl) + "');"; + try { + return new Blob([script], { type: "application/javascript" }); + } catch (e) { + // Backwards-compatibility + var BlobBuilder = + window.BlobBuilder || + window.WebKitBlobBuilder || + window.MozBlobBuilder; + var blobBuilder = new BlobBuilder(); + blobBuilder.append(script); + return blobBuilder.getBlob("application/javascript"); + } + } + + function createWorker(workerUrl) { + if (typeof Worker == "undefined") + return { postMessage: function() {}, terminate: function() {} }; + if (config.get("loadWorkerFromBlob")) { + var blob = $workerBlob(workerUrl); + var URL = window.URL || window.webkitURL; + var blobURL = URL.createObjectURL(blob); + return new Worker(blobURL); + } + return new Worker(workerUrl); + } + + var WorkerClient = function(worker) { + if (!worker.postMessage) + worker = this.$createWorkerFromOldConfig.apply(this, arguments); + + this.$worker = worker; + this.$sendDeltaQueue = this.$sendDeltaQueue.bind(this); + this.changeListener = this.changeListener.bind(this); + this.onMessage = this.onMessage.bind(this); + + this.callbackId = 1; + this.callbacks = {}; + + this.$worker.onmessage = this.onMessage; + }; + + (function() { + oop.implement(this, EventEmitter); + + this.$createWorkerFromOldConfig = function( + topLevelNamespaces, + mod, + classname, + workerUrl, + importScripts + ) { + if (require.nameToUrl && !require.toUrl) + require.toUrl = require.nameToUrl; + + if (config.get("packaged") || !require.toUrl) { + workerUrl = workerUrl || config.moduleUrl(mod, "worker"); + } else { + var normalizePath = this.$normalizePath; + workerUrl = + workerUrl || + normalizePath(require.toUrl("ace/worker/worker.js", null, "_")); + + var tlns = {}; + topLevelNamespaces.forEach(function(ns) { + tlns[ns] = normalizePath( + require.toUrl(ns, null, "_").replace(/(\.js)?(\?.*)?$/, "") + ); + }); + } + + this.$worker = createWorker(workerUrl); + if (importScripts) { + this.send("importScripts", importScripts); + } + this.$worker.postMessage({ + init: true, + tlns: tlns, + module: mod, + classname: classname + }); + return this.$worker; + }; + + this.onMessage = function(e) { + var msg = e.data; + switch (msg.type) { + case "event": + this._signal(msg.name, { data: msg.data }); + break; + case "call": + var callback = this.callbacks[msg.id]; + if (callback) { + callback(msg.data); + delete this.callbacks[msg.id]; + } + break; + case "error": + this.reportError(msg.data); + break; + case "log": + window.console && + console.log && + console.log.apply(console, msg.data); + break; + } + }; + + this.reportError = function(err) { + window.console && console.error && console.error(err); + }; + + this.$normalizePath = function(path) { + return net.qualifyURL(path); + }; + + this.terminate = function() { + this._signal("terminate", {}); + this.deltaQueue = null; + this.$worker.terminate(); + this.$worker = null; + if (this.$doc) this.$doc.off("change", this.changeListener); + this.$doc = null; + }; + + this.send = function(cmd, args) { + this.$worker.postMessage({ command: cmd, args: args }); + }; + + this.call = function(cmd, args, callback) { + if (callback) { + var id = this.callbackId++; + this.callbacks[id] = callback; + args.push(id); + } + this.send(cmd, args); + }; + + this.emit = function(event, data) { + try { + if (data.data && data.data.err) + data.data.err = { + message: data.data.err.message, + stack: data.data.err.stack, + code: data.data.err.code + }; + this.$worker.postMessage({ event: event, data: { data: data.data } }); + } catch (ex) { + console.error(ex.stack); + } + }; + + this.attachToDocument = function(doc) { + if (this.$doc) this.terminate(); + + this.$doc = doc; + this.call("setValue", [doc.getValue()]); + doc.on("change", this.changeListener); + }; + + this.changeListener = function(delta) { + if (!this.deltaQueue) { + this.deltaQueue = []; + setTimeout(this.$sendDeltaQueue, 0); + } + if (delta.action == "insert") + this.deltaQueue.push(delta.start, delta.lines); + else this.deltaQueue.push(delta.start, delta.end); + }; + + this.$sendDeltaQueue = function() { + var q = this.deltaQueue; + if (!q) return; + this.deltaQueue = null; + if (q.length > 50 && q.length > this.$doc.getLength() >> 1) { + this.call("setValue", [this.$doc.getValue()]); + } else this.emit("change", { data: q }); + }; + }.call(WorkerClient.prototype)); + + var UIWorkerClient = function(topLevelNamespaces, mod, classname) { + var main = null; + var emitSync = false; + var sender = Object.create(EventEmitter); + + var messageBuffer = []; + var workerClient = new WorkerClient({ + messageBuffer: messageBuffer, + terminate: function() {}, + postMessage: function(e) { + messageBuffer.push(e); + if (!main) return; + if (emitSync) setTimeout(processNext); + else processNext(); + } + }); + + workerClient.setEmitSync = function(val) { + emitSync = val; + }; + + var processNext = function() { + var msg = messageBuffer.shift(); + if (msg.command) main[msg.command].apply(main, msg.args); + else if (msg.event) sender._signal(msg.event, msg.data); + }; + + sender.postMessage = function(msg) { + workerClient.onMessage({ data: msg }); + }; + sender.callback = function(data, callbackId) { + this.postMessage({ type: "call", id: callbackId, data: data }); + }; + sender.emit = function(name, data) { + this.postMessage({ type: "event", name: name, data: data }); + }; + + config.loadModule(["worker", mod], function(Main) { + main = new Main[classname](sender); + while (messageBuffer.length) processNext(); + }); + + return workerClient; + }; + + exports.UIWorkerClient = UIWorkerClient; + exports.WorkerClient = WorkerClient; + exports.createWorker = createWorker; + } +); + +ace.define( + "ace/placeholder", + [ + "require", + "exports", + "module", + "ace/range", + "ace/lib/event_emitter", + "ace/lib/oop" + ], + function(require, exports, module) { + "use strict"; + + var Range = require("./range").Range; + var EventEmitter = require("./lib/event_emitter").EventEmitter; + var oop = require("./lib/oop"); + + var PlaceHolder = function( + session, + length, + pos, + others, + mainClass, + othersClass + ) { + var _self = this; + this.length = length; + this.session = session; + this.doc = session.getDocument(); + this.mainClass = mainClass; + this.othersClass = othersClass; + this.$onUpdate = this.onUpdate.bind(this); + this.doc.on("change", this.$onUpdate); + this.$others = others; + + this.$onCursorChange = function() { + setTimeout(function() { + _self.onCursorChange(); + }); + }; + + this.$pos = pos; + var undoStack = session.getUndoManager().$undoStack || + session.getUndoManager().$undostack || { length: -1 }; + this.$undoStackDepth = undoStack.length; + this.setup(); + + session.selection.on("changeCursor", this.$onCursorChange); + }; + + (function() { + oop.implement(this, EventEmitter); + this.setup = function() { + var _self = this; + var doc = this.doc; + var session = this.session; + + this.selectionBefore = session.selection.toJSON(); + if (session.selection.inMultiSelectMode) + session.selection.toSingleRange(); + + this.pos = doc.createAnchor(this.$pos.row, this.$pos.column); + var pos = this.pos; + pos.$insertRight = true; + pos.detach(); + pos.markerId = session.addMarker( + new Range(pos.row, pos.column, pos.row, pos.column + this.length), + this.mainClass, + null, + false + ); + this.others = []; + this.$others.forEach(function(other) { + var anchor = doc.createAnchor(other.row, other.column); + anchor.$insertRight = true; + anchor.detach(); + _self.others.push(anchor); + }); + session.setUndoSelect(false); + }; + this.showOtherMarkers = function() { + if (this.othersActive) return; + var session = this.session; + var _self = this; + this.othersActive = true; + this.others.forEach(function(anchor) { + anchor.markerId = session.addMarker( + new Range( + anchor.row, + anchor.column, + anchor.row, + anchor.column + _self.length + ), + _self.othersClass, + null, + false + ); + }); + }; + this.hideOtherMarkers = function() { + if (!this.othersActive) return; + this.othersActive = false; + for (var i = 0; i < this.others.length; i++) { + this.session.removeMarker(this.others[i].markerId); + } + }; + this.onUpdate = function(delta) { + if (this.$updating) return this.updateAnchors(delta); + + var range = delta; + if (range.start.row !== range.end.row) return; + if (range.start.row !== this.pos.row) return; + this.$updating = true; + var lengthDiff = + delta.action === "insert" + ? range.end.column - range.start.column + : range.start.column - range.end.column; + var inMainRange = + range.start.column >= this.pos.column && + range.start.column <= this.pos.column + this.length + 1; + var distanceFromStart = range.start.column - this.pos.column; + + this.updateAnchors(delta); + + if (inMainRange) this.length += lengthDiff; + + if (inMainRange && !this.session.$fromUndo) { + if (delta.action === "insert") { + for (var i = this.others.length - 1; i >= 0; i--) { + var otherPos = this.others[i]; + var newPos = { + row: otherPos.row, + column: otherPos.column + distanceFromStart + }; + this.doc.insertMergedLines(newPos, delta.lines); + } + } else if (delta.action === "remove") { + for (var i = this.others.length - 1; i >= 0; i--) { + var otherPos = this.others[i]; + var newPos = { + row: otherPos.row, + column: otherPos.column + distanceFromStart + }; + this.doc.remove( + new Range( + newPos.row, + newPos.column, + newPos.row, + newPos.column - lengthDiff + ) + ); + } + } + } + + this.$updating = false; + this.updateMarkers(); + }; + + this.updateAnchors = function(delta) { + this.pos.onChange(delta); + for (var i = this.others.length; i--; ) this.others[i].onChange(delta); + this.updateMarkers(); + }; + + this.updateMarkers = function() { + if (this.$updating) return; + var _self = this; + var session = this.session; + var updateMarker = function(pos, className) { + session.removeMarker(pos.markerId); + pos.markerId = session.addMarker( + new Range(pos.row, pos.column, pos.row, pos.column + _self.length), + className, + null, + false + ); + }; + updateMarker(this.pos, this.mainClass); + for (var i = this.others.length; i--; ) + updateMarker(this.others[i], this.othersClass); + }; + + this.onCursorChange = function(event) { + if (this.$updating || !this.session) return; + var pos = this.session.selection.getCursor(); + if ( + pos.row === this.pos.row && + pos.column >= this.pos.column && + pos.column <= this.pos.column + this.length + ) { + this.showOtherMarkers(); + this._emit("cursorEnter", event); + } else { + this.hideOtherMarkers(); + this._emit("cursorLeave", event); + } + }; + this.detach = function() { + this.session.removeMarker(this.pos && this.pos.markerId); + this.hideOtherMarkers(); + this.doc.off("change", this.$onUpdate); + this.session.selection.off("changeCursor", this.$onCursorChange); + this.session.setUndoSelect(true); + this.session = null; + }; + this.cancel = function() { + if (this.$undoStackDepth === -1) return; + var undoManager = this.session.getUndoManager(); + var undosRequired = + (undoManager.$undoStack || undoManager.$undostack).length - + this.$undoStackDepth; + for (var i = 0; i < undosRequired; i++) { + undoManager.undo(this.session, true); + } + if (this.selectionBefore) + this.session.selection.fromJSON(this.selectionBefore); + }; + }.call(PlaceHolder.prototype)); + + exports.PlaceHolder = PlaceHolder; + } +); + +ace.define( + "ace/mouse/multi_select_handler", + ["require", "exports", "module", "ace/lib/event", "ace/lib/useragent"], + function(require, exports, module) { + var event = require("../lib/event"); + var useragent = require("../lib/useragent"); + function isSamePoint(p1, p2) { + return p1.row == p2.row && p1.column == p2.column; + } + + function onMouseDown(e) { + var ev = e.domEvent; + var alt = ev.altKey; + var shift = ev.shiftKey; + var ctrl = ev.ctrlKey; + var accel = e.getAccelKey(); + var button = e.getButton(); + + if (ctrl && useragent.isMac) button = ev.button; + + if (e.editor.inMultiSelectMode && button == 2) { + e.editor.textInput.onContextMenu(e.domEvent); + return; + } + + if (!ctrl && !alt && !accel) { + if (button === 0 && e.editor.inMultiSelectMode) + e.editor.exitMultiSelectMode(); + return; + } + + if (button !== 0) return; + + var editor = e.editor; + var selection = editor.selection; + var isMultiSelect = editor.inMultiSelectMode; + var pos = e.getDocumentPosition(); + var cursor = selection.getCursor(); + var inSelection = + e.inSelection() || (selection.isEmpty() && isSamePoint(pos, cursor)); + + var mouseX = e.x, + mouseY = e.y; + var onMouseSelection = function(e) { + mouseX = e.clientX; + mouseY = e.clientY; + }; + + var session = editor.session; + var screenAnchor = editor.renderer.pixelToScreenCoordinates( + mouseX, + mouseY + ); + var screenCursor = screenAnchor; + + var selectionMode; + if (editor.$mouseHandler.$enableJumpToDef) { + if ((ctrl && alt) || (accel && alt)) + selectionMode = shift ? "block" : "add"; + else if (alt && editor.$blockSelectEnabled) selectionMode = "block"; + } else { + if (accel && !alt) { + selectionMode = "add"; + if (!isMultiSelect && shift) return; + } else if (alt && editor.$blockSelectEnabled) { + selectionMode = "block"; + } + } + + if (selectionMode && useragent.isMac && ev.ctrlKey) { + editor.$mouseHandler.cancelContextMenu(); + } + + if (selectionMode == "add") { + if (!isMultiSelect && inSelection) return; // dragging + + if (!isMultiSelect) { + var range = selection.toOrientedRange(); + editor.addSelectionMarker(range); + } + + var oldRange = selection.rangeList.rangeAtPoint(pos); + + editor.inVirtualSelectionMode = true; + + if (shift) { + oldRange = null; + range = selection.ranges[0] || range; + editor.removeSelectionMarker(range); + } + editor.once("mouseup", function() { + var tmpSel = selection.toOrientedRange(); + + if ( + oldRange && + tmpSel.isEmpty() && + isSamePoint(oldRange.cursor, tmpSel.cursor) + ) + selection.substractPoint(tmpSel.cursor); + else { + if (shift) { + selection.substractPoint(range.cursor); + } else if (range) { + editor.removeSelectionMarker(range); + selection.addRange(range); + } + selection.addRange(tmpSel); + } + editor.inVirtualSelectionMode = false; + }); + } else if (selectionMode == "block") { + e.stop(); + editor.inVirtualSelectionMode = true; + var initialRange; + var rectSel = []; + var blockSelect = function() { + var newCursor = editor.renderer.pixelToScreenCoordinates( + mouseX, + mouseY + ); + var cursor = session.screenToDocumentPosition( + newCursor.row, + newCursor.column, + newCursor.offsetX + ); + + if ( + isSamePoint(screenCursor, newCursor) && + isSamePoint(cursor, selection.lead) + ) + return; + screenCursor = newCursor; + + editor.selection.moveToPosition(cursor); + editor.renderer.scrollCursorIntoView(); + + editor.removeSelectionMarkers(rectSel); + rectSel = selection.rectangularRangeBlock(screenCursor, screenAnchor); + if ( + editor.$mouseHandler.$clickSelection && + rectSel.length == 1 && + rectSel[0].isEmpty() + ) + rectSel[0] = editor.$mouseHandler.$clickSelection.clone(); + rectSel.forEach(editor.addSelectionMarker, editor); + editor.updateSelectionMarkers(); + }; + if (isMultiSelect && !accel) { + selection.toSingleRange(); + } else if (!isMultiSelect && accel) { + initialRange = selection.toOrientedRange(); + editor.addSelectionMarker(initialRange); + } + + if (shift) + screenAnchor = session.documentToScreenPosition(selection.lead); + else selection.moveToPosition(pos); + + screenCursor = { row: -1, column: -1 }; + + var onMouseSelectionEnd = function(e) { + blockSelect(); + clearInterval(timerId); + editor.removeSelectionMarkers(rectSel); + if (!rectSel.length) rectSel = [selection.toOrientedRange()]; + if (initialRange) { + editor.removeSelectionMarker(initialRange); + selection.toSingleRange(initialRange); + } + for (var i = 0; i < rectSel.length; i++) + selection.addRange(rectSel[i]); + editor.inVirtualSelectionMode = false; + editor.$mouseHandler.$clickSelection = null; + }; + + var onSelectionInterval = blockSelect; + + event.capture(editor.container, onMouseSelection, onMouseSelectionEnd); + var timerId = setInterval(function() { + onSelectionInterval(); + }, 20); + + return e.preventDefault(); + } + } + + exports.onMouseDown = onMouseDown; + } +); + +ace.define( + "ace/commands/multi_select_commands", + ["require", "exports", "module", "ace/keyboard/hash_handler"], + function(require, exports, module) { + exports.defaultCommands = [ + { + name: "addCursorAbove", + description: "Add cursor above", + exec: function(editor) { + editor.selectMoreLines(-1); + }, + bindKey: { win: "Ctrl-Alt-Up", mac: "Ctrl-Alt-Up" }, + scrollIntoView: "cursor", + readOnly: true + }, + { + name: "addCursorBelow", + description: "Add cursor below", + exec: function(editor) { + editor.selectMoreLines(1); + }, + bindKey: { win: "Ctrl-Alt-Down", mac: "Ctrl-Alt-Down" }, + scrollIntoView: "cursor", + readOnly: true + }, + { + name: "addCursorAboveSkipCurrent", + description: "Add cursor above (skip current)", + exec: function(editor) { + editor.selectMoreLines(-1, true); + }, + bindKey: { win: "Ctrl-Alt-Shift-Up", mac: "Ctrl-Alt-Shift-Up" }, + scrollIntoView: "cursor", + readOnly: true + }, + { + name: "addCursorBelowSkipCurrent", + description: "Add cursor below (skip current)", + exec: function(editor) { + editor.selectMoreLines(1, true); + }, + bindKey: { win: "Ctrl-Alt-Shift-Down", mac: "Ctrl-Alt-Shift-Down" }, + scrollIntoView: "cursor", + readOnly: true + }, + { + name: "selectMoreBefore", + description: "Select more before", + exec: function(editor) { + editor.selectMore(-1); + }, + bindKey: { win: "Ctrl-Alt-Left", mac: "Ctrl-Alt-Left" }, + scrollIntoView: "cursor", + readOnly: true + }, + { + name: "selectMoreAfter", + description: "Select more after", + exec: function(editor) { + editor.selectMore(1); + }, + bindKey: { win: "Ctrl-Alt-Right", mac: "Ctrl-Alt-Right" }, + scrollIntoView: "cursor", + readOnly: true + }, + { + name: "selectNextBefore", + description: "Select next before", + exec: function(editor) { + editor.selectMore(-1, true); + }, + bindKey: { win: "Ctrl-Alt-Shift-Left", mac: "Ctrl-Alt-Shift-Left" }, + scrollIntoView: "cursor", + readOnly: true + }, + { + name: "selectNextAfter", + description: "Select next after", + exec: function(editor) { + editor.selectMore(1, true); + }, + bindKey: { win: "Ctrl-Alt-Shift-Right", mac: "Ctrl-Alt-Shift-Right" }, + scrollIntoView: "cursor", + readOnly: true + }, + { + name: "toggleSplitSelectionIntoLines", + description: "Split into lines", + exec: function(editor) { + if (editor.multiSelect.rangeCount > 1) + editor.multiSelect.joinSelections(); + else editor.multiSelect.splitIntoLines(); + }, + bindKey: { win: "Ctrl-Alt-L", mac: "Ctrl-Alt-L" }, + readOnly: true + }, + { + name: "splitSelectionIntoLines", + description: "Split into lines", + exec: function(editor) { + editor.multiSelect.splitIntoLines(); + }, + readOnly: true + }, + { + name: "alignCursors", + description: "Align cursors", + exec: function(editor) { + editor.alignCursors(); + }, + bindKey: { win: "Ctrl-Alt-A", mac: "Ctrl-Alt-A" }, + scrollIntoView: "cursor" + }, + { + name: "findAll", + description: "Find all", + exec: function(editor) { + editor.findAll(); + }, + bindKey: { win: "Ctrl-Alt-K", mac: "Ctrl-Alt-G" }, + scrollIntoView: "cursor", + readOnly: true + } + ]; + exports.multiSelectCommands = [ + { + name: "singleSelection", + description: "Single selection", + bindKey: "esc", + exec: function(editor) { + editor.exitMultiSelectMode(); + }, + scrollIntoView: "cursor", + readOnly: true, + isAvailable: function(editor) { + return editor && editor.inMultiSelectMode; + } + } + ]; + + var HashHandler = require("../keyboard/hash_handler").HashHandler; + exports.keyboardHandler = new HashHandler(exports.multiSelectCommands); + } +); + +ace.define( + "ace/multi_select", + [ + "require", + "exports", + "module", + "ace/range_list", + "ace/range", + "ace/selection", + "ace/mouse/multi_select_handler", + "ace/lib/event", + "ace/lib/lang", + "ace/commands/multi_select_commands", + "ace/search", + "ace/edit_session", + "ace/editor", + "ace/config" + ], + function(require, exports, module) { + var RangeList = require("./range_list").RangeList; + var Range = require("./range").Range; + var Selection = require("./selection").Selection; + var onMouseDown = require("./mouse/multi_select_handler").onMouseDown; + var event = require("./lib/event"); + var lang = require("./lib/lang"); + var commands = require("./commands/multi_select_commands"); + exports.commands = commands.defaultCommands.concat( + commands.multiSelectCommands + ); + var Search = require("./search").Search; + var search = new Search(); + + function find(session, needle, dir) { + search.$options.wrap = true; + search.$options.needle = needle; + search.$options.backwards = dir == -1; + return search.find(session); + } + var EditSession = require("./edit_session").EditSession; + (function() { + this.getSelectionMarkers = function() { + return this.$selectionMarkers; + }; + }.call(EditSession.prototype)); + (function() { + this.ranges = null; + this.rangeList = null; + this.addRange = function(range, $blockChangeEvents) { + if (!range) return; + + if (!this.inMultiSelectMode && this.rangeCount === 0) { + var oldRange = this.toOrientedRange(); + this.rangeList.add(oldRange); + this.rangeList.add(range); + if (this.rangeList.ranges.length != 2) { + this.rangeList.removeAll(); + return $blockChangeEvents || this.fromOrientedRange(range); + } + this.rangeList.removeAll(); + this.rangeList.add(oldRange); + this.$onAddRange(oldRange); + } + + if (!range.cursor) range.cursor = range.end; + + var removed = this.rangeList.add(range); + + this.$onAddRange(range); + + if (removed.length) this.$onRemoveRange(removed); + + if (this.rangeCount > 1 && !this.inMultiSelectMode) { + this._signal("multiSelect"); + this.inMultiSelectMode = true; + this.session.$undoSelect = false; + this.rangeList.attach(this.session); + } + + return $blockChangeEvents || this.fromOrientedRange(range); + }; + this.toSingleRange = function(range) { + range = range || this.ranges[0]; + var removed = this.rangeList.removeAll(); + if (removed.length) this.$onRemoveRange(removed); + + range && this.fromOrientedRange(range); + }; + this.substractPoint = function(pos) { + var removed = this.rangeList.substractPoint(pos); + if (removed) { + this.$onRemoveRange(removed); + return removed[0]; + } + }; + this.mergeOverlappingRanges = function() { + var removed = this.rangeList.merge(); + if (removed.length) this.$onRemoveRange(removed); + }; + + this.$onAddRange = function(range) { + this.rangeCount = this.rangeList.ranges.length; + this.ranges.unshift(range); + this._signal("addRange", { range: range }); + }; + + this.$onRemoveRange = function(removed) { + this.rangeCount = this.rangeList.ranges.length; + if (this.rangeCount == 1 && this.inMultiSelectMode) { + var lastRange = this.rangeList.ranges.pop(); + removed.push(lastRange); + this.rangeCount = 0; + } + + for (var i = removed.length; i--; ) { + var index = this.ranges.indexOf(removed[i]); + this.ranges.splice(index, 1); + } + + this._signal("removeRange", { ranges: removed }); + + if (this.rangeCount === 0 && this.inMultiSelectMode) { + this.inMultiSelectMode = false; + this._signal("singleSelect"); + this.session.$undoSelect = true; + this.rangeList.detach(this.session); + } + + lastRange = lastRange || this.ranges[0]; + if (lastRange && !lastRange.isEqual(this.getRange())) + this.fromOrientedRange(lastRange); + }; + this.$initRangeList = function() { + if (this.rangeList) return; + + this.rangeList = new RangeList(); + this.ranges = []; + this.rangeCount = 0; + }; + this.getAllRanges = function() { + return this.rangeCount + ? this.rangeList.ranges.concat() + : [this.getRange()]; + }; + this.splitIntoLines = function() { + var ranges = this.ranges.length ? this.ranges : [this.getRange()]; + var newRanges = []; + for (var i = 0; i < ranges.length; i++) { + var range = ranges[i]; + var row = range.start.row; + var endRow = range.end.row; + if (row === endRow) { + newRanges.push(range.clone()); + } else { + newRanges.push( + new Range( + row, + range.start.column, + row, + this.session.getLine(row).length + ) + ); + while (++row < endRow) newRanges.push(this.getLineRange(row, true)); + newRanges.push(new Range(endRow, 0, endRow, range.end.column)); + } + if (i == 0 && !this.isBackwards()) newRanges = newRanges.reverse(); + } + this.toSingleRange(); + for (var i = newRanges.length; i--; ) this.addRange(newRanges[i]); + }; + + this.joinSelections = function() { + var ranges = this.rangeList.ranges; + var lastRange = ranges[ranges.length - 1]; + var range = Range.fromPoints(ranges[0].start, lastRange.end); + + this.toSingleRange(); + this.setSelectionRange(range, lastRange.cursor == lastRange.start); + }; + this.toggleBlockSelection = function() { + if (this.rangeCount > 1) { + var ranges = this.rangeList.ranges; + var lastRange = ranges[ranges.length - 1]; + var range = Range.fromPoints(ranges[0].start, lastRange.end); + + this.toSingleRange(); + this.setSelectionRange(range, lastRange.cursor == lastRange.start); + } else { + var cursor = this.session.documentToScreenPosition(this.cursor); + var anchor = this.session.documentToScreenPosition(this.anchor); + + var rectSel = this.rectangularRangeBlock(cursor, anchor); + rectSel.forEach(this.addRange, this); + } + }; + this.rectangularRangeBlock = function( + screenCursor, + screenAnchor, + includeEmptyLines + ) { + var rectSel = []; + + var xBackwards = screenCursor.column < screenAnchor.column; + if (xBackwards) { + var startColumn = screenCursor.column; + var endColumn = screenAnchor.column; + var startOffsetX = screenCursor.offsetX; + var endOffsetX = screenAnchor.offsetX; + } else { + var startColumn = screenAnchor.column; + var endColumn = screenCursor.column; + var startOffsetX = screenAnchor.offsetX; + var endOffsetX = screenCursor.offsetX; + } + + var yBackwards = screenCursor.row < screenAnchor.row; + if (yBackwards) { + var startRow = screenCursor.row; + var endRow = screenAnchor.row; + } else { + var startRow = screenAnchor.row; + var endRow = screenCursor.row; + } + + if (startColumn < 0) startColumn = 0; + if (startRow < 0) startRow = 0; + + if (startRow == endRow) includeEmptyLines = true; + + var docEnd; + for (var row = startRow; row <= endRow; row++) { + var range = Range.fromPoints( + this.session.screenToDocumentPosition( + row, + startColumn, + startOffsetX + ), + this.session.screenToDocumentPosition(row, endColumn, endOffsetX) + ); + if (range.isEmpty()) { + if (docEnd && isSamePoint(range.end, docEnd)) break; + docEnd = range.end; + } + range.cursor = xBackwards ? range.start : range.end; + rectSel.push(range); + } + + if (yBackwards) rectSel.reverse(); + + if (!includeEmptyLines) { + var end = rectSel.length - 1; + while (rectSel[end].isEmpty() && end > 0) end--; + if (end > 0) { + var start = 0; + while (rectSel[start].isEmpty()) start++; + } + for (var i = end; i >= start; i--) { + if (rectSel[i].isEmpty()) rectSel.splice(i, 1); + } + } + + return rectSel; + }; + }.call(Selection.prototype)); + var Editor = require("./editor").Editor; + (function() { + this.updateSelectionMarkers = function() { + this.renderer.updateCursor(); + this.renderer.updateBackMarkers(); + }; + this.addSelectionMarker = function(orientedRange) { + if (!orientedRange.cursor) orientedRange.cursor = orientedRange.end; + + var style = this.getSelectionStyle(); + orientedRange.marker = this.session.addMarker( + orientedRange, + "ace_selection", + style + ); + + this.session.$selectionMarkers.push(orientedRange); + this.session.selectionMarkerCount = this.session.$selectionMarkers.length; + return orientedRange; + }; + this.removeSelectionMarker = function(range) { + if (!range.marker) return; + this.session.removeMarker(range.marker); + var index = this.session.$selectionMarkers.indexOf(range); + if (index != -1) this.session.$selectionMarkers.splice(index, 1); + this.session.selectionMarkerCount = this.session.$selectionMarkers.length; + }; + + this.removeSelectionMarkers = function(ranges) { + var markerList = this.session.$selectionMarkers; + for (var i = ranges.length; i--; ) { + var range = ranges[i]; + if (!range.marker) continue; + this.session.removeMarker(range.marker); + var index = markerList.indexOf(range); + if (index != -1) markerList.splice(index, 1); + } + this.session.selectionMarkerCount = markerList.length; + }; + + this.$onAddRange = function(e) { + this.addSelectionMarker(e.range); + this.renderer.updateCursor(); + this.renderer.updateBackMarkers(); + }; + + this.$onRemoveRange = function(e) { + this.removeSelectionMarkers(e.ranges); + this.renderer.updateCursor(); + this.renderer.updateBackMarkers(); + }; + + this.$onMultiSelect = function(e) { + if (this.inMultiSelectMode) return; + this.inMultiSelectMode = true; + + this.setStyle("ace_multiselect"); + this.keyBinding.addKeyboardHandler(commands.keyboardHandler); + this.commands.setDefaultHandler("exec", this.$onMultiSelectExec); + + this.renderer.updateCursor(); + this.renderer.updateBackMarkers(); + }; + + this.$onSingleSelect = function(e) { + if (this.session.multiSelect.inVirtualMode) return; + this.inMultiSelectMode = false; + + this.unsetStyle("ace_multiselect"); + this.keyBinding.removeKeyboardHandler(commands.keyboardHandler); + + this.commands.removeDefaultHandler("exec", this.$onMultiSelectExec); + this.renderer.updateCursor(); + this.renderer.updateBackMarkers(); + this._emit("changeSelection"); + }; + + this.$onMultiSelectExec = function(e) { + var command = e.command; + var editor = e.editor; + if (!editor.multiSelect) return; + if (!command.multiSelectAction) { + var result = command.exec(editor, e.args || {}); + editor.multiSelect.addRange(editor.multiSelect.toOrientedRange()); + editor.multiSelect.mergeOverlappingRanges(); + } else if (command.multiSelectAction == "forEach") { + result = editor.forEachSelection(command, e.args); + } else if (command.multiSelectAction == "forEachLine") { + result = editor.forEachSelection(command, e.args, true); + } else if (command.multiSelectAction == "single") { + editor.exitMultiSelectMode(); + result = command.exec(editor, e.args || {}); + } else { + result = command.multiSelectAction(editor, e.args || {}); + } + return result; + }; + this.forEachSelection = function(cmd, args, options) { + if (this.inVirtualSelectionMode) return; + var keepOrder = options && options.keepOrder; + var $byLines = options == true || (options && options.$byLines); + var session = this.session; + var selection = this.selection; + var rangeList = selection.rangeList; + var ranges = (keepOrder ? selection : rangeList).ranges; + var result; + + if (!ranges.length) + return cmd.exec ? cmd.exec(this, args || {}) : cmd(this, args || {}); + + var reg = selection._eventRegistry; + selection._eventRegistry = {}; + + var tmpSel = new Selection(session); + this.inVirtualSelectionMode = true; + for (var i = ranges.length; i--; ) { + if ($byLines) { + while (i > 0 && ranges[i].start.row == ranges[i - 1].end.row) i--; + } + tmpSel.fromOrientedRange(ranges[i]); + tmpSel.index = i; + this.selection = session.selection = tmpSel; + var cmdResult = cmd.exec + ? cmd.exec(this, args || {}) + : cmd(this, args || {}); + if (!result && cmdResult !== undefined) result = cmdResult; + tmpSel.toOrientedRange(ranges[i]); + } + tmpSel.detach(); + + this.selection = session.selection = selection; + this.inVirtualSelectionMode = false; + selection._eventRegistry = reg; + selection.mergeOverlappingRanges(); + if (selection.ranges[0]) + selection.fromOrientedRange(selection.ranges[0]); + + var anim = this.renderer.$scrollAnimation; + this.onCursorChange(); + this.onSelectionChange(); + if (anim && anim.from == anim.to) + this.renderer.animateScrolling(anim.from); + + return result; + }; + this.exitMultiSelectMode = function() { + if (!this.inMultiSelectMode || this.inVirtualSelectionMode) return; + this.multiSelect.toSingleRange(); + }; + + this.getSelectedText = function() { + var text = ""; + if (this.inMultiSelectMode && !this.inVirtualSelectionMode) { + var ranges = this.multiSelect.rangeList.ranges; + var buf = []; + for (var i = 0; i < ranges.length; i++) { + buf.push(this.session.getTextRange(ranges[i])); + } + var nl = this.session.getDocument().getNewLineCharacter(); + text = buf.join(nl); + if (text.length == (buf.length - 1) * nl.length) text = ""; + } else if (!this.selection.isEmpty()) { + text = this.session.getTextRange(this.getSelectionRange()); + } + return text; + }; + + this.$checkMultiselectChange = function(e, anchor) { + if (this.inMultiSelectMode && !this.inVirtualSelectionMode) { + var range = this.multiSelect.ranges[0]; + if (this.multiSelect.isEmpty() && anchor == this.multiSelect.anchor) + return; + var pos = + anchor == this.multiSelect.anchor + ? range.cursor == range.start + ? range.end + : range.start + : range.cursor; + if ( + pos.row != anchor.row || + this.session.$clipPositionToDocument(pos.row, pos.column).column != + anchor.column + ) + this.multiSelect.toSingleRange(this.multiSelect.toOrientedRange()); + else this.multiSelect.mergeOverlappingRanges(); + } + }; + this.findAll = function(needle, options, additive) { + options = options || {}; + options.needle = needle || options.needle; + if (options.needle == undefined) { + var range = this.selection.isEmpty() + ? this.selection.getWordRange() + : this.selection.getRange(); + options.needle = this.session.getTextRange(range); + } + this.$search.set(options); + + var ranges = this.$search.findAll(this.session); + if (!ranges.length) return 0; + + var selection = this.multiSelect; + + if (!additive) selection.toSingleRange(ranges[0]); + + for (var i = ranges.length; i--; ) selection.addRange(ranges[i], true); + if (range && selection.rangeList.rangeAtPoint(range.start)) + selection.addRange(range, true); + + return ranges.length; + }; + this.selectMoreLines = function(dir, skip) { + var range = this.selection.toOrientedRange(); + var isBackwards = range.cursor == range.end; + + var screenLead = this.session.documentToScreenPosition(range.cursor); + if (this.selection.$desiredColumn) + screenLead.column = this.selection.$desiredColumn; + + var lead = this.session.screenToDocumentPosition( + screenLead.row + dir, + screenLead.column + ); + + if (!range.isEmpty()) { + var screenAnchor = this.session.documentToScreenPosition( + isBackwards ? range.end : range.start + ); + var anchor = this.session.screenToDocumentPosition( + screenAnchor.row + dir, + screenAnchor.column + ); + } else { + var anchor = lead; + } + + if (isBackwards) { + var newRange = Range.fromPoints(lead, anchor); + newRange.cursor = newRange.start; + } else { + var newRange = Range.fromPoints(anchor, lead); + newRange.cursor = newRange.end; + } + + newRange.desiredColumn = screenLead.column; + if (!this.selection.inMultiSelectMode) { + this.selection.addRange(range); + } else { + if (skip) var toRemove = range.cursor; + } + + this.selection.addRange(newRange); + if (toRemove) this.selection.substractPoint(toRemove); + }; + this.transposeSelections = function(dir) { + var session = this.session; + var sel = session.multiSelect; + var all = sel.ranges; + + for (var i = all.length; i--; ) { + var range = all[i]; + if (range.isEmpty()) { + var tmp = session.getWordRange(range.start.row, range.start.column); + range.start.row = tmp.start.row; + range.start.column = tmp.start.column; + range.end.row = tmp.end.row; + range.end.column = tmp.end.column; + } + } + sel.mergeOverlappingRanges(); + + var words = []; + for (var i = all.length; i--; ) { + var range = all[i]; + words.unshift(session.getTextRange(range)); + } + + if (dir < 0) words.unshift(words.pop()); + else words.push(words.shift()); + + for (var i = all.length; i--; ) { + var range = all[i]; + var tmp = range.clone(); + session.replace(range, words[i]); + range.start.row = tmp.start.row; + range.start.column = tmp.start.column; + } + sel.fromOrientedRange(sel.ranges[0]); + }; + this.selectMore = function(dir, skip, stopAtFirst) { + var session = this.session; + var sel = session.multiSelect; + + var range = sel.toOrientedRange(); + if (range.isEmpty()) { + range = session.getWordRange(range.start.row, range.start.column); + range.cursor = dir == -1 ? range.start : range.end; + this.multiSelect.addRange(range); + if (stopAtFirst) return; + } + var needle = session.getTextRange(range); + + var newRange = find(session, needle, dir); + if (newRange) { + newRange.cursor = dir == -1 ? newRange.start : newRange.end; + this.session.unfold(newRange); + this.multiSelect.addRange(newRange); + this.renderer.scrollCursorIntoView(null, 0.5); + } + if (skip) this.multiSelect.substractPoint(range.cursor); + }; + this.alignCursors = function() { + var session = this.session; + var sel = session.multiSelect; + var ranges = sel.ranges; + var row = -1; + var sameRowRanges = ranges.filter(function(r) { + if (r.cursor.row == row) return true; + row = r.cursor.row; + }); + + if (!ranges.length || sameRowRanges.length == ranges.length - 1) { + var range = this.selection.getRange(); + var fr = range.start.row, + lr = range.end.row; + var guessRange = fr == lr; + if (guessRange) { + var max = this.session.getLength(); + var line; + do { + line = this.session.getLine(lr); + } while (/[=:]/.test(line) && ++lr < max); + do { + line = this.session.getLine(fr); + } while (/[=:]/.test(line) && --fr > 0); + + if (fr < 0) fr = 0; + if (lr >= max) lr = max - 1; + } + var lines = this.session.removeFullLines(fr, lr); + lines = this.$reAlignText(lines, guessRange); + this.session.insert({ row: fr, column: 0 }, lines.join("\n") + "\n"); + if (!guessRange) { + range.start.column = 0; + range.end.column = lines[lines.length - 1].length; + } + this.selection.setRange(range); + } else { + sameRowRanges.forEach(function(r) { + sel.substractPoint(r.cursor); + }); + + var maxCol = 0; + var minSpace = Infinity; + var spaceOffsets = ranges.map(function(r) { + var p = r.cursor; + var line = session.getLine(p.row); + var spaceOffset = line.substr(p.column).search(/\S/g); + if (spaceOffset == -1) spaceOffset = 0; + + if (p.column > maxCol) maxCol = p.column; + if (spaceOffset < minSpace) minSpace = spaceOffset; + return spaceOffset; + }); + ranges.forEach(function(r, i) { + var p = r.cursor; + var l = maxCol - p.column; + var d = spaceOffsets[i] - minSpace; + if (l > d) session.insert(p, lang.stringRepeat(" ", l - d)); + else + session.remove( + new Range(p.row, p.column, p.row, p.column - l + d) + ); + + r.start.column = r.end.column = maxCol; + r.start.row = r.end.row = p.row; + r.cursor = r.end; + }); + sel.fromOrientedRange(ranges[0]); + this.renderer.updateCursor(); + this.renderer.updateBackMarkers(); + } + }; + + this.$reAlignText = function(lines, forceLeft) { + var isLeftAligned = true, + isRightAligned = true; + var startW, textW, endW; + + return lines + .map(function(line) { + var m = line.match(/(\s*)(.*?)(\s*)([=:].*)/); + if (!m) return [line]; + + if (startW == null) { + startW = m[1].length; + textW = m[2].length; + endW = m[3].length; + return m; + } + + if ( + startW + textW + endW != + m[1].length + m[2].length + m[3].length + ) + isRightAligned = false; + if (startW != m[1].length) isLeftAligned = false; + + if (startW > m[1].length) startW = m[1].length; + if (textW < m[2].length) textW = m[2].length; + if (endW > m[3].length) endW = m[3].length; + + return m; + }) + .map( + forceLeft + ? alignLeft + : isLeftAligned + ? isRightAligned + ? alignRight + : alignLeft + : unAlign + ); + + function spaces(n) { + return lang.stringRepeat(" ", n); + } + + function alignLeft(m) { + return !m[2] + ? m[0] + : spaces(startW) + + m[2] + + spaces(textW - m[2].length + endW) + + m[4].replace(/^([=:])\s+/, "$1 "); + } + function alignRight(m) { + return !m[2] + ? m[0] + : spaces(startW + textW - m[2].length) + + m[2] + + spaces(endW) + + m[4].replace(/^([=:])\s+/, "$1 "); + } + function unAlign(m) { + return !m[2] + ? m[0] + : spaces(startW) + + m[2] + + spaces(endW) + + m[4].replace(/^([=:])\s+/, "$1 "); + } + }; + }.call(Editor.prototype)); + + function isSamePoint(p1, p2) { + return p1.row == p2.row && p1.column == p2.column; + } + exports.onSessionChange = function(e) { + var session = e.session; + if (session && !session.multiSelect) { + session.$selectionMarkers = []; + session.selection.$initRangeList(); + session.multiSelect = session.selection; + } + this.multiSelect = session && session.multiSelect; + + var oldSession = e.oldSession; + if (oldSession) { + oldSession.multiSelect.off("addRange", this.$onAddRange); + oldSession.multiSelect.off("removeRange", this.$onRemoveRange); + oldSession.multiSelect.off("multiSelect", this.$onMultiSelect); + oldSession.multiSelect.off("singleSelect", this.$onSingleSelect); + oldSession.multiSelect.lead.off("change", this.$checkMultiselectChange); + oldSession.multiSelect.anchor.off( + "change", + this.$checkMultiselectChange + ); + } + + if (session) { + session.multiSelect.on("addRange", this.$onAddRange); + session.multiSelect.on("removeRange", this.$onRemoveRange); + session.multiSelect.on("multiSelect", this.$onMultiSelect); + session.multiSelect.on("singleSelect", this.$onSingleSelect); + session.multiSelect.lead.on("change", this.$checkMultiselectChange); + session.multiSelect.anchor.on("change", this.$checkMultiselectChange); + } + + if ( + session && + this.inMultiSelectMode != session.selection.inMultiSelectMode + ) { + if (session.selection.inMultiSelectMode) this.$onMultiSelect(); + else this.$onSingleSelect(); + } + }; + function MultiSelect(editor) { + if (editor.$multiselectOnSessionChange) return; + editor.$onAddRange = editor.$onAddRange.bind(editor); + editor.$onRemoveRange = editor.$onRemoveRange.bind(editor); + editor.$onMultiSelect = editor.$onMultiSelect.bind(editor); + editor.$onSingleSelect = editor.$onSingleSelect.bind(editor); + editor.$multiselectOnSessionChange = exports.onSessionChange.bind(editor); + editor.$checkMultiselectChange = editor.$checkMultiselectChange.bind( + editor + ); + + editor.$multiselectOnSessionChange(editor); + editor.on("changeSession", editor.$multiselectOnSessionChange); + + editor.on("mousedown", onMouseDown); + editor.commands.addCommands(commands.defaultCommands); + + addAltCursorListeners(editor); + } + + function addAltCursorListeners(editor) { + if (!editor.textInput) return; + var el = editor.textInput.getElement(); + var altCursor = false; + event.addListener( + el, + "keydown", + function(e) { + var altDown = + e.keyCode == 18 && !(e.ctrlKey || e.shiftKey || e.metaKey); + if (editor.$blockSelectEnabled && altDown) { + if (!altCursor) { + editor.renderer.setMouseCursor("crosshair"); + altCursor = true; + } + } else if (altCursor) { + reset(); + } + }, + editor + ); + + event.addListener(el, "keyup", reset, editor); + event.addListener(el, "blur", reset, editor); + function reset(e) { + if (altCursor) { + editor.renderer.setMouseCursor(""); + altCursor = false; + } + } + } + + exports.MultiSelect = MultiSelect; + + require("./config").defineOptions(Editor.prototype, "editor", { + enableMultiselect: { + set: function(val) { + MultiSelect(this); + if (val) { + this.on("changeSession", this.$multiselectOnSessionChange); + this.on("mousedown", onMouseDown); + } else { + this.off("changeSession", this.$multiselectOnSessionChange); + this.off("mousedown", onMouseDown); + } + }, + value: true + }, + enableBlockSelect: { + set: function(val) { + this.$blockSelectEnabled = val; + }, + value: true + } + }); + } +); + +ace.define( + "ace/mode/folding/fold_mode", + ["require", "exports", "module", "ace/range"], + function(require, exports, module) { + "use strict"; + + var Range = require("../../range").Range; + + var FoldMode = (exports.FoldMode = function() {}); + + (function() { + this.foldingStartMarker = null; + this.foldingStopMarker = null; + this.getFoldWidget = function(session, foldStyle, row) { + var line = session.getLine(row); + if (this.foldingStartMarker.test(line)) return "start"; + if ( + foldStyle == "markbeginend" && + this.foldingStopMarker && + this.foldingStopMarker.test(line) + ) + return "end"; + return ""; + }; + + this.getFoldWidgetRange = function(session, foldStyle, row) { + return null; + }; + + this.indentationBlock = function(session, row, column) { + var re = /\S/; + var line = session.getLine(row); + var startLevel = line.search(re); + if (startLevel == -1) return; + + var startColumn = column || line.length; + var maxRow = session.getLength(); + var startRow = row; + var endRow = row; + + while (++row < maxRow) { + var level = session.getLine(row).search(re); + + if (level == -1) continue; + + if (level <= startLevel) { + var token = session.getTokenAt(row, 0); + if (!token || token.type !== "string") break; + } + + endRow = row; + } + + if (endRow > startRow) { + var endColumn = session.getLine(endRow).length; + return new Range(startRow, startColumn, endRow, endColumn); + } + }; + + this.openingBracketBlock = function( + session, + bracket, + row, + column, + typeRe + ) { + var start = { row: row, column: column + 1 }; + var end = session.$findClosingBracket(bracket, start, typeRe); + if (!end) return; + + var fw = session.foldWidgets[end.row]; + if (fw == null) fw = session.getFoldWidget(end.row); + + if (fw == "start" && end.row > start.row) { + end.row--; + end.column = session.getLine(end.row).length; + } + return Range.fromPoints(start, end); + }; + + this.closingBracketBlock = function( + session, + bracket, + row, + column, + typeRe + ) { + var end = { row: row, column: column }; + var start = session.$findOpeningBracket(bracket, end); + + if (!start) return; + + start.column++; + end.column--; + + return Range.fromPoints(start, end); + }; + }.call(FoldMode.prototype)); + } +); + +ace.define( + "ace/theme/textmate", + ["require", "exports", "module", "ace/lib/dom"], + function(require, exports, module) { + "use strict"; + + exports.isDark = false; + exports.cssClass = "ace-tm"; + exports.cssText = + '.ace-tm .ace_gutter {\ + background: #f0f0f0;\ + color: #333;\ + }\ + .ace-tm .ace_print-margin {\ + width: 1px;\ + background: #e8e8e8;\ + }\ + .ace-tm .ace_fold {\ + background-color: #6B72E6;\ + }\ + .ace-tm {\ + background-color: #FFFFFF;\ + color: black;\ + }\ + .ace-tm .ace_cursor {\ + color: black;\ + }\ + .ace-tm .ace_invisible {\ + color: rgb(191, 191, 191);\ + }\ + .ace-tm .ace_storage,\ + .ace-tm .ace_keyword {\ + color: blue;\ + }\ + .ace-tm .ace_constant {\ + color: rgb(197, 6, 11);\ + }\ + .ace-tm .ace_constant.ace_buildin {\ + color: rgb(88, 72, 246);\ + }\ + .ace-tm .ace_constant.ace_language {\ + color: rgb(88, 92, 246);\ + }\ + .ace-tm .ace_constant.ace_library {\ + color: rgb(6, 150, 14);\ + }\ + .ace-tm .ace_invalid {\ + background-color: rgba(255, 0, 0, 0.1);\ + color: red;\ + }\ + .ace-tm .ace_support.ace_function {\ + color: rgb(60, 76, 114);\ + }\ + .ace-tm .ace_support.ace_constant {\ + color: rgb(6, 150, 14);\ + }\ + .ace-tm .ace_support.ace_type,\ + .ace-tm .ace_support.ace_class {\ + color: rgb(109, 121, 222);\ + }\ + .ace-tm .ace_keyword.ace_operator {\ + color: rgb(104, 118, 135);\ + }\ + .ace-tm .ace_string {\ + color: rgb(3, 106, 7);\ + }\ + .ace-tm .ace_comment {\ + color: rgb(76, 136, 107);\ + }\ + .ace-tm .ace_comment.ace_doc {\ + color: rgb(0, 102, 255);\ + }\ + .ace-tm .ace_comment.ace_doc.ace_tag {\ + color: rgb(128, 159, 191);\ + }\ + .ace-tm .ace_constant.ace_numeric {\ + color: rgb(0, 0, 205);\ + }\ + .ace-tm .ace_variable {\ + color: rgb(49, 132, 149);\ + }\ + .ace-tm .ace_xml-pe {\ + color: rgb(104, 104, 91);\ + }\ + .ace-tm .ace_entity.ace_name.ace_function {\ + color: #0000A2;\ + }\ + .ace-tm .ace_heading {\ + color: rgb(12, 7, 255);\ + }\ + .ace-tm .ace_list {\ + color:rgb(185, 6, 144);\ + }\ + .ace-tm .ace_meta.ace_tag {\ + color:rgb(0, 22, 142);\ + }\ + .ace-tm .ace_string.ace_regex {\ + color: rgb(255, 0, 0)\ + }\ + .ace-tm .ace_marker-layer .ace_selection {\ + background: rgb(181, 213, 255);\ + }\ + .ace-tm.ace_multiselect .ace_selection.ace_start {\ + box-shadow: 0 0 3px 0px white;\ + }\ + .ace-tm .ace_marker-layer .ace_step {\ + background: rgb(252, 255, 0);\ + }\ + .ace-tm .ace_marker-layer .ace_stack {\ + background: rgb(164, 229, 101);\ + }\ + .ace-tm .ace_marker-layer .ace_bracket {\ + margin: -1px 0 0 -1px;\ + border: 1px solid rgb(192, 192, 192);\ + }\ + .ace-tm .ace_marker-layer .ace_active-line {\ + background: rgba(0, 0, 0, 0.07);\ + }\ + .ace-tm .ace_gutter-active-line {\ + background-color : #dcdcdc;\ + }\ + .ace-tm .ace_marker-layer .ace_selected-word {\ + background: rgb(250, 250, 255);\ + border: 1px solid rgb(200, 200, 250);\ + }\ + .ace-tm .ace_indent-guide {\ + background: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAACCAYAAACZgbYnAAAAE0lEQVQImWP4////f4bLly//BwAmVgd1/w11/gAAAABJRU5ErkJggg==") right repeat-y;\ + }\ + '; + exports.$id = "ace/theme/textmate"; + + var dom = require("../lib/dom"); + dom.importCssString(exports.cssText, exports.cssClass); + } +); + +ace.define( + "ace/line_widgets", + ["require", "exports", "module", "ace/lib/dom"], + function(require, exports, module) { + "use strict"; + + var dom = require("./lib/dom"); + + function LineWidgets(session) { + this.session = session; + this.session.widgetManager = this; + this.session.getRowLength = this.getRowLength; + this.session.$getWidgetScreenLength = this.$getWidgetScreenLength; + this.updateOnChange = this.updateOnChange.bind(this); + this.renderWidgets = this.renderWidgets.bind(this); + this.measureWidgets = this.measureWidgets.bind(this); + this.session._changedWidgets = []; + this.$onChangeEditor = this.$onChangeEditor.bind(this); + + this.session.on("change", this.updateOnChange); + this.session.on("changeFold", this.updateOnFold); + this.session.on("changeEditor", this.$onChangeEditor); + } + + (function() { + this.getRowLength = function(row) { + var h; + if (this.lineWidgets) + h = (this.lineWidgets[row] && this.lineWidgets[row].rowCount) || 0; + else h = 0; + if (!this.$useWrapMode || !this.$wrapData[row]) { + return 1 + h; + } else { + return this.$wrapData[row].length + 1 + h; + } + }; + + this.$getWidgetScreenLength = function() { + var screenRows = 0; + this.lineWidgets.forEach(function(w) { + if (w && w.rowCount && !w.hidden) screenRows += w.rowCount; + }); + return screenRows; + }; + + this.$onChangeEditor = function(e) { + this.attach(e.editor); + }; + + this.attach = function(editor) { + if (editor && editor.widgetManager && editor.widgetManager != this) + editor.widgetManager.detach(); + + if (this.editor == editor) return; + + this.detach(); + this.editor = editor; + + if (editor) { + editor.widgetManager = this; + editor.renderer.on("beforeRender", this.measureWidgets); + editor.renderer.on("afterRender", this.renderWidgets); + } + }; + this.detach = function(e) { + var editor = this.editor; + if (!editor) return; + + this.editor = null; + editor.widgetManager = null; + + editor.renderer.off("beforeRender", this.measureWidgets); + editor.renderer.off("afterRender", this.renderWidgets); + var lineWidgets = this.session.lineWidgets; + lineWidgets && + lineWidgets.forEach(function(w) { + if (w && w.el && w.el.parentNode) { + w._inDocument = false; + w.el.parentNode.removeChild(w.el); + } + }); + }; + + this.updateOnFold = function(e, session) { + var lineWidgets = session.lineWidgets; + if (!lineWidgets || !e.action) return; + var fold = e.data; + var start = fold.start.row; + var end = fold.end.row; + var hide = e.action == "add"; + for (var i = start + 1; i < end; i++) { + if (lineWidgets[i]) lineWidgets[i].hidden = hide; + } + if (lineWidgets[end]) { + if (hide) { + if (!lineWidgets[start]) lineWidgets[start] = lineWidgets[end]; + else lineWidgets[end].hidden = hide; + } else { + if (lineWidgets[start] == lineWidgets[end]) + lineWidgets[start] = undefined; + lineWidgets[end].hidden = hide; + } + } + }; + + this.updateOnChange = function(delta) { + var lineWidgets = this.session.lineWidgets; + if (!lineWidgets) return; + + var startRow = delta.start.row; + var len = delta.end.row - startRow; + + if (len === 0) { + } else if (delta.action == "remove") { + var removed = lineWidgets.splice(startRow + 1, len); + if (!lineWidgets[startRow] && removed[removed.length - 1]) { + lineWidgets[startRow] = removed.pop(); + } + removed.forEach(function(w) { + w && this.removeLineWidget(w); + }, this); + this.$updateRows(); + } else { + var args = new Array(len); + if (lineWidgets[startRow] && lineWidgets[startRow].column != null) { + if (delta.start.column > lineWidgets[startRow].column) startRow++; + } + args.unshift(startRow, 0); + lineWidgets.splice.apply(lineWidgets, args); + this.$updateRows(); + } + }; + + this.$updateRows = function() { + var lineWidgets = this.session.lineWidgets; + if (!lineWidgets) return; + var noWidgets = true; + lineWidgets.forEach(function(w, i) { + if (w) { + noWidgets = false; + w.row = i; + while (w.$oldWidget) { + w.$oldWidget.row = i; + w = w.$oldWidget; + } + } + }); + if (noWidgets) this.session.lineWidgets = null; + }; + + this.$registerLineWidget = function(w) { + if (!this.session.lineWidgets) + this.session.lineWidgets = new Array(this.session.getLength()); + + var old = this.session.lineWidgets[w.row]; + if (old) { + w.$oldWidget = old; + if (old.el && old.el.parentNode) { + old.el.parentNode.removeChild(old.el); + old._inDocument = false; + } + } + + this.session.lineWidgets[w.row] = w; + return w; + }; + + this.addLineWidget = function(w) { + this.$registerLineWidget(w); + w.session = this.session; + + if (!this.editor) return w; + + var renderer = this.editor.renderer; + if (w.html && !w.el) { + w.el = dom.createElement("div"); + w.el.innerHTML = w.html; + } + if (w.el) { + dom.addCssClass(w.el, "ace_lineWidgetContainer"); + w.el.style.position = "absolute"; + w.el.style.zIndex = 5; + renderer.container.appendChild(w.el); + w._inDocument = true; + + if (!w.coverGutter) { + w.el.style.zIndex = 3; + } + if (w.pixelHeight == null) { + w.pixelHeight = w.el.offsetHeight; + } + } + if (w.rowCount == null) { + w.rowCount = w.pixelHeight / renderer.layerConfig.lineHeight; + } + + var fold = this.session.getFoldAt(w.row, 0); + w.$fold = fold; + if (fold) { + var lineWidgets = this.session.lineWidgets; + if (w.row == fold.end.row && !lineWidgets[fold.start.row]) + lineWidgets[fold.start.row] = w; + else w.hidden = true; + } + + this.session._emit("changeFold", { data: { start: { row: w.row } } }); + + this.$updateRows(); + this.renderWidgets(null, renderer); + this.onWidgetChanged(w); + return w; + }; + + this.removeLineWidget = function(w) { + w._inDocument = false; + w.session = null; + if (w.el && w.el.parentNode) w.el.parentNode.removeChild(w.el); + if (w.editor && w.editor.destroy) + try { + w.editor.destroy(); + } catch (e) {} + if (this.session.lineWidgets) { + var w1 = this.session.lineWidgets[w.row]; + if (w1 == w) { + this.session.lineWidgets[w.row] = w.$oldWidget; + if (w.$oldWidget) this.onWidgetChanged(w.$oldWidget); + } else { + while (w1) { + if (w1.$oldWidget == w) { + w1.$oldWidget = w.$oldWidget; + break; + } + w1 = w1.$oldWidget; + } + } + } + this.session._emit("changeFold", { data: { start: { row: w.row } } }); + this.$updateRows(); + }; + + this.getWidgetsAtRow = function(row) { + var lineWidgets = this.session.lineWidgets; + var w = lineWidgets && lineWidgets[row]; + var list = []; + while (w) { + list.push(w); + w = w.$oldWidget; + } + return list; + }; + + this.onWidgetChanged = function(w) { + this.session._changedWidgets.push(w); + this.editor && this.editor.renderer.updateFull(); + }; + + this.measureWidgets = function(e, renderer) { + var changedWidgets = this.session._changedWidgets; + var config = renderer.layerConfig; + + if (!changedWidgets || !changedWidgets.length) return; + var min = Infinity; + for (var i = 0; i < changedWidgets.length; i++) { + var w = changedWidgets[i]; + if (!w || !w.el) continue; + if (w.session != this.session) continue; + if (!w._inDocument) { + if (this.session.lineWidgets[w.row] != w) continue; + w._inDocument = true; + renderer.container.appendChild(w.el); + } + + w.h = w.el.offsetHeight; + + if (!w.fixedWidth) { + w.w = w.el.offsetWidth; + w.screenWidth = Math.ceil(w.w / config.characterWidth); + } + + var rowCount = w.h / config.lineHeight; + if (w.coverLine) { + rowCount -= this.session.getRowLineCount(w.row); + if (rowCount < 0) rowCount = 0; + } + if (w.rowCount != rowCount) { + w.rowCount = rowCount; + if (w.row < min) min = w.row; + } + } + if (min != Infinity) { + this.session._emit("changeFold", { data: { start: { row: min } } }); + this.session.lineWidgetWidth = null; + } + this.session._changedWidgets = []; + }; + + this.renderWidgets = function(e, renderer) { + var config = renderer.layerConfig; + var lineWidgets = this.session.lineWidgets; + if (!lineWidgets) return; + var first = Math.min(this.firstRow, config.firstRow); + var last = Math.max(this.lastRow, config.lastRow, lineWidgets.length); + + while (first > 0 && !lineWidgets[first]) first--; + + this.firstRow = config.firstRow; + this.lastRow = config.lastRow; + + renderer.$cursorLayer.config = config; + for (var i = first; i <= last; i++) { + var w = lineWidgets[i]; + if (!w || !w.el) continue; + if (w.hidden) { + w.el.style.top = -100 - (w.pixelHeight || 0) + "px"; + continue; + } + if (!w._inDocument) { + w._inDocument = true; + renderer.container.appendChild(w.el); + } + var top = renderer.$cursorLayer.getPixelPosition( + { row: i, column: 0 }, + true + ).top; + if (!w.coverLine) + top += config.lineHeight * this.session.getRowLineCount(w.row); + w.el.style.top = top - config.offset + "px"; + + var left = w.coverGutter ? 0 : renderer.gutterWidth; + if (!w.fixedWidth) left -= renderer.scrollLeft; + w.el.style.left = left + "px"; + + if (w.fullWidth && w.screenWidth) { + w.el.style.minWidth = config.width + 2 * config.padding + "px"; + } + + if (w.fixedWidth) { + w.el.style.right = renderer.scrollBar.getWidth() + "px"; + } else { + w.el.style.right = ""; + } + } + }; + }.call(LineWidgets.prototype)); + + exports.LineWidgets = LineWidgets; + } +); + +ace.define( + "ace/ext/error_marker", + [ + "require", + "exports", + "module", + "ace/line_widgets", + "ace/lib/dom", + "ace/range" + ], + function(require, exports, module) { + "use strict"; + var LineWidgets = require("../line_widgets").LineWidgets; + var dom = require("../lib/dom"); + var Range = require("../range").Range; + + function binarySearch(array, needle, comparator) { + var first = 0; + var last = array.length - 1; + + while (first <= last) { + var mid = (first + last) >> 1; + var c = comparator(needle, array[mid]); + if (c > 0) first = mid + 1; + else if (c < 0) last = mid - 1; + else return mid; + } + return -(first + 1); + } + + function findAnnotations(session, row, dir) { + var annotations = session.getAnnotations().sort(Range.comparePoints); + if (!annotations.length) return; + + var i = binarySearch( + annotations, + { row: row, column: -1 }, + Range.comparePoints + ); + if (i < 0) i = -i - 1; + + if (i >= annotations.length) i = dir > 0 ? 0 : annotations.length - 1; + else if (i === 0 && dir < 0) i = annotations.length - 1; + + var annotation = annotations[i]; + if (!annotation || !dir) return; + + if (annotation.row === row) { + do { + annotation = annotations[(i += dir)]; + } while (annotation && annotation.row === row); + if (!annotation) return annotations.slice(); + } + + var matched = []; + row = annotation.row; + do { + matched[dir < 0 ? "unshift" : "push"](annotation); + annotation = annotations[(i += dir)]; + } while (annotation && annotation.row == row); + return matched.length && matched; + } + + exports.showErrorMarker = function(editor, dir) { + var session = editor.session; + if (!session.widgetManager) { + session.widgetManager = new LineWidgets(session); + session.widgetManager.attach(editor); + } + + var pos = editor.getCursorPosition(); + var row = pos.row; + var oldWidget = session.widgetManager + .getWidgetsAtRow(row) + .filter(function(w) { + return w.type == "errorMarker"; + })[0]; + if (oldWidget) { + oldWidget.destroy(); + } else { + row -= dir; + } + var annotations = findAnnotations(session, row, dir); + var gutterAnno; + if (annotations) { + var annotation = annotations[0]; + pos.column = + (annotation.pos && typeof annotation.column != "number" + ? annotation.pos.sc + : annotation.column) || 0; + pos.row = annotation.row; + gutterAnno = editor.renderer.$gutterLayer.$annotations[pos.row]; + } else if (oldWidget) { + return; + } else { + gutterAnno = { + text: ["Looks good!"], + className: "ace_ok" + }; + } + editor.session.unfold(pos.row); + editor.selection.moveToPosition(pos); + + var w = { + row: pos.row, + fixedWidth: true, + coverGutter: true, + el: dom.createElement("div"), + type: "errorMarker" + }; + var el = w.el.appendChild(dom.createElement("div")); + var arrow = w.el.appendChild(dom.createElement("div")); + arrow.className = "error_widget_arrow " + gutterAnno.className; + + var left = editor.renderer.$cursorLayer.getPixelPosition(pos).left; + arrow.style.left = left + editor.renderer.gutterWidth - 5 + "px"; + + w.el.className = "error_widget_wrapper"; + el.className = "error_widget " + gutterAnno.className; + el.innerHTML = gutterAnno.text.join("
"); + + el.appendChild(dom.createElement("div")); + + var kb = function(_, hashId, keyString) { + if (hashId === 0 && (keyString === "esc" || keyString === "return")) { + w.destroy(); + return { command: "null" }; + } + }; + + w.destroy = function() { + if (editor.$mouseHandler.isMousePressed) return; + editor.keyBinding.removeKeyboardHandler(kb); + session.widgetManager.removeLineWidget(w); + editor.off("changeSelection", w.destroy); + editor.off("changeSession", w.destroy); + editor.off("mouseup", w.destroy); + editor.off("change", w.destroy); + }; + + editor.keyBinding.addKeyboardHandler(kb); + editor.on("changeSelection", w.destroy); + editor.on("changeSession", w.destroy); + editor.on("mouseup", w.destroy); + editor.on("change", w.destroy); + + editor.session.widgetManager.addLineWidget(w); + + w.el.onmousedown = editor.focus.bind(editor); + + editor.renderer.scrollCursorIntoView(null, 0.5, { + bottom: w.el.offsetHeight + }); + }; + + dom.importCssString( + "\ + .error_widget_wrapper {\ + background: inherit;\ + color: inherit;\ + border:none\ + }\ + .error_widget {\ + border-top: solid 2px;\ + border-bottom: solid 2px;\ + margin: 5px 0;\ + padding: 10px 40px;\ + white-space: pre-wrap;\ + }\ + .error_widget.ace_error, .error_widget_arrow.ace_error{\ + border-color: #ff5a5a\ + }\ + .error_widget.ace_warning, .error_widget_arrow.ace_warning{\ + border-color: #F1D817\ + }\ + .error_widget.ace_info, .error_widget_arrow.ace_info{\ + border-color: #5a5a5a\ + }\ + .error_widget.ace_ok, .error_widget_arrow.ace_ok{\ + border-color: #5aaa5a\ + }\ + .error_widget_arrow {\ + position: absolute;\ + border: solid 5px;\ + border-top-color: transparent!important;\ + border-right-color: transparent!important;\ + border-left-color: transparent!important;\ + top: -5px;\ + }\ + ", + "" + ); + } +); + +ace.define( + "ace/ace", + [ + "require", + "exports", + "module", + "ace/lib/fixoldbrowsers", + "ace/lib/dom", + "ace/lib/event", + "ace/range", + "ace/editor", + "ace/edit_session", + "ace/undomanager", + "ace/virtual_renderer", + "ace/worker/worker_client", + "ace/keyboard/hash_handler", + "ace/placeholder", + "ace/multi_select", + "ace/mode/folding/fold_mode", + "ace/theme/textmate", + "ace/ext/error_marker", + "ace/config" + ], + function(require, exports, module) { + "use strict"; + + require("./lib/fixoldbrowsers"); + + var dom = require("./lib/dom"); + var event = require("./lib/event"); + + var Range = require("./range").Range; + var Editor = require("./editor").Editor; + var EditSession = require("./edit_session").EditSession; + var UndoManager = require("./undomanager").UndoManager; + var Renderer = require("./virtual_renderer").VirtualRenderer; + require("./worker/worker_client"); + require("./keyboard/hash_handler"); + require("./placeholder"); + require("./multi_select"); + require("./mode/folding/fold_mode"); + require("./theme/textmate"); + require("./ext/error_marker"); + + exports.config = require("./config"); + exports.require = require; + + if (typeof define === "function") exports.define = define; + exports.edit = function(el, options) { + if (typeof el == "string") { + var _id = el; + el = document.getElementById(_id); + if (!el) throw new Error("ace.edit can't find div #" + _id); + } + + if (el && el.env && el.env.editor instanceof Editor) return el.env.editor; + + var value = ""; + if (el && /input|textarea/i.test(el.tagName)) { + var oldNode = el; + value = oldNode.value; + el = dom.createElement("pre"); + oldNode.parentNode.replaceChild(el, oldNode); + } else if (el) { + value = el.textContent; + el.innerHTML = ""; + } + + var doc = exports.createEditSession(value); + + var editor = new Editor(new Renderer(el), doc, options); + + var env = { + document: doc, + editor: editor, + onResize: editor.resize.bind(editor, null) + }; + if (oldNode) env.textarea = oldNode; + event.addListener(window, "resize", env.onResize); + editor.on("destroy", function() { + event.removeListener(window, "resize", env.onResize); + env.editor.container.env = null; // prevent memory leak on old ie + }); + editor.container.env = editor.env = env; + return editor; + }; + exports.createEditSession = function(text, mode) { + var doc = new EditSession(text, mode); + doc.setUndoManager(new UndoManager()); + return doc; + }; + exports.Range = Range; + exports.Editor = Editor; + exports.EditSession = EditSession; + exports.UndoManager = UndoManager; + exports.VirtualRenderer = Renderer; + exports.version = exports.config.version; + } +); +(function() { + ace.require(["ace/ace"], function(a) { + if (a) { + a.config.init(true); + a.define = ace.define; + } + if (!window.ace) window.ace = a; + for (var key in a) if (a.hasOwnProperty(key)) window.ace[key] = a[key]; + window.ace["default"] = window.ace; + if (typeof module == "object" && typeof exports == "object" && module) { + module.exports = window.ace; + } + }); +})(); diff --git a/pandora_console/include/javascript/ace/mode-json.js b/pandora_console/include/javascript/ace/mode-json.js new file mode 100644 index 0000000000..89442986e7 --- /dev/null +++ b/pandora_console/include/javascript/ace/mode-json.js @@ -0,0 +1,396 @@ +ace.define( + "ace/mode/json_highlight_rules", + [ + "require", + "exports", + "module", + "ace/lib/oop", + "ace/mode/text_highlight_rules" + ], + function(require, exports, module) { + "use strict"; + + var oop = require("../lib/oop"); + var TextHighlightRules = require("./text_highlight_rules") + .TextHighlightRules; + + var JsonHighlightRules = function() { + this.$rules = { + start: [ + { + token: "variable", // single line + regex: '["](?:(?:\\\\.)|(?:[^"\\\\]))*?["]\\s*(?=:)' + }, + { + token: "string", // single line + regex: '"', + next: "string" + }, + { + token: "constant.numeric", // hex + regex: "0[xX][0-9a-fA-F]+\\b" + }, + { + token: "constant.numeric", // float + regex: "[+-]?\\d+(?:(?:\\.\\d*)?(?:[eE][+-]?\\d+)?)?\\b" + }, + { + token: "constant.language.boolean", + regex: "(?:true|false)\\b" + }, + { + token: "text", // single quoted strings are not allowed + regex: "['](?:(?:\\\\.)|(?:[^'\\\\]))*?[']" + }, + { + token: "comment", // comments are not allowed, but who cares? + regex: "\\/\\/.*$" + }, + { + token: "comment.start", // comments are not allowed, but who cares? + regex: "\\/\\*", + next: "comment" + }, + { + token: "paren.lparen", + regex: "[[({]" + }, + { + token: "paren.rparen", + regex: "[\\])}]" + }, + { + token: "text", + regex: "\\s+" + } + ], + string: [ + { + token: "constant.language.escape", + regex: /\\(?:x[0-9a-fA-F]{2}|u[0-9a-fA-F]{4}|["\\\/bfnrt])/ + }, + { + token: "string", + regex: '"|$', + next: "start" + }, + { + defaultToken: "string" + } + ], + comment: [ + { + token: "comment.end", // comments are not allowed, but who cares? + regex: "\\*\\/", + next: "start" + }, + { + defaultToken: "comment" + } + ] + }; + }; + + oop.inherits(JsonHighlightRules, TextHighlightRules); + + exports.JsonHighlightRules = JsonHighlightRules; + } +); + +ace.define( + "ace/mode/matching_brace_outdent", + ["require", "exports", "module", "ace/range"], + function(require, exports, module) { + "use strict"; + + var Range = require("../range").Range; + + var MatchingBraceOutdent = function() {}; + + (function() { + this.checkOutdent = function(line, input) { + if (!/^\s+$/.test(line)) return false; + + return /^\s*\}/.test(input); + }; + + this.autoOutdent = function(doc, row) { + var line = doc.getLine(row); + var match = line.match(/^(\s*\})/); + + if (!match) return 0; + + var column = match[1].length; + var openBracePos = doc.findMatchingBracket({ + row: row, + column: column + }); + + if (!openBracePos || openBracePos.row == row) return 0; + + var indent = this.$getIndent(doc.getLine(openBracePos.row)); + doc.replace(new Range(row, 0, row, column - 1), indent); + }; + + this.$getIndent = function(line) { + return line.match(/^\s*/)[0]; + }; + }.call(MatchingBraceOutdent.prototype)); + + exports.MatchingBraceOutdent = MatchingBraceOutdent; + } +); + +ace.define( + "ace/mode/folding/cstyle", + [ + "require", + "exports", + "module", + "ace/lib/oop", + "ace/range", + "ace/mode/folding/fold_mode" + ], + function(require, exports, module) { + "use strict"; + + var oop = require("../../lib/oop"); + var Range = require("../../range").Range; + var BaseFoldMode = require("./fold_mode").FoldMode; + + var FoldMode = (exports.FoldMode = function(commentRegex) { + if (commentRegex) { + this.foldingStartMarker = new RegExp( + this.foldingStartMarker.source.replace( + /\|[^|]*?$/, + "|" + commentRegex.start + ) + ); + this.foldingStopMarker = new RegExp( + this.foldingStopMarker.source.replace( + /\|[^|]*?$/, + "|" + commentRegex.end + ) + ); + } + }); + oop.inherits(FoldMode, BaseFoldMode); + + (function() { + this.foldingStartMarker = /([\{\[\(])[^\}\]\)]*$|^\s*(\/\*)/; + this.foldingStopMarker = /^[^\[\{\(]*([\}\]\)])|^[\s\*]*(\*\/)/; + this.singleLineBlockCommentRe = /^\s*(\/\*).*\*\/\s*$/; + this.tripleStarBlockCommentRe = /^\s*(\/\*\*\*).*\*\/\s*$/; + this.startRegionRe = /^\s*(\/\*|\/\/)#?region\b/; + this._getFoldWidgetBase = this.getFoldWidget; + this.getFoldWidget = function(session, foldStyle, row) { + var line = session.getLine(row); + + if (this.singleLineBlockCommentRe.test(line)) { + if ( + !this.startRegionRe.test(line) && + !this.tripleStarBlockCommentRe.test(line) + ) + return ""; + } + + var fw = this._getFoldWidgetBase(session, foldStyle, row); + + if (!fw && this.startRegionRe.test(line)) return "start"; // lineCommentRegionStart + + return fw; + }; + + this.getFoldWidgetRange = function( + session, + foldStyle, + row, + forceMultiline + ) { + var line = session.getLine(row); + + if (this.startRegionRe.test(line)) + return this.getCommentRegionBlock(session, line, row); + + var match = line.match(this.foldingStartMarker); + if (match) { + var i = match.index; + + if (match[1]) + return this.openingBracketBlock(session, match[1], row, i); + + var range = session.getCommentFoldRange(row, i + match[0].length, 1); + + if (range && !range.isMultiLine()) { + if (forceMultiline) { + range = this.getSectionRange(session, row); + } else if (foldStyle != "all") range = null; + } + + return range; + } + + if (foldStyle === "markbegin") return; + + var match = line.match(this.foldingStopMarker); + if (match) { + var i = match.index + match[0].length; + + if (match[1]) + return this.closingBracketBlock(session, match[1], row, i); + + return session.getCommentFoldRange(row, i, -1); + } + }; + + this.getSectionRange = function(session, row) { + var line = session.getLine(row); + var startIndent = line.search(/\S/); + var startRow = row; + var startColumn = line.length; + row = row + 1; + var endRow = row; + var maxRow = session.getLength(); + while (++row < maxRow) { + line = session.getLine(row); + var indent = line.search(/\S/); + if (indent === -1) continue; + if (startIndent > indent) break; + var subRange = this.getFoldWidgetRange(session, "all", row); + + if (subRange) { + if (subRange.start.row <= startRow) { + break; + } else if (subRange.isMultiLine()) { + row = subRange.end.row; + } else if (startIndent == indent) { + break; + } + } + endRow = row; + } + + return new Range( + startRow, + startColumn, + endRow, + session.getLine(endRow).length + ); + }; + this.getCommentRegionBlock = function(session, line, row) { + var startColumn = line.search(/\s*$/); + var maxRow = session.getLength(); + var startRow = row; + + var re = /^\s*(?:\/\*|\/\/|--)#?(end)?region\b/; + var depth = 1; + while (++row < maxRow) { + line = session.getLine(row); + var m = re.exec(line); + if (!m) continue; + if (m[1]) depth--; + else depth++; + + if (!depth) break; + } + + var endRow = row; + if (endRow > startRow) { + return new Range(startRow, startColumn, endRow, line.length); + } + }; + }.call(FoldMode.prototype)); + } +); + +ace.define( + "ace/mode/json", + [ + "require", + "exports", + "module", + "ace/lib/oop", + "ace/mode/text", + "ace/mode/json_highlight_rules", + "ace/mode/matching_brace_outdent", + "ace/mode/behaviour/cstyle", + "ace/mode/folding/cstyle", + "ace/worker/worker_client" + ], + function(require, exports, module) { + "use strict"; + + var oop = require("../lib/oop"); + var TextMode = require("./text").Mode; + var HighlightRules = require("./json_highlight_rules").JsonHighlightRules; + var MatchingBraceOutdent = require("./matching_brace_outdent") + .MatchingBraceOutdent; + var CstyleBehaviour = require("./behaviour/cstyle").CstyleBehaviour; + var CStyleFoldMode = require("./folding/cstyle").FoldMode; + var WorkerClient = require("../worker/worker_client").WorkerClient; + + var Mode = function() { + this.HighlightRules = HighlightRules; + this.$outdent = new MatchingBraceOutdent(); + this.$behaviour = new CstyleBehaviour(); + this.foldingRules = new CStyleFoldMode(); + }; + oop.inherits(Mode, TextMode); + + (function() { + this.lineCommentStart = "//"; + this.blockComment = { start: "/*", end: "*/" }; + + this.getNextLineIndent = function(state, line, tab) { + var indent = this.$getIndent(line); + + if (state == "start") { + var match = line.match(/^.*[\{\(\[]\s*$/); + if (match) { + indent += tab; + } + } + + return indent; + }; + + this.checkOutdent = function(state, line, input) { + return this.$outdent.checkOutdent(line, input); + }; + + this.autoOutdent = function(state, doc, row) { + this.$outdent.autoOutdent(doc, row); + }; + + this.createWorker = function(session) { + var worker = new WorkerClient( + ["ace"], + "ace/mode/json_worker", + "JsonWorker" + ); + worker.attachToDocument(session.getDocument()); + + worker.on("annotate", function(e) { + session.setAnnotations(e.data); + }); + + worker.on("terminate", function() { + session.clearAnnotations(); + }); + + return worker; + }; + + this.$id = "ace/mode/json"; + }.call(Mode.prototype)); + + exports.Mode = Mode; + } +); +(function() { + ace.require(["ace/mode/json"], function(m) { + if (typeof module == "object" && typeof exports == "object" && module) { + module.exports = m; + } + }); +})(); diff --git a/pandora_console/include/javascript/ace/textmate.css b/pandora_console/include/javascript/ace/textmate.css new file mode 100644 index 0000000000..ec2a96f4ea --- /dev/null +++ b/pandora_console/include/javascript/ace/textmate.css @@ -0,0 +1,154 @@ +.ace-tm .ace_gutter { + background: #f0f0f0; + color: #333; +} + +.ace-tm .ace_print-margin { + width: 1px; + background: #e8e8e8; +} + +.ace-tm .ace_fold { + background-color: #6b72e6; +} + +.ace-tm { + background-color: #ffffff; + color: black; +} + +.ace-tm .ace_cursor { + color: black; +} + +.ace-tm .ace_invisible { + color: rgb(191, 191, 191); +} + +.ace-tm .ace_storage, +.ace-tm .ace_keyword { + color: blue; +} + +.ace-tm .ace_constant { + color: rgb(197, 6, 11); +} + +.ace-tm .ace_constant.ace_buildin { + color: rgb(88, 72, 246); +} + +.ace-tm .ace_constant.ace_language { + color: rgb(88, 92, 246); +} + +.ace-tm .ace_constant.ace_library { + color: rgb(6, 150, 14); +} + +.ace-tm .ace_invalid { + background-color: rgba(255, 0, 0, 0.1); + color: red; +} + +.ace-tm .ace_support.ace_function { + color: rgb(60, 76, 114); +} + +.ace-tm .ace_support.ace_constant { + color: rgb(6, 150, 14); +} + +.ace-tm .ace_support.ace_type, +.ace-tm .ace_support.ace_class { + color: rgb(109, 121, 222); +} + +.ace-tm .ace_keyword.ace_operator { + color: rgb(104, 118, 135); +} + +.ace-tm .ace_string { + color: rgb(3, 106, 7); +} + +.ace-tm .ace_comment { + color: rgb(76, 136, 107); +} + +.ace-tm .ace_comment.ace_doc { + color: rgb(0, 102, 255); +} + +.ace-tm .ace_comment.ace_doc.ace_tag { + color: rgb(128, 159, 191); +} + +.ace-tm .ace_constant.ace_numeric { + color: rgb(0, 0, 205); +} + +.ace-tm .ace_variable { + color: rgb(49, 132, 149); +} + +.ace-tm .ace_xml-pe { + color: rgb(104, 104, 91); +} + +.ace-tm .ace_entity.ace_name.ace_function { + color: #0000a2; +} + +.ace-tm .ace_heading { + color: rgb(12, 7, 255); +} + +.ace-tm .ace_list { + color: rgb(185, 6, 144); +} + +.ace-tm .ace_meta.ace_tag { + color: rgb(0, 22, 142); +} + +.ace-tm .ace_string.ace_regex { + color: rgb(255, 0, 0); +} + +.ace-tm .ace_marker-layer .ace_selection { + background: rgb(181, 213, 255); +} +.ace-tm.ace_multiselect .ace_selection.ace_start { + box-shadow: 0 0 3px 0px white; +} +.ace-tm .ace_marker-layer .ace_step { + background: rgb(252, 255, 0); +} + +.ace-tm .ace_marker-layer .ace_stack { + background: rgb(164, 229, 101); +} + +.ace-tm .ace_marker-layer .ace_bracket { + margin: -1px 0 0 -1px; + border: 1px solid rgb(192, 192, 192); +} + +.ace-tm .ace_marker-layer .ace_active-line { + background: rgba(0, 0, 0, 0.07); +} + +.ace-tm .ace_gutter-active-line { + background-color: #dcdcdc; +} + +.ace-tm .ace_marker-layer .ace_selected-word { + background: rgb(250, 250, 255); + border: 1px solid rgb(200, 200, 250); +} + +.ace-tm .ace_indent-guide { + background: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAACCAYAAACZgbYnAAAAE0lEQVQImWP4////f4bLly//BwAmVgd1/w11/gAAAABJRU5ErkJggg==") + right repeat-y; +} diff --git a/pandora_console/include/javascript/ace/textmate.js b/pandora_console/include/javascript/ace/textmate.js new file mode 100644 index 0000000000..7ad45d8da2 --- /dev/null +++ b/pandora_console/include/javascript/ace/textmate.js @@ -0,0 +1,41 @@ +/* ***** BEGIN LICENSE BLOCK ***** + * Distributed under the BSD license: + * + * Copyright (c) 2010, Ajax.org B.V. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name of Ajax.org B.V. nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL AJAX.ORG B.V. BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * ***** END LICENSE BLOCK ***** */ + +define(function(require, exports, module) { + "use strict"; + + exports.isDark = false; + exports.cssClass = "ace-tm"; + exports.cssText = require("../requirejs/text!./textmate.css"); + exports.$id = "ace/theme/textmate"; + + var dom = require("../lib/dom"); + dom.importCssString(exports.cssText, exports.cssClass); +}); diff --git a/pandora_console/include/javascript/ace/worker-json.js b/pandora_console/include/javascript/ace/worker-json.js new file mode 100644 index 0000000000..998180b778 --- /dev/null +++ b/pandora_console/include/javascript/ace/worker-json.js @@ -0,0 +1,2468 @@ +"no use strict"; +!(function(window) { + if (typeof window.window != "undefined" && window.document) return; + if (window.require && window.define) return; + + if (!window.console) { + window.console = function() { + var msgs = Array.prototype.slice.call(arguments, 0); + postMessage({ type: "log", data: msgs }); + }; + window.console.error = window.console.warn = window.console.log = window.console.trace = + window.console; + } + window.window = window; + window.ace = window; + + window.onerror = function(message, file, line, col, err) { + postMessage({ + type: "error", + data: { + message: message, + data: err.data, + file: file, + line: line, + col: col, + stack: err.stack + } + }); + }; + + window.normalizeModule = function(parentId, moduleName) { + // normalize plugin requires + if (moduleName.indexOf("!") !== -1) { + var chunks = moduleName.split("!"); + return ( + window.normalizeModule(parentId, chunks[0]) + + "!" + + window.normalizeModule(parentId, chunks[1]) + ); + } + // normalize relative requires + if (moduleName.charAt(0) == ".") { + var base = parentId + .split("/") + .slice(0, -1) + .join("/"); + moduleName = (base ? base + "/" : "") + moduleName; + + while (moduleName.indexOf(".") !== -1 && previous != moduleName) { + var previous = moduleName; + moduleName = moduleName + .replace(/^\.\//, "") + .replace(/\/\.\//, "/") + .replace(/[^\/]+\/\.\.\//, ""); + } + } + + return moduleName; + }; + + window.require = function require(parentId, id) { + if (!id) { + id = parentId; + parentId = null; + } + if (!id.charAt) + throw new Error( + "worker.js require() accepts only (parentId, id) as arguments" + ); + + id = window.normalizeModule(parentId, id); + + var module = window.require.modules[id]; + if (module) { + if (!module.initialized) { + module.initialized = true; + module.exports = module.factory().exports; + } + return module.exports; + } + + if (!window.require.tlns) return console.log("unable to load " + id); + + var path = resolveModuleId(id, window.require.tlns); + if (path.slice(-3) != ".js") path += ".js"; + + window.require.id = id; + window.require.modules[id] = {}; // prevent infinite loop on broken modules + importScripts(path); + return window.require(parentId, id); + }; + function resolveModuleId(id, paths) { + var testPath = id, + tail = ""; + while (testPath) { + var alias = paths[testPath]; + if (typeof alias == "string") { + return alias + tail; + } else if (alias) { + return ( + alias.location.replace(/\/*$/, "/") + + (tail || alias.main || alias.name) + ); + } else if (alias === false) { + return ""; + } + var i = testPath.lastIndexOf("/"); + if (i === -1) break; + tail = testPath.substr(i) + tail; + testPath = testPath.slice(0, i); + } + return id; + } + window.require.modules = {}; + window.require.tlns = {}; + + window.define = function(id, deps, factory) { + if (arguments.length == 2) { + factory = deps; + if (typeof id != "string") { + deps = id; + id = window.require.id; + } + } else if (arguments.length == 1) { + factory = id; + deps = []; + id = window.require.id; + } + + if (typeof factory != "function") { + window.require.modules[id] = { + exports: factory, + initialized: true + }; + return; + } + + if (!deps.length) + // If there is no dependencies, we inject "require", "exports" and + // "module" as dependencies, to provide CommonJS compatibility. + deps = ["require", "exports", "module"]; + + var req = function(childId) { + return window.require(id, childId); + }; + + window.require.modules[id] = { + exports: {}, + factory: function() { + var module = this; + var returnExports = factory.apply( + this, + deps.slice(0, factory.length).map(function(dep) { + switch (dep) { + // Because "require", "exports" and "module" aren't actual + // dependencies, we must handle them seperately. + case "require": + return req; + case "exports": + return module.exports; + case "module": + return module; + // But for all other dependencies, we can just go ahead and + // require them. + default: + return req(dep); + } + }) + ); + if (returnExports) module.exports = returnExports; + return module; + } + }; + }; + window.define.amd = {}; + require.tlns = {}; + window.initBaseUrls = function initBaseUrls(topLevelNamespaces) { + for (var i in topLevelNamespaces) require.tlns[i] = topLevelNamespaces[i]; + }; + + window.initSender = function initSender() { + var EventEmitter = window.require("ace/lib/event_emitter").EventEmitter; + var oop = window.require("ace/lib/oop"); + + var Sender = function() {}; + + (function() { + oop.implement(this, EventEmitter); + + this.callback = function(data, callbackId) { + postMessage({ + type: "call", + id: callbackId, + data: data + }); + }; + + this.emit = function(name, data) { + postMessage({ + type: "event", + name: name, + data: data + }); + }; + }.call(Sender.prototype)); + + return new Sender(); + }; + + var main = (window.main = null); + var sender = (window.sender = null); + + window.onmessage = function(e) { + var msg = e.data; + if (msg.event && sender) { + sender._signal(msg.event, msg.data); + } else if (msg.command) { + if (main[msg.command]) main[msg.command].apply(main, msg.args); + else if (window[msg.command]) window[msg.command].apply(window, msg.args); + else throw new Error("Unknown command:" + msg.command); + } else if (msg.init) { + window.initBaseUrls(msg.tlns); + require("ace/lib/es5-shim"); + sender = window.sender = window.initSender(); + var clazz = require(msg.module)[msg.classname]; + main = window.main = new clazz(sender); + } + }; +})(this); + +ace.define("ace/lib/oop", [], function(require, exports, module) { + "use strict"; + + exports.inherits = function(ctor, superCtor) { + ctor.super_ = superCtor; + ctor.prototype = Object.create(superCtor.prototype, { + constructor: { + value: ctor, + enumerable: false, + writable: true, + configurable: true + } + }); + }; + + exports.mixin = function(obj, mixin) { + for (var key in mixin) { + obj[key] = mixin[key]; + } + return obj; + }; + + exports.implement = function(proto, mixin) { + exports.mixin(proto, mixin); + }; +}); + +ace.define("ace/range", [], function(require, exports, module) { + "use strict"; + var comparePoints = function(p1, p2) { + return p1.row - p2.row || p1.column - p2.column; + }; + var Range = function(startRow, startColumn, endRow, endColumn) { + this.start = { + row: startRow, + column: startColumn + }; + + this.end = { + row: endRow, + column: endColumn + }; + }; + + (function() { + this.isEqual = function(range) { + return ( + this.start.row === range.start.row && + this.end.row === range.end.row && + this.start.column === range.start.column && + this.end.column === range.end.column + ); + }; + this.toString = function() { + return ( + "Range: [" + + this.start.row + + "/" + + this.start.column + + "] -> [" + + this.end.row + + "/" + + this.end.column + + "]" + ); + }; + + this.contains = function(row, column) { + return this.compare(row, column) == 0; + }; + this.compareRange = function(range) { + var cmp, + end = range.end, + start = range.start; + + cmp = this.compare(end.row, end.column); + if (cmp == 1) { + cmp = this.compare(start.row, start.column); + if (cmp == 1) { + return 2; + } else if (cmp == 0) { + return 1; + } else { + return 0; + } + } else if (cmp == -1) { + return -2; + } else { + cmp = this.compare(start.row, start.column); + if (cmp == -1) { + return -1; + } else if (cmp == 1) { + return 42; + } else { + return 0; + } + } + }; + this.comparePoint = function(p) { + return this.compare(p.row, p.column); + }; + this.containsRange = function(range) { + return ( + this.comparePoint(range.start) == 0 && this.comparePoint(range.end) == 0 + ); + }; + this.intersects = function(range) { + var cmp = this.compareRange(range); + return cmp == -1 || cmp == 0 || cmp == 1; + }; + this.isEnd = function(row, column) { + return this.end.row == row && this.end.column == column; + }; + this.isStart = function(row, column) { + return this.start.row == row && this.start.column == column; + }; + this.setStart = function(row, column) { + if (typeof row == "object") { + this.start.column = row.column; + this.start.row = row.row; + } else { + this.start.row = row; + this.start.column = column; + } + }; + this.setEnd = function(row, column) { + if (typeof row == "object") { + this.end.column = row.column; + this.end.row = row.row; + } else { + this.end.row = row; + this.end.column = column; + } + }; + this.inside = function(row, column) { + if (this.compare(row, column) == 0) { + if (this.isEnd(row, column) || this.isStart(row, column)) { + return false; + } else { + return true; + } + } + return false; + }; + this.insideStart = function(row, column) { + if (this.compare(row, column) == 0) { + if (this.isEnd(row, column)) { + return false; + } else { + return true; + } + } + return false; + }; + this.insideEnd = function(row, column) { + if (this.compare(row, column) == 0) { + if (this.isStart(row, column)) { + return false; + } else { + return true; + } + } + return false; + }; + this.compare = function(row, column) { + if (!this.isMultiLine()) { + if (row === this.start.row) { + return column < this.start.column + ? -1 + : column > this.end.column + ? 1 + : 0; + } + } + + if (row < this.start.row) return -1; + + if (row > this.end.row) return 1; + + if (this.start.row === row) return column >= this.start.column ? 0 : -1; + + if (this.end.row === row) return column <= this.end.column ? 0 : 1; + + return 0; + }; + this.compareStart = function(row, column) { + if (this.start.row == row && this.start.column == column) { + return -1; + } else { + return this.compare(row, column); + } + }; + this.compareEnd = function(row, column) { + if (this.end.row == row && this.end.column == column) { + return 1; + } else { + return this.compare(row, column); + } + }; + this.compareInside = function(row, column) { + if (this.end.row == row && this.end.column == column) { + return 1; + } else if (this.start.row == row && this.start.column == column) { + return -1; + } else { + return this.compare(row, column); + } + }; + this.clipRows = function(firstRow, lastRow) { + if (this.end.row > lastRow) var end = { row: lastRow + 1, column: 0 }; + else if (this.end.row < firstRow) var end = { row: firstRow, column: 0 }; + + if (this.start.row > lastRow) var start = { row: lastRow + 1, column: 0 }; + else if (this.start.row < firstRow) + var start = { row: firstRow, column: 0 }; + + return Range.fromPoints(start || this.start, end || this.end); + }; + this.extend = function(row, column) { + var cmp = this.compare(row, column); + + if (cmp == 0) return this; + else if (cmp == -1) var start = { row: row, column: column }; + else var end = { row: row, column: column }; + + return Range.fromPoints(start || this.start, end || this.end); + }; + + this.isEmpty = function() { + return ( + this.start.row === this.end.row && this.start.column === this.end.column + ); + }; + this.isMultiLine = function() { + return this.start.row !== this.end.row; + }; + this.clone = function() { + return Range.fromPoints(this.start, this.end); + }; + this.collapseRows = function() { + if (this.end.column == 0) + return new Range( + this.start.row, + 0, + Math.max(this.start.row, this.end.row - 1), + 0 + ); + else return new Range(this.start.row, 0, this.end.row, 0); + }; + this.toScreenRange = function(session) { + var screenPosStart = session.documentToScreenPosition(this.start); + var screenPosEnd = session.documentToScreenPosition(this.end); + + return new Range( + screenPosStart.row, + screenPosStart.column, + screenPosEnd.row, + screenPosEnd.column + ); + }; + this.moveBy = function(row, column) { + this.start.row += row; + this.start.column += column; + this.end.row += row; + this.end.column += column; + }; + }.call(Range.prototype)); + Range.fromPoints = function(start, end) { + return new Range(start.row, start.column, end.row, end.column); + }; + Range.comparePoints = comparePoints; + + Range.comparePoints = function(p1, p2) { + return p1.row - p2.row || p1.column - p2.column; + }; + + exports.Range = Range; +}); + +ace.define("ace/apply_delta", [], function(require, exports, module) { + "use strict"; + + function throwDeltaError(delta, errorText) { + console.log("Invalid Delta:", delta); + throw "Invalid Delta: " + errorText; + } + + function positionInDocument(docLines, position) { + return ( + position.row >= 0 && + position.row < docLines.length && + position.column >= 0 && + position.column <= docLines[position.row].length + ); + } + + function validateDelta(docLines, delta) { + if (delta.action != "insert" && delta.action != "remove") + throwDeltaError(delta, "delta.action must be 'insert' or 'remove'"); + if (!(delta.lines instanceof Array)) + throwDeltaError(delta, "delta.lines must be an Array"); + if (!delta.start || !delta.end) + throwDeltaError(delta, "delta.start/end must be an present"); + var start = delta.start; + if (!positionInDocument(docLines, delta.start)) + throwDeltaError(delta, "delta.start must be contained in document"); + var end = delta.end; + if (delta.action == "remove" && !positionInDocument(docLines, end)) + throwDeltaError( + delta, + "delta.end must contained in document for 'remove' actions" + ); + var numRangeRows = end.row - start.row; + var numRangeLastLineChars = + end.column - (numRangeRows == 0 ? start.column : 0); + if ( + numRangeRows != delta.lines.length - 1 || + delta.lines[numRangeRows].length != numRangeLastLineChars + ) + throwDeltaError(delta, "delta.range must match delta lines"); + } + + exports.applyDelta = function(docLines, delta, doNotValidate) { + var row = delta.start.row; + var startColumn = delta.start.column; + var line = docLines[row] || ""; + switch (delta.action) { + case "insert": + var lines = delta.lines; + if (lines.length === 1) { + docLines[row] = + line.substring(0, startColumn) + + delta.lines[0] + + line.substring(startColumn); + } else { + var args = [row, 1].concat(delta.lines); + docLines.splice.apply(docLines, args); + docLines[row] = line.substring(0, startColumn) + docLines[row]; + docLines[row + delta.lines.length - 1] += line.substring(startColumn); + } + break; + case "remove": + var endColumn = delta.end.column; + var endRow = delta.end.row; + if (row === endRow) { + docLines[row] = + line.substring(0, startColumn) + line.substring(endColumn); + } else { + docLines.splice( + row, + endRow - row + 1, + line.substring(0, startColumn) + + docLines[endRow].substring(endColumn) + ); + } + break; + } + }; +}); + +ace.define("ace/lib/event_emitter", [], function(require, exports, module) { + "use strict"; + + var EventEmitter = {}; + var stopPropagation = function() { + this.propagationStopped = true; + }; + var preventDefault = function() { + this.defaultPrevented = true; + }; + + EventEmitter._emit = EventEmitter._dispatchEvent = function(eventName, e) { + this._eventRegistry || (this._eventRegistry = {}); + this._defaultHandlers || (this._defaultHandlers = {}); + + var listeners = this._eventRegistry[eventName] || []; + var defaultHandler = this._defaultHandlers[eventName]; + if (!listeners.length && !defaultHandler) return; + + if (typeof e != "object" || !e) e = {}; + + if (!e.type) e.type = eventName; + if (!e.stopPropagation) e.stopPropagation = stopPropagation; + if (!e.preventDefault) e.preventDefault = preventDefault; + + listeners = listeners.slice(); + for (var i = 0; i < listeners.length; i++) { + listeners[i](e, this); + if (e.propagationStopped) break; + } + + if (defaultHandler && !e.defaultPrevented) return defaultHandler(e, this); + }; + + EventEmitter._signal = function(eventName, e) { + var listeners = (this._eventRegistry || {})[eventName]; + if (!listeners) return; + listeners = listeners.slice(); + for (var i = 0; i < listeners.length; i++) listeners[i](e, this); + }; + + EventEmitter.once = function(eventName, callback) { + var _self = this; + this.on(eventName, function newCallback() { + _self.off(eventName, newCallback); + callback.apply(null, arguments); + }); + if (!callback) { + return new Promise(function(resolve) { + callback = resolve; + }); + } + }; + + EventEmitter.setDefaultHandler = function(eventName, callback) { + var handlers = this._defaultHandlers; + if (!handlers) handlers = this._defaultHandlers = { _disabled_: {} }; + + if (handlers[eventName]) { + var old = handlers[eventName]; + var disabled = handlers._disabled_[eventName]; + if (!disabled) handlers._disabled_[eventName] = disabled = []; + disabled.push(old); + var i = disabled.indexOf(callback); + if (i != -1) disabled.splice(i, 1); + } + handlers[eventName] = callback; + }; + EventEmitter.removeDefaultHandler = function(eventName, callback) { + var handlers = this._defaultHandlers; + if (!handlers) return; + var disabled = handlers._disabled_[eventName]; + + if (handlers[eventName] == callback) { + if (disabled) this.setDefaultHandler(eventName, disabled.pop()); + } else if (disabled) { + var i = disabled.indexOf(callback); + if (i != -1) disabled.splice(i, 1); + } + }; + + EventEmitter.on = EventEmitter.addEventListener = function( + eventName, + callback, + capturing + ) { + this._eventRegistry = this._eventRegistry || {}; + + var listeners = this._eventRegistry[eventName]; + if (!listeners) listeners = this._eventRegistry[eventName] = []; + + if (listeners.indexOf(callback) == -1) + listeners[capturing ? "unshift" : "push"](callback); + return callback; + }; + + EventEmitter.off = EventEmitter.removeListener = EventEmitter.removeEventListener = function( + eventName, + callback + ) { + this._eventRegistry = this._eventRegistry || {}; + + var listeners = this._eventRegistry[eventName]; + if (!listeners) return; + + var index = listeners.indexOf(callback); + if (index !== -1) listeners.splice(index, 1); + }; + + EventEmitter.removeAllListeners = function(eventName) { + if (!eventName) this._eventRegistry = this._defaultHandlers = undefined; + if (this._eventRegistry) this._eventRegistry[eventName] = undefined; + if (this._defaultHandlers) this._defaultHandlers[eventName] = undefined; + }; + + exports.EventEmitter = EventEmitter; +}); + +ace.define("ace/anchor", [], function(require, exports, module) { + "use strict"; + + var oop = require("./lib/oop"); + var EventEmitter = require("./lib/event_emitter").EventEmitter; + + var Anchor = (exports.Anchor = function(doc, row, column) { + this.$onChange = this.onChange.bind(this); + this.attach(doc); + + if (typeof column == "undefined") this.setPosition(row.row, row.column); + else this.setPosition(row, column); + }); + + (function() { + oop.implement(this, EventEmitter); + this.getPosition = function() { + return this.$clipPositionToDocument(this.row, this.column); + }; + this.getDocument = function() { + return this.document; + }; + this.$insertRight = false; + this.onChange = function(delta) { + if (delta.start.row == delta.end.row && delta.start.row != this.row) + return; + + if (delta.start.row > this.row) return; + + var point = $getTransformedPoint( + delta, + { row: this.row, column: this.column }, + this.$insertRight + ); + this.setPosition(point.row, point.column, true); + }; + + function $pointsInOrder(point1, point2, equalPointsInOrder) { + var bColIsAfter = equalPointsInOrder + ? point1.column <= point2.column + : point1.column < point2.column; + return ( + point1.row < point2.row || (point1.row == point2.row && bColIsAfter) + ); + } + + function $getTransformedPoint(delta, point, moveIfEqual) { + var deltaIsInsert = delta.action == "insert"; + var deltaRowShift = + (deltaIsInsert ? 1 : -1) * (delta.end.row - delta.start.row); + var deltaColShift = + (deltaIsInsert ? 1 : -1) * (delta.end.column - delta.start.column); + var deltaStart = delta.start; + var deltaEnd = deltaIsInsert ? deltaStart : delta.end; // Collapse insert range. + if ($pointsInOrder(point, deltaStart, moveIfEqual)) { + return { + row: point.row, + column: point.column + }; + } + if ($pointsInOrder(deltaEnd, point, !moveIfEqual)) { + return { + row: point.row + deltaRowShift, + column: point.column + (point.row == deltaEnd.row ? deltaColShift : 0) + }; + } + + return { + row: deltaStart.row, + column: deltaStart.column + }; + } + this.setPosition = function(row, column, noClip) { + var pos; + if (noClip) { + pos = { + row: row, + column: column + }; + } else { + pos = this.$clipPositionToDocument(row, column); + } + + if (this.row == pos.row && this.column == pos.column) return; + + var old = { + row: this.row, + column: this.column + }; + + this.row = pos.row; + this.column = pos.column; + this._signal("change", { + old: old, + value: pos + }); + }; + this.detach = function() { + this.document.off("change", this.$onChange); + }; + this.attach = function(doc) { + this.document = doc || this.document; + this.document.on("change", this.$onChange); + }; + this.$clipPositionToDocument = function(row, column) { + var pos = {}; + + if (row >= this.document.getLength()) { + pos.row = Math.max(0, this.document.getLength() - 1); + pos.column = this.document.getLine(pos.row).length; + } else if (row < 0) { + pos.row = 0; + pos.column = 0; + } else { + pos.row = row; + pos.column = Math.min( + this.document.getLine(pos.row).length, + Math.max(0, column) + ); + } + + if (column < 0) pos.column = 0; + + return pos; + }; + }.call(Anchor.prototype)); +}); + +ace.define("ace/document", [], function(require, exports, module) { + "use strict"; + + var oop = require("./lib/oop"); + var applyDelta = require("./apply_delta").applyDelta; + var EventEmitter = require("./lib/event_emitter").EventEmitter; + var Range = require("./range").Range; + var Anchor = require("./anchor").Anchor; + + var Document = function(textOrLines) { + this.$lines = [""]; + if (textOrLines.length === 0) { + this.$lines = [""]; + } else if (Array.isArray(textOrLines)) { + this.insertMergedLines({ row: 0, column: 0 }, textOrLines); + } else { + this.insert({ row: 0, column: 0 }, textOrLines); + } + }; + + (function() { + oop.implement(this, EventEmitter); + this.setValue = function(text) { + var len = this.getLength() - 1; + this.remove(new Range(0, 0, len, this.getLine(len).length)); + this.insert({ row: 0, column: 0 }, text); + }; + this.getValue = function() { + return this.getAllLines().join(this.getNewLineCharacter()); + }; + this.createAnchor = function(row, column) { + return new Anchor(this, row, column); + }; + if ("aaa".split(/a/).length === 0) { + this.$split = function(text) { + return text.replace(/\r\n|\r/g, "\n").split("\n"); + }; + } else { + this.$split = function(text) { + return text.split(/\r\n|\r|\n/); + }; + } + + this.$detectNewLine = function(text) { + var match = text.match(/^.*?(\r\n|\r|\n)/m); + this.$autoNewLine = match ? match[1] : "\n"; + this._signal("changeNewLineMode"); + }; + this.getNewLineCharacter = function() { + switch (this.$newLineMode) { + case "windows": + return "\r\n"; + case "unix": + return "\n"; + default: + return this.$autoNewLine || "\n"; + } + }; + + this.$autoNewLine = ""; + this.$newLineMode = "auto"; + this.setNewLineMode = function(newLineMode) { + if (this.$newLineMode === newLineMode) return; + + this.$newLineMode = newLineMode; + this._signal("changeNewLineMode"); + }; + this.getNewLineMode = function() { + return this.$newLineMode; + }; + this.isNewLine = function(text) { + return text == "\r\n" || text == "\r" || text == "\n"; + }; + this.getLine = function(row) { + return this.$lines[row] || ""; + }; + this.getLines = function(firstRow, lastRow) { + return this.$lines.slice(firstRow, lastRow + 1); + }; + this.getAllLines = function() { + return this.getLines(0, this.getLength()); + }; + this.getLength = function() { + return this.$lines.length; + }; + this.getTextRange = function(range) { + return this.getLinesForRange(range).join(this.getNewLineCharacter()); + }; + this.getLinesForRange = function(range) { + var lines; + if (range.start.row === range.end.row) { + lines = [ + this.getLine(range.start.row).substring( + range.start.column, + range.end.column + ) + ]; + } else { + lines = this.getLines(range.start.row, range.end.row); + lines[0] = (lines[0] || "").substring(range.start.column); + var l = lines.length - 1; + if (range.end.row - range.start.row == l) + lines[l] = lines[l].substring(0, range.end.column); + } + return lines; + }; + this.insertLines = function(row, lines) { + console.warn( + "Use of document.insertLines is deprecated. Use the insertFullLines method instead." + ); + return this.insertFullLines(row, lines); + }; + this.removeLines = function(firstRow, lastRow) { + console.warn( + "Use of document.removeLines is deprecated. Use the removeFullLines method instead." + ); + return this.removeFullLines(firstRow, lastRow); + }; + this.insertNewLine = function(position) { + console.warn( + "Use of document.insertNewLine is deprecated. Use insertMergedLines(position, ['', '']) instead." + ); + return this.insertMergedLines(position, ["", ""]); + }; + this.insert = function(position, text) { + if (this.getLength() <= 1) this.$detectNewLine(text); + + return this.insertMergedLines(position, this.$split(text)); + }; + this.insertInLine = function(position, text) { + var start = this.clippedPos(position.row, position.column); + var end = this.pos(position.row, position.column + text.length); + + this.applyDelta( + { + start: start, + end: end, + action: "insert", + lines: [text] + }, + true + ); + + return this.clonePos(end); + }; + + this.clippedPos = function(row, column) { + var length = this.getLength(); + if (row === undefined) { + row = length; + } else if (row < 0) { + row = 0; + } else if (row >= length) { + row = length - 1; + column = undefined; + } + var line = this.getLine(row); + if (column == undefined) column = line.length; + column = Math.min(Math.max(column, 0), line.length); + return { row: row, column: column }; + }; + + this.clonePos = function(pos) { + return { row: pos.row, column: pos.column }; + }; + + this.pos = function(row, column) { + return { row: row, column: column }; + }; + + this.$clipPosition = function(position) { + var length = this.getLength(); + if (position.row >= length) { + position.row = Math.max(0, length - 1); + position.column = this.getLine(length - 1).length; + } else { + position.row = Math.max(0, position.row); + position.column = Math.min( + Math.max(position.column, 0), + this.getLine(position.row).length + ); + } + return position; + }; + this.insertFullLines = function(row, lines) { + row = Math.min(Math.max(row, 0), this.getLength()); + var column = 0; + if (row < this.getLength()) { + lines = lines.concat([""]); + column = 0; + } else { + lines = [""].concat(lines); + row--; + column = this.$lines[row].length; + } + this.insertMergedLines({ row: row, column: column }, lines); + }; + this.insertMergedLines = function(position, lines) { + var start = this.clippedPos(position.row, position.column); + var end = { + row: start.row + lines.length - 1, + column: + (lines.length == 1 ? start.column : 0) + + lines[lines.length - 1].length + }; + + this.applyDelta({ + start: start, + end: end, + action: "insert", + lines: lines + }); + + return this.clonePos(end); + }; + this.remove = function(range) { + var start = this.clippedPos(range.start.row, range.start.column); + var end = this.clippedPos(range.end.row, range.end.column); + this.applyDelta({ + start: start, + end: end, + action: "remove", + lines: this.getLinesForRange({ start: start, end: end }) + }); + return this.clonePos(start); + }; + this.removeInLine = function(row, startColumn, endColumn) { + var start = this.clippedPos(row, startColumn); + var end = this.clippedPos(row, endColumn); + + this.applyDelta( + { + start: start, + end: end, + action: "remove", + lines: this.getLinesForRange({ start: start, end: end }) + }, + true + ); + + return this.clonePos(start); + }; + this.removeFullLines = function(firstRow, lastRow) { + firstRow = Math.min(Math.max(0, firstRow), this.getLength() - 1); + lastRow = Math.min(Math.max(0, lastRow), this.getLength() - 1); + var deleteFirstNewLine = lastRow == this.getLength() - 1 && firstRow > 0; + var deleteLastNewLine = lastRow < this.getLength() - 1; + var startRow = deleteFirstNewLine ? firstRow - 1 : firstRow; + var startCol = deleteFirstNewLine ? this.getLine(startRow).length : 0; + var endRow = deleteLastNewLine ? lastRow + 1 : lastRow; + var endCol = deleteLastNewLine ? 0 : this.getLine(endRow).length; + var range = new Range(startRow, startCol, endRow, endCol); + var deletedLines = this.$lines.slice(firstRow, lastRow + 1); + + this.applyDelta({ + start: range.start, + end: range.end, + action: "remove", + lines: this.getLinesForRange(range) + }); + return deletedLines; + }; + this.removeNewLine = function(row) { + if (row < this.getLength() - 1 && row >= 0) { + this.applyDelta({ + start: this.pos(row, this.getLine(row).length), + end: this.pos(row + 1, 0), + action: "remove", + lines: ["", ""] + }); + } + }; + this.replace = function(range, text) { + if (!(range instanceof Range)) + range = Range.fromPoints(range.start, range.end); + if (text.length === 0 && range.isEmpty()) return range.start; + if (text == this.getTextRange(range)) return range.end; + + this.remove(range); + var end; + if (text) { + end = this.insert(range.start, text); + } else { + end = range.start; + } + + return end; + }; + this.applyDeltas = function(deltas) { + for (var i = 0; i < deltas.length; i++) { + this.applyDelta(deltas[i]); + } + }; + this.revertDeltas = function(deltas) { + for (var i = deltas.length - 1; i >= 0; i--) { + this.revertDelta(deltas[i]); + } + }; + this.applyDelta = function(delta, doNotValidate) { + var isInsert = delta.action == "insert"; + if ( + isInsert + ? delta.lines.length <= 1 && !delta.lines[0] + : !Range.comparePoints(delta.start, delta.end) + ) { + return; + } + + if (isInsert && delta.lines.length > 20000) { + this.$splitAndapplyLargeDelta(delta, 20000); + } else { + applyDelta(this.$lines, delta, doNotValidate); + this._signal("change", delta); + } + }; + + this.$safeApplyDelta = function(delta) { + var docLength = this.$lines.length; + if ( + (delta.action == "remove" && + delta.start.row < docLength && + delta.end.row < docLength) || + (delta.action == "insert" && delta.start.row <= docLength) + ) { + this.applyDelta(delta); + } + }; + + this.$splitAndapplyLargeDelta = function(delta, MAX) { + var lines = delta.lines; + var l = lines.length - MAX + 1; + var row = delta.start.row; + var column = delta.start.column; + for (var from = 0, to = 0; from < l; from = to) { + to += MAX - 1; + var chunk = lines.slice(from, to); + chunk.push(""); + this.applyDelta( + { + start: this.pos(row + from, column), + end: this.pos(row + to, (column = 0)), + action: delta.action, + lines: chunk + }, + true + ); + } + delta.lines = lines.slice(from); + delta.start.row = row + from; + delta.start.column = column; + this.applyDelta(delta, true); + }; + this.revertDelta = function(delta) { + this.$safeApplyDelta({ + start: this.clonePos(delta.start), + end: this.clonePos(delta.end), + action: delta.action == "insert" ? "remove" : "insert", + lines: delta.lines.slice() + }); + }; + this.indexToPosition = function(index, startRow) { + var lines = this.$lines || this.getAllLines(); + var newlineLength = this.getNewLineCharacter().length; + for (var i = startRow || 0, l = lines.length; i < l; i++) { + index -= lines[i].length + newlineLength; + if (index < 0) + return { row: i, column: index + lines[i].length + newlineLength }; + } + return { + row: l - 1, + column: index + lines[l - 1].length + newlineLength + }; + }; + this.positionToIndex = function(pos, startRow) { + var lines = this.$lines || this.getAllLines(); + var newlineLength = this.getNewLineCharacter().length; + var index = 0; + var row = Math.min(pos.row, lines.length); + for (var i = startRow || 0; i < row; ++i) + index += lines[i].length + newlineLength; + + return index + pos.column; + }; + }.call(Document.prototype)); + + exports.Document = Document; +}); + +ace.define("ace/lib/lang", [], function(require, exports, module) { + "use strict"; + + exports.last = function(a) { + return a[a.length - 1]; + }; + + exports.stringReverse = function(string) { + return string + .split("") + .reverse() + .join(""); + }; + + exports.stringRepeat = function(string, count) { + var result = ""; + while (count > 0) { + if (count & 1) result += string; + + if ((count >>= 1)) string += string; + } + return result; + }; + + var trimBeginRegexp = /^\s\s*/; + var trimEndRegexp = /\s\s*$/; + + exports.stringTrimLeft = function(string) { + return string.replace(trimBeginRegexp, ""); + }; + + exports.stringTrimRight = function(string) { + return string.replace(trimEndRegexp, ""); + }; + + exports.copyObject = function(obj) { + var copy = {}; + for (var key in obj) { + copy[key] = obj[key]; + } + return copy; + }; + + exports.copyArray = function(array) { + var copy = []; + for (var i = 0, l = array.length; i < l; i++) { + if (array[i] && typeof array[i] == "object") + copy[i] = this.copyObject(array[i]); + else copy[i] = array[i]; + } + return copy; + }; + + exports.deepCopy = function deepCopy(obj) { + if (typeof obj !== "object" || !obj) return obj; + var copy; + if (Array.isArray(obj)) { + copy = []; + for (var key = 0; key < obj.length; key++) { + copy[key] = deepCopy(obj[key]); + } + return copy; + } + if (Object.prototype.toString.call(obj) !== "[object Object]") return obj; + + copy = {}; + for (var key in obj) copy[key] = deepCopy(obj[key]); + return copy; + }; + + exports.arrayToMap = function(arr) { + var map = {}; + for (var i = 0; i < arr.length; i++) { + map[arr[i]] = 1; + } + return map; + }; + + exports.createMap = function(props) { + var map = Object.create(null); + for (var i in props) { + map[i] = props[i]; + } + return map; + }; + exports.arrayRemove = function(array, value) { + for (var i = 0; i <= array.length; i++) { + if (value === array[i]) { + array.splice(i, 1); + } + } + }; + + exports.escapeRegExp = function(str) { + return str.replace(/([.*+?^${}()|[\]\/\\])/g, "\\$1"); + }; + + exports.escapeHTML = function(str) { + return ("" + str) + .replace(/&/g, "&") + .replace(/"/g, """) + .replace(/'/g, "'") + .replace(/= "0" && ch <= "9") { + string += ch; + next(); + } + if (ch === ".") { + string += "."; + while (next() && ch >= "0" && ch <= "9") { + string += ch; + } + } + if (ch === "e" || ch === "E") { + string += ch; + next(); + if (ch === "-" || ch === "+") { + string += ch; + next(); + } + while (ch >= "0" && ch <= "9") { + string += ch; + next(); + } + } + number = +string; + if (isNaN(number)) { + error("Bad number"); + } else { + return number; + } + }, + string = function() { + var hex, + i, + string = "", + uffff; + + if (ch === '"') { + while (next()) { + if (ch === '"') { + next(); + return string; + } else if (ch === "\\") { + next(); + if (ch === "u") { + uffff = 0; + for (i = 0; i < 4; i += 1) { + hex = parseInt(next(), 16); + if (!isFinite(hex)) { + break; + } + uffff = uffff * 16 + hex; + } + string += String.fromCharCode(uffff); + } else if (typeof escapee[ch] === "string") { + string += escapee[ch]; + } else { + break; + } + } else if (ch == "\n" || ch == "\r") { + break; + } else { + string += ch; + } + } + } + error("Bad string"); + }, + white = function() { + while (ch && ch <= " ") { + next(); + } + }, + word = function() { + switch (ch) { + case "t": + next("t"); + next("r"); + next("u"); + next("e"); + return true; + case "f": + next("f"); + next("a"); + next("l"); + next("s"); + next("e"); + return false; + case "n": + next("n"); + next("u"); + next("l"); + next("l"); + return null; + } + error("Unexpected '" + ch + "'"); + }, + value, // Place holder for the value function. + array = function() { + var array = []; + + if (ch === "[") { + next("["); + white(); + if (ch === "]") { + next("]"); + return array; // empty array + } + while (ch) { + array.push(value()); + white(); + if (ch === "]") { + next("]"); + return array; + } + next(","); + white(); + } + } + error("Bad array"); + }, + object = function() { + var key, + object = {}; + + if (ch === "{") { + next("{"); + white(); + if (ch === "}") { + next("}"); + return object; // empty object + } + while (ch) { + key = string(); + white(); + next(":"); + if (Object.hasOwnProperty.call(object, key)) { + error('Duplicate key "' + key + '"'); + } + object[key] = value(); + white(); + if (ch === "}") { + next("}"); + return object; + } + next(","); + white(); + } + } + error("Bad object"); + }; + + value = function() { + white(); + switch (ch) { + case "{": + return object(); + case "[": + return array(); + case '"': + return string(); + case "-": + return number(); + default: + return ch >= "0" && ch <= "9" ? number() : word(); + } + }; + + return function(source, reviver) { + var result; + + text = source; + at = 0; + ch = " "; + result = value(); + white(); + if (ch) { + error("Syntax error"); + } + + return typeof reviver === "function" + ? (function walk(holder, key) { + var k, + v, + value = holder[key]; + if (value && typeof value === "object") { + for (k in value) { + if (Object.hasOwnProperty.call(value, k)) { + v = walk(value, k); + if (v !== undefined) { + value[k] = v; + } else { + delete value[k]; + } + } + } + } + return reviver.call(holder, key, value); + })({ "": result }, "") + : result; + }; +}); + +ace.define("ace/mode/json_worker", [], function(require, exports, module) { + "use strict"; + + var oop = require("../lib/oop"); + var Mirror = require("../worker/mirror").Mirror; + var parse = require("./json/json_parse"); + + var JsonWorker = (exports.JsonWorker = function(sender) { + Mirror.call(this, sender); + this.setTimeout(200); + }); + + oop.inherits(JsonWorker, Mirror); + + (function() { + this.onUpdate = function() { + var value = this.doc.getValue(); + var errors = []; + try { + if (value) parse(value); + } catch (e) { + var pos = this.doc.indexToPosition(e.at - 1); + errors.push({ + row: pos.row, + column: pos.column, + text: e.message, + type: "error" + }); + } + this.sender.emit("annotate", errors); + }; + }.call(JsonWorker.prototype)); +}); + +ace.define("ace/lib/es5-shim", [], function(require, exports, module) { + function Empty() {} + + if (!Function.prototype.bind) { + Function.prototype.bind = function bind(that) { + // .length is 1 + var target = this; + if (typeof target != "function") { + throw new TypeError( + "Function.prototype.bind called on incompatible " + target + ); + } + var args = slice.call(arguments, 1); // for normal call + var bound = function() { + if (this instanceof bound) { + var result = target.apply(this, args.concat(slice.call(arguments))); + if (Object(result) === result) { + return result; + } + return this; + } else { + return target.apply(that, args.concat(slice.call(arguments))); + } + }; + if (target.prototype) { + Empty.prototype = target.prototype; + bound.prototype = new Empty(); + Empty.prototype = null; + } + return bound; + }; + } + var call = Function.prototype.call; + var prototypeOfArray = Array.prototype; + var prototypeOfObject = Object.prototype; + var slice = prototypeOfArray.slice; + var _toString = call.bind(prototypeOfObject.toString); + var owns = call.bind(prototypeOfObject.hasOwnProperty); + var defineGetter; + var defineSetter; + var lookupGetter; + var lookupSetter; + var supportsAccessors; + if ((supportsAccessors = owns(prototypeOfObject, "__defineGetter__"))) { + defineGetter = call.bind(prototypeOfObject.__defineGetter__); + defineSetter = call.bind(prototypeOfObject.__defineSetter__); + lookupGetter = call.bind(prototypeOfObject.__lookupGetter__); + lookupSetter = call.bind(prototypeOfObject.__lookupSetter__); + } + if ([1, 2].splice(0).length != 2) { + if ( + (function() { + // test IE < 9 to splice bug - see issue #138 + function makeArray(l) { + var a = new Array(l + 2); + a[0] = a[1] = 0; + return a; + } + var array = [], + lengthBefore; + + array.splice.apply(array, makeArray(20)); + array.splice.apply(array, makeArray(26)); + + lengthBefore = array.length; //46 + array.splice(5, 0, "XXX"); // add one element + + lengthBefore + 1 == array.length; + + if (lengthBefore + 1 == array.length) { + return true; // has right splice implementation without bugs + } + })() + ) { + //IE 6/7 + var array_splice = Array.prototype.splice; + Array.prototype.splice = function(start, deleteCount) { + if (!arguments.length) { + return []; + } else { + return array_splice.apply( + this, + [ + start === void 0 ? 0 : start, + deleteCount === void 0 ? this.length - start : deleteCount + ].concat(slice.call(arguments, 2)) + ); + } + }; + } else { + //IE8 + Array.prototype.splice = function(pos, removeCount) { + var length = this.length; + if (pos > 0) { + if (pos > length) pos = length; + } else if (pos == void 0) { + pos = 0; + } else if (pos < 0) { + pos = Math.max(length + pos, 0); + } + + if (!(pos + removeCount < length)) removeCount = length - pos; + + var removed = this.slice(pos, pos + removeCount); + var insert = slice.call(arguments, 2); + var add = insert.length; + if (pos === length) { + if (add) { + this.push.apply(this, insert); + } + } else { + var remove = Math.min(removeCount, length - pos); + var tailOldPos = pos + remove; + var tailNewPos = tailOldPos + add - remove; + var tailCount = length - tailOldPos; + var lengthAfterRemove = length - remove; + + if (tailNewPos < tailOldPos) { + // case A + for (var i = 0; i < tailCount; ++i) { + this[tailNewPos + i] = this[tailOldPos + i]; + } + } else if (tailNewPos > tailOldPos) { + // case B + for (i = tailCount; i--; ) { + this[tailNewPos + i] = this[tailOldPos + i]; + } + } // else, add == remove (nothing to do) + + if (add && pos === lengthAfterRemove) { + this.length = lengthAfterRemove; // truncate array + this.push.apply(this, insert); + } else { + this.length = lengthAfterRemove + add; // reserves space + for (i = 0; i < add; ++i) { + this[pos + i] = insert[i]; + } + } + } + return removed; + }; + } + } + if (!Array.isArray) { + Array.isArray = function isArray(obj) { + return _toString(obj) == "[object Array]"; + }; + } + var boxedString = Object("a"), + splitString = boxedString[0] != "a" || !(0 in boxedString); + + if (!Array.prototype.forEach) { + Array.prototype.forEach = function forEach(fun /*, thisp*/) { + var object = toObject(this), + self = + splitString && _toString(this) == "[object String]" + ? this.split("") + : object, + thisp = arguments[1], + i = -1, + length = self.length >>> 0; + if (_toString(fun) != "[object Function]") { + throw new TypeError(); // TODO message + } + + while (++i < length) { + if (i in self) { + fun.call(thisp, self[i], i, object); + } + } + }; + } + if (!Array.prototype.map) { + Array.prototype.map = function map(fun /*, thisp*/) { + var object = toObject(this), + self = + splitString && _toString(this) == "[object String]" + ? this.split("") + : object, + length = self.length >>> 0, + result = Array(length), + thisp = arguments[1]; + if (_toString(fun) != "[object Function]") { + throw new TypeError(fun + " is not a function"); + } + + for (var i = 0; i < length; i++) { + if (i in self) result[i] = fun.call(thisp, self[i], i, object); + } + return result; + }; + } + if (!Array.prototype.filter) { + Array.prototype.filter = function filter(fun /*, thisp */) { + var object = toObject(this), + self = + splitString && _toString(this) == "[object String]" + ? this.split("") + : object, + length = self.length >>> 0, + result = [], + value, + thisp = arguments[1]; + if (_toString(fun) != "[object Function]") { + throw new TypeError(fun + " is not a function"); + } + + for (var i = 0; i < length; i++) { + if (i in self) { + value = self[i]; + if (fun.call(thisp, value, i, object)) { + result.push(value); + } + } + } + return result; + }; + } + if (!Array.prototype.every) { + Array.prototype.every = function every(fun /*, thisp */) { + var object = toObject(this), + self = + splitString && _toString(this) == "[object String]" + ? this.split("") + : object, + length = self.length >>> 0, + thisp = arguments[1]; + if (_toString(fun) != "[object Function]") { + throw new TypeError(fun + " is not a function"); + } + + for (var i = 0; i < length; i++) { + if (i in self && !fun.call(thisp, self[i], i, object)) { + return false; + } + } + return true; + }; + } + if (!Array.prototype.some) { + Array.prototype.some = function some(fun /*, thisp */) { + var object = toObject(this), + self = + splitString && _toString(this) == "[object String]" + ? this.split("") + : object, + length = self.length >>> 0, + thisp = arguments[1]; + if (_toString(fun) != "[object Function]") { + throw new TypeError(fun + " is not a function"); + } + + for (var i = 0; i < length; i++) { + if (i in self && fun.call(thisp, self[i], i, object)) { + return true; + } + } + return false; + }; + } + if (!Array.prototype.reduce) { + Array.prototype.reduce = function reduce(fun /*, initial*/) { + var object = toObject(this), + self = + splitString && _toString(this) == "[object String]" + ? this.split("") + : object, + length = self.length >>> 0; + if (_toString(fun) != "[object Function]") { + throw new TypeError(fun + " is not a function"); + } + if (!length && arguments.length == 1) { + throw new TypeError("reduce of empty array with no initial value"); + } + + var i = 0; + var result; + if (arguments.length >= 2) { + result = arguments[1]; + } else { + do { + if (i in self) { + result = self[i++]; + break; + } + if (++i >= length) { + throw new TypeError("reduce of empty array with no initial value"); + } + } while (true); + } + + for (; i < length; i++) { + if (i in self) { + result = fun.call(void 0, result, self[i], i, object); + } + } + + return result; + }; + } + if (!Array.prototype.reduceRight) { + Array.prototype.reduceRight = function reduceRight(fun /*, initial*/) { + var object = toObject(this), + self = + splitString && _toString(this) == "[object String]" + ? this.split("") + : object, + length = self.length >>> 0; + if (_toString(fun) != "[object Function]") { + throw new TypeError(fun + " is not a function"); + } + if (!length && arguments.length == 1) { + throw new TypeError("reduceRight of empty array with no initial value"); + } + + var result, + i = length - 1; + if (arguments.length >= 2) { + result = arguments[1]; + } else { + do { + if (i in self) { + result = self[i--]; + break; + } + if (--i < 0) { + throw new TypeError( + "reduceRight of empty array with no initial value" + ); + } + } while (true); + } + + do { + if (i in this) { + result = fun.call(void 0, result, self[i], i, object); + } + } while (i--); + + return result; + }; + } + if (!Array.prototype.indexOf || [0, 1].indexOf(1, 2) != -1) { + Array.prototype.indexOf = function indexOf(sought /*, fromIndex */) { + var self = + splitString && _toString(this) == "[object String]" + ? this.split("") + : toObject(this), + length = self.length >>> 0; + + if (!length) { + return -1; + } + + var i = 0; + if (arguments.length > 1) { + i = toInteger(arguments[1]); + } + i = i >= 0 ? i : Math.max(0, length + i); + for (; i < length; i++) { + if (i in self && self[i] === sought) { + return i; + } + } + return -1; + }; + } + if (!Array.prototype.lastIndexOf || [0, 1].lastIndexOf(0, -3) != -1) { + Array.prototype.lastIndexOf = function lastIndexOf( + sought /*, fromIndex */ + ) { + var self = + splitString && _toString(this) == "[object String]" + ? this.split("") + : toObject(this), + length = self.length >>> 0; + + if (!length) { + return -1; + } + var i = length - 1; + if (arguments.length > 1) { + i = Math.min(i, toInteger(arguments[1])); + } + i = i >= 0 ? i : length - Math.abs(i); + for (; i >= 0; i--) { + if (i in self && sought === self[i]) { + return i; + } + } + return -1; + }; + } + if (!Object.getPrototypeOf) { + Object.getPrototypeOf = function getPrototypeOf(object) { + return ( + object.__proto__ || + (object.constructor ? object.constructor.prototype : prototypeOfObject) + ); + }; + } + if (!Object.getOwnPropertyDescriptor) { + var ERR_NON_OBJECT = + "Object.getOwnPropertyDescriptor called on a " + "non-object: "; + Object.getOwnPropertyDescriptor = function getOwnPropertyDescriptor( + object, + property + ) { + if ( + (typeof object != "object" && typeof object != "function") || + object === null + ) + throw new TypeError(ERR_NON_OBJECT + object); + if (!owns(object, property)) return; + + var descriptor, getter, setter; + descriptor = { enumerable: true, configurable: true }; + if (supportsAccessors) { + var prototype = object.__proto__; + object.__proto__ = prototypeOfObject; + + var getter = lookupGetter(object, property); + var setter = lookupSetter(object, property); + object.__proto__ = prototype; + + if (getter || setter) { + if (getter) descriptor.get = getter; + if (setter) descriptor.set = setter; + return descriptor; + } + } + descriptor.value = object[property]; + return descriptor; + }; + } + if (!Object.getOwnPropertyNames) { + Object.getOwnPropertyNames = function getOwnPropertyNames(object) { + return Object.keys(object); + }; + } + if (!Object.create) { + var createEmpty; + if (Object.prototype.__proto__ === null) { + createEmpty = function() { + return { __proto__: null }; + }; + } else { + createEmpty = function() { + var empty = {}; + for (var i in empty) empty[i] = null; + empty.constructor = empty.hasOwnProperty = empty.propertyIsEnumerable = empty.isPrototypeOf = empty.toLocaleString = empty.toString = empty.valueOf = empty.__proto__ = null; + return empty; + }; + } + + Object.create = function create(prototype, properties) { + var object; + if (prototype === null) { + object = createEmpty(); + } else { + if (typeof prototype != "object") + throw new TypeError( + "typeof prototype[" + typeof prototype + "] != 'object'" + ); + var Type = function() {}; + Type.prototype = prototype; + object = new Type(); + object.__proto__ = prototype; + } + if (properties !== void 0) Object.defineProperties(object, properties); + return object; + }; + } + + function doesDefinePropertyWork(object) { + try { + Object.defineProperty(object, "sentinel", {}); + return "sentinel" in object; + } catch (exception) {} + } + if (Object.defineProperty) { + var definePropertyWorksOnObject = doesDefinePropertyWork({}); + var definePropertyWorksOnDom = + typeof document == "undefined" || + doesDefinePropertyWork(document.createElement("div")); + if (!definePropertyWorksOnObject || !definePropertyWorksOnDom) { + var definePropertyFallback = Object.defineProperty; + } + } + + if (!Object.defineProperty || definePropertyFallback) { + var ERR_NON_OBJECT_DESCRIPTOR = "Property description must be an object: "; + var ERR_NON_OBJECT_TARGET = "Object.defineProperty called on non-object: "; + var ERR_ACCESSORS_NOT_SUPPORTED = + "getters & setters can not be defined " + "on this javascript engine"; + + Object.defineProperty = function defineProperty( + object, + property, + descriptor + ) { + if ( + (typeof object != "object" && typeof object != "function") || + object === null + ) + throw new TypeError(ERR_NON_OBJECT_TARGET + object); + if ( + (typeof descriptor != "object" && typeof descriptor != "function") || + descriptor === null + ) + throw new TypeError(ERR_NON_OBJECT_DESCRIPTOR + descriptor); + if (definePropertyFallback) { + try { + return definePropertyFallback.call( + Object, + object, + property, + descriptor + ); + } catch (exception) {} + } + if (owns(descriptor, "value")) { + if ( + supportsAccessors && + (lookupGetter(object, property) || lookupSetter(object, property)) + ) { + var prototype = object.__proto__; + object.__proto__ = prototypeOfObject; + delete object[property]; + object[property] = descriptor.value; + object.__proto__ = prototype; + } else { + object[property] = descriptor.value; + } + } else { + if (!supportsAccessors) + throw new TypeError(ERR_ACCESSORS_NOT_SUPPORTED); + if (owns(descriptor, "get")) + defineGetter(object, property, descriptor.get); + if (owns(descriptor, "set")) + defineSetter(object, property, descriptor.set); + } + + return object; + }; + } + if (!Object.defineProperties) { + Object.defineProperties = function defineProperties(object, properties) { + for (var property in properties) { + if (owns(properties, property)) + Object.defineProperty(object, property, properties[property]); + } + return object; + }; + } + if (!Object.seal) { + Object.seal = function seal(object) { + return object; + }; + } + if (!Object.freeze) { + Object.freeze = function freeze(object) { + return object; + }; + } + try { + Object.freeze(function() {}); + } catch (exception) { + Object.freeze = (function freeze(freezeObject) { + return function freeze(object) { + if (typeof object == "function") { + return object; + } else { + return freezeObject(object); + } + }; + })(Object.freeze); + } + if (!Object.preventExtensions) { + Object.preventExtensions = function preventExtensions(object) { + return object; + }; + } + if (!Object.isSealed) { + Object.isSealed = function isSealed(object) { + return false; + }; + } + if (!Object.isFrozen) { + Object.isFrozen = function isFrozen(object) { + return false; + }; + } + if (!Object.isExtensible) { + Object.isExtensible = function isExtensible(object) { + if (Object(object) === object) { + throw new TypeError(); // TODO message + } + var name = ""; + while (owns(object, name)) { + name += "?"; + } + object[name] = true; + var returnValue = owns(object, name); + delete object[name]; + return returnValue; + }; + } + if (!Object.keys) { + var hasDontEnumBug = true, + dontEnums = [ + "toString", + "toLocaleString", + "valueOf", + "hasOwnProperty", + "isPrototypeOf", + "propertyIsEnumerable", + "constructor" + ], + dontEnumsLength = dontEnums.length; + + for (var key in { toString: null }) { + hasDontEnumBug = false; + } + + Object.keys = function keys(object) { + if ( + (typeof object != "object" && typeof object != "function") || + object === null + ) { + throw new TypeError("Object.keys called on a non-object"); + } + + var keys = []; + for (var name in object) { + if (owns(object, name)) { + keys.push(name); + } + } + + if (hasDontEnumBug) { + for (var i = 0, ii = dontEnumsLength; i < ii; i++) { + var dontEnum = dontEnums[i]; + if (owns(object, dontEnum)) { + keys.push(dontEnum); + } + } + } + return keys; + }; + } + if (!Date.now) { + Date.now = function now() { + return new Date().getTime(); + }; + } + var ws = + "\x09\x0A\x0B\x0C\x0D\x20\xA0\u1680\u2000\u2001\u2002\u2003" + + "\u2004\u2005\u2006\u2007\u2008\u2009\u200A\u202F\u205F\u3000\u2028" + + "\u2029\uFEFF"; + if (!String.prototype.trim) { + ws = "[" + ws + "]"; + var trimBeginRegexp = new RegExp("^" + ws + ws + "*"), + trimEndRegexp = new RegExp(ws + ws + "*$"); + String.prototype.trim = function trim() { + return String(this) + .replace(trimBeginRegexp, "") + .replace(trimEndRegexp, ""); + }; + } + + function toInteger(n) { + n = +n; + if (n !== n) { + // isNaN + n = 0; + } else if (n !== 0 && n !== 1 / 0 && n !== -(1 / 0)) { + n = (n > 0 || -1) * Math.floor(Math.abs(n)); + } + return n; + } + + function isPrimitive(input) { + var type = typeof input; + return ( + input === null || + type === "undefined" || + type === "boolean" || + type === "number" || + type === "string" + ); + } + + function toPrimitive(input) { + var val, valueOf, toString; + if (isPrimitive(input)) { + return input; + } + valueOf = input.valueOf; + if (typeof valueOf === "function") { + val = valueOf.call(input); + if (isPrimitive(val)) { + return val; + } + } + toString = input.toString; + if (typeof toString === "function") { + val = toString.call(input); + if (isPrimitive(val)) { + return val; + } + } + throw new TypeError(); + } + var toObject = function(o) { + if (o == null) { + // this matches both null and undefined + throw new TypeError("can't convert " + o + " to object"); + } + return Object(o); + }; +}); diff --git a/pandora_console/include/javascript/elasticsearch_queryResult.js b/pandora_console/include/javascript/elasticsearch_queryResult.js new file mode 100644 index 0000000000..82b63217b1 --- /dev/null +++ b/pandora_console/include/javascript/elasticsearch_queryResult.js @@ -0,0 +1,69 @@ +var editor = ace.edit("elasticsearch_editor"); +editor.setValue(`GET _search \n{\n "query": {\n "match_all": {}\n }\n}`); +editor.clearSelection(); + +var view = ace.edit("elasticsearch_view"); +view.setTheme("ace/theme/textmate"); +view.session.setMode("ace/mode/json"); +view.renderer.setShowGutter(false); +view.setReadOnly(true); +view.setShowPrintMargin(false); + +$("#submit-execute_query").click(function() { + view.setValue(""); + let selectText = editor.getSelectedText(); + if (selectText === "") { + let allText = editor.getValue(); + if (allText === "") { + return; + } + + allText = allText.split("\n").join(""); + allText = allText.concat("\n"); + var text = allText.match("(GET|PUT|POST)(.*?)({.*?}.*?)?(GET|POST|PUT|\n)"); + } else { + selectText = selectText.split("\n").join(""); + selectText = selectText.concat("\n"); + var text = selectText.match("(GET|PUT|POST)(.*?)({.*?}.*?)?(\n)"); + } + + if ( + text === null || + text === undefined || + text[2] === "" || + text[2] === undefined + ) { + view.setValue(`Syntax error`); + view.clearSelection(); + return; + } + + const head = text[1]; + let index = text[2].trim(); + if (index.match("^/") === null) { + index = `/${index}`; + } + + let json = text[3]; + if (json !== "" && json !== undefined) { + json = json.match("^{.*}")[0]; + } + + jQuery.post( + $("#pandora_full_url").text() + "ajax.php", + { + page: "enterprise/include/ajax/log_viewer.ajax", + elasticsearch_curl: 1, + head: head, + index: index, + json: json + }, + function(data) { + view.setValue(data); + view.clearSelection(); + + forced_title_callback(); + }, + "html" + ); +}); diff --git a/pandora_console/include/styles/ace.css b/pandora_console/include/styles/ace.css index db0895a7a0..43e3e4c888 100644 --- a/pandora_console/include/styles/ace.css +++ b/pandora_console/include/styles/ace.css @@ -1,19 +1,19 @@ -#elasticsearch_container { +#query_result_container { display: flex; } -.elasticsearch_editor_container { +.query_result_editor_container { width: 30%; } -.elasticsearch_editor_container p { +.query_result_editor_container p { padding-left: 40px; font-size: medium; margin-top: 5px; margin-bottom: 5px; } -.elasticsearch_editor { +.query_result_editor { width: 100%; height: 65em; border-top: 1px solid #e2e2e2; @@ -24,19 +24,19 @@ border-radius: 4px; } -.elasticsearch_view_container { +.query_result_view_container { width: 70%; margin-left: 30px; } -.elasticsearch_view_container p { +.query_result_view_container p { padding-left: 40px; font-size: medium; margin-top: 5px; margin-bottom: 5px; } -.elasticsearch_view { +.query_result_view { min-height: 65em; border-top: 1px solid #e2e2e2; border-left: 1px solid #e2e2e2; From 53362f64244aa0fb1e48a673a9bbdc46676b3354 Mon Sep 17 00:00:00 2001 From: marcos Date: Tue, 13 Oct 2020 15:04:33 +0200 Subject: [PATCH 026/263] update branch --- pandora_console/godmode/setup/setup_auth.php | 8 +------- 1 file changed, 1 insertion(+), 7 deletions(-) diff --git a/pandora_console/godmode/setup/setup_auth.php b/pandora_console/godmode/setup/setup_auth.php index 273fa17de5..9b39734b57 100644 --- a/pandora_console/godmode/setup/setup_auth.php +++ b/pandora_console/godmode/setup/setup_auth.php @@ -329,13 +329,7 @@ echo ''; $( document ).ready(function() { //For change autocreate remote users - console.log($('input[type=checkbox][id=checkbox-double_auth_enabled]:checked').val()); - if ($('input[type=checkbox][name=double_auth_enabled]:checked').val() == 1) { - $('#table1-2FA_all_users').show(); - } - else { - $('#table1-2FA_all_users').hide(); - } + $('input[type=checkbox][name=double_auth_enabled]').change(function () { if ($('input[type=checkbox][name=double_auth_enabled]:checked').val() == 1) { $('#table1-2FA_all_users').show(); From 65851afe4dd098d14bf1710ce40fdf140d796481 Mon Sep 17 00:00:00 2001 From: Jose Gonzalez Date: Wed, 14 Oct 2020 10:59:55 +0200 Subject: [PATCH 027/263] Database update --- pandora_console/extras/mr/43.SQL | 6 ++++++ .../extras/pandoradb_migrate_6.0_to_7.0.mysql.sql | 2 ++ pandora_console/pandoradb.sql | 2 ++ 3 files changed, 10 insertions(+) create mode 100644 pandora_console/extras/mr/43.SQL diff --git a/pandora_console/extras/mr/43.SQL b/pandora_console/extras/mr/43.SQL new file mode 100644 index 0000000000..91196d4bc9 --- /dev/null +++ b/pandora_console/extras/mr/43.SQL @@ -0,0 +1,6 @@ +START TRANSACTION; + +ALTER TABLE `tagente_modulo` ADD COLUMN `debug` tinyint(1) UNSIGNED NOT NULL default 0; +ALTER TABLE `tagente_modulo` ADD COLUMN `debug_content` varchar(200); + +COMMIT; \ No newline at end of file diff --git a/pandora_console/extras/pandoradb_migrate_6.0_to_7.0.mysql.sql b/pandora_console/extras/pandoradb_migrate_6.0_to_7.0.mysql.sql index e6208f034a..081b94996c 100644 --- a/pandora_console/extras/pandoradb_migrate_6.0_to_7.0.mysql.sql +++ b/pandora_console/extras/pandoradb_migrate_6.0_to_7.0.mysql.sql @@ -1480,6 +1480,8 @@ ALTER TABLE `tagente_modulo` DROP COLUMN `ff_normal`, MODIFY COLUMN `dynamic_next` bigint(20) NOT NULL DEFAULT '0', MODIFY COLUMN `dynamic_two_tailed` tinyint(1) unsigned NULL DEFAULT '0'; ALTER TABLE tagente_modulo MODIFY COLUMN `custom_string_1` MEDIUMTEXT; +ALTER TABLE `tagente_modulo` ADD COLUMN `debug` tinyint(1) UNSIGNED NOT NULL default 0; +ALTER TABLE `tagente_modulo` ADD COLUMN `debug_content` varchar(200); -- --------------------------------------------------------------------- -- Table `tagente_datos` diff --git a/pandora_console/pandoradb.sql b/pandora_console/pandoradb.sql index f17bdf0c9e..6b7db6678a 100644 --- a/pandora_console/pandoradb.sql +++ b/pandora_console/pandoradb.sql @@ -266,6 +266,8 @@ CREATE TABLE IF NOT EXISTS `tagente_modulo` ( `prediction_threshold` int(4) default 0, `parent_module_id` int(10) unsigned NOT NULL default 0, `cps` int NOT NULL default 0, + `debug` tinyint(1) UNSIGNED NOT NULL default 0, + `debug_content` varchar(200), PRIMARY KEY (`id_agente_modulo`), KEY `main_idx` (`id_agente_modulo`,`id_agente`), KEY `tam_agente` (`id_agente`), From c28e2917edce50400d32a820956e8154d91bed7f Mon Sep 17 00:00:00 2001 From: Jose Gonzalez Date: Thu, 15 Oct 2020 11:29:47 +0200 Subject: [PATCH 028/263] Modified database files --- pandora_console/extras/mr/{43.SQL => 43.sql} | 1 - pandora_console/extras/pandoradb_migrate_6.0_to_7.0.mysql.sql | 1 - pandora_console/pandoradb.sql | 1 - 3 files changed, 3 deletions(-) rename pandora_console/extras/mr/{43.SQL => 43.sql} (52%) diff --git a/pandora_console/extras/mr/43.SQL b/pandora_console/extras/mr/43.sql similarity index 52% rename from pandora_console/extras/mr/43.SQL rename to pandora_console/extras/mr/43.sql index 91196d4bc9..3845154eb1 100644 --- a/pandora_console/extras/mr/43.SQL +++ b/pandora_console/extras/mr/43.sql @@ -1,6 +1,5 @@ START TRANSACTION; -ALTER TABLE `tagente_modulo` ADD COLUMN `debug` tinyint(1) UNSIGNED NOT NULL default 0; ALTER TABLE `tagente_modulo` ADD COLUMN `debug_content` varchar(200); COMMIT; \ No newline at end of file diff --git a/pandora_console/extras/pandoradb_migrate_6.0_to_7.0.mysql.sql b/pandora_console/extras/pandoradb_migrate_6.0_to_7.0.mysql.sql index 081b94996c..8287f2f5a5 100644 --- a/pandora_console/extras/pandoradb_migrate_6.0_to_7.0.mysql.sql +++ b/pandora_console/extras/pandoradb_migrate_6.0_to_7.0.mysql.sql @@ -1480,7 +1480,6 @@ ALTER TABLE `tagente_modulo` DROP COLUMN `ff_normal`, MODIFY COLUMN `dynamic_next` bigint(20) NOT NULL DEFAULT '0', MODIFY COLUMN `dynamic_two_tailed` tinyint(1) unsigned NULL DEFAULT '0'; ALTER TABLE tagente_modulo MODIFY COLUMN `custom_string_1` MEDIUMTEXT; -ALTER TABLE `tagente_modulo` ADD COLUMN `debug` tinyint(1) UNSIGNED NOT NULL default 0; ALTER TABLE `tagente_modulo` ADD COLUMN `debug_content` varchar(200); -- --------------------------------------------------------------------- diff --git a/pandora_console/pandoradb.sql b/pandora_console/pandoradb.sql index 6b7db6678a..046e8039a0 100644 --- a/pandora_console/pandoradb.sql +++ b/pandora_console/pandoradb.sql @@ -266,7 +266,6 @@ CREATE TABLE IF NOT EXISTS `tagente_modulo` ( `prediction_threshold` int(4) default 0, `parent_module_id` int(10) unsigned NOT NULL default 0, `cps` int NOT NULL default 0, - `debug` tinyint(1) UNSIGNED NOT NULL default 0, `debug_content` varchar(200), PRIMARY KEY (`id_agente_modulo`), KEY `main_idx` (`id_agente_modulo`,`id_agente`), From def59508ae162be8540391cd9a6fbcf797385a8a Mon Sep 17 00:00:00 2001 From: alejandro-campos Date: Thu, 22 Oct 2020 12:00:44 +0200 Subject: [PATCH 029/263] changed behavior of all group acl check and fixed acl vulnerabilities --- .../agentes/planned_downtime.editor.php | 40 +++++++++--- .../godmode/agentes/planned_downtime.list.php | 38 ++++++++--- .../godmode/alerts/alert_actions.php | 39 ++++++++++- .../godmode/alerts/configure_alert_action.php | 17 ++++- .../godmode/events/event_edit_filter.php | 18 ++++- .../godmode/events/event_filter.php | 33 +++++++++- .../godmode/events/event_responses.editor.php | 18 ++++- .../godmode/events/event_responses.list.php | 4 ++ .../godmode/gis_maps/configure_gis_map.php | 8 +++ pandora_console/godmode/netflow/nf_edit.php | 35 ++++++++-- .../godmode/reporting/graph_builder.main.php | 12 +++- .../godmode/reporting/graph_builder.php | 14 ++++ pandora_console/godmode/reporting/graphs.php | 15 +++-- .../godmode/reporting/map_builder.php | 10 +-- .../reporting/reporting_builder.main.php | 8 ++- .../godmode/reporting/reporting_builder.php | 28 ++++++-- .../reporting/visual_console_builder.data.php | 9 ++- .../reporting/visual_console_builder.php | 8 +-- .../include/class/CredentialStore.class.php | 25 ++++++- pandora_console/include/functions.php | 65 +++++++++++++++++++ pandora_console/include/functions_events.php | 8 ++- pandora_console/include/functions_users.php | 7 +- .../include/lib/Dashboard/Manager.php | 2 + .../agentes/pandora_networkmap.editor.php | 18 ++++- .../operation/agentes/pandora_networkmap.php | 30 +++++---- .../operation/gis_maps/gis_map.php | 6 +- .../operation/reporting/graph_viewer.php | 13 +++- .../operation/reporting/reporting_viewer.php | 11 +++- .../operation/visual_console/legacy_view.php | 6 +- .../operation/visual_console/view.php | 6 +- .../views/dashboard/formDashboard.php | 17 +++-- pandora_console/views/dashboard/header.php | 4 +- pandora_console/views/dashboard/list.php | 5 ++ 33 files changed, 483 insertions(+), 94 deletions(-) diff --git a/pandora_console/godmode/agentes/planned_downtime.editor.php b/pandora_console/godmode/agentes/planned_downtime.editor.php index b3e7029dbd..a26405aa2c 100644 --- a/pandora_console/godmode/agentes/planned_downtime.editor.php +++ b/pandora_console/godmode/agentes/planned_downtime.editor.php @@ -143,16 +143,29 @@ $user_groups_ad = array_keys( users_get_groups($config['id_user'], $access) ); +// Check AD permission on downtime. +$downtime_group = db_get_value( + 'id_group', + 'tplanned_downtime', + 'id', + $id_downtime +); + +if ($id_downtime > 0) { + if (!check_acl_restricted_all($config['id_user'], $downtime_group, 'AW') + && !check_acl_restricted_all($config['id_user'], $downtime_group, 'AD') + ) { + db_pandora_audit( + 'ACL Violation', + 'Trying to access downtime scheduler' + ); + include 'general/noaccess.php'; + return; + } +} + // INSERT A NEW DOWNTIME_AGENT ASSOCIATION. if ($insert_downtime_agent === 1) { - // Check AD permission on downtime. - $downtime_group = db_get_value( - 'id_group', - 'tplanned_downtime', - 'id', - $id_downtime - ); - if ($downtime_group === false || !in_array($downtime_group, $user_groups_ad) ) { @@ -644,11 +657,20 @@ $table->data[0][1] = html_print_input_text( true, $disabled_in_execution ); + +$return_all_group = false; + +if (users_can_manage_group_all('AW') === true + || users_can_manage_group_all('AD') === true +) { + $return_all_group = true; +} + $table->data[1][0] = __('Group'); $table->data[1][1] = '
'.html_print_select_groups( false, $access, - true, + $return_all_group, 'id_group', $id_group, '', diff --git a/pandora_console/godmode/agentes/planned_downtime.list.php b/pandora_console/godmode/agentes/planned_downtime.list.php index acfa2a9039..c48e9a71d4 100755 --- a/pandora_console/godmode/agentes/planned_downtime.list.php +++ b/pandora_console/godmode/agentes/planned_downtime.list.php @@ -476,22 +476,42 @@ else { if (in_array($downtime['id_group'], $groupsAD)) { // Stop button if ($downtime['type_execution'] == 'once' && $downtime['executed'] == 1) { - $data['stop'] = ''.html_print_image('images/cancel.png', true, ['title' => __('Stop downtime')]); + if (check_acl_restricted_all($config['id_user'], $downtime['id_group'], 'AW') + || check_acl_restricted_all($config['id_user'], $downtime['id_group'], 'AD') + ) { + $data['stop'] = ''.html_print_image('images/cancel.png', true, ['title' => __('Stop downtime')]); + } else { + $data['stop'] = html_print_image('images/cancel.png', true, ['title' => __('Stop downtime')]); + } } else { $data['stop'] = ''; } // Edit & delete buttons. if ($downtime['executed'] == 0) { - // Edit. - $data['edit'] = ''.html_print_image('images/config.png', true, ['title' => __('Update')]).''; - // Delete. - $data['delete'] = ''.html_print_image('images/cross.png', true, ['title' => __('Delete')]); + if (check_acl_restricted_all($config['id_user'], $downtime['id_group'], 'AW') + || check_acl_restricted_all($config['id_user'], $downtime['id_group'], 'AD') + ) { + // Edit. + $data['edit'] = ''.html_print_image('images/config.png', true, ['title' => __('Update')]).''; + // Delete. + $data['delete'] = ''.html_print_image('images/cross.png', true, ['title' => __('Delete')]); + } else { + $data['edit'] = ''; + $data['delete'] = ''; + } } else if ($downtime['executed'] == 1 && $downtime['type_execution'] == 'once') { - // Edit. - $data['edit'] = ''.html_print_image('images/config.png', true, ['title' => __('Update')]).''; - // Delete. - $data['delete'] = __('N/A'); + if (check_acl_restricted_all($config['id_user'], $downtime['id_group'], 'AW') + || check_acl_restricted_all($config['id_user'], $downtime['id_group'], 'AD') + ) { + // Edit. + $data['edit'] = ''.html_print_image('images/config.png', true, ['title' => __('Update')]).''; + // Delete. + $data['delete'] = __('N/A'); + } else { + $data['edit'] = ''; + $data['delete'] = ''; + } } else { $data['edit'] = ''; $data['delete'] = ''; diff --git a/pandora_console/godmode/alerts/alert_actions.php b/pandora_console/godmode/alerts/alert_actions.php index 70f4c46713..6e3f1c1f42 100644 --- a/pandora_console/godmode/alerts/alert_actions.php +++ b/pandora_console/godmode/alerts/alert_actions.php @@ -77,6 +77,15 @@ if ($copy_action) { $al_action = alerts_get_alert_action($id); + if (!check_acl_restricted_all($config['id_user'], $al_action['id_group'], 'LM')) { + db_pandora_audit( + 'ACL Violation', + 'Trying to access Alert Management' + ); + include 'general/noaccess.php'; + exit; + } + if ($al_action !== false) { // If user tries to copy an action with group=ALL. if ($al_action['id_group'] == 0) { @@ -144,6 +153,15 @@ if ($delete_action) { $al_action = alerts_get_alert_action($id); + if (!check_acl_restricted_all($config['id_user'], $al_action['id_group'], 'LM')) { + db_pandora_audit( + 'ACL Violation', + 'Trying to access Alert Management' + ); + include 'general/noaccess.php'; + exit; + } + if ($al_action !== false) { // If user tries to delete an action with group=ALL. if ($al_action['id_group'] == 0) { @@ -236,11 +254,18 @@ $table_filter->data[0][1] = html_print_input_text( 255, true ); + +$return_all_group = false; + +if (users_can_manage_group_all('LM') === true) { + $return_all_group = true; +} + $table_filter->data[0][2] = __('Group'); $table_filter->data[0][3] = html_print_select_groups( $config['id_user'], 'LM', - true, + $return_all_group, 'group_search', $group_search, '', @@ -370,7 +395,12 @@ foreach ($actions as $action) { $data = []; - $data[0] = ''.$action['name'].''; + if (check_acl_restricted_all($config['id_user'], $action['id_group'], 'LM')) { + $data[0] = ''.$action['name'].''; + } else { + $data[0] = $action['name']; + } + $data[1] = $action['command_name']; $data[2] = ui_print_group_icon($action['id_group'], true).' '; if (!alerts_validate_command_to_action($action['id_group'], $action['command_group'])) { @@ -384,8 +414,11 @@ foreach ($actions as $action) { ); } + $data[3] = ''; + $data[4] = ''; + if (is_central_policies_on_node() === false - && check_acl($config['id_user'], $action['id_group'], 'LM') + && check_acl_restricted_all($config['id_user'], $action['id_group'], 'LM') ) { $table->cellclass[] = [ 3 => 'action_buttons', diff --git a/pandora_console/godmode/alerts/configure_alert_action.php b/pandora_console/godmode/alerts/configure_alert_action.php index 4d49cdd22b..02d86b0d0e 100644 --- a/pandora_console/godmode/alerts/configure_alert_action.php +++ b/pandora_console/godmode/alerts/configure_alert_action.php @@ -101,6 +101,15 @@ if ($id) { $group = $action['id_group']; $action_threshold = $action['action_threshold']; + + if (!check_acl_restricted_all($config['id_user'], $action['id_group'], 'LM')) { + db_pandora_audit( + 'ACL Violation', + 'Trying to access Alert Management' + ); + include 'general/noaccess.php'; + exit; + } } // Hidden div with help hint to fill with javascript. @@ -168,10 +177,16 @@ $table->data[1][0] = __('Group'); $own_info = get_user_info($config['id_user']); +$return_all_group = false; + +if (users_can_manage_group_all('LW') === true) { + $return_all_group = true; +} + $table->data[1][1] = '
'.html_print_select_groups( false, 'LW', - true, + $return_all_group, 'group', $group, '', diff --git a/pandora_console/godmode/events/event_edit_filter.php b/pandora_console/godmode/events/event_edit_filter.php index 56201e3a28..4ae9b2e78c 100644 --- a/pandora_console/godmode/events/event_edit_filter.php +++ b/pandora_console/godmode/events/event_edit_filter.php @@ -40,7 +40,15 @@ $strict_user = db_get_value( ); if ($id) { - $permission = events_check_event_filter_group($id); + $restrict_all_group = false; + + if (!users_can_manage_group_all('EW') === true + && !users_can_manage_group_all('EM') === true + ) { + $restrict_all_group = true; + } + + $permission = events_check_event_filter_group($id, $restrict_all_group); if (!$permission) { // User doesn't have permissions to see this filter include 'general/noaccess.php'; @@ -262,12 +270,18 @@ $table->data[1][1] = '
'.html_print_select_groups( $strict_user ).'
'; +$return_all_group = false; + +if (users_can_manage_group_all('AR') === true) { + $return_all_group = true; +} + $table->data[2][0] = ''.__('Group').''; $display_all_group = (users_is_admin() || users_can_manage_group_all('AR')); $table->data[2][1] = '
'.html_print_select_groups( $config['id_user'], 'AR', - $display_all_group, + $return_all_group, 'id_group', $id_group, '', diff --git a/pandora_console/godmode/events/event_filter.php b/pandora_console/godmode/events/event_filter.php index c9c3f7f226..357a745cb6 100644 --- a/pandora_console/godmode/events/event_filter.php +++ b/pandora_console/godmode/events/event_filter.php @@ -35,6 +35,19 @@ $multiple_delete = (bool) get_parameter('multiple_delete', 0); if ($delete) { $id = (int) get_parameter('id'); + $filter_group = (int) db_get_value('id_group', 'tevent_filter', 'id_filter', $id); + + if (!check_acl_restricted_all($config['id_user'], $filter_group, 'EW') + && !check_acl_restricted_all($config['id_user'], $filter_group, 'EM') + ) { + db_pandora_audit( + 'ACL Violation', + 'Trying to access events filter editor' + ); + include 'general/noaccess.php'; + return; + } + $id_filter = db_get_value('id_filter', 'tevent_filter', 'id_filter', $id); if ($id_filter === false) { @@ -151,13 +164,27 @@ foreach ($filters as $filter) { $data = []; $data[0] = html_print_checkbox_extended('delete_multiple[]', $filter['id_filter'], false, false, '', 'class="check_delete"', true); - $data[1] = ''.$filter['id_name'].''; + + if (!check_acl_restricted_all($config['id_user'], $filter['id_group'], 'EW') + && !check_acl_restricted_all($config['id_user'], $filter['id_group'], 'EM') + ) { + $data[1] = $filter['id_name']; + } else { + $data[1] = ''.$filter['id_name'].''; + } + $data[2] = ui_print_group_icon($filter['id_group_filter'], true); $data[3] = events_get_event_types($filter['event_type']); $data[4] = events_get_status($filter['status']); $data[5] = events_get_severity_types($filter['severity']); - $table->cellclass[][6] = 'action_buttons'; - $data[6] = "".html_print_image('images/cross.png', true, ['title' => __('Delete')]).''; + $data[6] = ''; + + if (check_acl_restricted_all($config['id_user'], $filter['id_group'], 'EW') + || check_acl_restricted_all($config['id_user'], $filter['id_group'], 'EM') + ) { + $table->cellclass[][6] = 'action_buttons'; + $data[6] = "".html_print_image('images/cross.png', true, ['title' => __('Delete')]).''; + } array_push($table->data, $data); } diff --git a/pandora_console/godmode/events/event_responses.editor.php b/pandora_console/godmode/events/event_responses.editor.php index 80ca390b51..7ed046a8cb 100644 --- a/pandora_console/godmode/events/event_responses.editor.php +++ b/pandora_console/godmode/events/event_responses.editor.php @@ -39,6 +39,16 @@ $event_response_id = get_parameter('id_response', 0); if ($event_response_id > 0) { $event_response = db_get_row('tevent_response', 'id', $event_response_id); + + // ACL check for event response edition. + if (!check_acl_restricted_all($config['id_user'], $event_response['id_group'], 'PM')) { + db_pandora_audit( + 'ACL Violation', + 'Trying to access Group Management' + ); + include 'general/noaccess.php'; + return; + } } else { $event_response = []; $event_response['name'] = ''; @@ -84,8 +94,14 @@ $data[1] = html_print_input_text( ); $data[1] .= html_print_input_hidden('id_response', $event_response['id'], true); +$return_all_group = false; + +if (users_can_manage_group_all('PM') === true) { + $return_all_group = true; +} + $data[2] = __('Group'); -$data[3] = html_print_select_groups(false, 'PM', true, 'id_group', $event_response['id_group'], '', '', '', true); +$data[3] = html_print_select_groups(false, 'PM', $return_all_group, 'id_group', $event_response['id_group'], '', '', '', true); $table->data[0] = $data; $data = []; diff --git a/pandora_console/godmode/events/event_responses.list.php b/pandora_console/godmode/events/event_responses.list.php index f8e4de7357..7c4a52bb74 100644 --- a/pandora_console/godmode/events/event_responses.list.php +++ b/pandora_console/godmode/events/event_responses.list.php @@ -55,6 +55,10 @@ $table->head[3] = __('Actions'); $table->data = []; foreach ($event_responses as $response) { + if (!check_acl_restricted_all($config['id_user'], $response['id_group'], 'PM')) { + continue; + } + $data = []; $data[0] = ''.$response['name'].''; $data[1] = $response['description']; diff --git a/pandora_console/godmode/gis_maps/configure_gis_map.php b/pandora_console/godmode/gis_maps/configure_gis_map.php index a56e69dcb1..907786e6fd 100644 --- a/pandora_console/godmode/gis_maps/configure_gis_map.php +++ b/pandora_console/godmode/gis_maps/configure_gis_map.php @@ -30,6 +30,14 @@ require_once 'include/functions_gis.php'; $idMap = (int) get_parameter('map_id', 0); $action = get_parameter('action', 'new_map'); +$gis_map_group = db_get_value('group_id', 'tgis_map', 'id_tgis_map', $idMap); + +if (!check_acl_restricted_all($config['id_user'], $gis_map_group, 'MW') && !check_acl_restricted_all($config['id_user'], $gis_map_group, 'MW')) { + db_pandora_audit('ACL Violation', 'Trying to access map builder'); + include 'general/noaccess.php'; + return; +} + $sec2 = get_parameter_get('sec2'); $sec2 = safe_url_extraclean($sec2); diff --git a/pandora_console/godmode/netflow/nf_edit.php b/pandora_console/godmode/netflow/nf_edit.php index bb7a456a69..37e8f53431 100644 --- a/pandora_console/godmode/netflow/nf_edit.php +++ b/pandora_console/godmode/netflow/nf_edit.php @@ -68,6 +68,19 @@ $multiple_delete = (bool) get_parameter('multiple_delete', 0); $id = (int) get_parameter('id'); $name = (string) get_parameter('name'); +if ($id > 0) { + $filter_group = db_get_value('id_group', 'tnetflow_filter', 'id_sg', $id); + + if (!check_acl_restricted_all($config['id_user'], $filter_group, 'AW')) { + db_pandora_audit( + 'ACL Violation', + 'Trying to access events filter editor' + ); + include 'general/noaccess.php'; + return; + } +} + if ($delete) { $id_filter = db_get_value('id_name', 'tnetflow_filter', 'id_sg', $id); $result = db_process_sql_delete( @@ -164,12 +177,24 @@ $total_filters = $total_filters[0]['total']; foreach ($filters as $filter) { $data = []; - $data[0] = html_print_checkbox_extended('delete_multiple[]', $filter['id_sg'], false, false, '', 'class="check_delete"', true); - $data[1] = ''.$filter['id_name'].''; + $data[0] = ''; + + if (check_acl_restricted_all($config['id_user'], $filter['id_group'], 'AW')) { + $data[0] = html_print_checkbox_extended('delete_multiple[]', $filter['id_sg'], false, false, '', 'class="check_delete"', true); + $data[1] = ''.$filter['id_name'].''; + } else { + $data[1] = $filter['id_name']; + } + + $data[2] = ui_print_group_icon($filter['id_group'], true, 'groups_small', '', !defined('METACONSOLE')); - $table->cellclass[][3] = 'action_buttons'; - $data[3] = "".html_print_image('images/cross.png', true, ['title' => __('Delete')]).''; + $data[3] = ''; + + if (check_acl_restricted_all($config['id_user'], $filter['id_group'], 'AW')) { + $table->cellclass[][3] = 'action_buttons'; + $data[3] = "".html_print_image('images/cross.png', true, ['title' => __('Delete')]).''; + } array_push($table->data, $data); } diff --git a/pandora_console/godmode/reporting/graph_builder.main.php b/pandora_console/godmode/reporting/graph_builder.main.php index d50a5d8b55..ea59795da7 100644 --- a/pandora_console/godmode/reporting/graph_builder.main.php +++ b/pandora_console/godmode/reporting/graph_builder.main.php @@ -132,12 +132,20 @@ $output .= '>'; $own_info = get_user_info($config['id_user']); +$return_all_group = true; + +if (users_can_manage_group_all('RW') === false + && users_can_manage_group_all('RM') === false +) { + $return_all_group = false; +} + $output .= ''.__('Group').''; if (check_acl($config['id_user'], 0, 'RW')) { $output .= html_print_select_groups( $config['id_user'], 'RW', - true, + $return_all_group, 'graph_id_group', $id_group, '', @@ -149,7 +157,7 @@ if (check_acl($config['id_user'], 0, 'RW')) { $output .= html_print_select_groups( $config['id_user'], 'RM', - true, + $return_all_group, 'graph_id_group', $id_group, '', diff --git a/pandora_console/godmode/reporting/graph_builder.php b/pandora_console/godmode/reporting/graph_builder.php index f0262f6e5f..4fde79ed82 100644 --- a/pandora_console/godmode/reporting/graph_builder.php +++ b/pandora_console/godmode/reporting/graph_builder.php @@ -83,6 +83,20 @@ $change_weight = (bool) get_parameter('change_weight', false); $change_label = (bool) get_parameter('change_label', false); $id_graph = (int) get_parameter('id', 0); +if ($id_graph > 0) { + $graph_group = db_get_value('id_group', 'tgraph', 'id_graph', $id_graph); + if (!check_acl_restricted_all($config['id_user'], $graph_group, 'RW') + && !check_acl_restricted_all($config['id_user'], $graph_group, 'RM') + ) { + db_pandora_audit( + 'ACL Violation', + 'Trying to access graph builder' + ); + include 'general/noaccess.php'; + exit; + } +} + if ($id_graph !== 0) { $sql = "SELECT * FROM tgraph WHERE (private = 0 OR (private = 1 AND id_user = '".$config['id_user']."')) diff --git a/pandora_console/godmode/reporting/graphs.php b/pandora_console/godmode/reporting/graphs.php index 958819a95b..980197cd54 100644 --- a/pandora_console/godmode/reporting/graphs.php +++ b/pandora_console/godmode/reporting/graphs.php @@ -88,7 +88,11 @@ ui_print_page_header(__('Reporting').' » '.__('Custom graphs'), 'images/ch // Delete module SQL code if ($delete_graph) { - if ($report_w || $report_m) { + $graph_group = db_get_value('id_group', 'tgraph', 'id_graph', $id); + + if (check_acl_restricted_all($config['id_user'], $graph_group, 'RW') + || check_acl_restricted_all($config['id_user'], $graph_group, 'RM') + ) { $exist = db_get_value('id_graph', 'tgraph_source', 'id_graph', $id); if ($exist) { $result = db_process_sql_delete('tgraph_source', ['id_graph' => $id]); @@ -299,16 +303,17 @@ $table_aux = new stdClass(); $data[4] = ''; $table->cellclass[][4] = 'action_buttons'; - if (($report_w || $report_m)) { + if (check_acl_restricted_all($config['id_user'], $graph['id_group'], 'RM') + || check_acl_restricted_all($config['id_user'], $graph['id_group'], 'RW') + ) { $data[4] = ''.html_print_image('images/config.png', true).''; } - if ($report_m) { + $data[5] = ''; + if (check_acl_restricted_all($config['id_user'], $graph['id_group'], 'RM')) { $data[4] .= ''.html_print_image('images/cross.png', true, ['alt' => __('Delete'), 'title' => __('Delete')]).''; - } - if ($report_m) { $data[5] .= html_print_checkbox_extended('delete_multiple[]', $graph['id_graph'], false, false, '', 'class="check_delete" style="margin-left:2px;"', true); } diff --git a/pandora_console/godmode/reporting/map_builder.php b/pandora_console/godmode/reporting/map_builder.php index 9f8b8f0162..f1fab5f2d8 100644 --- a/pandora_console/godmode/reporting/map_builder.php +++ b/pandora_console/godmode/reporting/map_builder.php @@ -123,8 +123,8 @@ if ($delete_layout || $copy_layout) { // ACL for the visual console // $vconsole_read = check_acl ($config['id_user'], $group_id, "VR"); - $vconsole_write = check_acl($config['id_user'], $group_id, 'VW'); - $vconsole_manage = check_acl($config['id_user'], $group_id, 'VM'); + $vconsole_write = check_acl_restricted_all($config['id_user'], $group_id, 'VW'); + $vconsole_manage = check_acl_restricted_all($config['id_user'], $group_id, 'VM'); if (!$vconsole_write && !$vconsole_manage) { db_pandora_audit( @@ -441,8 +441,10 @@ if (!$maps && !is_metaconsole()) { $data[1] = ui_print_group_icon($map['id_group'], true); $data[2] = db_get_sql('SELECT COUNT(*) FROM tlayout_data WHERE id_layout = '.$map['id']); - // Fix: IW was the old ACL for report editing, now is RW - if ($vconsoles_write || $vconsoles_manage) { + $vconsoles_write_action_btn = check_acl_restricted_all($config['id_user'], $map['id_group'], 'VW'); + $vconsoles_manage_action_btn = check_acl_restricted_all($config['id_user'], $map['id_group'], 'VM'); + + if ($vconsoles_write_action_btn || $vconsoles_manage_action_btn) { if (!is_metaconsole()) { $table->cellclass[] = [ 3 => 'action_buttons', diff --git a/pandora_console/godmode/reporting/reporting_builder.main.php b/pandora_console/godmode/reporting/reporting_builder.main.php index 224b2ce517..5ba61658fa 100755 --- a/pandora_console/godmode/reporting/reporting_builder.main.php +++ b/pandora_console/godmode/reporting/reporting_builder.main.php @@ -114,11 +114,17 @@ if (isset($write_groups[$idGroupReport]) === false && $idGroupReport) { $write_groups[$idGroupReport] = groups_get_name($idGroupReport); } +$return_all_group = false; + +if (users_can_manage_group_all('RW') === true) { + $return_all_group = true; +} + $table->data['group'][1] = '
'; $table->data['group'][1] .= html_print_select_groups( $config['id_user'], 'AR', - true, + $return_all_group, 'id_group', $idGroupReport, '', diff --git a/pandora_console/godmode/reporting/reporting_builder.php b/pandora_console/godmode/reporting/reporting_builder.php index 8ac3a11e37..5a0fe779c4 100755 --- a/pandora_console/godmode/reporting/reporting_builder.php +++ b/pandora_console/godmode/reporting/reporting_builder.php @@ -158,6 +158,26 @@ $pure = get_parameter('pure', 0); $schedule_report = get_parameter('schbutton', ''); $pagination = (int) get_parameter('pagination', $config['block_size']); +if ($action == 'edit' && $idReport > 0) { + $report_group = db_get_value( + 'id_group', + 'treport', + 'id_report', + $idReport + ); + + if (! check_acl_restricted_all($config['id_user'], $report_group, 'RW') + && ! check_acl_restricted_all($config['id_user'], $report_group, 'RM') + ) { + db_pandora_audit( + 'ACL Violation', + 'Trying to access report builder' + ); + include 'general/noaccess.php'; + exit; + } +} + if ($schedule_report != '') { $id_user_task = 1; $scheduled = 'no'; @@ -909,8 +929,8 @@ switch ($action) { $data = []; - if (check_acl($config['id_user'], $report['id_group'], 'RW') - || check_acl($config['id_user'], $report['id_group'], 'RM') + if (check_acl_restricted_all($config['id_user'], $report['id_group'], 'RW') + || check_acl_restricted_all($config['id_user'], $report['id_group'], 'RM') ) { $data[0] = ''.ui_print_truncate_text($report['name'], 70).''; } else { @@ -994,7 +1014,7 @@ switch ($action) { switch ($type_access_selected) { case 'group_view': - $edit = check_acl( + $edit = check_acl_restricted_all( $config['id_user'], $report['id_group'], 'RW' @@ -1005,7 +1025,7 @@ switch ($action) { break; case 'group_edit': - $edit = check_acl( + $edit = check_acl_restricted_all( $config['id_user'], $report['id_group_edit'], 'RW' diff --git a/pandora_console/godmode/reporting/visual_console_builder.data.php b/pandora_console/godmode/reporting/visual_console_builder.data.php index 5a0d4fb0f5..79b561edf0 100644 --- a/pandora_console/godmode/reporting/visual_console_builder.data.php +++ b/pandora_console/godmode/reporting/visual_console_builder.data.php @@ -133,13 +133,18 @@ if ($action == 'new') { src="">'; } -$table->data[1][0] = __('Group:'); +$table->data[1][0] = __('Group'); +$return_all_group = false; + +if (users_can_manage_group_all('RW') === true) { + $return_all_group = true; +} $table->data[1][1] = '
'.html_print_select_groups( $config['id_user'], 'RW', - true, + $return_all_group, 'id_group', $idGroup, '', diff --git a/pandora_console/godmode/reporting/visual_console_builder.php b/pandora_console/godmode/reporting/visual_console_builder.php index 775230c471..67194d21fa 100755 --- a/pandora_console/godmode/reporting/visual_console_builder.php +++ b/pandora_console/godmode/reporting/visual_console_builder.php @@ -85,8 +85,8 @@ else if ($activeTab != 'data' || ($activeTab == 'data' && $action != 'new')) { // ACL for the existing visual console // $vconsole_read = check_acl ($config['id_user'], $visualConsole['id_group'], "VR"); - $vconsole_write = check_acl($config['id_user'], $visualConsole['id_group'], 'VW'); - $vconsole_manage = check_acl($config['id_user'], $visualConsole['id_group'], 'VM'); + $vconsole_write = check_acl_restricted_all($config['id_user'], $visualConsole['id_group'], 'VW'); + $vconsole_manage = check_acl_restricted_all($config['id_user'], $visualConsole['id_group'], 'VM'); } else { db_pandora_audit( 'ACL Violation', @@ -143,8 +143,8 @@ switch ($activeTab) { // ACL for the new visual console // $vconsole_read_new = check_acl ($config['id_user'], $idGroup, "VR"); - $vconsole_write_new = check_acl($config['id_user'], $idGroup, 'VW'); - $vconsole_manage_new = check_acl($config['id_user'], $idGroup, 'VM'); + $vconsole_write_new = check_acl_restricted_all($config['id_user'], $idGroup, 'VW'); + $vconsole_manage_new = check_acl_restricted_all($config['id_user'], $idGroup, 'VM'); // The user should have permissions on the new group if (!$vconsole_write_new && !$vconsole_manage_new) { diff --git a/pandora_console/include/class/CredentialStore.class.php b/pandora_console/include/class/CredentialStore.class.php index 073f0831a3..0d918e5e5a 100644 --- a/pandora_console/include/class/CredentialStore.class.php +++ b/pandora_console/include/class/CredentialStore.class.php @@ -351,7 +351,21 @@ class CredentialStore extends Wizard return db_get_value_sql($sql); } - return db_get_all_rows_sql($sql); + $return = db_get_all_rows_sql($sql); + + // Filter out those items of group all that cannot be edited by user. + $return = array_filter( + $return, + function ($item) { + if ($item['id_group'] == 0 && users_can_manage_group_all('AR') === false) { + return false; + } else { + return true; + } + } + ); + + return $return; } @@ -826,6 +840,12 @@ class CredentialStore extends Wizard $values = []; } + $return_all_group = false; + + if (users_can_manage_group_all('AR') === true) { + $return_all_group = true; + } + $form = [ 'action' => '#', 'id' => 'modal_form', @@ -855,7 +875,7 @@ class CredentialStore extends Wizard 'id' => 'id_group', 'input_class' => 'flex-row', 'type' => 'select_groups', - 'returnAllGroup' => true, + 'returnAllGroup' => $return_all_group, 'selected' => $values['id_group'], 'return' => true, 'class' => 'w50p', @@ -999,6 +1019,7 @@ class CredentialStore extends Wizard * Process datatable item before draw it. */ function process_datatables_item(item) { + id = item.identifier; idrow = ' $this->dashboardFields['name'], 'hash' => self::generatePublicHash(), 'publicLink' => $this->publicLink, + 'dashboardGroup' => $this->dashboardFields['id_group'], ] ); } else { @@ -1025,6 +1026,7 @@ class Manager 'cells' => $this->cells, 'cellModeSlides' => $this->cellModeSlides, 'cellId' => ($this->cellId === 0) ? $this->cells[0]['id'] : $this->cellId, + 'dashboardGroup' => $this->dashboardFields['id_group'], ] ); } diff --git a/pandora_console/operation/agentes/pandora_networkmap.editor.php b/pandora_console/operation/agentes/pandora_networkmap.editor.php index a16874705c..1858985406 100644 --- a/pandora_console/operation/agentes/pandora_networkmap.editor.php +++ b/pandora_console/operation/agentes/pandora_networkmap.editor.php @@ -80,9 +80,15 @@ if ($edit_networkmap) { } else { $id_group = $values['id_group']; + $id_group_acl_check = $id_group_map; + + if ($id_group_map === null) { + $id_group_acl_check = $values['id_group_map']; + } + // ACL for the network map. - $networkmap_write = check_acl($config['id_user'], $id_group_map, 'MW'); - $networkmap_manage = check_acl($config['id_user'], $id_group_map, 'MM'); + $networkmap_write = check_acl_restricted_all($config['id_user'], $id_group_acl_check, 'MW'); + $networkmap_manage = check_acl_restricted_all($config['id_user'], $id_group_acl_check, 'MM'); if (!$networkmap_write && !$networkmap_manage) { db_pandora_audit( @@ -265,6 +271,12 @@ if ($not_found) { true ); + $return_all_group = false; + + if (users_can_manage_group_all('AR') === true) { + $return_all_group = true; + } + $table->data[1][0] = __('Group'); $table->data[1][1] = '
'.html_print_select_groups( // Id_user. @@ -272,7 +284,7 @@ if ($not_found) { // Privilege. 'AR', // ReturnAllGroup. - true, + $return_all_group, // Name. 'id_group_map', // Selected. diff --git a/pandora_console/operation/agentes/pandora_networkmap.php b/pandora_console/operation/agentes/pandora_networkmap.php index c50bfd9d0e..3c42108008 100644 --- a/pandora_console/operation/agentes/pandora_networkmap.php +++ b/pandora_console/operation/agentes/pandora_networkmap.php @@ -57,8 +57,8 @@ if (enterprise_installed()) { // ACL for the network map. // $networkmap_read = check_acl ($config['id_user'], $id_group, "MR"); - $networkmap_write = check_acl($config['id_user'], $id_group_map, 'MW'); - $networkmap_manage = check_acl($config['id_user'], $id_group_map, 'MM'); + $networkmap_write = check_acl_restricted_all($config['id_user'], $id_group_map, 'MW'); + $networkmap_manage = check_acl_restricted_all($config['id_user'], $id_group_map, 'MM'); if (!$networkmap_write && !$networkmap_manage) { db_pandora_audit( @@ -145,8 +145,8 @@ if (enterprise_installed()) { // ACL for the new network map - $networkmap_write_new = check_acl($config['id_user'], $id_group_map, 'MW'); - $networkmap_manage_new = check_acl($config['id_user'], $id_group_map, 'MM'); + $networkmap_write_new = check_acl_restricted_all($config['id_user'], $id_group_map, 'MW'); + $networkmap_manage_new = check_acl_restricted_all($config['id_user'], $id_group_map, 'MM'); if (!$networkmap_write && !$networkmap_manage) { db_pandora_audit( @@ -230,8 +230,8 @@ if ($new_networkmap || $save_networkmap) { // ACL for the network map // $networkmap_read = check_acl ($config['id_user'], $id_group, "MR"); - $networkmap_write = check_acl($config['id_user'], $id_group_map, 'MW'); - $networkmap_manage = check_acl($config['id_user'], $id_group_map, 'MM'); + $networkmap_write = check_acl_restricted_all($config['id_user'], $id_group_map, 'MW'); + $networkmap_manage = check_acl_restricted_all($config['id_user'], $id_group_map, 'MM'); if (!$networkmap_write && !$networkmap_manage) { db_pandora_audit( @@ -420,8 +420,8 @@ else if ($update_networkmap || $copy_networkmap || $delete) { return; } - $networkmap_write = check_acl($config['id_user'], $id_group_map_old, 'MW'); - $networkmap_manage = check_acl($config['id_user'], $id_group_map_old, 'MM'); + $networkmap_write = check_acl_restricted_all($config['id_user'], $id_group_map_old, 'MW'); + $networkmap_manage = check_acl_restricted_all($config['id_user'], $id_group_map_old, 'MM'); if (!$networkmap_write && !$networkmap_manage) { db_pandora_audit( @@ -440,8 +440,8 @@ else if ($update_networkmap || $copy_networkmap || $delete) { // ACL for the new network map $id_group_map = (int) get_parameter('id_group_map', 0); - $networkmap_write_new = check_acl($config['id_user'], $id_group_map, 'MW'); - $networkmap_manage_new = check_acl($config['id_user'], $id_group_map, 'MM'); + $networkmap_write_new = check_acl_restricted_all($config['id_user'], $id_group_map, 'MW'); + $networkmap_manage_new = check_acl_restricted_all($config['id_user'], $id_group_map, 'MM'); if (!$networkmap_write && !$networkmap_manage) { db_pandora_audit( @@ -727,9 +727,9 @@ switch ($tab) { foreach ($network_maps as $network_map) { // ACL for the network map - $networkmap_read = check_acl($config['id_user'], $network_map['id_group_map'], 'MR'); - $networkmap_write = check_acl($config['id_user'], $network_map['id_group_map'], 'MW'); - $networkmap_manage = check_acl($config['id_user'], $network_map['id_group_map'], 'MM'); + $networkmap_read = check_acl_restricted_all($config['id_user'], $network_map['id_group_map'], 'MR'); + $networkmap_write = check_acl_restricted_all($config['id_user'], $network_map['id_group_map'], 'MW'); + $networkmap_manage = check_acl_restricted_all($config['id_user'], $network_map['id_group_map'], 'MM'); if (!$networkmap_read && !$networkmap_write && !$networkmap_manage) { db_pandora_audit( @@ -785,6 +785,10 @@ switch ($tab) { $data['groups'] = ui_print_group_icon($network_map['id_group_map'], true); + $data['copy'] = ''; + $data['edit'] = ''; + $data['delete'] = ''; + if ($networkmap_write || $networkmap_manage) { $table->cellclass[] = [ 'copy' => 'action_buttons', diff --git a/pandora_console/operation/gis_maps/gis_map.php b/pandora_console/operation/gis_maps/gis_map.php index 59b3b6da64..01653e958b 100644 --- a/pandora_console/operation/gis_maps/gis_map.php +++ b/pandora_console/operation/gis_maps/gis_map.php @@ -170,8 +170,10 @@ if ($maps !== false) { $data['name'] = ''.$map['map_name'].' '; $data['group'] = ui_print_group_icon($map['group_id'], true); - if ($edit_gis_maps) { - if ($display_default_column) { + $data['op'] = ''; + + if (check_acl_restricted_all($config['id_user'], $map['group_id'], 'MW') || check_acl_restricted_all($config['id_user'], $map['group_id'], 'MM')) { + if (check_acl_restricted_all($config['id_user'], 0, 'MM')) { $checked = false; if ($map['default_map']) { $checked = true; diff --git a/pandora_console/operation/reporting/graph_viewer.php b/pandora_console/operation/reporting/graph_viewer.php index 68acde60a1..47610af86a 100644 --- a/pandora_console/operation/reporting/graph_viewer.php +++ b/pandora_console/operation/reporting/graph_viewer.php @@ -145,7 +145,9 @@ if ($view_graph) { $options = []; - if (check_acl($config['id_user'], 0, 'RW')) { + if (check_acl_restricted_all($config['id_user'], $graph['id_group'], 'RW') + || check_acl_restricted_all($config['id_user'], $graph['id_group'], 'RM') + ) { $options = [ 'graph_list' => [ 'active' => false, @@ -160,6 +162,13 @@ if ($view_graph) { 'text' => ''.html_print_image('images/builder.png', true, ['title' => __('Graph editor')]).'', ], ]; + } else { + $options = [ + 'graph_list' => [ + 'active' => false, + 'text' => ''.html_print_image('images/list.png', true, ['title' => __('Graph list')]).'', + ], + ]; } $options['view']['text'] = ''.html_print_image( @@ -275,7 +284,7 @@ if ($view_graph) { echo ''; echo ""; - echo "
  ".__('Equalize maximum thresholds').''.ui_print_help_tip(__('If an option is selected, all graphs will have the highest value from all modules included in the graph as a maximum threshold'), true); + echo "
  ".__('Equalize maxiddmum thresholds').''.ui_print_help_tip(__('If an option is selected, all graphs will have the highest value from all modules included in the graph as a maximum threshold'), true); html_print_checkbox('threshold', CUSTOM_GRAPH_BULLET_CHART_THRESHOLD, $check, false, false, '', false); echo '
'; diff --git a/pandora_console/operation/reporting/reporting_viewer.php b/pandora_console/operation/reporting/reporting_viewer.php index 7528f15253..cc35a6cdfa 100755 --- a/pandora_console/operation/reporting/reporting_viewer.php +++ b/pandora_console/operation/reporting/reporting_viewer.php @@ -80,7 +80,16 @@ $options['list_reports'] = [ ).'
', ]; -if (check_acl($config['id_user'], 0, 'RW')) { +if ($id_report > 0) { + $report_group = db_get_value( + 'id_group', + 'treport', + 'id_report', + $id_report + ); +} + +if (check_acl_restricted_all($config['id_user'], $report_group, 'RW')) { $options['main']['text'] = ''.html_print_image( 'images/op_reporting.png', true, diff --git a/pandora_console/operation/visual_console/legacy_view.php b/pandora_console/operation/visual_console/legacy_view.php index d4405876c4..986830f086 100644 --- a/pandora_console/operation/visual_console/legacy_view.php +++ b/pandora_console/operation/visual_console/legacy_view.php @@ -97,9 +97,9 @@ $bheight = $layout['height']; $pure_url = '&pure='.$config['pure']; // ACL -$vconsole_read = check_acl($config['id_user'], $id_group, 'VR'); -$vconsole_write = check_acl($config['id_user'], $id_group, 'VW'); -$vconsole_manage = check_acl($config['id_user'], $id_group, 'VM'); +$vconsole_read = check_acl_restricted_all($config['id_user'], $id_group, 'VR'); +$vconsole_write = check_acl_restricted_all($config['id_user'], $id_group, 'VW'); +$vconsole_manage = check_acl_restricted_all($config['id_user'], $id_group, 'VM'); if (! $vconsole_read && !$vconsole_write && !$vconsole_manage) { db_pandora_audit( diff --git a/pandora_console/operation/visual_console/view.php b/pandora_console/operation/visual_console/view.php index 3b70215aa2..164d38ac2c 100644 --- a/pandora_console/operation/visual_console/view.php +++ b/pandora_console/operation/visual_console/view.php @@ -95,9 +95,9 @@ $groupId = $visualConsoleData['groupId']; $visualConsoleName = $visualConsoleData['name']; // ACL. -$aclRead = check_acl($config['id_user'], $groupId, 'VR'); -$aclWrite = check_acl($config['id_user'], $groupId, 'VW'); -$aclManage = check_acl($config['id_user'], $groupId, 'VM'); +$aclRead = check_acl_restricted_all($config['id_user'], $groupId, 'VR'); +$aclWrite = check_acl_restricted_all($config['id_user'], $groupId, 'VW'); +$aclManage = check_acl_restricted_all($config['id_user'], $groupId, 'VM'); if (!$aclRead && !$aclWrite && !$aclManage) { db_pandora_audit( diff --git a/pandora_console/views/dashboard/formDashboard.php b/pandora_console/views/dashboard/formDashboard.php index b80f3ab557..bcbdd67622 100644 --- a/pandora_console/views/dashboard/formDashboard.php +++ b/pandora_console/views/dashboard/formDashboard.php @@ -43,6 +43,12 @@ if (empty($arrayDashboard) === true) { } } +$return_all_group = false; + +if (users_can_manage_group_all('RW') === true) { + $return_all_group = true; +} + $dataQuery = ['dashboardId' => $dashboardId]; $url = ui_get_full_url( @@ -100,11 +106,12 @@ $inputs = [ [ 'label' => __('Group'), 'arguments' => [ - 'name' => 'id_group', - 'id' => 'id_group', - 'type' => 'select_groups', - 'selected' => $arrayDashboard['id_group'], - 'return' => true, + 'name' => 'id_group', + 'id' => 'id_group', + 'type' => 'select_groups', + 'returnAllGroup' => $return_all_group, + 'selected' => $arrayDashboard['id_group'], + 'return' => true, ], ], ], diff --git a/pandora_console/views/dashboard/header.php b/pandora_console/views/dashboard/header.php index f54a7c04b6..082011cb8e 100644 --- a/pandora_console/views/dashboard/header.php +++ b/pandora_console/views/dashboard/header.php @@ -194,7 +194,7 @@ if ($config['public_dashboard'] === true) { 'combo_refresh_countdown' => $comboRefreshCountdown, ]; } else if ($config['pure']) { - if (check_acl($config['id_user'], 0, 'RW') === 0) { + if (check_acl_restricted_all($config['id_user'], $dashboardGroup, 'RW') === 0) { $buttons = [ 'back_to_dashboard_list' => $back_to_dashboard_list, 'normalscreen' => $normalscreen, @@ -221,7 +221,7 @@ if ($config['public_dashboard'] === true) { } } } else { - if (check_acl($config['id_user'], 0, 'RW') === 0) { + if (check_acl_restricted_all($config['id_user'], $dashboardGroup, 'RW') === 0) { $buttons = [ 'back_to_dashboard_list' => $back_to_dashboard_list, 'fullscreen' => $fullscreen, diff --git a/pandora_console/views/dashboard/list.php b/pandora_console/views/dashboard/list.php index e5c37b2500..629c5f8eae 100644 --- a/pandora_console/views/dashboard/list.php +++ b/pandora_console/views/dashboard/list.php @@ -155,6 +155,11 @@ if (empty($dashboards) === true) { $data['full_screen'] .= ''; if ($manageDashboards === 1) { + $data['copy'] = ''; + $data['delete'] = ''; + } + + if (check_acl_restricted_all($config['id_user'], $dashboard['id_group'], 'RM')) { $dataQueryCopy = [ 'dashboardId' => $dashboard['id'], 'copyDashboard' => 1, From cd52b30eb5b218b86171a03e950656a309d1670b Mon Sep 17 00:00:00 2001 From: alejandro-campos Date: Thu, 22 Oct 2020 15:47:39 +0200 Subject: [PATCH 030/263] visual fixes --- .../godmode/gis_maps/configure_gis_map.php | 2 +- pandora_console/operation/gis_maps/gis_map.php | 11 +++++++++-- 2 files changed, 10 insertions(+), 3 deletions(-) diff --git a/pandora_console/godmode/gis_maps/configure_gis_map.php b/pandora_console/godmode/gis_maps/configure_gis_map.php index 907786e6fd..b89ad86fad 100644 --- a/pandora_console/godmode/gis_maps/configure_gis_map.php +++ b/pandora_console/godmode/gis_maps/configure_gis_map.php @@ -32,7 +32,7 @@ $action = get_parameter('action', 'new_map'); $gis_map_group = db_get_value('group_id', 'tgis_map', 'id_tgis_map', $idMap); -if (!check_acl_restricted_all($config['id_user'], $gis_map_group, 'MW') && !check_acl_restricted_all($config['id_user'], $gis_map_group, 'MW')) { +if ($idMap > 0 && !check_acl_restricted_all($config['id_user'], $gis_map_group, 'MW') && !check_acl_restricted_all($config['id_user'], $gis_map_group, 'MW')) { db_pandora_audit('ACL Violation', 'Trying to access map builder'); include 'general/noaccess.php'; return; diff --git a/pandora_console/operation/gis_maps/gis_map.php b/pandora_console/operation/gis_maps/gis_map.php index 01653e958b..ca66058050 100644 --- a/pandora_console/operation/gis_maps/gis_map.php +++ b/pandora_console/operation/gis_maps/gis_map.php @@ -170,9 +170,16 @@ if ($maps !== false) { $data['name'] = ''.$map['map_name'].' '; $data['group'] = ui_print_group_icon($map['group_id'], true); - $data['op'] = ''; + if (check_acl($config['id_user'], 0, 'MW') + || check_acl($config['id_user'], 0, 'MM') + ) { + $data['default'] = ''; + $data['op'] = ''; + } - if (check_acl_restricted_all($config['id_user'], $map['group_id'], 'MW') || check_acl_restricted_all($config['id_user'], $map['group_id'], 'MM')) { + if (check_acl_restricted_all($config['id_user'], $map['group_id'], 'MW') + || check_acl_restricted_all($config['id_user'], $map['group_id'], 'MM') + ) { if (check_acl_restricted_all($config['id_user'], 0, 'MM')) { $checked = false; if ($map['default_map']) { From 5842235846b2ed94b85c7205cb696a2f82d86cb0 Mon Sep 17 00:00:00 2001 From: fbsanchez Date: Mon, 26 Oct 2020 13:37:05 +0100 Subject: [PATCH 031/263] Orphan service modules + control require argument select from sql --- pandora_console/include/functions_html.php | 19 ++++++++++++++++--- .../lib/PandoraFMS/PredictionServer.pm | 11 +++++++++-- 2 files changed, 25 insertions(+), 5 deletions(-) diff --git a/pandora_console/include/functions_html.php b/pandora_console/include/functions_html.php index 092044765c..b5c9078bfb 100644 --- a/pandora_console/include/functions_html.php +++ b/pandora_console/include/functions_html.php @@ -1351,6 +1351,7 @@ function html_print_select_multiple_modules_filtered(array $data):string * @param mixed $size Max elements showed in select or default (size=10) * @param integer $truncante_size Truncate size of the element, by default is set to GENERIC_SIZE_TEXT constant * @param integer $class Class to apply. + * @param boolean $required Select is required or not. * * @return string HTML code if return parameter is true. */ @@ -1368,7 +1369,8 @@ function html_print_select_from_sql( $style=false, $size=false, $trucate_size=GENERIC_SIZE_TEXT, - $class='' + $class='', + $required=false ) { global $config; @@ -1404,7 +1406,17 @@ function html_print_select_from_sql( $disabled, $style, '', - $size + $size, + // Modal. + false, + // Message. + '', + // Select_all. + false, + // Simple_multiple_options. + false, + // Required. + $required ); } @@ -4363,7 +4375,8 @@ function html_print_input($data, $wrapper='div', $input_only=false) ((isset($data['style']) === true) ? $data['style'] : false), ((isset($data['size']) === true) ? $data['size'] : false), ((isset($data['trucate_size']) === true) ? $data['trucate_size'] : GENERIC_SIZE_TEXT), - ((isset($data['class']) === true) ? $data['class'] : '') + ((isset($data['class']) === true) ? $data['class'] : ''), + ((isset($data['required']) === true) ? $data['required'] : false) ); break; diff --git a/pandora_server/lib/PandoraFMS/PredictionServer.pm b/pandora_server/lib/PandoraFMS/PredictionServer.pm index 6f77845456..ab38cca2aa 100644 --- a/pandora_server/lib/PandoraFMS/PredictionServer.pm +++ b/pandora_server/lib/PandoraFMS/PredictionServer.pm @@ -108,13 +108,16 @@ sub data_producer ($) { ORDER BY last_execution_try ASC ', safe_input($pa_config->{'servername'})); } else { + # If is metaconsole server, will evaluate orphan modules also. @rows = get_db_rows ($dbh, 'SELECT DISTINCT(tagente_modulo.id_agente_modulo), tagente_modulo.flag, last_execution_try FROM tagente, tagente_modulo, tagente_estado WHERE ((server_name = ?) OR (server_name = ANY(SELECT name FROM tserver - WHERE status = 0 AND server_type = ?))) + WHERE status = 0 AND server_type = ?)) + OR ((server_name = 0 OR server_name IS NULL) AND 1=?) + ) AND tagente_modulo.id_agente = tagente.id_agente AND tagente.disabled = 0 AND tagente_modulo.disabled = 0 @@ -123,7 +126,11 @@ sub data_producer ($) { AND tagente_modulo.id_modulo = 5 AND (tagente_modulo.flag = 1 OR (tagente_estado.last_execution_try + tagente_estado.current_interval) < UNIX_TIMESTAMP()) - ORDER BY last_execution_try ASC', safe_input($pa_config->{'servername'}), PREDICTIONSERVER); + ORDER BY last_execution_try ASC', + safe_input($pa_config->{'servername'}), + PREDICTIONSERVER, + is_metaconsole($pa_config) + ); } foreach my $row (@rows) { From 633116a7935e35781f0f0818b5529f52b7bd60d9 Mon Sep 17 00:00:00 2001 From: fbsanchez Date: Mon, 26 Oct 2020 17:12:27 +0100 Subject: [PATCH 032/263] Added -force- option to Agent::lastStatus --- pandora_console/include/lib/Agent.php | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/pandora_console/include/lib/Agent.php b/pandora_console/include/lib/Agent.php index f7a39bdab6..16e1c7c5a0 100644 --- a/pandora_console/include/lib/Agent.php +++ b/pandora_console/include/lib/Agent.php @@ -104,13 +104,22 @@ class Agent extends Entity /** * Return last value (status) of the agent. * + * @param boolean $recalc Forcre recalculation. + * * @return integer Status of the agent. */ - public function lastStatus() + public function lastStatus($force=false) { + if ($force === true) { + return \agents_get_status( + $this->id_agente() + ); + } + return \agents_get_status_from_counts( $this->toArray() ); + } From 1c14fa4bcba18ce9f419042812fa67ae90a0eea4 Mon Sep 17 00:00:00 2001 From: fbsanchez Date: Mon, 26 Oct 2020 17:12:55 +0100 Subject: [PATCH 033/263] Added -force- option to Agent::lastStatus --- pandora_console/include/lib/Agent.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pandora_console/include/lib/Agent.php b/pandora_console/include/lib/Agent.php index 16e1c7c5a0..cfba92f526 100644 --- a/pandora_console/include/lib/Agent.php +++ b/pandora_console/include/lib/Agent.php @@ -104,11 +104,11 @@ class Agent extends Entity /** * Return last value (status) of the agent. * - * @param boolean $recalc Forcre recalculation. + * @param boolean $force Force recalculation. * * @return integer Status of the agent. */ - public function lastStatus($force=false) + public function lastStatus(bool $force=false) { if ($force === true) { return \agents_get_status( From 6d781bc96187df21394700412878ce331c2398f5 Mon Sep 17 00:00:00 2001 From: fbsanchez Date: Mon, 26 Oct 2020 18:12:36 +0100 Subject: [PATCH 034/263] uncompressing data before serving it api_get_module_data -absurd format kept- --- pandora_console/include/functions_api.php | 70 ++++++++++++----------- pandora_console/include/functions_db.php | 2 +- 2 files changed, 39 insertions(+), 33 deletions(-) diff --git a/pandora_console/include/functions_api.php b/pandora_console/include/functions_api.php index 6401910faa..6e48cbd3c0 100644 --- a/pandora_console/include/functions_api.php +++ b/pandora_console/include/functions_api.php @@ -194,6 +194,7 @@ function returnData($returnType, $data, $separator=';') if (is_array($data['data'])) { if (array_key_exists('list_index', $data)) { if ($returnType == 'csv_head') { + header('Content-type: text/csv'); foreach ($data['list_index'] as $index) { echo $index; if (end($data['list_index']) == $index) { @@ -8443,10 +8444,16 @@ function api_get_module_data($id, $thrash1, $other, $returnType) return; } - $separator = $other['data'][0]; - $periodSeconds = $other['data'][1]; - $tstart = $other['data'][2]; - $tend = $other['data'][3]; + $separator = ';'; + $tstart = null; + $tend = null; + $periodSeconds = null; + if (is_array($other) === true && is_array($other['data']) === true) { + $separator = $other['data'][0]; + $periodSeconds = $other['data'][1]; + $tstart = $other['data'][2]; + $tend = $other['data'][3]; + } if (($tstart != '') && ($tend != '')) { try { @@ -8467,36 +8474,18 @@ function api_get_module_data($id, $thrash1, $other, $returnType) $date_end = $date_end->format('U'); } catch (Exception $e) { returnError('error_query_module_data', 'Error in date format. '); + return; } - - $sql = sprintf( - 'SELECT utimestamp, datos - FROM tagente_datos - WHERE id_agente_modulo = %d AND utimestamp > %d - AND utimestamp < %d - ORDER BY utimestamp DESC', - $id, - $date_start, - $date_end - ); } else { - if ($periodSeconds == null) { - $sql = sprintf( - 'SELECT utimestamp, datos - FROM tagente_datos - WHERE id_agente_modulo = %d - ORDER BY utimestamp DESC', - $id - ); + if ($periodSeconds !== null) { + $date_end = get_system_time(); + $date_start = (get_system_time() - $periodSeconds); } else { - $sql = sprintf( - 'SELECT utimestamp, datos - FROM tagente_datos - WHERE id_agente_modulo = %d AND utimestamp > %d - ORDER BY utimestamp DESC', - $id, - (get_system_time() - $periodSeconds) - ); + $date_end = get_system_time(); + $result = modules_get_first_date($id, $tstart); + if ($result !== false) { + $date_start = $result['first_utimestamp']; + } } } @@ -8505,13 +8494,30 @@ function api_get_module_data($id, $thrash1, $other, $returnType) 'utimestamp', 'datos', ]; - $data['data'] = db_get_all_rows_sql($sql); + + $data['data'] = array_reduce( + db_uncompress_module_data($id, $date_start, $date_end), + function ($carry, $item) { + if (is_array($item['data']) === true) { + foreach ($item['data'] as $i => $v) { + $carry[] = [ + 'utimestamp' => $v['utimestamp'], + 'datos' => $v['datos'], + ]; + } + } + + return $carry; + }, + [] + ); if ($data === false) { returnError('error_query_module_data', 'Error in the query of module data.'); } else if ($data['data'] == '') { returnError('error_query_module_data', 'No data to show.'); } else { + // returnData('csv_head', $data, $separator); returnData('csv', $data, $separator); } } diff --git a/pandora_console/include/functions_db.php b/pandora_console/include/functions_db.php index 1e2a5d79f8..1f9d6883d0 100644 --- a/pandora_console/include/functions_db.php +++ b/pandora_console/include/functions_db.php @@ -702,7 +702,7 @@ function db_get_module_ranges_unknown( * @param utimestamp $tend End of the catch. * @param integer $slice_size Size of slice(default-> module_interval). * - * @return hash with the data uncompressed in blocks of module_interval + * @return array with the data uncompressed in blocks of module_interval * false in case of empty result * * Note: All "unknown" data are marked as NULL From c1a7fc2e9a53bed6e8c97343542034ea0391fa70 Mon Sep 17 00:00:00 2001 From: Daniel Maya Date: Wed, 28 Oct 2020 10:57:21 +0100 Subject: [PATCH 035/263] Added snmp_storm_silence_period --- pandora_server/FreeBSD/pandora_server.conf.new | 3 +++ pandora_server/conf/pandora_server.conf.new | 3 +++ pandora_server/conf/pandora_server.conf.windows | 3 +++ pandora_server/lib/PandoraFMS/Config.pm | 4 ++++ pandora_server/lib/PandoraFMS/SNMPServer.pm | 15 +++++++++++++-- 5 files changed, 26 insertions(+), 2 deletions(-) diff --git a/pandora_server/FreeBSD/pandora_server.conf.new b/pandora_server/FreeBSD/pandora_server.conf.new index c6394319b5..c303931b49 100644 --- a/pandora_server/FreeBSD/pandora_server.conf.new +++ b/pandora_server/FreeBSD/pandora_server.conf.new @@ -481,6 +481,9 @@ snmp_storm_protection 25 # Time interval for snmp_storm protection (in seconds). snmp_storm_timeout 10 +# Silenced time period in seconds, when trap storm is detected +snmp_storm_silence_period 300 + # Default texts for some events. The macros _module_ and _data_ are supported. #text_going_down_normal Module '_module_' is going to NORMAL (_data_) #text_going_up_critical Module '_module_' is going to CRITICAL (_data_) diff --git a/pandora_server/conf/pandora_server.conf.new b/pandora_server/conf/pandora_server.conf.new index 2c5e6d0c1b..98eff5ca78 100644 --- a/pandora_server/conf/pandora_server.conf.new +++ b/pandora_server/conf/pandora_server.conf.new @@ -537,6 +537,9 @@ snmp_storm_protection 25 # Time interval for snmp_storm protection (in seconds). snmp_storm_timeout 10 +# Silenced time period in seconds, when trap storm is detected +snmp_storm_silence_period 300 + # Default texts for some events. The macros _module_ and _data_ are supported. #text_going_down_normal Module '_module_' is going to NORMAL (_data_) #text_going_up_critical Module '_module_' is going to CRITICAL (_data_) diff --git a/pandora_server/conf/pandora_server.conf.windows b/pandora_server/conf/pandora_server.conf.windows index 3c890f83eb..226efc52fe 100644 --- a/pandora_server/conf/pandora_server.conf.windows +++ b/pandora_server/conf/pandora_server.conf.windows @@ -471,6 +471,9 @@ snmp_storm_protection 25 # Time interval for snmp_storm protection (in seconds). snmp_storm_timeout 10 +# Silenced time period in seconds, when trap storm is detected +snmp_storm_silence_period 300 + # Default texts for some events. The macros _module_ and _data_ are supported. #text_going_down_normal Module '_module_' is going to NORMAL (_data_) #text_going_up_critical Module '_module_' is going to CRITICAL (_data_) diff --git a/pandora_server/lib/PandoraFMS/Config.pm b/pandora_server/lib/PandoraFMS/Config.pm index 4029ed9bc3..093224acea 100644 --- a/pandora_server/lib/PandoraFMS/Config.pm +++ b/pandora_server/lib/PandoraFMS/Config.pm @@ -321,6 +321,7 @@ sub pandora_load_config { $pa_config->{"snmp_pdu_address"} = 0; # 5.0 $pa_config->{"snmp_storm_protection"} = 0; # 5.0 $pa_config->{"snmp_storm_timeout"} = 600; # 5.0 + $pa_config->{"snmp_storm_silence_period"} = 0; # 7.0 $pa_config->{"snmp_delay"} = 0; # > 6.0SP3 $pa_config->{"snmpconsole_threads"} = 1; # 5.1 $pa_config->{"translate_variable_bindings"} = 0; # 5.1 @@ -675,6 +676,9 @@ sub pandora_load_config { elsif ($parametro =~ m/^snmp_storm_timeout\s+(\d+)/i) { $pa_config->{'snmp_storm_timeout'}= clean_blank($1); } + elsif ($parametro =~ m/^snmp_storm_silence_period\s+(\d+)/i) { + $pa_config->{'snmp_storm_silence_period'}= clean_blank($1); + } elsif ($parametro =~ m/^snmp_delay\s+(\d+)/i) { $pa_config->{'snmp_delay'}= clean_blank($1); } diff --git a/pandora_server/lib/PandoraFMS/SNMPServer.pm b/pandora_server/lib/PandoraFMS/SNMPServer.pm index 5a5a741525..06e5a15c2f 100644 --- a/pandora_server/lib/PandoraFMS/SNMPServer.pm +++ b/pandora_server/lib/PandoraFMS/SNMPServer.pm @@ -49,6 +49,8 @@ my $TaskSem :shared; # Trap statistics by agent my %AGENTS = (); +# Sources silenced by storm protection. +my %SILENCEDSOURCES = (); # Index and buffer management for trap log files my $SNMPTRAPD = { 'log_file' => '', 'fd' => undef, 'idx_file' => '', 'last_line' => 0, 'last_size' => 0, 'read_ahead_line' => '', 'read_ahead_pos' => 0 }; @@ -167,17 +169,26 @@ sub data_producer ($) { if (! defined ($AGENTS{$source})) { $AGENTS{$source}{'count'} = 1; $AGENTS{$source}{'event'} = 0; + if (! defined ($SILENCEDSOURCES{$source})) { + $SILENCEDSOURCES{$source} = 0; + } } else { $AGENTS{$source}{'count'} += 1; } + # Silence source. + if ((defined ($SILENCEDSOURCES{$source})) && ($SILENCEDSOURCES{$source} > $curr_time)) { + next; + } if ($pa_config->{'snmp_storm_protection'} > 0 && $AGENTS{$source}{'count'} > $pa_config->{'snmp_storm_protection'}) { if ($AGENTS{$source}{'event'} == 0) { - pandora_event ($pa_config, "Too many traps coming from $source. Silenced for " . int ($pa_config->{"snmp_storm_timeout"} / 60) . " minutes.", 0, 0, 4, 0, 0, 'system', 0, $dbh); + $SILENCEDSOURCES{$source} = $curr_time + $pa_config->{'snmp_storm_silence_period'}; + my $silenced_time = ($pa_config->{'snmp_storm_silence_period'} eq 0 ? $pa_config->{"snmp_storm_timeout"} : $pa_config->{'snmp_storm_silence_period'}); + pandora_event ($pa_config, "Too many traps coming from $source. Silenced for " . $silenced_time . " seconds.", 0, 0, 4, 0, 0, 'system', 0, $dbh); } $AGENTS{$source}{'event'} = 1; next; } - + push (@tasks, $line); } } From e300f2244d027d65fa20100629bb81be24f13c99 Mon Sep 17 00:00:00 2001 From: Jose Gonzalez Date: Wed, 28 Oct 2020 17:56:42 +0100 Subject: [PATCH 036/263] Adding delete_agent control for meta --- pandora_console/include/functions_api.php | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/pandora_console/include/functions_api.php b/pandora_console/include/functions_api.php index 6401910faa..f9fd4efaa9 100644 --- a/pandora_console/include/functions_api.php +++ b/pandora_console/include/functions_api.php @@ -1885,11 +1885,17 @@ function api_set_delete_agent($id, $thrash1, $other, $thrash3) } } else { // Delete only if the centralised mode is disabled. - if (is_central_policies_on_node()) { + $headers = getallheaders(); + if (!isset($headers['idk']) || !is_management_allowed($headers['idk'])) { returnError('centralized'); exit; } + // Support for Pandora Enterprise. + if (license_free() === false) { + define('PANDORA_ENTERPRISE', true); + } + if ($agent_by_alias) { $idsAgents = agents_get_agent_id_by_alias(io_safe_input($id)); } else { From 241c90f2012f05b1efa546b5e4536e5c67827d6b Mon Sep 17 00:00:00 2001 From: alejandro-campos Date: Tue, 3 Nov 2020 15:58:53 +0100 Subject: [PATCH 037/263] fixed security vulnerability --- pandora_console/godmode/servers/plugin.php | 13 ++ .../godmode/setup/file_manager.php | 13 ++ .../include/functions_filemanager.php | 214 ++++++++---------- .../snmpconsole/snmp_mib_uploader.php | 13 ++ 4 files changed, 138 insertions(+), 115 deletions(-) diff --git a/pandora_console/godmode/servers/plugin.php b/pandora_console/godmode/servers/plugin.php index c32152e12d..9b98221a56 100644 --- a/pandora_console/godmode/servers/plugin.php +++ b/pandora_console/godmode/servers/plugin.php @@ -242,6 +242,19 @@ if ($filemanager) { $chunck_url = '&create=1'; } + $upload_file_or_zip = (bool) get_parameter('upload_file_or_zip'); + $create_text_file = (bool) get_parameter('create_text_file'); + + $default_real_directory = realpath($config['homedir'].'/'.$fallback_directory); + + if ($upload_file_or_zip) { + upload_file($upload_file_or_zip, $default_real_directory); + } + + if ($create_text_file) { + create_text_file($default_real_directory); + } + filemanager_file_explorer( $real_directory, $directory, diff --git a/pandora_console/godmode/setup/file_manager.php b/pandora_console/godmode/setup/file_manager.php index 6d5e7679b7..10b1ab740a 100644 --- a/pandora_console/godmode/setup/file_manager.php +++ b/pandora_console/godmode/setup/file_manager.php @@ -66,6 +66,19 @@ $real_directory = realpath($config['homedir'].'/'.$directory); echo '

'.__('Index of %s', $directory).'

'; +$upload_file_or_zip = (bool) get_parameter('upload_file_or_zip'); +$create_text_file = (bool) get_parameter('create_text_file'); + +$default_real_directory = realpath($config['homedir'].'/'.$fallback_directory); + +if ($upload_file_or_zip) { + upload_file($upload_file_or_zip, $default_real_directory); +} + +if ($create_text_file) { + create_text_file($default_real_directory); +} + filemanager_file_explorer( $real_directory, $directory, diff --git a/pandora_console/include/functions_filemanager.php b/pandora_console/include/functions_filemanager.php index cf587f8081..2ff77d74fa 100644 --- a/pandora_console/include/functions_filemanager.php +++ b/pandora_console/include/functions_filemanager.php @@ -123,26 +123,11 @@ if ($sec2 == 'enterprise/godmode/agentes/collections' || $sec2 == 'advanced/coll $homedir_filemanager .= '/attachment/collection/'; } -$upload_file_or_zip = (bool) get_parameter('upload_file_or_zip'); -if ($upload_file_or_zip) { - $decompress = get_parameter('decompress'); - if (!$decompress) { - $upload_file = true; - $upload_zip = false; - } else { - $upload_file = false; - $upload_zip = true; - } -} else { - $upload_file = (bool) get_parameter('upload_file'); - $upload_zip = (bool) get_parameter('upload_zip'); -} - -// Upload file -if ($upload_file) { - // Load global vars +function upload_file($upload_file_or_zip, $default_real_directory) +{ global $config; + global $homedir_filemanager; $config['filemanager'] = []; $config['filemanager']['correct_upload_file'] = 0; @@ -156,43 +141,102 @@ if ($upload_file) { return; } - if (isset($_FILES['file']) && $_FILES['file']['name'] != '') { - $filename = $_FILES['file']['name']; - $filesize = $_FILES['file']['size']; - $real_directory = io_safe_output((string) get_parameter('real_directory')); - $directory = io_safe_output((string) get_parameter('directory')); - $umask = io_safe_output((string) get_parameter('umask', '')); - - $hash = get_parameter('hash', ''); - $testHash = md5($real_directory.$directory.$config['dbpass']); - - if ($hash != $testHash) { - $config['filemanager']['message'] = ui_print_error_message(__('Security error'), '', true); + if ($upload_file_or_zip) { + $decompress = get_parameter('decompress'); + if (!$decompress) { + $upload_file = true; + $upload_zip = false; } else { - // Copy file to directory and change name - if ($directory == '') { - $nombre_archivo = $real_directory.'/'.$filename; - } else { - $nombre_archivo = $homedir_filemanager.'/'.$directory.'/'.$filename; - } + $upload_file = false; + $upload_zip = true; + } + } else { + $upload_file = (bool) get_parameter('upload_file'); + $upload_zip = (bool) get_parameter('upload_zip'); + } - if (! @copy($_FILES['file']['tmp_name'], $nombre_archivo)) { - $config['filemanager']['message'] = ui_print_error_message(__('Upload error'), '', true); + // Upload file + if ($upload_file) { + if (isset($_FILES['file']) && $_FILES['file']['name'] != '') { + $filename = $_FILES['file']['name']; + $filesize = $_FILES['file']['size']; + $real_directory = io_safe_output((string) get_parameter('real_directory')); + $directory = io_safe_output((string) get_parameter('directory')); + $umask = io_safe_output((string) get_parameter('umask', '')); + + if (strpos($real_directory, $default_real_directory) !== 0) { + // Perform security check to determine whether received upload directory is part of the default path for caller uploader and user is not trying to access an external path (avoid execution of PHP files in directories that are not explicitly controlled by corresponding .htaccess). + ui_print_error_message(__('Security error')); } else { - if ($umask !== '') { - chmod($nombre_archivo, $umask); + // Copy file to directory and change name + if ($directory == '') { + $nombre_archivo = $real_directory.'/'.$filename; + } else { + $nombre_archivo = $homedir_filemanager.'/'.$directory.'/'.$filename; } - $config['filemanager']['correct_upload_file'] = 1; - $config['filemanager']['message'] = ui_print_success_message(__('Upload correct'), '', true); + if (! @copy($_FILES['file']['tmp_name'], $nombre_archivo)) { + $config['filemanager']['message'] = ui_print_error_message(__('Upload error')); + } else { + if ($umask !== '') { + chmod($nombre_archivo, $umask); + } - // Delete temporal file - unlink($_FILES['file']['tmp_name']); + $config['filemanager']['correct_upload_file'] = 1; + ui_print_success_message(__('Upload correct')); + + // Delete temporal file + unlink($_FILES['file']['tmp_name']); + } + } + } + } + + // Upload zip + if ($upload_zip) { + if (isset($_FILES['file']) && $_FILES['file']['name'] != '') { + $filename = $_FILES['file']['name']; + $filesize = $_FILES['file']['size']; + $real_directory = (string) get_parameter('real_directory'); + $real_directory = io_safe_output($real_directory); + $directory = (string) get_parameter('directory'); + $directory = io_safe_output($directory); + + if (strpos($real_directory, $default_real_directory) !== 0) { + // Perform security check to determine whether received upload directory is part of the default path for caller uploader and user is not trying to access an external path (avoid execution of PHP files in directories that are not explicitly controlled by corresponding .htaccess). + ui_print_error_message(__('Security error')); + } else { + // Copy file to directory and change name + if ($directory == '') { + $nombre_archivo = $real_directory.'/'.$filename; + } else { + $nombre_archivo = $homedir_filemanager.'/'.$directory.'/'.$filename; + } + + if (! @copy($_FILES['file']['tmp_name'], $nombre_archivo)) { + ui_print_error_message(__('Attach error')); + } else { + // Delete temporal file + unlink($_FILES['file']['tmp_name']); + + // Extract the zip file + $zip = new ZipArchive; + $pathname = $homedir_filemanager.'/'.$directory.'/'; + + if ($zip->open($nombre_archivo) === true) { + $zip->extractTo($pathname); + unlink($nombre_archivo); + } + + ui_print_success_message(__('Upload correct')); + $config['filemanager']['correct_upload_file'] = 1; + } } } } } + if (isset($_SERVER['CONTENT_LENGTH'])) { // Control the max_post_size exceed if (intval($_SERVER['CONTENT_LENGTH']) > 0 && empty($_POST) and empty($_FILES)) { @@ -201,11 +245,11 @@ if (isset($_SERVER['CONTENT_LENGTH'])) { } } -// Create text file -$create_text_file = (bool) get_parameter('create_text_file'); -if ($create_text_file) { - // Load global vars + +function create_text_file($default_real_directory) +{ global $config; + global $homedir_filemanager; $config['filemanager'] = []; $config['filemanager']['correct_upload_file'] = 0; @@ -228,11 +272,9 @@ if ($create_text_file) { $directory = io_safe_output($directory); $umask = (string) get_parameter('umask', ''); - $hash = get_parameter('hash', ''); - $testHash = md5($real_directory.$directory.$config['dbpass']); - - if ($hash != $testHash) { - ui_print_error_message(__('Security error'), '', true); + if (strpos($real_directory, $default_real_directory) !== 0) { + // Perform security check to determine whether received upload directory is part of the default path for caller uploader and user is not trying to access an external path (avoid execution of PHP files in directories that are not explicitly controlled by corresponding .htaccess). + ui_print_error_message(__('Security error')); } else { if ($directory == '') { $nombre_archivo = $real_directory.'/'.$filename; @@ -241,80 +283,22 @@ if ($create_text_file) { } if (! @touch($nombre_archivo)) { - $config['filemanager']['message'] = ui_print_error_message(__('Error creating file'), '', true); + $config['filemanager']['message'] = ui_print_error_message(__('Error creating file')); } else { if ($umask !== '') { chmod($nombre_archivo, $umask); } - $config['filemanager']['message'] = ui_print_success_message(__('Upload correct'), '', true); + ui_print_success_message(__('Upload correct')); + $config['filemanager']['correct_upload_file'] = 1; } } } else { - $config['filemanager']['message'] = ui_print_error_message(__('Error creating file with empty name'), '', true); + ui_print_error_message(__('Error creating file with empty name')); } } -// Upload zip -if ($upload_zip) { - // Load global vars - global $config; - - $config['filemanager'] = []; - $config['filemanager']['correct_upload_file'] = 0; - $config['filemanager']['message'] = null; - - check_login(); - - if (! check_acl($config['id_user'], 0, 'AW')) { - db_pandora_audit('ACL Violation', 'Trying to access File manager'); - include 'general/noaccess.php'; - return; - } - - if (isset($_FILES['file']) && $_FILES['file']['name'] != '') { - $filename = $_FILES['file']['name']; - $filesize = $_FILES['file']['size']; - $real_directory = (string) get_parameter('real_directory'); - $real_directory = io_safe_output($real_directory); - $directory = (string) get_parameter('directory'); - $directory = io_safe_output($directory); - - $hash = get_parameter('hash', ''); - $testHash = md5($real_directory.$directory.$config['dbpass']); - - if ($hash != $testHash) { - $config['filemanager']['message'] = ui_print_error_message(__('Security error'), '', true); - } else { - // Copy file to directory and change name - if ($directory == '') { - $nombre_archivo = $real_directory.'/'.$filename; - } else { - $nombre_archivo = $homedir_filemanager.'/'.$directory.'/'.$filename; - } - - if (! @copy($_FILES['file']['tmp_name'], $nombre_archivo)) { - $config['filemanager']['message'] = ui_print_error_message(__('Attach error'), '', true); - } else { - // Delete temporal file - unlink($_FILES['file']['tmp_name']); - - // Extract the zip file - $zip = new ZipArchive; - $pathname = $homedir_filemanager.'/'.$directory.'/'; - - if ($zip->open($nombre_archivo) === true) { - $zip->extractTo($pathname); - unlink($nombre_archivo); - } - - $config['filemanager']['message'] = ui_print_success_message(__('Upload correct'), '', true); - $config['filemanager']['correct_upload_file'] = 1; - } - } - } -} // CREATE DIR $create_dir = (bool) get_parameter('create_dir'); diff --git a/pandora_console/operation/snmpconsole/snmp_mib_uploader.php b/pandora_console/operation/snmpconsole/snmp_mib_uploader.php index 1fbe2b1823..36ddb57072 100644 --- a/pandora_console/operation/snmpconsole/snmp_mib_uploader.php +++ b/pandora_console/operation/snmpconsole/snmp_mib_uploader.php @@ -69,6 +69,19 @@ $real_directory = realpath($config['homedir'].'/'.$directory); ui_print_info_message(__('MIB files will be installed on the system. Please note that a MIB may depend on other MIB. To customize trap definitions use the SNMP trap editor.')); +$upload_file_or_zip = (bool) get_parameter('upload_file_or_zip'); +$create_text_file = (bool) get_parameter('create_text_file'); + +$default_real_directory = realpath($config['homedir'].'/'.$fallback_directory); + +if ($upload_file_or_zip) { + upload_file($upload_file_or_zip, $default_real_directory); +} + +if ($create_text_file) { + create_text_file($default_real_directory); +} + filemanager_file_explorer( $real_directory, $directory, From d75bb46f3431ff7d78d5d5df43436cf4300c1711 Mon Sep 17 00:00:00 2001 From: alejandro-campos Date: Thu, 5 Nov 2020 12:47:25 +0100 Subject: [PATCH 038/263] changed action/alert priority of fields for snmp alerts --- pandora_server/lib/PandoraFMS/Core.pm | 40 +++++++++++++-------------- 1 file changed, 20 insertions(+), 20 deletions(-) diff --git a/pandora_server/lib/PandoraFMS/Core.pm b/pandora_server/lib/PandoraFMS/Core.pm index 0d3bc2652f..cddc89117f 100644 --- a/pandora_server/lib/PandoraFMS/Core.pm +++ b/pandora_server/lib/PandoraFMS/Core.pm @@ -1023,26 +1023,26 @@ sub pandora_execute_action ($$$$$$$$$;$) { $field20 = defined($action->{'field20'}) && $action->{'field20'} ne "" ? $action->{'field20'} : $alert->{'field20'}; } else { - $field1 = defined($action->{'field1'}) && $action->{'field1'} ne "" ? $action->{'field1'} : $alert->{'field1'}; - $field2 = defined($action->{'field2'}) && $action->{'field2'} ne "" ? $action->{'field2'} : $alert->{'field2'}; - $field3 = defined($action->{'field3'}) && $action->{'field3'} ne "" ? $action->{'field3'} : $alert->{'field3'}; - $field4 = defined($action->{'field4'}) && $action->{'field4'} ne "" ? $action->{'field4'} : $alert->{'field4'}; - $field5 = defined($action->{'field5'}) && $action->{'field5'} ne "" ? $action->{'field5'} : $alert->{'field5'}; - $field6 = defined($action->{'field6'}) && $action->{'field6'} ne "" ? $action->{'field6'} : $alert->{'field6'}; - $field7 = defined($action->{'field7'}) && $action->{'field7'} ne "" ? $action->{'field7'} : $alert->{'field7'}; - $field8 = defined($action->{'field8'}) && $action->{'field8'} ne "" ? $action->{'field8'} : $alert->{'field8'}; - $field9 = defined($action->{'field9'}) && $action->{'field9'} ne "" ? $action->{'field9'} : $alert->{'field9'}; - $field10 = defined($action->{'field10'}) && $action->{'field10'} ne "" ? $action->{'field10'} : $alert->{'field10'}; - $field11 = defined($action->{'field11'}) && $action->{'field11'} ne "" ? $action->{'field11'} : $alert->{'field11'}; - $field12 = defined($action->{'field12'}) && $action->{'field12'} ne "" ? $action->{'field12'} : $alert->{'field12'}; - $field13 = defined($action->{'field13'}) && $action->{'field13'} ne "" ? $action->{'field13'} : $alert->{'field13'}; - $field14 = defined($action->{'field14'}) && $action->{'field14'} ne "" ? $action->{'field14'} : $alert->{'field14'}; - $field15 = defined($action->{'field15'}) && $action->{'field15'} ne "" ? $action->{'field15'} : $alert->{'field15'}; - $field16 = defined($action->{'field16'}) && $action->{'field16'} ne "" ? $action->{'field16'} : $alert->{'field16'}; - $field17 = defined($action->{'field17'}) && $action->{'field17'} ne "" ? $action->{'field17'} : $alert->{'field17'}; - $field18 = defined($action->{'field18'}) && $action->{'field18'} ne "" ? $action->{'field18'} : $alert->{'field18'}; - $field19 = defined($action->{'field19'}) && $action->{'field19'} ne "" ? $action->{'field19'} : $alert->{'field19'}; - $field20 = defined($action->{'field20'}) && $action->{'field20'} ne "" ? $action->{'field20'} : $alert->{'field20'}; + $field1 = defined($alert->{'field1'}) && $alert->{'field1'} ne "" ? $alert->{'field1'} : $action->{'field1'}; + $field2 = defined($alert->{'field2'}) && $alert->{'field2'} ne "" ? $alert->{'field2'} : $action->{'field2'}; + $field3 = defined($alert->{'field3'}) && $alert->{'field3'} ne "" ? $alert->{'field3'} : $action->{'field3'}; + $field4 = defined($alert->{'field4'}) && $alert->{'field4'} ne "" ? $alert->{'field4'} : $action->{'field4'}; + $field5 = defined($alert->{'field5'}) && $alert->{'field5'} ne "" ? $alert->{'field5'} : $action->{'field5'}; + $field6 = defined($alert->{'field6'}) && $alert->{'field6'} ne "" ? $alert->{'field6'} : $action->{'field6'}; + $field7 = defined($alert->{'field7'}) && $alert->{'field7'} ne "" ? $alert->{'field7'} : $action->{'field7'}; + $field8 = defined($alert->{'field8'}) && $alert->{'field8'} ne "" ? $alert->{'field8'} : $action->{'field8'}; + $field9 = defined($alert->{'field9'}) && $alert->{'field9'} ne "" ? $alert->{'field9'} : $action->{'field9'}; + $field10 = defined($alert->{'field10'}) && $alert->{'field10'} ne "" ? $alert->{'field10'} : $action->{'field10'}; + $field11 = defined($alert->{'field11'}) && $alert->{'field11'} ne "" ? $alert->{'field11'} : $action->{'field11'}; + $field12 = defined($alert->{'field12'}) && $alert->{'field12'} ne "" ? $alert->{'field12'} : $action->{'field12'}; + $field13 = defined($alert->{'field13'}) && $alert->{'field13'} ne "" ? $alert->{'field13'} : $action->{'field13'}; + $field14 = defined($alert->{'field14'}) && $alert->{'field14'} ne "" ? $alert->{'field14'} : $action->{'field14'}; + $field15 = defined($alert->{'field15'}) && $alert->{'field15'} ne "" ? $alert->{'field15'} : $action->{'field15'}; + $field16 = defined($alert->{'field16'}) && $alert->{'field16'} ne "" ? $alert->{'field16'} : $action->{'field16'}; + $field17 = defined($alert->{'field17'}) && $alert->{'field17'} ne "" ? $alert->{'field17'} : $action->{'field17'}; + $field18 = defined($alert->{'field18'}) && $alert->{'field18'} ne "" ? $alert->{'field18'} : $action->{'field18'}; + $field19 = defined($alert->{'field19'}) && $alert->{'field19'} ne "" ? $alert->{'field19'} : $action->{'field19'}; + $field20 = defined($alert->{'field20'}) && $alert->{'field20'} ne "" ? $alert->{'field20'} : $action->{'field20'}; } # Recovery fields, thanks to Kato Atsushi From 6337fd3667e9599c56c78330d17a5b735437e382 Mon Sep 17 00:00:00 2001 From: Luis Calvo Date: Thu, 5 Nov 2020 14:31:26 +0100 Subject: [PATCH 039/263] Fixed CLI update_group --- pandora_server/util/pandora_manage.pl | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/pandora_server/util/pandora_manage.pl b/pandora_server/util/pandora_manage.pl index ba141b545f..a6fe3bf63c 100755 --- a/pandora_server/util/pandora_manage.pl +++ b/pandora_server/util/pandora_manage.pl @@ -5754,16 +5754,22 @@ sub cli_delete_group() { sub cli_update_group() { my ($group_id,$group_name,$parent_group_name,$icon,$description) = @ARGV[2..6]; my $result; - $result = db_do ($dbh, 'SELECT * FROM tgrupo WHERE id_grupo=?', $group_id); + + $result = get_db_value ($dbh, 'SELECT * FROM tgrupo WHERE id_grupo=?', $group_id); if($result == "0E0"){ print_log "[ERROR] Group '$group_id' doesn`t exist \n\n"; }else{ if(defined($group_name)){ if(defined($parent_group_name)){ - my $parent_group_id = get_group_id($dbh,$parent_group_name); - exist_check($parent_group_id, 'group name', $parent_group_name); + my $parent_group_id = 0; + + if($parent_group_name ne 'All') { + $parent_group_id = get_group_id($dbh,$parent_group_name); + exist_check($parent_group_id, 'group name', $parent_group_name); + } + if(defined($icon)){ if(defined($description)){ db_do ($dbh,'UPDATE tgrupo SET nombre=? , parent=? , icon=? , description=? WHERE id_grupo=?',$group_name,$parent_group_id,$icon,$description,$group_id); From 64bd9bfc79b397cf1394e4a1269459cbc3f3de4e Mon Sep 17 00:00:00 2001 From: Luis Calvo Date: Thu, 5 Nov 2020 16:14:06 +0100 Subject: [PATCH 040/263] Fixed safe input on cli delete_group --- pandora_server/util/pandora_manage.pl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pandora_server/util/pandora_manage.pl b/pandora_server/util/pandora_manage.pl index ba141b545f..193635a3f9 100755 --- a/pandora_server/util/pandora_manage.pl +++ b/pandora_server/util/pandora_manage.pl @@ -5734,7 +5734,7 @@ sub cli_delete_group() { my $group_id = get_group_id($dbh,$group_name); exist_check($group_id, 'group name', $group_name); - $group_id = db_do ($dbh, 'DELETE FROM tgrupo WHERE nombre=?', $group_name); + $group_id = db_do ($dbh, 'DELETE FROM tgrupo WHERE nombre=?', safe_input($group_name)); if($group_id == -1) { print_log "[ERROR] A problem has been ocurred deleting group '$group_name'\n\n"; From ac762469b621e29cf07d2823bf6ad2f76286934c Mon Sep 17 00:00:00 2001 From: Daniel Maya Date: Tue, 10 Nov 2020 13:54:15 +0100 Subject: [PATCH 041/263] Added trim in search --- pandora_console/godmode/modules/manage_network_components.php | 3 +++ 1 file changed, 3 insertions(+) diff --git a/pandora_console/godmode/modules/manage_network_components.php b/pandora_console/godmode/modules/manage_network_components.php index 5ff2132d12..2252f48362 100644 --- a/pandora_console/godmode/modules/manage_network_components.php +++ b/pandora_console/godmode/modules/manage_network_components.php @@ -643,6 +643,9 @@ $url = ui_get_url_refresh( $search_id_group = (int) get_parameter('search_id_group'); $search_string = (string) get_parameter('search_string'); +if (!empty($search_string)) { + $search_string = trim($search_string, ' '); +} $table = new stdClass(); $table->width = '100%'; From fc429efce2d449b1547d1b80948197302e2c9759 Mon Sep 17 00:00:00 2001 From: alejandro-campos Date: Wed, 11 Nov 2020 09:52:15 +0100 Subject: [PATCH 042/263] fixed wrong error message: recon task is not necessarily linked to script --- .../class/ManageNetScanScripts.class.php | 25 +++++++------------ 1 file changed, 9 insertions(+), 16 deletions(-) diff --git a/pandora_console/include/class/ManageNetScanScripts.class.php b/pandora_console/include/class/ManageNetScanScripts.class.php index e41e692146..16dfa70f42 100644 --- a/pandora_console/include/class/ManageNetScanScripts.class.php +++ b/pandora_console/include/class/ManageNetScanScripts.class.php @@ -350,28 +350,21 @@ class ManageNetScanScripts extends Wizard ['id_recon_script' => $id_script] ); + $result_dlt2 = db_process_sql_delete( + 'trecon_task', + ['id_recon_script' => $id_script] + ); + if (!$result_dlt) { $result = [ 'error' => 1, 'msg' => __('Problem deleting Net scan Scripts'), ]; } else { - $result_dlt2 = db_process_sql_delete( - 'trecon_task', - ['id_recon_script' => $id_script] - ); - - if (!$result_dlt2) { - $result = [ - 'error' => 1, - 'msg' => __('Problem deleting Net scan Scripts'), - ]; - } else { - $result = [ - 'error' => 0, - 'msg' => __('Deleted successfully'), - ]; - } + $result = [ + 'error' => 0, + 'msg' => __('Deleted successfully'), + ]; } return $result; From c6cf4ab9ee64726f0f77498577cf09894414e250 Mon Sep 17 00:00:00 2001 From: alejandro-campos Date: Wed, 11 Nov 2020 16:25:58 +0100 Subject: [PATCH 043/263] fixed query --- pandora_console/include/lib/Dashboard/Manager.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pandora_console/include/lib/Dashboard/Manager.php b/pandora_console/include/lib/Dashboard/Manager.php index 07e253a2d8..eebfc3233c 100644 --- a/pandora_console/include/lib/Dashboard/Manager.php +++ b/pandora_console/include/lib/Dashboard/Manager.php @@ -702,8 +702,8 @@ class Manager FROM tdashboard td LEFT JOIN twidget_dashboard twd ON td.id = twd.id_dashboard - WHERE (td.id_group IN (%s) AND td.id_user = '') OR - td.id_user = '%s' %s + WHERE ((td.id_group IN (%s) AND td.id_user = '') OR + td.id_user = '%s') %s GROUP BY td.id ORDER BY name%s", $string_groups, From 78df603c07ab98fcf146a3b4ede291071ccf96e6 Mon Sep 17 00:00:00 2001 From: Luis Calvo Date: Wed, 11 Nov 2020 17:07:03 +0100 Subject: [PATCH 044/263] Fixed alert count bug on tactical view --- pandora_console/include/functions_tactical.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pandora_console/include/functions_tactical.php b/pandora_console/include/functions_tactical.php index 4e29dda423..df0e334d64 100644 --- a/pandora_console/include/functions_tactical.php +++ b/pandora_console/include/functions_tactical.php @@ -142,7 +142,7 @@ function tactical_get_data($id_user=false, $user_strict=false, $acltags, $return $list['_monitors_warning_'] += (int) $value['monitors_warning']; $list['_monitors_unknown_'] += (int) $value['monitors_unknown']; $list['_monitors_not_init_'] += (int) $value['monitors_not_init']; - $list['_monitor_alerts_fire_count_'] += (int) $value['alerts_fired']; + $list['_monitors_alerts_fired_'] += (int) $value['alerts_fired']; } if (!empty($data_stats_unknown)) { From 2632256c10e2a74a441dd9f2e4b65024f78b14c7 Mon Sep 17 00:00:00 2001 From: alejandro-campos Date: Wed, 11 Nov 2020 17:45:21 +0100 Subject: [PATCH 045/263] fixed agent creation with unique ip condition --- pandora_console/include/functions_api.php | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/pandora_console/include/functions_api.php b/pandora_console/include/functions_api.php index a7f6ef8c81..b69bc9efe0 100644 --- a/pandora_console/include/functions_api.php +++ b/pandora_console/include/functions_api.php @@ -1570,7 +1570,9 @@ function api_set_new_agent($thrash1, $thrash2, $other, $thrash3) $nombre_agente = $alias; } - if ($direccion_agente != '') { + $exists_ip = false; + + if ($config['unique_ip'] && $direccion_agente != '') { $exists_ip = db_get_row_sql('SELECT direccion FROM tagente WHERE direccion = "'.$direccion_agente.'"'); } From 915c039fcd233f3b5059db5aeece9ae486203be9 Mon Sep 17 00:00:00 2001 From: marcos Date: Thu, 12 Nov 2020 12:13:57 +0100 Subject: [PATCH 046/263] fixed sql error with secundary groups ZOS --- pandora_console/include/functions_tags.php | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/pandora_console/include/functions_tags.php b/pandora_console/include/functions_tags.php index fe36bc34fa..c759597706 100644 --- a/pandora_console/include/functions_tags.php +++ b/pandora_console/include/functions_tags.php @@ -900,7 +900,11 @@ function tags_get_acl_tags_module_condition($acltags, $modules_table='', $force_ } $in_group = implode(',', $without_tags); - $condition .= sprintf('(tagente.id_grupo IN (%s) OR tasg.id_group IN (%s))', $in_group, $in_group); + if ($has_secondary) { + $condition .= sprintf('(tagente.id_grupo IN (%s) OR tasg.id_group IN (%s))', $in_group, $in_group); + } else { + $condition .= sprintf('(tagente.id_grupo IN (%s))', $in_group); + } } $condition = !empty($condition) ? "($condition)" : ''; From 0ea8196db003a646c5469a61a20279f23ef1fcbe Mon Sep 17 00:00:00 2001 From: Daniel Maya Date: Thu, 12 Nov 2020 13:44:52 +0100 Subject: [PATCH 047/263] Fixed create network components --- pandora_console/include/ajax/snmp_browser.ajax.php | 2 +- pandora_console/include/functions_snmp_browser.php | 2 +- pandora_console/include/javascript/pandora_snmp_browser.js | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/pandora_console/include/ajax/snmp_browser.ajax.php b/pandora_console/include/ajax/snmp_browser.ajax.php index 571399c703..f91bc7ef3e 100644 --- a/pandora_console/include/ajax/snmp_browser.ajax.php +++ b/pandora_console/include/ajax/snmp_browser.ajax.php @@ -170,7 +170,7 @@ if (is_ajax()) { $id_target = explode(',', $id_items[0]); } - if (empty($id_items[0])) { + if (empty($id_items[0]) && $module_target !== 'network_component') { echo json_encode([0 => -1]); exit; } diff --git a/pandora_console/include/functions_snmp_browser.php b/pandora_console/include/functions_snmp_browser.php index c4ef3b05ab..7146752554 100644 --- a/pandora_console/include/functions_snmp_browser.php +++ b/pandora_console/include/functions_snmp_browser.php @@ -616,7 +616,7 @@ function snmp_browser_print_oid( $output .= html_print_table($table, true); $url = 'index.php?'.'sec=gmodules&'.'sec2=godmode/modules/manage_network_components'; - $output .= '
'; + $output .= ''; $output .= html_print_input_hidden('create_network_from_snmp_browser', 1, true); $output .= html_print_input_hidden('id_component_type', 2, true); $output .= html_print_input_hidden('type', 17, true); diff --git a/pandora_console/include/javascript/pandora_snmp_browser.js b/pandora_console/include/javascript/pandora_snmp_browser.js index 961619b80b..f02bdad4fc 100644 --- a/pandora_console/include/javascript/pandora_snmp_browser.js +++ b/pandora_console/include/javascript/pandora_snmp_browser.js @@ -541,7 +541,7 @@ function setOID() { } /** - * Create module on selected module_target (agent, networlk component or policy). + * Create module on selected module_target (agent, network component or policy). * * @param string module_target Target to create module. * @param return_values Return snmp values. From 5e8e37494d1d68083c54a88012b8268fe8f5f3b0 Mon Sep 17 00:00:00 2001 From: Jose Gonzalez Date: Thu, 12 Nov 2020 16:55:31 +0100 Subject: [PATCH 048/263] WIP: Backup upload --- pandora_server/lib/PandoraFMS/DiscoveryServer.pm | 3 ++- pandora_server/lib/PandoraFMS/Recon/Base.pm | 2 +- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/pandora_server/lib/PandoraFMS/DiscoveryServer.pm b/pandora_server/lib/PandoraFMS/DiscoveryServer.pm index 2b57922a80..f49e9bffb4 100644 --- a/pandora_server/lib/PandoraFMS/DiscoveryServer.pm +++ b/pandora_server/lib/PandoraFMS/DiscoveryServer.pm @@ -387,7 +387,7 @@ sub exec_recon_script ($$$) { } else { $ent_script = 1; } - + logger($pa_config, 'Executing command args: ' . $command . ' ' . $args, 1); if (-x $command) { my $exec_output = `$command $args`; logger($pa_config, "Execution output: \n". $exec_output, 10); @@ -1769,6 +1769,7 @@ sub PandoraFMS::Recon::Base::update_progress ($$) { my $stats = {}; eval { + logger($self->{'pa_config'}, 'Summary es '.$self->{'summary'}, 3); local $SIG{__DIE__}; if (defined($self->{'summary'}) && $self->{'summary'} ne '') { $stats->{'summary'} = $self->{'summary'}; diff --git a/pandora_server/lib/PandoraFMS/Recon/Base.pm b/pandora_server/lib/PandoraFMS/Recon/Base.pm index e14b200fa3..c78dac354f 100644 --- a/pandora_server/lib/PandoraFMS/Recon/Base.pm +++ b/pandora_server/lib/PandoraFMS/Recon/Base.pm @@ -1898,7 +1898,7 @@ sub scan($) { # 1% $self->call('update_progress', 1); - + logger($self->{'pa_config'}, 'Tipo de Task: '.$self->{'task_data'}->{'type'}); if (defined($self->{'task_data'})) { if ( $self->{'task_data'}->{'type'} == DISCOVERY_APP_MYSQL || $self->{'task_data'}->{'type'} == DISCOVERY_APP_ORACLE From c3044b5d13cdb70b203ed1557c4d5fd811709ac2 Mon Sep 17 00:00:00 2001 From: Jose Gonzalez Date: Thu, 12 Nov 2020 17:15:33 +0100 Subject: [PATCH 049/263] WIP: Backup upload --- pandora_server/lib/PandoraFMS/DiscoveryServer.pm | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pandora_server/lib/PandoraFMS/DiscoveryServer.pm b/pandora_server/lib/PandoraFMS/DiscoveryServer.pm index f49e9bffb4..88e7f37e62 100644 --- a/pandora_server/lib/PandoraFMS/DiscoveryServer.pm +++ b/pandora_server/lib/PandoraFMS/DiscoveryServer.pm @@ -32,7 +32,7 @@ use MIME::Base64; # Default lib dir for RPM and DEB packages use lib '/usr/lib/perl5'; - +use Data::Dumper; use PandoraFMS::Tools; use PandoraFMS::DB; use PandoraFMS::Core; @@ -1769,7 +1769,7 @@ sub PandoraFMS::Recon::Base::update_progress ($$) { my $stats = {}; eval { - logger($self->{'pa_config'}, 'Summary es '.$self->{'summary'}, 3); + logger($self->{'pa_config'}, 'Summary es '.Dumper($self->{'summary'}), 3); local $SIG{__DIE__}; if (defined($self->{'summary'}) && $self->{'summary'} ne '') { $stats->{'summary'} = $self->{'summary'}; From e98a3bf5f3863c8faf9538967124870d60b75086 Mon Sep 17 00:00:00 2001 From: Jose Gonzalez Date: Thu, 12 Nov 2020 17:21:16 +0100 Subject: [PATCH 050/263] WIP: Backup upload --- pandora_server/lib/PandoraFMS/DiscoveryServer.pm | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pandora_server/lib/PandoraFMS/DiscoveryServer.pm b/pandora_server/lib/PandoraFMS/DiscoveryServer.pm index 88e7f37e62..8dc6f644a0 100644 --- a/pandora_server/lib/PandoraFMS/DiscoveryServer.pm +++ b/pandora_server/lib/PandoraFMS/DiscoveryServer.pm @@ -1769,7 +1769,7 @@ sub PandoraFMS::Recon::Base::update_progress ($$) { my $stats = {}; eval { - logger($self->{'pa_config'}, 'Summary es '.Dumper($self->{'summary'}), 3); + logger($self->{'pa_config'}, 'Task data: '.$self->{'task_data'}.' - summary es '.Dumper($self->{'summary'}), 3); local $SIG{__DIE__}; if (defined($self->{'summary'}) && $self->{'summary'} ne '') { $stats->{'summary'} = $self->{'summary'}; From c8ea054614513efa44ba214aafa5fde28c9ff215 Mon Sep 17 00:00:00 2001 From: Jose Gonzalez Date: Thu, 12 Nov 2020 17:23:46 +0100 Subject: [PATCH 051/263] WIP: Backup upload --- pandora_server/lib/PandoraFMS/DiscoveryServer.pm | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pandora_server/lib/PandoraFMS/DiscoveryServer.pm b/pandora_server/lib/PandoraFMS/DiscoveryServer.pm index 8dc6f644a0..735114a985 100644 --- a/pandora_server/lib/PandoraFMS/DiscoveryServer.pm +++ b/pandora_server/lib/PandoraFMS/DiscoveryServer.pm @@ -1769,7 +1769,7 @@ sub PandoraFMS::Recon::Base::update_progress ($$) { my $stats = {}; eval { - logger($self->{'pa_config'}, 'Task data: '.$self->{'task_data'}.' - summary es '.Dumper($self->{'summary'}), 3); + logger($self->{'pa_config'}, 'Task data: '.Dumper($self->{'task_data'}).' - summary es '.Dumper($self->{'summary'}), 3); local $SIG{__DIE__}; if (defined($self->{'summary'}) && $self->{'summary'} ne '') { $stats->{'summary'} = $self->{'summary'}; From 064ae49d56dae374190f7760189fb20f76c07782 Mon Sep 17 00:00:00 2001 From: Jose Gonzalez Date: Thu, 12 Nov 2020 17:36:31 +0100 Subject: [PATCH 052/263] WIP: Backup upload --- pandora_server/lib/PandoraFMS/DiscoveryServer.pm | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pandora_server/lib/PandoraFMS/DiscoveryServer.pm b/pandora_server/lib/PandoraFMS/DiscoveryServer.pm index 735114a985..74c713fd38 100644 --- a/pandora_server/lib/PandoraFMS/DiscoveryServer.pm +++ b/pandora_server/lib/PandoraFMS/DiscoveryServer.pm @@ -1769,7 +1769,7 @@ sub PandoraFMS::Recon::Base::update_progress ($$) { my $stats = {}; eval { - logger($self->{'pa_config'}, 'Task data: '.Dumper($self->{'task_data'}).' - summary es '.Dumper($self->{'summary'}), 3); + logger($self->{'pa_config'}, 'Task data: '.Dumper($self->{'task_data'}->{'description'}).' - summary es '.Dumper($self->{'summary'}), 3); local $SIG{__DIE__}; if (defined($self->{'summary'}) && $self->{'summary'} ne '') { $stats->{'summary'} = $self->{'summary'}; From 08cc67696a4b1697afd864b63492117acdef68cf Mon Sep 17 00:00:00 2001 From: Jose Gonzalez Date: Fri, 13 Nov 2020 11:36:06 +0100 Subject: [PATCH 053/263] WIP: Backup upload --- .../wizards/DiscoveryTaskList.class.php | 36 +++++++++++++------ 1 file changed, 26 insertions(+), 10 deletions(-) diff --git a/pandora_console/godmode/wizards/DiscoveryTaskList.class.php b/pandora_console/godmode/wizards/DiscoveryTaskList.class.php index 59a9fab84a..f664b761c1 100644 --- a/pandora_console/godmode/wizards/DiscoveryTaskList.class.php +++ b/pandora_console/godmode/wizards/DiscoveryTaskList.class.php @@ -546,16 +546,26 @@ class DiscoveryTaskList extends HTML true ); } else if ($task['review_mode'] == DISCOVERY_STANDARD) { - if ($task['status'] <= 0 - && empty($task['summary']) === false - ) { - $data[5] = __('Done'); - } else if ($task['utimestamp'] == 0 - && empty($task['summary']) - ) { - $data[5] = __('Not started'); + if ($task['type'] === DISCOVERY_APP_VMWARE) { + if ($task['status'] <= 0) { + $data[5] == __('Done'); + } else if ($task['status'] > 0 && $task['utimestamp'] == 0) { + $data[5] == __('Pending'); + } else { + $data[5] == __('Not started'); + } } else { - $data[5] = __('Pending'); + if ($task['status'] <= 0 + && empty($task['summary']) === false + ) { + $data[5] = __('Done'); + } else if ($task['utimestamp'] == 0 + && empty($task['summary']) + ) { + $data[5] = __('Not started'); + } else { + $data[5] = __('Pending'); + } } } else { if ($task['status'] <= 0 @@ -1201,8 +1211,14 @@ class DiscoveryTaskList extends HTML // Header information. if ((int) $task['status'] <= 0 && empty($summary)) { + if ($task['type'] == DISCOVERY_APP_VMWARE) { + $outputMessage = __('Task completed.'); + } else { + $outputMessage = __('This task has never executed'); + } + $output .= ui_print_info_message( - __('This task has never executed'), + $outputMessage, '', true ); From 98205973a5873214055b48990b21271ba9523d0e Mon Sep 17 00:00:00 2001 From: Jose Gonzalez Date: Fri, 13 Nov 2020 11:43:26 +0100 Subject: [PATCH 054/263] WIP: Backup upload --- pandora_console/godmode/wizards/DiscoveryTaskList.class.php | 4 ++-- pandora_server/lib/PandoraFMS/DiscoveryServer.pm | 3 +-- pandora_server/lib/PandoraFMS/Recon/Base.pm | 2 +- 3 files changed, 4 insertions(+), 5 deletions(-) diff --git a/pandora_console/godmode/wizards/DiscoveryTaskList.class.php b/pandora_console/godmode/wizards/DiscoveryTaskList.class.php index f664b761c1..b43be9e4ff 100644 --- a/pandora_console/godmode/wizards/DiscoveryTaskList.class.php +++ b/pandora_console/godmode/wizards/DiscoveryTaskList.class.php @@ -547,9 +547,9 @@ class DiscoveryTaskList extends HTML ); } else if ($task['review_mode'] == DISCOVERY_STANDARD) { if ($task['type'] === DISCOVERY_APP_VMWARE) { - if ($task['status'] <= 0) { + if ($task['status'] <= 0 && $task['utimestamp'] != 0) { $data[5] == __('Done'); - } else if ($task['status'] > 0 && $task['utimestamp'] == 0) { + } else if ($task['status'] > 0) { $data[5] == __('Pending'); } else { $data[5] == __('Not started'); diff --git a/pandora_server/lib/PandoraFMS/DiscoveryServer.pm b/pandora_server/lib/PandoraFMS/DiscoveryServer.pm index 74c713fd38..7e562f06ca 100644 --- a/pandora_server/lib/PandoraFMS/DiscoveryServer.pm +++ b/pandora_server/lib/PandoraFMS/DiscoveryServer.pm @@ -387,7 +387,7 @@ sub exec_recon_script ($$$) { } else { $ent_script = 1; } - logger($pa_config, 'Executing command args: ' . $command . ' ' . $args, 1); + if (-x $command) { my $exec_output = `$command $args`; logger($pa_config, "Execution output: \n". $exec_output, 10); @@ -1769,7 +1769,6 @@ sub PandoraFMS::Recon::Base::update_progress ($$) { my $stats = {}; eval { - logger($self->{'pa_config'}, 'Task data: '.Dumper($self->{'task_data'}->{'description'}).' - summary es '.Dumper($self->{'summary'}), 3); local $SIG{__DIE__}; if (defined($self->{'summary'}) && $self->{'summary'} ne '') { $stats->{'summary'} = $self->{'summary'}; diff --git a/pandora_server/lib/PandoraFMS/Recon/Base.pm b/pandora_server/lib/PandoraFMS/Recon/Base.pm index c78dac354f..e14b200fa3 100644 --- a/pandora_server/lib/PandoraFMS/Recon/Base.pm +++ b/pandora_server/lib/PandoraFMS/Recon/Base.pm @@ -1898,7 +1898,7 @@ sub scan($) { # 1% $self->call('update_progress', 1); - logger($self->{'pa_config'}, 'Tipo de Task: '.$self->{'task_data'}->{'type'}); + if (defined($self->{'task_data'})) { if ( $self->{'task_data'}->{'type'} == DISCOVERY_APP_MYSQL || $self->{'task_data'}->{'type'} == DISCOVERY_APP_ORACLE From a0a1be254d676055a4b14c83e86fe228c7b184d0 Mon Sep 17 00:00:00 2001 From: Jose Gonzalez Date: Fri, 13 Nov 2020 13:28:07 +0100 Subject: [PATCH 055/263] WIP: Backup upload --- pandora_console/godmode/wizards/DiscoveryTaskList.class.php | 1 + pandora_server/lib/PandoraFMS/DiscoveryServer.pm | 4 ++-- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/pandora_console/godmode/wizards/DiscoveryTaskList.class.php b/pandora_console/godmode/wizards/DiscoveryTaskList.class.php index b43be9e4ff..1354464cf3 100644 --- a/pandora_console/godmode/wizards/DiscoveryTaskList.class.php +++ b/pandora_console/godmode/wizards/DiscoveryTaskList.class.php @@ -546,6 +546,7 @@ class DiscoveryTaskList extends HTML true ); } else if ($task['review_mode'] == DISCOVERY_STANDARD) { + hd($task); if ($task['type'] === DISCOVERY_APP_VMWARE) { if ($task['status'] <= 0 && $task['utimestamp'] != 0) { $data[5] == __('Done'); diff --git a/pandora_server/lib/PandoraFMS/DiscoveryServer.pm b/pandora_server/lib/PandoraFMS/DiscoveryServer.pm index 7e562f06ca..2b57922a80 100644 --- a/pandora_server/lib/PandoraFMS/DiscoveryServer.pm +++ b/pandora_server/lib/PandoraFMS/DiscoveryServer.pm @@ -32,7 +32,7 @@ use MIME::Base64; # Default lib dir for RPM and DEB packages use lib '/usr/lib/perl5'; -use Data::Dumper; + use PandoraFMS::Tools; use PandoraFMS::DB; use PandoraFMS::Core; @@ -387,7 +387,7 @@ sub exec_recon_script ($$$) { } else { $ent_script = 1; } - + if (-x $command) { my $exec_output = `$command $args`; logger($pa_config, "Execution output: \n". $exec_output, 10); From 1ca65e59d51c5f0ca48502172b38b9c8e87c1912 Mon Sep 17 00:00:00 2001 From: Jose Gonzalez Date: Fri, 13 Nov 2020 13:31:13 +0100 Subject: [PATCH 056/263] WIP: Backup upload --- pandora_console/godmode/wizards/DiscoveryTaskList.class.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pandora_console/godmode/wizards/DiscoveryTaskList.class.php b/pandora_console/godmode/wizards/DiscoveryTaskList.class.php index 1354464cf3..62aa92da31 100644 --- a/pandora_console/godmode/wizards/DiscoveryTaskList.class.php +++ b/pandora_console/godmode/wizards/DiscoveryTaskList.class.php @@ -546,8 +546,8 @@ class DiscoveryTaskList extends HTML true ); } else if ($task['review_mode'] == DISCOVERY_STANDARD) { - hd($task); if ($task['type'] === DISCOVERY_APP_VMWARE) { + hd($task); if ($task['status'] <= 0 && $task['utimestamp'] != 0) { $data[5] == __('Done'); } else if ($task['status'] > 0) { From 63395dd88fe7230f40767e500db75a9483cc000c Mon Sep 17 00:00:00 2001 From: Jose Gonzalez Date: Fri, 13 Nov 2020 13:33:05 +0100 Subject: [PATCH 057/263] WIP: Backup upload --- pandora_console/godmode/wizards/DiscoveryTaskList.class.php | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/pandora_console/godmode/wizards/DiscoveryTaskList.class.php b/pandora_console/godmode/wizards/DiscoveryTaskList.class.php index 62aa92da31..926d031a94 100644 --- a/pandora_console/godmode/wizards/DiscoveryTaskList.class.php +++ b/pandora_console/godmode/wizards/DiscoveryTaskList.class.php @@ -546,8 +546,7 @@ class DiscoveryTaskList extends HTML true ); } else if ($task['review_mode'] == DISCOVERY_STANDARD) { - if ($task['type'] === DISCOVERY_APP_VMWARE) { - hd($task); + if ($task['type'] == DISCOVERY_APP_VMWARE) { if ($task['status'] <= 0 && $task['utimestamp'] != 0) { $data[5] == __('Done'); } else if ($task['status'] > 0) { From d623606c0fea08ceb86e214c731a83713dc262e3 Mon Sep 17 00:00:00 2001 From: Jose Gonzalez Date: Fri, 13 Nov 2020 13:38:54 +0100 Subject: [PATCH 058/263] WIP: Backup upload --- pandora_console/godmode/wizards/DiscoveryTaskList.class.php | 1 + 1 file changed, 1 insertion(+) diff --git a/pandora_console/godmode/wizards/DiscoveryTaskList.class.php b/pandora_console/godmode/wizards/DiscoveryTaskList.class.php index 926d031a94..08162606f6 100644 --- a/pandora_console/godmode/wizards/DiscoveryTaskList.class.php +++ b/pandora_console/godmode/wizards/DiscoveryTaskList.class.php @@ -546,6 +546,7 @@ class DiscoveryTaskList extends HTML true ); } else if ($task['review_mode'] == DISCOVERY_STANDARD) { + hd($task['type']); if ($task['type'] == DISCOVERY_APP_VMWARE) { if ($task['status'] <= 0 && $task['utimestamp'] != 0) { $data[5] == __('Done'); From a049ff1e16e9c11448f1b449168868884c2b118c Mon Sep 17 00:00:00 2001 From: Jose Gonzalez Date: Fri, 13 Nov 2020 13:44:40 +0100 Subject: [PATCH 059/263] WIP: Backup upload --- pandora_console/godmode/wizards/DiscoveryTaskList.class.php | 1 + 1 file changed, 1 insertion(+) diff --git a/pandora_console/godmode/wizards/DiscoveryTaskList.class.php b/pandora_console/godmode/wizards/DiscoveryTaskList.class.php index 08162606f6..a56f93e1f9 100644 --- a/pandora_console/godmode/wizards/DiscoveryTaskList.class.php +++ b/pandora_console/godmode/wizards/DiscoveryTaskList.class.php @@ -547,6 +547,7 @@ class DiscoveryTaskList extends HTML ); } else if ($task['review_mode'] == DISCOVERY_STANDARD) { hd($task['type']); + hd($task['type'] == DISCOVERY_APP_VMWARE); if ($task['type'] == DISCOVERY_APP_VMWARE) { if ($task['status'] <= 0 && $task['utimestamp'] != 0) { $data[5] == __('Done'); From 2fdd34265fa2b7d18dcc20bd60111b5364a49c35 Mon Sep 17 00:00:00 2001 From: Jose Gonzalez Date: Fri, 13 Nov 2020 13:53:54 +0100 Subject: [PATCH 060/263] WIP: Backup upload --- .../godmode/wizards/DiscoveryTaskList.class.php | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/pandora_console/godmode/wizards/DiscoveryTaskList.class.php b/pandora_console/godmode/wizards/DiscoveryTaskList.class.php index a56f93e1f9..20f3d78f9d 100644 --- a/pandora_console/godmode/wizards/DiscoveryTaskList.class.php +++ b/pandora_console/godmode/wizards/DiscoveryTaskList.class.php @@ -546,15 +546,13 @@ class DiscoveryTaskList extends HTML true ); } else if ($task['review_mode'] == DISCOVERY_STANDARD) { - hd($task['type']); - hd($task['type'] == DISCOVERY_APP_VMWARE); if ($task['type'] == DISCOVERY_APP_VMWARE) { if ($task['status'] <= 0 && $task['utimestamp'] != 0) { - $data[5] == __('Done'); + $data[5] = __('Done'); } else if ($task['status'] > 0) { - $data[5] == __('Pending'); + $data[5] = __('Pending'); } else { - $data[5] == __('Not started'); + $data[5] = __('Not started'); } } else { if ($task['status'] <= 0 From 8baf5df61a56214822e84fd16c38266c5b541839 Mon Sep 17 00:00:00 2001 From: Jose Gonzalez Date: Fri, 13 Nov 2020 14:09:22 +0100 Subject: [PATCH 061/263] Fix visual issue --- pandora_console/godmode/wizards/DiscoveryTaskList.class.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pandora_console/godmode/wizards/DiscoveryTaskList.class.php b/pandora_console/godmode/wizards/DiscoveryTaskList.class.php index 20f3d78f9d..39d76f1286 100644 --- a/pandora_console/godmode/wizards/DiscoveryTaskList.class.php +++ b/pandora_console/godmode/wizards/DiscoveryTaskList.class.php @@ -407,7 +407,7 @@ class DiscoveryTaskList extends HTML // Status. $table->headstyle[5] .= 'min-width: 50px; width: 100px;'; // Task type. - $table->headstyle[6] .= 'min-width: 150px; width: 150px;'; + $table->headstyle[6] .= 'min-width: 200px; width: 200px;'; // Progress. $table->headstyle[7] .= 'min-width: 50px; width: 150px;'; // Updated at. From 86d22909612cf26b166aa3785df2b19cecacc59b Mon Sep 17 00:00:00 2001 From: Jose Gonzalez Date: Fri, 13 Nov 2020 14:53:31 +0100 Subject: [PATCH 062/263] Solve issue --- pandora_console/include/functions_events.php | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/pandora_console/include/functions_events.php b/pandora_console/include/functions_events.php index 33c2309e30..5fabdedda0 100644 --- a/pandora_console/include/functions_events.php +++ b/pandora_console/include/functions_events.php @@ -4166,7 +4166,15 @@ function events_page_details($event, $server='') if (!empty($agent)) { $data = []; $data[0] = '
'.__('Name').'
'; - if (can_user_access_node()) { + if (can_user_access_node() && is_metaconsole() && agents_check_agent_exists($event['id_agente'], true, true)) { + $data[1] = ui_print_truncate_text( + $agent['alias'], + 'agent_medium', + true, + true, + true + ).ui_print_help_tip(__('This agent belongs to metaconsole, is not possible display it'), true); + } else if (can_user_access_node()) { $data[1] = ui_print_agent_name( $event['id_agente'], true, From 4023327091890ad44f7e7f6d50c37dbb068fb62a Mon Sep 17 00:00:00 2001 From: Daniel Maya Date: Mon, 16 Nov 2020 12:30:58 +0100 Subject: [PATCH 063/263] Added search by secondary group name --- .../operation/search_agents.getdata.php | 21 ++++++++++++------- 1 file changed, 14 insertions(+), 7 deletions(-) diff --git a/pandora_console/operation/search_agents.getdata.php b/pandora_console/operation/search_agents.getdata.php index aad2835a36..e9d315d291 100644 --- a/pandora_console/operation/search_agents.getdata.php +++ b/pandora_console/operation/search_agents.getdata.php @@ -179,6 +179,8 @@ if ($searchAgents) { $userGroups = users_get_groups($config['id_user'], 'AR', false); $id_userGroups = array_keys($userGroups); + $has_secondary = enterprise_hook('agents_is_using_secondary_groups'); + $sql = "SELECT DISTINCT taddress_agent.id_agent FROM taddress INNER JOIN taddress_agent ON taddress.id_a = taddress_agent.id_a @@ -188,10 +190,10 @@ if ($searchAgents) { if ($id != '') { $aux = $id[0]['id_agent']; $search_sql = " t1.nombre COLLATE utf8_general_ci LIKE '%%cd ".$stringSearchSQL."%%' OR - t2.nombre COLLATE utf8_general_ci LIKE '%%".$stringSearchSQL."%%' OR - t1.alias COLLATE utf8_general_ci LIKE '%%".$stringSearchSQL."%%' OR - t1.comentarios COLLATE utf8_general_ci LIKE '%%".$stringSearchSQL."%%' OR - t1.id_agente = $aux"; + t2.nombre COLLATE utf8_general_ci LIKE '%%".$stringSearchSQL."%%' OR + t1.alias COLLATE utf8_general_ci LIKE '%%".$stringSearchSQL."%%' OR + t1.comentarios COLLATE utf8_general_ci LIKE '%%".$stringSearchSQL."%%' OR + t1.id_agente = $aux"; if (count($id) >= 2) { for ($i = 1; $i < count($id); $i++) { @@ -201,10 +203,15 @@ if ($searchAgents) { } } else { $search_sql = " t1.nombre COLLATE utf8_general_ci LIKE '%%".$stringSearchSQL."%%' OR - t2.nombre COLLATE utf8_general_ci LIKE '%%".$stringSearchSQL."%%' OR + t2.nombre COLLATE utf8_general_ci LIKE '%%".$stringSearchSQL."%%' OR t1.direccion COLLATE utf8_general_ci LIKE '%%".$stringSearchSQL."%%' OR t1.comentarios COLLATE utf8_general_ci LIKE '%%".$stringSearchSQL."%%' OR - t1.alias COLLATE utf8_general_ci LIKE '%%".$stringSearchSQL."%%'"; + t1.alias COLLATE utf8_general_ci LIKE '%%".$stringSearchSQL."%%'"; + } + + if ($has_secondary === true) { + $search_sql .= " OR (tasg.id_group IS NOT NULL AND + tasg.id_group IN (SELECT id_grupo FROM tgrupo WHERE nombre COLLATE utf8_general_ci LIKE '%%".$stringSearchSQL."%%'))"; } $sql = " @@ -235,7 +242,7 @@ if ($searchAgents) { AND ( ".$search_sql.' ) - '; + '; $select = 'SELECT DISTINCT(t1.id_agente), t1.ultimo_contacto, t1.nombre, t1.comentarios, t1.id_os, t1.intervalo, t1.id_grupo, t1.disabled, t1.alias, t1.quiet'; if ($only_count) { From 55c368f957547ae09d20fd99ea85b19b5b13fd6d Mon Sep 17 00:00:00 2001 From: Daniel Maya Date: Mon, 16 Nov 2020 17:34:23 +0100 Subject: [PATCH 064/263] fixed last_compact in history db --- pandora_server/util/pandora_db.pl | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/pandora_server/util/pandora_db.pl b/pandora_server/util/pandora_db.pl index 48104968ac..206f854f90 100755 --- a/pandora_server/util/pandora_db.pl +++ b/pandora_server/util/pandora_db.pl @@ -428,8 +428,8 @@ sub pandora_purgedb ($$) { ############################################################################### # Compact agent data. ############################################################################### -sub pandora_compactdb ($$) { - my ($conf, $dbh) = @_; +sub pandora_compactdb ($$$) { + my ($conf, $dbh, $dbh_conf) = @_; my %count_hash; my %id_agent_hash; @@ -534,9 +534,9 @@ sub pandora_compactdb ($$) { # Mark the last compact date if (defined ($conf->{'_last_compact'})) { - db_do ($dbh, 'UPDATE tconfig SET value=? WHERE token=?', $last_compact, 'last_compact'); + db_do ($dbh_conf, 'UPDATE tconfig SET value=? WHERE token=?', $last_compact, 'last_compact'); } else { - db_do ($dbh, 'INSERT INTO tconfig (value, token) VALUES (?, ?)', $last_compact, 'last_compact'); + db_do ($dbh_conf, 'INSERT INTO tconfig (value, token) VALUES (?, ?)', $last_compact, 'last_compact'); } } @@ -1018,7 +1018,7 @@ sub pandoradb_main ($$$) { # Compact on if enable and DaysCompact are below DaysPurge if (($conf->{'_onlypurge'} == 0) && ($conf->{'_days_compact'} < $conf->{'_days_purge'})) { - pandora_compactdb ($conf, defined ($history_dbh) ? $history_dbh : $dbh); + pandora_compactdb ($conf, defined ($history_dbh) ? $history_dbh : $dbh, $dbh); } # Update tconfig with last time of database maintance time (now) From 2ee187ae53468e90d4b1b8fae1182efc272e5785 Mon Sep 17 00:00:00 2001 From: Luis Calvo Date: Mon, 16 Nov 2020 18:30:23 +0100 Subject: [PATCH 065/263] Fixed acl on discovery tasklist --- pandora_console/godmode/wizards/DiscoveryTaskList.class.php | 4 ++++ pandora_console/include/class/HTML.class.php | 4 ++-- 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/pandora_console/godmode/wizards/DiscoveryTaskList.class.php b/pandora_console/godmode/wizards/DiscoveryTaskList.class.php index 59a9fab84a..de2f70a629 100644 --- a/pandora_console/godmode/wizards/DiscoveryTaskList.class.php +++ b/pandora_console/godmode/wizards/DiscoveryTaskList.class.php @@ -448,6 +448,10 @@ class DiscoveryTaskList extends HTML $table->align[9] = 'left'; foreach ($recon_tasks as $task) { + if ($this->aclMulticheck('AR|AW|AM', $task['id_group']) === false) { + continue; + } + $no_operations = false; $data = []; $server_name = servers_get_name($task['id_recon_server']); diff --git a/pandora_console/include/class/HTML.class.php b/pandora_console/include/class/HTML.class.php index 41ceabf5ab..92cd8cc29f 100644 --- a/pandora_console/include/class/HTML.class.php +++ b/pandora_console/include/class/HTML.class.php @@ -254,7 +254,7 @@ class HTML * * @return boolean Alowed or not. */ - public function aclMulticheck($access=null) + public function aclMulticheck($access=null, $id_group=0) { global $config; @@ -268,7 +268,7 @@ class HTML foreach ($perms as $perm) { $allowed = $allowed || (bool) check_acl( $config['id_user'], - 0, + $id_group, $perm ); } From e0404d565a6f9fe8ce9724c1e761ecb59e77f606 Mon Sep 17 00:00:00 2001 From: Jose Gonzalez Date: Tue, 17 Nov 2020 14:49:26 +0100 Subject: [PATCH 066/263] Fix vulnerability --- pandora_console/godmode/wizards/HostDevices.class.php | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/pandora_console/godmode/wizards/HostDevices.class.php b/pandora_console/godmode/wizards/HostDevices.class.php index cb90d927b8..14ff80b163 100755 --- a/pandora_console/godmode/wizards/HostDevices.class.php +++ b/pandora_console/godmode/wizards/HostDevices.class.php @@ -336,6 +336,12 @@ class HostDevices extends Wizard ) ) ); + + // Forbidden chars cleaning. + foreach ($network as $key => $singleNetwork) { + $network[$key] = preg_replace('/[A-Za-z-()\']/', '', $singleNetwork); + } + unlink($_FILES['network_csv']['tmp_name']); if (empty($network) || is_array($network) === false) { $this->msg = __( From ef899ef529cf4d8608e88e8bba1eb78788465592 Mon Sep 17 00:00:00 2001 From: Jose Gonzalez Date: Tue, 17 Nov 2020 15:21:38 +0100 Subject: [PATCH 067/263] Fix error --- pandora_server/lib/PandoraFMS/Core.pm | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/pandora_server/lib/PandoraFMS/Core.pm b/pandora_server/lib/PandoraFMS/Core.pm index 0d3bc2652f..74e81778c8 100644 --- a/pandora_server/lib/PandoraFMS/Core.pm +++ b/pandora_server/lib/PandoraFMS/Core.pm @@ -5373,7 +5373,9 @@ sub pandora_self_monitoring ($$) { get_db_value($dbh, "SELECT COUNT(*) FROM tagente_datos"); my $read_speed = int((time - $start_performance) * 1e6); - $xml_output .= enterprise_hook("elasticsearch_performance", [$pa_config, $dbh]); + my $elasticsearch_perfomance = enterprise_hook("elasticsearch_performance", [$pa_config, $dbh]); + + $xml_output .= $elasticsearch_perfomance if defined($elasticsearch_perfomance); $xml_output .=" "; $xml_output .=" Database Maintenance"; From ea259f0aa7c8cb1548019dbd8d5f0600c69a3f5b Mon Sep 17 00:00:00 2001 From: Jose Gonzalez Date: Tue, 17 Nov 2020 16:36:30 +0100 Subject: [PATCH 068/263] Fixed issue --- .../include/class/ModuleTemplates.class.php | 40 ++++++++++++------- 1 file changed, 25 insertions(+), 15 deletions(-) diff --git a/pandora_console/include/class/ModuleTemplates.class.php b/pandora_console/include/class/ModuleTemplates.class.php index f9573b6c0e..adee4ec353 100644 --- a/pandora_console/include/class/ModuleTemplates.class.php +++ b/pandora_console/include/class/ModuleTemplates.class.php @@ -309,14 +309,19 @@ class ModuleTemplates extends HTML switch ($this->action) { case 'update': - $dbResult_tnp = db_process_sql_update( - 'tnetwork_profile', - [ - 'name' => $this->name, - 'description' => $this->description, - ], - ['id_np' => $this->id_np] - ); + if (empty($this->name)) { + $dbResult_tnp = false; + } else { + $dbResult_tnp = db_process_sql_update( + 'tnetwork_profile', + [ + 'name' => $this->name, + 'description' => $this->description, + ], + ['id_np' => $this->id_np] + ); + } + if ($dbResult_tnp === false) { $success = false; } else { @@ -352,13 +357,18 @@ class ModuleTemplates extends HTML break; case 'create': - $dbResult_tnp = db_process_sql_insert( - 'tnetwork_profile', - [ - 'name' => $this->name, - 'description' => $this->description, - ] - ); + if (empty($this->name)) { + $dbResult_tnp = false; + } else { + $dbResult_tnp = db_process_sql_insert( + 'tnetwork_profile', + [ + 'name' => $this->name, + 'description' => $this->description, + ] + ); + } + // The insert gone fine! if ($dbResult_tnp != false) { // Set the new id_np. From ea346afcc51bf08e08304daa05f0f230735a0bd4 Mon Sep 17 00:00:00 2001 From: fbsanchez Date: Tue, 17 Nov 2020 17:06:36 +0100 Subject: [PATCH 069/263] allowing self signed certs for quickshell --- pandora_console/extensions/quick_shell.php | 18 +++++++++++++++--- 1 file changed, 15 insertions(+), 3 deletions(-) diff --git a/pandora_console/extensions/quick_shell.php b/pandora_console/extensions/quick_shell.php index 27807e6029..219e8944af 100644 --- a/pandora_console/extensions/quick_shell.php +++ b/pandora_console/extensions/quick_shell.php @@ -94,12 +94,23 @@ function quickShell() config_update_value('gotty_ssh_port', 8081); } + // Context to allow self-signed certs. + $context = stream_context_create( + [ + 'http' => [ 'method' => 'GET'], + 'ssl' => [ + 'verify_peer' => false, + 'verify_peer_name' => false, + ], + ] + ); + // Username. Retrieve from form. if (empty($username) === true) { // No username provided, ask for it. $wiz = new Wizard(); - $test = file_get_contents($ws_url); + $test = file_get_contents($ws_url, false, $context); if ($test === false) { ui_print_error_message(__('WebService engine has not been started, please check documentation.')); $wiz->printForm( @@ -197,8 +208,9 @@ function quickShell() return; } - // If rediretion is enabled, we will try to connect to http:// or https:// endpoint. - $test = get_headers($ws_url); + // If rediretion is enabled, we will try to connect using + // http:// or https:// endpoint. + $test = get_headers($ws_url, null, $context); if ($test === false) { if (empty($wiz) === true) { $wiz = new Wizard(); From 96387a874f1b81e0ff4474f7a52bb4aaba04a454 Mon Sep 17 00:00:00 2001 From: marcos Date: Wed, 18 Nov 2020 10:38:49 +0100 Subject: [PATCH 070/263] remove console log --- pandora_console/general/register.php | 1 - 1 file changed, 1 deletion(-) diff --git a/pandora_console/general/register.php b/pandora_console/general/register.php index 12196fab08..07b63421a5 100644 --- a/pandora_console/general/register.php +++ b/pandora_console/general/register.php @@ -193,7 +193,6 @@ if (!$double_auth_enabled && $config['2FA_all_users'] != '' ?> = 15 && $module['id_tipo_modulo'] <= 18) { if ($snmp_version != '') { From 68ac02db70115d01c75191edfeb6a5e35e6df99e Mon Sep 17 00:00:00 2001 From: Daniel Barbero Martin Date: Fri, 20 Nov 2020 13:01:47 +0100 Subject: [PATCH 074/263] fix api function show graph --- pandora_console/include/api.php | 2 -- pandora_console/include/functions_api.php | 17 ++++++++++++----- 2 files changed, 12 insertions(+), 7 deletions(-) diff --git a/pandora_console/include/api.php b/pandora_console/include/api.php index 1161786e97..b7bfb2e508 100644 --- a/pandora_console/include/api.php +++ b/pandora_console/include/api.php @@ -55,8 +55,6 @@ $password = get_parameter('pass', ''); $user = get_parameter('user', ''); $info = get_parameter('info', ''); -$other = parseOtherParameter($otherSerialize, $otherMode); - $other = parseOtherParameter($otherSerialize, $otherMode); $apiPassword = io_output_password( db_get_value_filter( diff --git a/pandora_console/include/functions_api.php b/pandora_console/include/functions_api.php index a7f6ef8c81..b898d28530 100644 --- a/pandora_console/include/functions_api.php +++ b/pandora_console/include/functions_api.php @@ -13758,12 +13758,19 @@ function api_get_module_graph($id_module, $thrash2, $other, $thrash4) return; } - $graph_seconds = (!empty($other) && isset($other['data'][0])) ? $other['data'][0] : SECONDS_1HOUR; - // 1 hour by default. - $graph_threshold = (!empty($other) && isset($other['data'][2]) && $other['data'][2]) ? $other['data'][2] : 0; + if (is_array($other['data']) === true) { + $graph_seconds = (!empty($other) && isset($other['data'][0])) ? $other['data'][0] : SECONDS_1HOUR; + // 1 hour by default. + $graph_threshold = (!empty($other) && isset($other['data'][2]) && $other['data'][2]) ? $other['data'][2] : 0; - // Graph height when send email by alert - $height = (!empty($other) && isset($other['data'][3]) && $other['data'][3]) ? $other['data'][3] : null; + // Graph height when send email by alert + $height = (!empty($other) && isset($other['data'][3]) && $other['data'][3]) ? $other['data'][3] : 225; + } else { + $graph_seconds = $other['data']; + $graph_threshold = 0; + $other['data'][1] = 0; + $height = 225; + } if (is_nan($graph_seconds) || $graph_seconds <= 0) { // returnError('error_module_graph', __('')); From 1ad1671612273fb02dd97dd412f3b41c32cd1b4e Mon Sep 17 00:00:00 2001 From: Daniel Barbero Martin Date: Mon, 23 Nov 2020 09:29:11 +0100 Subject: [PATCH 075/263] fix remove acl widget agent/module --- .../lib/Dashboard/Widgets/agent_module.php | 137 ++++++++---------- pandora_console/include/styles/dashboards.css | 4 + 2 files changed, 61 insertions(+), 80 deletions(-) diff --git a/pandora_console/include/lib/Dashboard/Widgets/agent_module.php b/pandora_console/include/lib/Dashboard/Widgets/agent_module.php index 9913b28aaf..7953550b7a 100644 --- a/pandora_console/include/lib/Dashboard/Widgets/agent_module.php +++ b/pandora_console/include/lib/Dashboard/Widgets/agent_module.php @@ -27,6 +27,8 @@ */ namespace PandoraFMS\Dashboard; +use PandoraFMS\Agent; +use PandoraFMS\Module; /** * Agent module Widgets. @@ -324,7 +326,7 @@ class AgentModuleWidget extends Widget } foreach ($agents as $agent) { - if (!users_access_to_agent($agent['id_agente'])) { + if (users_access_to_agent($agent['id_agente']) === false) { continue; } @@ -343,8 +345,10 @@ class AgentModuleWidget extends Widget foreach ($modules_by_name as $module) { $row['modules'][$module['name']] = null; foreach ($module['id'] as $module_id) { - if (array_key_exists($module_id, $agent_modules)) { - $row['modules'][$module['name']] = modules_get_agentmodule_status($module_id); + if (array_key_exists($module_id, $agent_modules) === true) { + $row['modules'][$module['name']] = modules_get_agentmodule_status( + $module_id + ); break; } } @@ -361,12 +365,16 @@ class AgentModuleWidget extends Widget * Draw table Agent/Module. * * @param array $visualData Data for draw. + * @param array $allModules Data for th draw. * * @return string Html output. */ - private function generateViewAgentModule(array $visualData):string - { - $table_data = '
'; + private function generateViewAgentModule( + array $visualData, + array $allModules + ):string { + $style = 'display:flex; width:100%; height:100%; margin: 10px;'; + $table_data = '
'; $table_data .= ''; if (empty($visualData) === false) { @@ -374,20 +382,7 @@ class AgentModuleWidget extends Widget $array_names = []; - foreach ($visualData as $data) { - foreach ($data['modules'] as $module_name => $module) { - if ($module === null - || in_array($module_name, $array_names) - ) { - continue; - } else { - $array_names[] = $module_name; - } - } - } - - natcasesort($array_names); - foreach ($array_names as $module_name) { + foreach ($allModules as $module_name) { $file_name = ui_print_truncate_text( $module_name, 'module_small', @@ -444,14 +439,17 @@ class AgentModuleWidget extends Widget foreach ($row['modules'] as $module_name => $module) { if ($module === null) { - if (in_array($module_name, $array_names)) { - $table_data .= "'; } else { continue; } } else { - $table_data .= " - +
"; + if (in_array($module_name, $allModules) === true) { + $style = 'background-color: transparent;'; + $table_data .= ""; $table_data .= '"; + $style = 'text-align: center;'; + $style .= ' background-color: transparent;'; + $table_data .= ""; switch ($module) { case AGENT_STATUS_NORMAL: $table_data .= \ui_print_status_image( @@ -592,70 +590,49 @@ class AgentModuleWidget extends Widget return $output; } - if (isset($this->values['mAgents']) === true - && empty($this->values['mAgents']) === false - ) { - $sql = sprintf( - 'SELECT id_agente,nombre,alias - FROM tagente - WHERE id_agente IN (%s) - ORDER BY id_agente', - $this->values['mAgents'] - ); - $agents = db_get_all_rows_sql($sql); - if ($agents === false) { - $agents = []; + // Estract info all modules selected. + $target_modules = explode(',', $this->values['mModules']); + $all_modules = Module::search( + ['id_agente_modulo' => $target_modules] + ); + $reduceAllModules = array_reduce( + $all_modules, + function ($carry, $item) { + $carry[$item->name()] = null; + return $carry; } + ); - $modules = false; - if (isset($this->values['mModules']) === true - && empty($this->values['mModules']) === false - ) { - $sql = sprintf( - 'SELECT nombre - FROM tagente_modulo - WHERE id_agente_modulo IN (%s)', - $this->values['mModules'] + \ksort($reduceAllModules); + + $visualData = []; + // Estract info agents selected. + $target_agents = explode(',', $this->values['mAgents']); + foreach ($target_agents as $agent_id) { + try { + $agent = new Agent($agent_id); + $visualData[$agent_id]['agent_status'] = $agent->lastStatus(); + $visualData[$agent_id]['agent_name'] = $agent->name(); + $visualData[$agent_id]['agent_alias'] = $agent->alias(); + + $modules = $agent->searchModules( + ['id_agente_modulo' => $target_modules] ); - $arrayNames = db_get_all_rows_sql($sql); - $names = array_reduce( - $arrayNames, - function ($carry, $item) { - $carry[] = $item['nombre']; - return $carry; - } - ); - - $sql = sprintf( - 'SELECT id_agente_modulo,nombre - FROM tagente_modulo - WHERE id_agente IN (%s) - AND nombre IN ("%s") - AND delete_pending = 0 - ORDER BY nombre', - $this->values['mAgents'], - implode('","', $names) - ); - - $modules = index_array( - db_get_all_rows_sql($sql), - 'id_agente_modulo', - 'nombre' - ); + $visualData[$agent_id]['modules'] = $reduceAllModules; + foreach ($modules as $module) { + $visualData[$agent_id]['modules'][$module->name()] = $module->getStatus()->estado(); + } + } catch (Exception $e) { + echo 'Error: '.$e->getMessage(); } - - if ($modules === false) { - $modules = []; - } - } else { - $agents = []; - $modules = []; } - $visualData = $this->generateDataAgentModule($agents, $modules); - - $output = $this->generateViewAgentModule($visualData); + $allModules = array_keys($reduceAllModules); + $output = $this->generateViewAgentModule( + $visualData, + $allModules + ); return $output; } diff --git a/pandora_console/include/styles/dashboards.css b/pandora_console/include/styles/dashboards.css index bbbf993112..70c41dd643 100644 --- a/pandora_console/include/styles/dashboards.css +++ b/pandora_console/include/styles/dashboards.css @@ -407,6 +407,10 @@ table.group_modules_status_box tr td span a { text-align: center; } +table.widget_agent_module tbody tr td { + padding: 5px; +} + .container-center .widget-histogram-chart .flot-text .flot-x-axis div { white-space: nowrap; } From 5978e46141ec3399a2689179fb044bd4f9863a95 Mon Sep 17 00:00:00 2001 From: marcos Date: Mon, 23 Nov 2020 13:29:35 +0100 Subject: [PATCH 076/263] add toolkit on sql query report item editor --- .../godmode/reporting/reporting_builder.item_editor.php | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/pandora_console/godmode/reporting/reporting_builder.item_editor.php b/pandora_console/godmode/reporting/reporting_builder.item_editor.php index 7762f7b752..6c889c36d7 100755 --- a/pandora_console/godmode/reporting/reporting_builder.item_editor.php +++ b/pandora_console/godmode/reporting/reporting_builder.item_editor.php @@ -1896,7 +1896,14 @@ $class = 'databox filters';
+ + Date: Mon, 23 Nov 2020 13:49:59 +0100 Subject: [PATCH 077/263] fix inserts demo VC --- .../include/functions_visual_map.php | 2 +- pandora_console/pandoradb_data.sql | 495 +++++++++--------- 2 files changed, 247 insertions(+), 250 deletions(-) diff --git a/pandora_console/include/functions_visual_map.php b/pandora_console/include/functions_visual_map.php index 39d19bd7ec..12b00a868d 100755 --- a/pandora_console/include/functions_visual_map.php +++ b/pandora_console/include/functions_visual_map.php @@ -1712,7 +1712,7 @@ function visual_map_print_item( $img_style_title = strip_tags($label); if ($layoutData['type'] == STATIC_GRAPH) { if ($layoutData['id_agente_modulo'] != 0) { - if ($layoutData['id_metaconsole'] != 0) { + if (is_metaconsole() && $layoutData['id_metaconsole'] != 0) { // Metaconsole db connection $connection = db_get_row_filter( 'tmetaconsole_setup', diff --git a/pandora_console/pandoradb_data.sql b/pandora_console/pandoradb_data.sql index 53b1a1027f..1121e39b59 100644 --- a/pandora_console/pandoradb_data.sql +++ b/pandora_console/pandoradb_data.sql @@ -1348,255 +1348,252 @@ VALUES -- -- Dumping data for table `tlayout_data` -- -INSERT INTO `tlayout_data` -VALUES - (1,1,998,699,80,18,'','rack_hard_disk_2',0,3600,9,2,0,0,1,0,0,0,0,'line','down','','',0,0,'default',0,'0.000','0.000',0,0,'analogic_1','time','Europe/Madrid',0,60), - (2,1,998,783,80,18,'','rack_hard_disk_2',0,3600,9,2,0,0,1,0,0,0,0,'line','down','','',0,0,'default',0,'0.000','0.000',0,0,'analogic_1','time','Europe/Madrid',0,60), - (3,1,1016,783,80,18,'','rack_hard_disk_2',0,3600,9,2,0,0,1,0,0,0,0,'line','down','','',0,0,'default',0,'0.000','0.000',0,0,'analogic_1','time','Europe/Madrid',0,60), - (4,1,1016,699,80,18,'','rack_hard_disk_2',0,3600,9,2,0,0,1,0,0,0,0,'line','down','','',0,0,'default',0,'0.000','0.000',0,0,'analogic_1','time','Europe/Madrid',0,60), - (5,1,1034,783,80,18,'','rack_hard_disk_2',0,3600,9,2,0,0,1,0,0,0,0,'line','down','','',0,0,'default',0,'0.000','0.000',0,0,'analogic_1','time','Europe/Madrid',0,60), - (6,1,1034,699,80,18,'','rack_hard_disk_2',0,3600,9,2,0,0,1,0,0,0,0,'line','down','','',0,0,'default',0,'0.000','0.000',0,0,'analogic_1','time','Europe/Madrid',0,60), - (7,1,1052,783,80,18,'','rack_hard_disk_2',0,3600,9,2,0,0,1,0,0,0,0,'line','down','','',0,0,'default',0,'0.000','0.000',0,0,'analogic_1','time','Europe/Madrid',0,60), - (8,1,1052,699,80,18,'','rack_hard_disk_2',0,3600,9,2,0,0,1,0,0,0,0,'line','down','','',0,0,'default',0,'0.000','0.000',0,0,'analogic_1','time','Europe/Madrid',0,60), - (9,1,1070,783,80,18,'','rack_hard_disk_2',0,3600,9,2,0,0,1,0,0,0,0,'line','down','','',0,0,'default',0,'0.000','0.000',0,0,'analogic_1','time','Europe/Madrid',0,60), - (10,1,1070,699,80,18,'','rack_hard_disk_2',0,3600,9,2,0,0,1,0,0,0,0,'line','down','','',0,0,'default',0,'0.000','0.000',0,0,'analogic_1','time','Europe/Madrid',0,60), - (11,1,1088,783,80,18,'','rack_hard_disk_2',0,3600,9,2,0,0,1,0,0,0,0,'line','down','','',0,0,'default',0,'0.000','0.000',0,0,'analogic_1','time','Europe/Madrid',0,60), - (12,1,1088,699,80,18,'','rack_hard_disk_2',0,3600,9,2,0,0,1,0,0,0,0,'line','down','','',0,0,'default',0,'0.000','0.000',0,0,'analogic_1','time','Europe/Madrid',0,60), - (13,1,1106,699,80,18,'','rack_hard_disk_2',0,3600,9,2,0,0,1,0,0,0,0,'line','down','','',0,0,'default',0,'0.000','0.000',0,0,'analogic_1','time','Europe/Madrid',0,60), - (14,1,1106,783,80,18,'','rack_hard_disk_2',0,3600,9,2,0,0,1,0,0,0,0,'line','down','','',0,0,'default',0,'0.000','0.000',0,0,'analogic_1','time','Europe/Madrid',0,60), - (15,1,1124,699,80,18,'','rack_hard_disk_2',0,3600,9,2,0,0,1,0,0,0,0,'line','down','','',0,0,'default',0,'0.000','0.000',0,0,'analogic_1','time','Europe/Madrid',0,60), - (16,1,1124,783,80,18,'','rack_hard_disk_2',0,3600,9,2,0,0,1,0,0,0,0,'line','down','','',0,0,'default',0,'0.000','0.000',0,0,'analogic_1','time','Europe/Madrid',0,60), - (17,1,1142,699,80,18,'','rack_hard_disk_2',0,3600,9,2,0,0,1,0,0,0,0,'line','down','','',0,0,'default',0,'0.000','0.000',0,0,'analogic_1','time','Europe/Madrid',0,60), - (18,1,1142,783,80,18,'','rack_hard_disk_2',0,3600,9,2,0,0,1,0,0,0,0,'line','down','','',0,0,'default',0,'0.000','0.000',0,0,'analogic_1','time','Europe/Madrid',0,60), - (19,1,1160,699,80,18,'','rack_hard_disk_2',0,3600,9,2,0,0,1,0,0,0,0,'line','down','','',0,0,'default',0,'0.000','0.000',0,0,'analogic_1','time','Europe/Madrid',0,60), - (20,1,1160,783,80,18,'','rack_hard_disk_2',0,3600,9,2,0,0,1,0,0,0,0,'line','down','','',0,0,'default',0,'0.000','0.000',0,0,'analogic_1','time','Europe/Madrid',0,60), - (21,1,1178,699,80,18,'','rack_hard_disk_2',0,3600,9,2,0,0,1,0,0,0,0,'line','down','','',0,0,'default',0,'0.000','0.000',0,0,'analogic_1','time','Europe/Madrid',0,60), - (22,1,1178,783,80,18,'','rack_hard_disk_2',0,3600,9,2,0,0,1,0,0,0,0,'line','down','','',0,0,'default',0,'0.000','0.000',0,0,'analogic_1','time','Europe/Madrid',0,60), - (23,1,1196,699,80,18,'','rack_hard_disk_2',0,3600,9,2,0,0,1,0,0,0,0,'line','down','','',0,0,'default',0,'0.000','0.000',0,0,'analogic_1','time','Europe/Madrid',0,60), - (24,1,1196,783,80,18,'','rack_hard_disk_2',0,3600,9,2,0,0,1,0,0,0,0,'line','down','','',0,0,'default',0,'0.000','0.000',0,0,'analogic_1','time','Europe/Madrid',0,60), - (25,1,1214,699,80,18,'','rack_hard_disk_2',0,3600,9,2,0,0,1,0,0,0,0,'line','down','','',0,0,'default',0,'0.000','0.000',0,0,'analogic_1','time','Europe/Madrid',0,60), - (26,1,1214,783,80,18,'','rack_hard_disk_2',0,3600,9,2,0,0,1,0,0,0,0,'line','down','','',0,0,'default',0,'0.000','0.000',0,0,'analogic_1','time','Europe/Madrid',0,60), - (27,1,1232,699,80,18,'','rack_hard_disk_2',0,3600,9,2,0,0,1,0,0,0,0,'line','down','','',0,0,'default',0,'0.000','0.000',0,0,'analogic_1','time','Europe/Madrid',0,60), - (28,1,1232,783,80,18,'','rack_hard_disk_2',0,3600,9,2,0,0,1,0,0,0,0,'line','down','','',0,0,'default',0,'0.000','0.000',0,0,'analogic_1','time','Europe/Madrid',0,60), - (29,1,1250,699,80,18,'','rack_hard_disk_2',0,3600,9,2,0,0,1,0,0,0,0,'line','down','','',0,0,'default',0,'0.000','0.000',0,0,'analogic_1','time','Europe/Madrid',0,60), - (30,1,1250,783,80,18,'','rack_hard_disk_2',0,3600,9,2,0,0,1,0,0,0,0,'line','down','','',0,0,'default',0,'0.000','0.000',0,0,'analogic_1','time','Europe/Madrid',0,60), - (31,1,1268,699,80,18,'','rack_hard_disk_2',0,3600,9,2,0,0,1,0,0,0,0,'line','down','','',0,0,'default',0,'0.000','0.000',0,0,'analogic_1','time','Europe/Madrid',0,60), - (32,1,1268,783,80,18,'','rack_hard_disk_2',0,3600,9,2,0,0,1,0,0,0,0,'line','down','','',0,0,'default',0,'0.000','0.000',0,0,'analogic_1','time','Europe/Madrid',0,60), - (33,1,1286,699,80,18,'','rack_hard_disk_2',0,3600,9,2,0,0,1,0,0,0,0,'line','down','','',0,0,'default',0,'0.000','0.000',0,0,'analogic_1','time','Europe/Madrid',0,60), - (34,1,1286,783,80,18,'','rack_hard_disk_2',0,3600,9,2,0,0,1,0,0,0,0,'line','down','','',0,0,'default',0,'0.000','0.000',0,0,'analogic_1','time','Europe/Madrid',0,60), - (35,1,1286,699,80,18,'','rack_hard_disk_2',0,3600,9,2,0,0,1,0,0,0,0,'line','down','','',0,0,'default',0,'0.000','0.000',0,0,'analogic_1','time','Europe/Madrid',0,60), - (36,1,1304,783,80,18,'','rack_hard_disk_2',0,3600,9,2,0,0,1,0,0,0,0,'line','down','','',0,0,'default',0,'0.000','0.000',0,0,'analogic_1','time','Europe/Madrid',0,60), - (37,1,1304,699,80,18,'','rack_hard_disk_2',0,3600,9,2,0,0,1,0,0,0,0,'line','down','','',0,0,'default',0,'0.000','0.000',0,0,'analogic_1','time','Europe/Madrid',0,60), - (38,1,1322,783,80,18,'','rack_hard_disk_2',0,3600,9,2,0,0,1,0,0,0,0,'line','down','','',0,0,'default',0,'0.000','0.000',0,0,'analogic_1','time','Europe/Madrid',0,60), - (39,1,1322,699,80,18,'','rack_hard_disk_2',0,3600,9,2,0,0,1,0,0,0,0,'line','down','','',0,0,'default',0,'0.000','0.000',0,0,'analogic_1','time','Europe/Madrid',0,60), - (40,1,1340,783,80,18,'','rack_hard_disk_2',0,3600,9,2,0,0,1,0,0,0,0,'line','down','','',0,0,'default',0,'0.000','0.000',0,0,'analogic_1','time','Europe/Madrid',0,60), - (41,1,1507,260,174,29,'','rack_server_rack',0,3600,1,1,0,0,1,0,0,0,0,'line','down','','',0,0,'default',0,'0.000','0.000',0,0,'analogic_1','time','Europe/Madrid',0,60), - (42,1,1536,260,174,29,'','rack_server_rack',0,3600,1,1,0,0,1,0,0,0,0,'line','down','','',0,0,'default',0,'0.000','0.000',0,0,'analogic_1','time','Europe/Madrid',0,60), - (43,1,1568,260,174,29,'','rack_server_rack',0,3600,1,1,0,0,1,0,0,0,0,'line','down','','',0,0,'default',0,'0.000','0.000',0,0,'analogic_1','time','Europe/Madrid',0,60), - (44,1,1599,260,174,29,'','rack_server_rack',0,3600,1,1,0,0,1,0,0,0,0,'line','down','','',0,0,'default',0,'0.000','0.000',0,0,'analogic_1','time','Europe/Madrid',0,60), - (45,1,1627,260,174,29,'','rack_server_rack',0,3600,1,1,0,0,1,0,0,0,0,'line','down','','',0,0,'default',0,'0.000','0.000',0,0,'analogic_1','time','Europe/Madrid',0,60), - (46,1,1656,260,174,29,'','rack_server_rack',0,3600,1,1,0,0,1,0,0,0,0,'line','down','','',0,0,'default',0,'0.000','0.000',0,0,'analogic_1','time','Europe/Madrid',0,60), - (47,1,1685,260,174,29,'','rack_server_rack',0,3600,1,1,0,0,1,0,0,0,0,'line','down','','',0,0,'default',0,'0.000','0.000',0,0,'analogic_1','time','Europe/Madrid',0,60), - (48,1,1714,260,174,29,'','rack_server_rack',0,3600,1,1,0,0,1,0,0,0,0,'line','down','','',0,0,'default',0,'0.000','0.000',0,0,'analogic_1','time','Europe/Madrid',0,60), - (49,1,1743,260,174,29,'','rack_server_rack',0,3600,1,1,0,0,1,0,0,0,0,'line','down','','',0,0,'default',0,'0.000','0.000',0,0,'analogic_1','time','Europe/Madrid',0,60), - (50,1,1772,260,174,29,'','rack_server_rack',0,3600,1,1,0,0,1,0,0,0,0,'line','down','','',0,0,'default',0,'0.000','0.000',0,0,'analogic_1','time','Europe/Madrid',0,60), - (51,1,1449,260,174,29,'','rack_server_rack',0,3600,1,1,0,0,1,0,0,0,0,'line','down','','',0,0,'default',0,'0.000','0.000',0,0,'analogic_1','time','Europe/Madrid',0,60), - (52,1,1800,260,174,29,'','rack_server_rack',0,3600,1,1,0,0,1,0,0,0,0,'line','down','','',0,0,'default',0,'0.000','0.000',0,0,'analogic_1','time','Europe/Madrid',0,60), - (53,1,1413,243,205,426,'','rack_frame',0,3600,1,1,0,0,1,0,0,0,0,'line','down','','',0,0,'default',0,'0.000','0.000',0,0,'analogic_1','time','Europe/Madrid',0,60), - (54,1,962,381,73,408,'','rack_firewall',0,3600,1,1,0,0,1,0,0,0,0,'line','down','','',0,0,'default',0,'0.000','0.000',0,0,'analogic_1','time','Europe/Madrid',0,60), - (55,1,962,454,73,408,'','rack_pdu',0,3600,1,1,0,0,1,0,0,0,0,'line','down','','',0,0,'default',0,'0.000','0.000',0,0,'analogic_1','time','Europe/Madrid',0,60), - (56,1,530,732,74,413,'','rack_server',0,3600,1,1,0,0,1,0,0,0,0,'line','down','','',0,0,'default',0,'0.000','0.000',0,0,'analogic_1','time','Europe/Madrid',0,60), - (57,1,962,233,74,413,'','rack_server',0,3600,1,1,0,0,1,0,0,0,0,'line','down','','',0,0,'default',0,'0.000','0.000',0,0,'analogic_1','time','Europe/Madrid',0,60), - (58,1,962,307,74,413,'','rack_server',0,3600,1,1,0,0,1,0,0,0,0,'line','down','','',0,0,'default',0,'0.000','0.000',0,0,'analogic_1','time','Europe/Madrid',0,60), - (59,1,530,658,74,413,'','rack_server',0,3600,1,1,0,0,1,0,0,0,0,'line','down','','',0,0,'default',0,'0.000','0.000',0,0,'analogic_1','time','Europe/Madrid',0,60), - (60,1,530,350,74,413,'','rack_server',0,3600,1,1,0,0,1,0,0,0,0,'line','down','','',0,0,'default',0,'0.000','0.000',0,0,'analogic_1','time','Europe/Madrid',0,60), - (61,1,530,204,73,408,'','rack_psa',0,3600,1,1,0,0,1,0,0,0,0,'line','down','','',0,0,'default',0,'0.000','0.000',0,0,'analogic_1','time','Europe/Madrid',0,60), - (62,1,530,277,73,408,'','rack_pdu',0,3600,1,1,0,0,1,0,0,0,0,'line','down','','',0,0,'default',0,'0.000','0.000',0,0,'analogic_1','time','Europe/Madrid',0,60), - (63,1,530,585,73,408,'','rack_firewall',0,3600,1,1,0,0,1,0,0,0,0,'line','down','','',0,0,'default',0,'0.000','0.000',0,0,'analogic_1','time','Europe/Madrid',0,60), - (64,1,530,424,161,411,'','rack_double_server',0,3600,1,1,0,0,1,0,0,0,0,'line','down','','',0,0,'default',0,'0.000','0.000',0,0,'analogic_1','time','Europe/Madrid',0,60), - (65,1,1426,448,74,413,'','rack_server',0,3600,1,1,0,0,1,0,0,0,0,'line','down','','',0,0,'default',0,'0.000','0.000',0,0,'analogic_1','time','Europe/Madrid',0,60), - (66,1,1495,540,174,29,'','rack_server_rack',0,3600,1,1,0,0,1,0,0,0,0,'line','down','','',0,0,'default',0,'0.000','0.000',0,0,'analogic_1','time','Europe/Madrid',0,60), - (67,1,1423,260,174,29,'','rack_server_rack',0,3600,1,1,0,0,1,0,0,0,0,'line','down','','',0,0,'default',0,'0.000','0.000',0,0,'analogic_1','time','Europe/Madrid',0,60), - (68,1,1463,540,174,29,'','rack_server_rack',0,3600,1,1,0,0,1,0,0,0,0,'line','down','','',0,0,'default',0,'0.000','0.000',0,0,'analogic_1','time','Europe/Madrid',0,60), - (69,1,1433,540,174,29,'','rack_server_rack',0,3600,1,1,0,0,1,0,0,0,0,'line','down','','',0,0,'default',0,'0.000','0.000',0,0,'analogic_1','time','Europe/Madrid',0,60), - (70,1,74,733,73,408,'','rack_pdu',0,3600,1,1,0,0,1,0,0,0,0,'line','down','','',0,0,'default',0,'0.000','0.000',0,0,'analogic_1','time','Europe/Madrid',0,60), - (71,1,1098,701,80,18,'','rack_hard_disk',0,3600,9,2,0,0,1,0,0,0,0,'line','down','','',0,0,'default',0,'0.000','0.000',0,0,'analogic_1','time','Europe/Madrid',0,60), - (72,1,1148,701,80,18,'','rack_hard_disk',0,3600,9,2,0,0,1,0,0,0,0,'line','down','','',0,0,'default',0,'0.000','0.000',0,0,'analogic_1','time','Europe/Madrid',0,60), - (73,1,1340,699,80,18,'','rack_hard_disk_2',0,3600,9,2,0,0,1,0,0,0,0,'line','down','','',0,0,'default',0,'0.000','0.000',0,0,'analogic_1','time','Europe/Madrid',0,60), - (74,1,1358,783,80,18,'','rack_hard_disk_2',0,3600,9,2,0,0,1,0,0,0,0,'line','down','','',0,0,'default',0,'0.000','0.000',0,0,'analogic_1','time','Europe/Madrid',0,60), - (75,1,1358,699,80,18,'','rack_hard_disk_2',0,3600,9,2,0,0,1,0,0,0,0,'line','down','','',0,0,'default',0,'0.000','0.000',0,0,'analogic_1','time','Europe/Madrid',0,60), - (76,1,1143,783,80,18,'','rack_hard_disk',0,3600,9,2,0,0,1,0,0,0,0,'line','down','','',0,0,'default',0,'0.000','0.000',0,0,'analogic_1','time','Europe/Madrid',0,60), - (77,1,962,682,205,426,'','rack_frame',0,3600,1,1,0,0,1,0,0,0,0,'line','down','','',0,0,'default',0,'0.000','0.000',0,0,'analogic_1','time','Europe/Madrid',0,60), - (78,1,1522,540,174,29,'','rack_server_rack',0,3600,1,1,0,0,1,0,0,0,0,'line','down','','',0,0,'default',0,'0.000','0.000',0,0,'analogic_1','time','Europe/Madrid',0,60), - (79,1,1419,521,205,426,'','rack_frame',0,3600,1,1,0,0,1,0,0,0,0,'line','down','','',0,0,'default',0,'0.000','0.000',0,0,'analogic_1','time','Europe/Madrid',0,60), - (80,1,74,278,74,413,'','rack_server',0,3600,1,1,0,0,1,0,0,0,0,'line','down','','',0,0,'default',0,'0.000','0.000',0,0,'analogic_1','time','Europe/Madrid',0,60), - (81,1,74,572,161,411,'','rack_double_server',0,3600,1,1,0,0,1,0,0,0,0,'line','down','','',0,0,'default',0,'0.000','0.000',0,0,'analogic_1','time','Europe/Madrid',0,60), - (82,1,1418,729,74,413,'','rack_server',0,3600,1,1,0,0,1,0,0,0,0,'line','down','','',0,0,'default',0,'0.000','0.000',0,0,'analogic_1','time','Europe/Madrid',0,60), - (83,1,962,527,73,408,'','rack_switch',0,3600,1,1,0,0,1,0,0,0,0,'line','down','','',0,0,'default',0,'0.000','0.000',0,0,'analogic_1','time','Europe/Madrid',0,60), - (84,1,74,352,73,408,'','rack_router',0,3600,1,1,0,0,1,0,0,0,0,'line','down','','',0,0,'default',0,'0.000','0.000',0,0,'analogic_1','time','Europe/Madrid',0,60), - (85,1,962,600,74,413,'','rack_server',0,3600,1,1,0,0,1,0,0,0,0,'line','down','','',0,0,'default',0,'0.000','0.000',0,0,'analogic_1','time','Europe/Madrid',0,60), - (86,1,530,806,73,408,'','rack_firewall',0,3600,1,1,0,0,1,0,0,0,0,'line','down','','',0,0,'default',0,'0.000','0.000',0,0,'analogic_1','time','Europe/Madrid',0,60), - (87,1,74,425,74,413,'','rack_server',0,3600,1,1,0,0,1,0,0,0,0,'line','down','','',0,0,'default',0,'0.000','0.000',0,0,'analogic_1','time','Europe/Madrid',0,60), - (88,1,74,499,73,408,'','rack_switch',0,3600,1,1,0,0,1,0,0,0,0,'line','down','','',0,0,'default',0,'0.000','0.000',0,0,'analogic_1','time','Europe/Madrid',0,60), - (89,1,74,806,73,408,'','rack_psa',0,3600,1,1,0,0,1,0,0,0,0,'line','down','','',0,0,'default',0,'0.000','0.000',0,0,'analogic_1','time','Europe/Madrid',0,60), - (90,1,74,204,74,413,'','rack_server',0,3600,1,1,0,0,1,0,0,0,0,'line','down','','',0,0,'default',0,'0.000','0.000',0,0,'analogic_1','time','Europe/Madrid',0,60), - (91,1,1424,806,73,408,'','rack_firewall',0,3600,1,1,0,0,1,0,0,0,0,'line','down','','',0,0,'default',0,'0.000','0.000',0,0,'analogic_1','time','Europe/Madrid',0,60), - (92,1,1486,907,0,0,'<p style="text-align: center; overflow: hidden;"><span class="visual_font_size_28pt" style="color: #ffffff; font-family: opensans;"><strong><span class="visual_font_size_28pt" style="color: #ffffff; font-family: opensans;">Office 8 -&nbsp;</span></strong></span><span class="visual_font_size_28pt" style="color: #ffffff; font-family: opensans;">Rack 2</span></p>','white',4,3600,1,1,0,0,1,0,0,0,0,'line','down','','',0,0,'default',0,'0.000','0.000',0,0,'analogic_1','time','Europe/Madrid',0,0), - (93,1,1048,889,58,281,'<p style="text-align: center; overflow: hidden;"><span class="visual_font_size_28pt" style="color: #ffffff; font-family: opensans;"><strong><span class="visual_font_size_28pt" style="color: #ffffff; font-family: opensans;">Office 8 -&nbsp;</span></strong></span><span class="visual_font_size_28pt" style="color: #ffffff; font-family: opensans;">Rack 1</span></p>','white',4,3600,1,1,0,0,1,0,0,0,0,'line','down','','',0,0,'default',0,'0.000','0.000',0,0,'analogic_1','time','Europe/Madrid',0,0), - (94,1,580,904,0,0,'<p style="text-align: center; overflow: hidden;"><span class="visual_font_size_28pt" style="color: #ffffff; font-family: opensans;"><strong><span class="visual_font_size_28pt" style="color: #ffffff; font-family: opensans;">Office 7 -&nbsp;</span></strong></span><span class="visual_font_size_28pt" style="color: #ffffff; font-family: opensans;">Rack 2</span></p>','white',4,3600,1,1,0,0,1,0,0,0,0,'line','down','','',0,0,'default',0,'0.000','0.000',0,0,'analogic_1','time','Europe/Madrid',0,0), - (95,1,132,907,0,0,'<p style="text-align: center; overflow: hidden;"><span class="visual_font_size_28pt" style="color: #ffffff; font-family: opensans;"><strong><span class="visual_font_size_28pt" style="color: #ffffff; font-family: opensans;">Office 7 -&nbsp;</span></strong></span><span class="visual_font_size_28pt" style="color: #ffffff; font-family: opensans;">Rack 1</span></p>','white',4,3600,1,1,0,0,1,0,0,0,0,'line','down','','',0,0,'default',0,'0.000','0.000',0,0,'analogic_1','time','Europe/Madrid',0,0), - (96,1,733,20,0,0,'<p style="overflow: hidden;"><span class="visual_font_size_48pt"><strong><span style="color: #ffffff; font-family: opensans;">OFFICE RACKS</span></strong></span></p>','white',4,3600,1,1,0,0,1,0,0,0,0,'line','down','','',0,0,'default',0,'0.000','0.000',0,0,'analogic_1','time','Europe/Madrid',0,0), - (97,1,1479,260,174,29,'','rack_server_rack',0,3600,1,1,0,0,1,0,0,0,0,'line','down','','',0,0,'default',0,'0.000','0.000',0,0,'analogic_1','time','Europe/Madrid',0,60), - (98,2,709,103,0,400,'','white',19,3600,0,0,0,0,1,0,0,0,0,'line','down','','#FFFFFF',0,0,'default',0,0.000,0.000,0,0,'digital_1','timedate','Europe/Madrid',0,0), - (99,2,178,481,111,111,'','status',0,3600,11556,430,0,0,1,0,0,0,0,'line','down','','',0,0,'default',0,0.000,0.000,0,0,'analogic_1','time','Europe/Madrid',0,0), - (100,2,542,481,111,111,'','status',0,3600,13,2,0,0,1,0,0,0,0,'line','down','','',0,0,'default',0,0.000,0.000,0,0,'analogic_1','time','Europe/Madrid',0,0), - (101,2,905,481,111,111,'','status',0,3600,114,11,0,0,1,0,0,0,0,'line','down','','',0,0,'default',0,0.000,0.000,0,0,'analogic_1','time','Europe/Madrid',0,0), - (102,2,1276,481,111,111,'','status',0,3600,7,1,0,0,1,0,0,0,0,'line','down','','',0,0,'default',0,0.000,0.000,0,0,'analogic_1','time','Europe/Madrid',0,0), - (103,2,1631,482,111,111,'','status',0,3600,11547,1,0,0,1,0,0,0,0,'line','down','','',0,0,'default',0,0.000,0.000,0,0,'analogic_1','time','Europe/Madrid',0,0), - (104,2,157,393,0,0,'

Backups

\n

 

','white', -4,3600,0,0,0,0,1,0,0,0,0,'line','down','','',0,0,'default',0,0.000,0.000,0,0,'analogic_1','time','Europe/Madrid',0,0), - (105,2,512,382,96,172,'<p style="overflow: hidden;"><span class="visual_font_size_28pt" style="font-family: opensans; color: #ffffff;">DB Status</span></p> <p style="overflow: hidden;">&nbsp;</p>','white',4,3600,0,0,0,0,1,0,0,0,0,'line','down','','',0,0,'default',0,0.000,0.000,0,0,'analogic_1','time','Europe/Madrid',0,0), - (106,2,886,382,0,0,'

Disk slave

\n

 

','white',4,3600,0,0,0,0,1,0,0,0,0,'line','down','','',0,0,'default',0,0.000,0.000,0,0,'analogic_1','time','Europe/Madrid',0,0), - (107,2,1251,382,0,0,'

Disk /var

\n

 

','white',4,3600,0,0,0,0,1,0,0,0,0,'line','down','','',0,0,'default',0,0.000,0.000,0,0,'analogic_1','time','Europe/Madrid',0,0), - (108,2,1547,382,0,0,'<p style="line-height: 18px; overflow: hidden;"><span class="visual_font_size_28pt" style="color: #ffffff; font-family: opensans;">Authentification</span></p>','white',4,3600,0,0,0,0,1,0,0,0,0,'line','down','','',0,0,'default',0,0.000,0.000,0,0,'analogic_1','time','Europe/Madrid',0,0), - (109,2,126,820,0,0,'<p style="line-height: 18px; overflow: hidden;"><strong><span class="visual_font_size_36pt" style="font-family: opensans; color: #ffffff;">Processing</span></strong></p>','white',4,3600,0,0,0,0,1,0,0,0,0,'line','down','','',0,0,'default',0,0.000,0.000,0,0,'analogic_1','time','Europe/Madrid',0,0), - (110,2,755,820,0,0,'<p style="line-height: 18px; overflow: hidden;"><strong><span class="visual_font_size_36pt" style="font-family: opensans; color: #ffffff;">Network</span></strong></p>','white',4,3600,0,0,0,0,1,0,0,0,0,'line','down','','',0,0,'default',0,0.000,0.000,0,0,'analogic_1','time','Europe/Madrid',0,0), - (111,2,1281,820,0,0,'<p style="line-height: 18px; overflow: hidden;"><strong><span class="visual_font_size_36pt" style="color: #ffffff; font-family: opensans;">Storage</span></strong></p>','white',4,3600,0,0,0,0,1,0,0,0,0,'line','down','','',0,0,'default',0,0.000,0.000,0,0,'analogic_1','time','Europe/Madrid',0,0), - (113,3,851,932,0,0,'','white',19,3600,0,0,0,0,1,0,0,0,0,'line','down','','#FFFFFF',0,0,'default',0,0.000,0.000,0,0,'digital_1','time','Europe/Madrid',0,0), - (114,3,946,314,60,60,'','status',0,3600,0,0,4,0,1,0,0,0,0,'line','down','','',0,0,'default',0,0.000,0.000,0,0,'analogic_1','time','Europe/Madrid',0,0), - (115,3,604,351,60,60,'','status',0,3600,0,0,5,0,1,0,0,0,0,'line','down','','',0,0,'default',0,0.000,0.000,0,0,'analogic_1','time','Europe/Madrid',0,0), - (116,3,840,314,0,0,'<p style="line-height: 18px; overflow: hidden;"><span class="visual_font_size_18pt"><strong><span style="color: #ffffff;">EUROPE</span></strong></span></p>',NULL,4,3600,0,0,4,0,1,0,0,0,0,'line','down','','',0,0,'default',0,0.000,0.000,0,0,'analogic_1','time','Europe/Madrid',0,0), - (117,3,664,374,0,0,'<p style="line-height: 18px; overflow: hidden;"><span class="visual_font_size_18pt"><strong><span style="color: #ffffff;">USA</span></strong></span></p>',NULL,4,3600,0,0,5,0,1,0,0,0,0,'line','down','','',0,0,'default',0,0.000,0.000,0,0,'analogic_1','time','Europe/Madrid',0,0), - (118,3,57,182,60,60,'','status',0,3600,0,0,5,0,1,0,0,0,0,'line','down','','',0,0,'default',0,0.000,0.000,0,0,'analogic_1','time','Europe/Madrid',0,0), - (119,3,56,258,60,60,'','status',0,3600,0,0,4,0,1,0,0,0,0,'line','down','','',0,0,'default',0,0.000,0.000,0,0,'analogic_1','time','Europe/Madrid',0,0), - (121,3,138,183,0,0,'<p style="line-height: 18px; overflow: hidden;"><span class="visual_font_size_18pt"><strong><span style="color: #ffffff;">USA</span></strong></span></p>',NULL,4,3600,0,0,5,0,1,0,0,0,0,'line','down','','',0,0,'default',0,0.000,0.000,0,0,'analogic_1','time','Europe/Madrid',0,0), - (122,3,138,259,0,0,'<p style="line-height: 18px; overflow: hidden;"><span class="visual_font_size_18pt"><strong><span style="color: #ffffff;">EUROPE</span></strong></span></p>',NULL,4,3600,0,0,4,0,1,0,0,0,0,'line','down','','',0,0,'default',0,0.000,0.000,0,0,'analogic_1','time','Europe/Madrid',0,0), - (123,4,839,525,60,60,'','status',0,3600,0,0,8,0,1,0,0,0,0,'line','down','','',0,0,'default',0,0.000,0.000,0,0,'analogic_1','time','Europe/Madrid',0,0), - (124,4,699,583,60,60,'','status',0,3600,0,0,9,0,1,0,0,0,0,'line','down','','',0,0,'default',0,0.000,0.000,0,0,'analogic_1','time','Europe/Madrid',0,0), - (125,4,585,705,60,60,'','status',0,3600,0,0,6,0,1,0,0,0,0,'line','down','','',0,0,'default',0,0.000,0.000,0,0,'analogic_1','time','Europe/Madrid',0,0), - (126,4,563,754,0,0,'<p style="overflow: hidden;"><span style="color: #000000;"><strong><span class="visual_font_size_18pt">SPAIN</span></strong></span></p>',NULL,4,3600,0,0,6,0,1,0,0,0,0,'line','down','','',0,0,'default',0,0.000,0.000,0,0,'analogic_1','time','Europe/Madrid',0,0), - (127,4,681,629,0,0,'<p style="overflow: hidden;"><span style="color: #000000;"><strong><span class="visual_font_size_18pt">FRANCE</span></strong></span></p>',NULL,4,3600,0,0,9,0,1,0,0,0,0,'line','down','','',0,0,'default',0,0.000,0.000,0,0,'analogic_1','time','Europe/Madrid',0,0), - (128,4,832,576,0,0,'<p style="overflow: hidden;"><span class="visual_font_size_18pt" style="color: #000000;"><strong><span style="color: #000000;">GERMANY</span></strong></span></p>',NULL,4,3600,0,0,8,0,1,0,0,0,0,'line','down','','',0,0,'default',0,0.000,0.000,0,0,'analogic_1','time','Europe/Madrid',0,0), - (129,4,51,177,60,60,'','status',0,3600,0,0,8,0,1,0,0,0,0,'line','down','','',0,0,'default',0,0.000,0.000,0,0,'analogic_1','time','Europe/Madrid',0,0), - (130,4,124,176,0,0,'<p style="overflow: hidden;"><span class="visual_font_size_18pt" style="color: #ffffff;"><strong>GERMANY</strong></span></p>',NULL,4,3600,0,0,8,0,1,0,0,0,0,'line','down','','',0,0,'default',0,0.000,0.000,0,0,'analogic_1','time','Europe/Madrid',0,0), - (131,4,51,255,60,60,'','status',0,3600,0,0,9,0,1,0,0,0,0,'line','down','','',0,0,'default',0,0.000,0.000,0,0,'analogic_1','time','Europe/Madrid',0,0), - (132,4,127,256,0,0,'<p style="overflow: hidden;"><span style="color: #ffffff;"><strong><span class="visual_font_size_18pt">FRANCE</span></strong></span></p>',NULL,4,3600,0,0,9,0,1,0,0,0,0,'line','down','','',0,0,'default',0,0.000,0.000,0,0,'analogic_1','time','Europe/Madrid',0,0), - (133,4,51,333,60,60,'','status',0,3600,0,0,7,0,1,0,0,0,0,'line','down','','',0,0,'default',0,0.000,0.000,0,0,'analogic_1','time','Europe/Madrid',0,0), - (134,4,129,333,0,0,'<p style="overflow: hidden;"><span style="color: #ffffff;"><strong><span class="visual_font_size_18pt">SPAIN</span></strong></span></p>',NULL,4,3600,0,0,6,0,1,0,0,0,0,'line','down','','',0,0,'default',0,0.000,0.000,0,0,'analogic_1','time','Europe/Madrid',0,0), - (136,4,816,928,0,0,'','white',19,3600,0,0,0,0,1,0,0,0,0,'line','down','','#FFFFFF',0,0,'default',0,0.000,0.000,0,0,'digital_1','time','Europe/Madrid',0,0), - (137,4,1674,184,132,200,'','worldmap',5,3600,0,0,3,0,1,0,0,0,0,'line','down','','',0,0,'default',0,0.000,0.000,0,0,'analogic_1','time','Europe/Madrid',0,0), - (143,6,972,452,60,60,'','status',0,3600,0,0,7,0,1,0,0,0,0,'line','down','','',0,0,'default',0,0.000,0.000,0,0,'analogic_1','time','Europe/Madrid',0,0), - (144,6,1039,223,60,60,'','status',0,3600,0,0,11,0,1,0,0,0,0,'line','down','','',0,0,'default',0,0.000,0.000,0,0,'analogic_1','time','Europe/Madrid',0,0), - (145,6,1339,366,60,60,'','status',0,3600,0,0,10,0,1,0,0,0,0,'line','down','','',0,0,'default',0,0.000,0.000,0,0,'analogic_1','time','Europe/Madrid',0,0), - (146,6,841,699,60,60,'','status',0,3600,0,0,12,0,1,0,0,0,0,'line','down','','',0,0,'default',0,0.000,0.000,0,0,'analogic_1','time','Europe/Madrid',0,0), - (147,6,1677,183,132,200,'','worldmap',5,3600,0,0,3,0,1,0,0,0,0,'line','down','','',0,0,'default',0,0.000,0.000,0,0,'analogic_1','time','Europe/Madrid',0,0), - (148,6,1674,341,132,200,'','europemap',5,3600,0,0,4,0,1,0,0,0,0,'line','down','','',0,0,'default',0,0.000,0.000,0,0,'analogic_1','time','Europe/Madrid',0,0), - (149,6,1023,288,0,0,'<p style="line-height: 18px; overflow: hidden;"><strong><span class="visual_font_size_18pt" style="color: #000000;">BASQUE COUNTRY</span></strong></p>',NULL,4,3600,0,0,11,0,1,0,0,0,0,'line','down','','',0,0,'default',0,0.000,0.000,0,0,'analogic_1','time','Europe/Madrid',0,0), - (150,6,1324,425,0,0,'<p style="line-height: 18px; overflow: hidden;"><span style="color: #ffffff;"><strong><span class="visual_font_size_18pt">CATALONIA</span></strong></span></p>',NULL,4,3600,0,0,10,0,1,0,0,0,0,'line','down','','',0,0,'default',0,0.000,0.000,0,0,'analogic_1','time','Europe/Madrid',0,0), - (151,6,963,511,0,0,'<p style="line-height: 18px; overflow: hidden;"><strong><span class="visual_font_size_18pt" style="color: #000000;">MADRID</span></strong></p>',NULL,4,3600,0,0,7,0,1,0,0,0,0,'line','down','','',0,0,'default',0,0.000,0.000,0,0,'analogic_1','time','Europe/Madrid',0,0), - (152,6,843,749,0,0,'<p style="line-height: 18px; overflow: hidden;"><strong><span class="visual_font_size_18pt" style="color: #000000;">ANDALUSIA</span></strong></p>',NULL,4,3600,0,0,12,0,1,0,0,0,0,'line','down','','',0,0,'default',0,0.000,0.000,0,0,'analogic_1','time','Europe/Madrid',0,0), - (153,6,52,179,60,60,'','status',0,3600,0,0,11,0,1,0,0,0,0,'line','down','','',0,0,'default',0,0.000,0.000,0,0,'analogic_1','time','Europe/Madrid',0,0), - (154,6,125,184,0,0,'<p style="line-height: 18px; overflow: hidden;"><span class="visual_font_size_14pt" style="color: #ffffff;"><strong>BASQUE COUNTRY</strong></span></p>',NULL,4,3600,0,0,11,0,1,0,0,0,0,'line','down','','',0,0,'default',0,0.000,0.000,0,0,'analogic_1','time','Europe/Madrid',0,0), - (155,6,53,260,60,60,'','status',0,3600,0,0,7,0,1,0,0,0,0,'line','down','','',0,0,'default',0,0.000,0.000,0,0,'analogic_1','time','Europe/Madrid',0,0), - (156,6,132,264,0,0,'<p style="line-height: 18px; overflow: hidden;"><span class="visual_font_size_14pt" style="color: #ffffff;"><strong>MADRID</strong></span></p>',NULL,4,3600,0,0,7,0,1,0,0,0,0,'line','down','','',0,0,'default',0,0.000,0.000,0,0,'analogic_1','time','Europe/Madrid',0,0), - (157,6,52,339,60,60,'','status',0,3600,0,0,10,0,1,0,0,0,0,'line','down','','',0,0,'default',0,0.000,0.000,0,0,'analogic_1','time','Europe/Madrid',0,0), - (158,6,132,343,0,0,'<p style="line-height: 18px; overflow: hidden;"><span class="visual_font_size_14pt" style="color: #ffffff;"><strong>CATALONIA</strong></span></p>',NULL,4,3600,0,0,10,0,1,0,0,0,0,'line','down','','',0,0,'default',0,0.000,0.000,0,0,'analogic_1','time','Europe/Madrid',0,0), - (159,6,52,413,60,60,'','status',0,3600,0,0,12,0,1,0,0,0,0,'line','down','','',0,0,'default',0,0.000,0.000,0,0,'analogic_1','time','Europe/Madrid',0,0), - (160,6,132,423,0,0,'<p style="line-height: 18px; overflow: hidden;"><span class="visual_font_size_14pt" style="color: #ffffff;"><strong>ANDALUSIA</strong></span></p>',NULL,4,3600,0,0,12,0,1,0,0,0,0,'line','down','','',0,0,'default',0,0.000,0.000,0,0,'analogic_1','time','Europe/Madrid',0,0), - (161,11,761,345,60,60,'','status',0,3600,0,1,0,0,1,0,0,0,0,'line','down','','',0,0,'default',0,0.000,0.000,0,0,'analogic_1','time','Europe/Madrid',0,0), - (162,11,791,401,0,0,'<p style="margin-top:"><span class="visual_font_size_18pt"><strong>BILBAO</strong></span></p>','white',4,3600,0,0,0,0,1,0,0,0,0,'line','down','','',0,0,'default',0,0.000,0.000,0,0,'analogic_1','time','Europe/Madrid',0,0), - (163,11,53,183,60,60,'','status',0,3600,0,1,0,0,1,0,0,0,0,'line','down','','',0,0,'default',0,0.000,0.000,0,0,'analogic_1','time','Europe/Madrid',0,0), - (164,11,131,191,0,0,'<p><span class="visual_font_size_18pt" style="color: #ffffff;"><strong>BILBAO</strong></span></p>','white',4,3600,0,0,0,0,1,0,0,0,0,'line','down','','',0,0,'default',0,0.000,0.000,0,0,'analogic_1','time','Europe/Madrid',0,0), - (165,11,1675,184,132,200,'','worldmap',5,3600,0,0,3,0,1,0,0,0,0,'line','down','','',0,0,'default',0,0.000,0.000,0,0,'analogic_1','time','Europe/Madrid',0,0), - (167,11,1675,340,132,200,'','europemap',5,3600,0,0,4,0,1,0,0,0,0,'line','down','','',0,0,'default',0,0.000,0.000,0,0,'analogic_1','time','Europe/Madrid',0,0), - (168,11,1673,507,132,200,'','spainmap',5,3600,0,0,6,0,1,0,0,0,0,'line','down','','',0,0,'default',0,0.000,0.000,0,0,'analogic_1','time','Europe/Madrid',0,0), - (169,10,1075,607,60,60,'','status',0,3600,0,1,0,0,1,0,0,0,0,'line','down','','',0,0,'default',0,0.000,0.000,0,0,'analogic_1','time','Europe/Madrid',0,0), - (170,10,1012,539,60,60,'','status',0,3600,0,1,0,0,1,0,0,0,0,'line','down','','',0,0,'default',0,0.000,0.000,0,0,'analogic_1','time','Europe/Madrid',0,0), - (171,10,57,266,60,60,'','status',0,3600,0,1,0,0,1,0,0,0,0,'line','down','','',0,0,'default',0,0.000,0.000,0,0,'analogic_1','time','Europe/Madrid',0,0), - (172,10,55,183,60,60,'','status',0,3600,0,1,0,0,1,0,0,0,0,'line','down','','',0,0,'default',0,0.000,0.000,0,0,'analogic_1','time','Europe/Madrid',0,0), - (173,10,131,189,0,0,'<p style="line-height: 18px; overflow: hidden;"><span style="color: #ffffff;"><strong><span class="visual_font_size_18pt">BARCELONA</span></strong></span></p>',NULL,4,3600,0,0,0,0,1,0,0,0,0,'line','down','','',0,0,'default',0,0.000,0.000,0,0,'analogic_1','time','Europe/Madrid',0,0), - (174,10,1044,670,0,0,'<p style="line-height: 18px; overflow: hidden;"><span style="color: #ffffff;"><strong><span class="visual_font_size_18pt">BARCELONA</span></strong></span></p>',NULL,4,3600,0,0,0,0,1,0,0,0,0,'line','down','','',0,0,'default',0,0.000,0.000,0,0,'analogic_1','time','Europe/Madrid',0,0), - (175,10,1000,488,0,0,'<p style="line-height: 18px; overflow: hidden;"><span class="visual_font_size_18pt"><strong><span style="color: #ffffff;">MANRESA</span></strong></span></p>',NULL,4,3600,0,0,0,0,1,0,0,0,0,'line','down','','',0,0,'default',0,0.000,0.000,0,0,'analogic_1','time','Europe/Madrid',0,0), - (176,10,134,265,0,0,'<p style="line-height: 18px; overflow: hidden;"><span style="color: #ffffff;"><strong><span class="visual_font_size_18pt">MANRESA</span></strong></span></p>',NULL,4,3600,0,0,0,0,1,0,0,0,0,'line','down','','',0,0,'default',0,0.000,0.000,0,0,'analogic_1','time','Europe/Madrid',0,0), - (177,10,1675,184,132,200,'','worldmap',5,3600,0,0,3,0,1,0,0,0,0,'line','down','','',0,0,'default',0,0.000,0.000,0,0,'analogic_1','time','Europe/Madrid',0,0), - (178,10,1675,342,132,200,'','europemap',5,3600,0,0,4,0,1,0,0,0,0,'line','down','','',0,0,'default',0,0.000,0.000,0,0,'analogic_1','time','Europe/Madrid',0,0), - (179,10,1673,508,132,200,'','spainmap',5,3600,0,0,6,0,1,0,0,0,0,'line','down','','',0,0,'default',0,0.000,0.000,0,0,'analogic_1','time','Europe/Madrid',0,0), - (180,7,1675,184,132,200,'','worldmap',5,3600,0,0,3,0,1,0,0,0,0,'line','down','','',0,0,'default',0,0.000,0.000,0,0,'analogic_1','time','Europe/Madrid',0,0), - (181,7,1673,340,132,200,'','europemap',5,3600,0,0,4,0,1,0,0,0,0,'line','down','','',0,0,'default',0,0.000,0.000,0,0,'analogic_1','time','Europe/Madrid',0,0), - (182,7,1675,505,132,200,'','spainmap',5,3600,0,0,6,0,1,0,0,0,0,'line','down','','',0,0,'default',0,0.000,0.000,0,0,'analogic_1','time','Europe/Madrid',0,0), - (183,7,960,571,60,60,'','status',0,3600,0,1,0,0,1,0,0,0,0,'line','down','','',0,0,'default',0,0.000,0.000,0,0,'analogic_1','time','Europe/Madrid',0,0), - (184,7,1089,529,60,60,'','status',0,3600,0,1,0,0,1,0,0,0,0,'line','down','','',0,0,'default',0,0.000,0.000,0,0,'analogic_1','time','Europe/Madrid',0,0), - (185,7,865,541,60,60,'','status',0,3600,0,1,0,0,1,0,0,0,0,'line','down','','',0,0,'default',0,0.000,0.000,0,0,'analogic_1','time','Europe/Madrid',0,0), - (186,7,958,475,60,60,'','status',0,3600,0,1,0,0,1,0,0,0,0,'line','down','','',0,0,'default',0,0.000,0.000,0,0,'analogic_1','time','Europe/Madrid',0,0), - (187,7,54,182,60,60,'','status',0,3600,0,1,0,0,1,0,0,0,0,'line','down','','',0,0,'default',0,0.000,0.000,0,0,'analogic_1','time','Europe/Madrid',0,0), - (188,7,58,417,60,60,'','status',0,3600,0,1,0,0,1,0,0,0,0,'line','down','','',0,0,'default',0,0.000,0.000,0,0,'analogic_1','time','Europe/Madrid',0,0), - (189,7,55,258,60,60,'','status',0,3600,0,1,0,0,1,0,0,0,0,'line','down','','',0,0,'default',0,0.000,0.000,0,0,'analogic_1','time','Europe/Madrid',0,0), - (191,7,56,338,60,60,'','status',0,3600,0,1,0,0,1,0,0,0,0,'line','down','','',0,0,'default',0,0.000,0.000,0,0,'analogic_1','time','Europe/Madrid',0,0), - (192,7,947,631,0,0,'<p style="line-height: 18px; overflow: hidden;"><strong><span class="visual_font_size_18pt">MADRID</span></strong></p>',NULL,4,3600,0,0,0,0,1,0,0,0,0,'line','down','','',0,0,'default',0,0.000,0.000,0,0,'analogic_1','time','Europe/Madrid',0,0), - (193,7,790,598,0,0,'<p style="line-height: 18px; overflow: hidden;"><strong><span class="visual_font_size_18pt">LAS ROZAS</span></strong></p>',NULL,4,3600,0,0,0,0,1,0,0,0,0,'line','down','','',0,0,'default',0,0.000,0.000,0,0,'analogic_1','time','Europe/Madrid',0,0), - (194,7,914,429,0,0,'<p style="line-height: 18px; overflow: hidden;"><strong><span class="visual_font_size_18pt">ALCOBENDAS</span></strong></p>',NULL,4,3600,0,0,0,0,1,0,0,0,0,'line','down','','',0,0,'default',0,0.000,0.000,0,0,'analogic_1','time','Europe/Madrid',0,0), - (195,7,1065,583,0,0,'<p style="line-height: 18px; overflow: hidden; text-align: center;"><strong><span class="visual_font_size_18pt">ALCAL&Aacute; DE </span></strong></p> <p style="line-height: 18px; overflow: hidden; text-align: center;"><strong><span class="visual_font_size_18pt">HENARES</span></strong></p>',NULL,4,3600,0,0,0,0,1,0,0,0,0,'line','down','','',0,0,'default',0,0.000,0.000,0,0,'analogic_1','time','Europe/Madrid',0,0), - (196,7,134,267,0,0,'<p style="line-height: 18px; overflow: hidden;"><span class="visual_font_size_14pt" style="color: #ffffff;"><strong>ALCOBENDAS</strong></span></p>',NULL,4,3600,0,0,0,0,1,0,0,0,0,'line','down','','',0,0,'default',0,0.000,0.000,0,0,'analogic_1','time','Europe/Madrid',0,0), - (197,7,133,193,0,0,'<p style="line-height: 18px; overflow: hidden;"><span class="visual_font_size_14pt" style="color: #ffffff;"><strong>MADRID</strong></span></p>',NULL,4,3600,0,0,0,0,1,0,0,0,0,'line','down','','',0,0,'default',0,0.000,0.000,0,0,'analogic_1','time','Europe/Madrid',0,0), - (198,7,134,347,0,0,'<p style="line-height: 18px; overflow: hidden;"><span class="visual_font_size_14pt" style="color: #ffffff;"><strong>LAS ROZAS</strong></span></p>',NULL,4,3600,0,0,0,0,1,0,0,0,0,'line','down','','',0,0,'default',0,0.000,0.000,0,0,'analogic_1','time','Europe/Madrid',0,0), - (199,7,135,425,0,0,'<p style="line-height: 18px; overflow: hidden;"><span class="visual_font_size_14pt" style="color: #ffffff;"><strong>ALCAL&Aacute; DE HENARES</strong></span></p>',NULL,4,3600,0,0,0,0,1,0,0,0,0,'line','down','','',0,0,'default',0,0.000,0.000,0,0,'analogic_1','time','Europe/Madrid',0,0), - (200,12,757,537,60,60,'','status',0,3600,0,1,0,0,1,0,0,0,0,'line','down','','',0,0,'default',0,0.000,0.000,0,0,'analogic_1','time','Europe/Madrid',0,0), - (201,12,1022,656,60,60,'','status',0,3600,0,1,0,0,1,0,0,0,0,'line','down','','',0,0,'default',0,0.000,0.000,0,0,'analogic_1','time','Europe/Madrid',0,0), - (202,12,54,182,60,60,'','status',0,3600,0,1,0,0,1,0,0,0,0,'line','down','','',0,0,'default',0,0.000,0.000,0,0,'analogic_1','time','Europe/Madrid',0,0), - (203,12,54,260,60,60,'','status',0,3600,0,1,0,0,1,0,0,0,0,'line','down','','',0,0,'default',0,0.000,0.000,0,0,'analogic_1','time','Europe/Madrid',0,0), - (204,12,751,593,0,0,'<p style="line-height: 18px; overflow: hidden;"><span class="visual_font_size_18pt"><strong>SEVILLA</strong></span></p>',NULL,4,3600,0,0,0,0,1,0,0,0,0,'line','down','','',0,0,'default',0,0.000,0.000,0,0,'analogic_1','time','Europe/Madrid',0,0), - (205,12,1047,716,0,0,'<p style="line-height: 18px; overflow: hidden;"><span class="visual_font_size_18pt" style="color: #ffffff;"><strong>MALAGA</strong></span></p>',NULL,4,3600,0,0,0,0,1,0,0,0,0,'line','down','','',0,0,'default',0,0.000,0.000,0,0,'analogic_1','time','Europe/Madrid',0,0), - (206,12,133,266,0,0,'<p style="line-height: 18px; overflow: hidden;"><span class="visual_font_size_18pt" style="color: #ffffff;"><strong>MALAGA</strong></span></p>',NULL,4,3600,0,0,0,0,1,0,0,0,0,'line','down','','',0,0,'default',0,0.000,0.000,0,0,'analogic_1','time','Europe/Madrid',0,0), - (207,12,132,190,0,0,'<p style="line-height: 18px; overflow: hidden;"><span class="visual_font_size_18pt" style="color: #ffffff;"><strong>SEVILLA</strong></span></p>',NULL,4,3600,0,0,0,0,1,0,0,0,0,'line','down','','',0,0,'default',0,0.000,0.000,0,0,'analogic_1','time','Europe/Madrid',0,0), - (208,12,1675,184,132,200,'','worldmap',5,3600,0,0,3,0,1,0,0,0,0,'line','down','','',0,0,'default',0,0.000,0.000,0,0,'analogic_1','time','Europe/Madrid',0,0), - (209,12,1675,341,132,200,'','europemap',5,3600,0,0,4,0,1,0,0,0,0,'line','down','','',0,0,'default',0,0.000,0.000,0,0,'analogic_1','time','Europe/Madrid',0,0), - (210,12,1674,505,132,200,'','spainmap',5,3600,0,0,6,0,1,0,0,0,0,'line','down','','',0,0,'default',0,0.000,0.000,0,0,'analogic_1','time','Europe/Madrid',0,0), - (211,12,842,935,0,0,'',NULL,19,3600,0,0,0,0,1,0,0,0,0,'line','down','','#FFFFFF',0,0,'default',0,0.000,0.000,0,0,'digital_1','time','Europe/Madrid',0,0), - (212,7,848,941,0,0,'','white',19,3600,0,0,0,0,1,0,0,0,0,'line','down','','#FFFFFF',0,0,'default',0,0.000,0.000,0,0,'digital_1','time','Europe/Madrid',0,0), - (213,10,844,940,0,0,'','white',19,3600,0,0,0,0,1,0,0,0,0,'line','down','','#FFFFFF',0,0,'default',0,0.000,0.000,0,0,'digital_1','time','Europe/Madrid',0,0), - (214,11,847,948,0,0,'','white',19,3600,0,0,0,0,1,0,0,0,0,'line','down','','#FFFFFF',0,0,'default',0,0.000,0.000,0,0,'digital_1','time','Europe/Madrid',0,0), - (215,5,525,608,60,60,'','status',0,3600,0,1,0,0,1,0,0,0,0,'line','down','','',0,0,'default',0,0.000,0.000,0,0,'analogic_1','time','Europe/Madrid',0,0), - (216,5,1412,351,60,60,'','status',0,3600,0,1,0,0,1,0,0,0,0,'line','down','','',0,0,'default',0,0.000,0.000,0,0,'analogic_1','time','Europe/Madrid',0,0), - (217,5,966,697,60,60,'','status',0,3600,0,1,0,0,1,0,0,0,0,'line','down','','',0,0,'default',0,0.000,0.000,0,0,'analogic_1','time','Europe/Madrid',0,0), - (218,5,458,672,0,0,'<p style="line-height: 18px; overflow: hidden;"><strong><span class="visual_font_size_18pt" style="color: #ffffff;">CALIFORNIA</span></strong></p>','white',4,3600,0,0,0,0,1,0,0,0,0,'line','down','','',0,0,'default',0,0.000,0.000,0,0,'analogic_1','time','Europe/Madrid',0,0), - (219,5,954,759,0,0,'<p style="line-height: 18px; overflow: hidden;"><span style="color: #000000;"><strong><span class="visual_font_size_18pt">TEXAS</span></strong></span></p>','white',4,3600,0,0,0,0,1,0,0,0,0,'line','down','','',0,0,'default',0,0.000,0.000,0,0,'analogic_1','time','Europe/Madrid',0,0), - (220,5,1468,389,0,0,'<p style="line-height: 18px; overflow: hidden;"><span style="color: #ffffff;"><strong><span class="visual_font_size_18pt">NEW YORK</span></strong></span></p>','white',4,3600,0,0,0,0,1,0,0,0,0,'line','down','','',0,0,'default',0,0.000,0.000,0,0,'analogic_1','time','Europe/Madrid',0,0), - (221,5,56,182,60,60,'','status',0,3600,0,1,0,0,1,0,0,0,0,'line','down','','',0,0,'default',0,0.000,0.000,0,0,'analogic_1','time','Europe/Madrid',0,0), - (222,5,57,258,60,60,'','status',0,3600,0,1,0,0,1,0,0,0,0,'line','down','','',0,0,'default',0,0.000,0.000,0,0,'analogic_1','time','Europe/Madrid',0,0), - (223,5,57,338,60,60,'','status',0,3600,0,1,0,0,1,0,0,0,0,'line','down','','',0,0,'default',0,0.000,0.000,0,0,'analogic_1','time','Europe/Madrid',0,0), - (224,5,137,189,0,0,'<p style="line-height: 18px; overflow: hidden;"><strong><span class="visual_font_size_18pt" style="color: #ffffff;">CALIFORNIA</span></strong></p>','white',4,3600,0,0,0,0,1,0,0,0,0,'line','down','','',0,0,'default',0,0.000,0.000,0,0,'analogic_1','time','Europe/Madrid',0,0), - (225,5,139,264,0,0,'<p style="line-height: 18px; overflow: hidden;"><strong><span class="visual_font_size_18pt" style="color: #ffffff;">TEXAS</span></strong></p>','white',4,3600,0,0,0,0,1,0,0,0,0,'line','down','','',0,0,'default',0,0.000,0.000,0,0,'analogic_1','time','Europe/Madrid',0,0), - (226,5,138,342,0,0,'<p style="line-height: 18px; overflow: hidden;"><strong><span class="visual_font_size_18pt" style="color: #ffffff;">NUEVA YORK</span></strong></p>','white',4,3600,0,0,0,0,1,0,0,0,0,'line','down','','',0,0,'default',0,0.000,0.000,0,0,'analogic_1','time','Europe/Madrid',0,0), - (227,5,1677,189,132,200,'','worldmap',5,3600,0,0,3,0,1,0,0,0,0,'line','down','','',0,0,'default',0,0.000,0.000,0,0,'analogic_1','time','Europe/Madrid',0,0), - (228,9,907,353,60,60,'','status',0,3600,0,1,0,0,1,0,0,0,0,'line','down','','',0,0,'default',0,0.000,0.000,0,0,'analogic_1','time','Europe/Madrid',0,0), - (229,9,1068,599,60,60,'','status',0,3600,0,1,0,0,1,0,0,0,0,'line','down','','',0,0,'default',0,0.000,0.000,0,0,'analogic_1','time','Europe/Madrid',0,0), - (230,9,60,182,60,60,'','status',0,3600,0,1,0,0,1,0,0,0,0,'line','down','','',0,0,'default',0,0.000,0.000,0,0,'analogic_1','time','Europe/Madrid',0,0), - (231,9,61,258,60,60,'','status',0,3600,0,1,0,0,1,0,0,0,0,'line','down','','',0,0,'default',0,0.000,0.000,0,0,'analogic_1','time','Europe/Madrid',0,0), - (232,9,138,187,0,0,'<p style="line-height: 18px; overflow: hidden;"><span class="visual_font_size_18pt" style="color: #ffffff;"><strong>PARIS</strong></span></p>',NULL,4,3600,0,0,0,0,1,0,0,0,0,'line','down','','',0,0,'default',0,0.000,0.000,0,0,'analogic_1','time','Europe/Madrid',0,0), - (233,9,139,257,0,0,'<p style="line-height: 18px; overflow: hidden;"><span class="visual_font_size_18pt" style="color: #ffffff;"><strong>LYON</strong></span></p>',NULL,4,3600,0,0,0,0,1,0,0,0,0,'line','down','','',0,0,'default',0,0.000,0.000,0,0,'analogic_1','time','Europe/Madrid',0,0), - (234,9,1063,660,0,0,'<p style="line-height: 18px; overflow: hidden;"><span class="visual_font_size_18pt" style="color: #333333;"><strong>LYON</strong></span></p>',NULL,4,3600,0,0,0,0,1,0,0,0,0,'line','down','','',0,0,'default',0,0.000,0.000,0,0,'analogic_1','time','Europe/Madrid',0,0), - (235,9,905,416,0,0,'<p style="line-height: 18px; overflow: hidden;"><span class="visual_font_size_18pt" style="color: #333333;"><strong>PARIS</strong></span></p>',NULL,4,3600,0,0,0,0,1,0,0,0,0,'line','down','','',0,0,'default',0,0.000,0.000,0,0,'analogic_1','time','Europe/Madrid',0,0), - (236,9,1676,184,132,200,'','worldmap',5,3600,0,0,3,0,1,0,0,0,0,'line','down','','',0,0,'default',0,0.000,0.000,0,0,'analogic_1','time','Europe/Madrid',0,0), - (237,9,1673,340,132,200,'','europemap',5,3600,0,0,4,0,1,0,0,0,0,'line','down','','',0,0,'default',0,0.000,0.000,0,0,'analogic_1','time','Europe/Madrid',0,0), - (238,8,1098,397,60,60,'','status',0,3600,0,1,0,0,1,0,0,0,0,'line','down','','',0,0,'default',0,0.000,0.000,0,0,'analogic_1','time','Europe/Madrid',0,0), - (239,8,989,775,60,60,'','status',0,3600,0,1,0,0,1,0,0,0,0,'line','down','','',0,0,'default',0,0.000,0.000,0,0,'analogic_1','time','Europe/Madrid',0,0), - (240,8,824,595,60,60,'','status',0,3600,0,1,0,0,1,0,0,0,0,'line','down','','',0,0,'default',0,0.000,0.000,0,0,'analogic_1','time','Europe/Madrid',0,0), - (241,8,59,183,60,60,'','status',0,3600,0,1,0,0,1,0,0,0,0,'line','down','','',0,0,'default',0,0.000,0.000,0,0,'analogic_1','time','Europe/Madrid',0,0), - (242,8,62,335,60,60,'','status',0,3600,0,1,0,0,1,0,0,0,0,'line','down','','',0,0,'default',0,0.000,0.000,0,0,'analogic_1','time','Europe/Madrid',0,0), - (243,8,60,260,60,60,'','status',0,3600,0,1,0,0,1,0,0,0,0,'line','down','','',0,0,'default',0,0.000,0.000,0,0,'analogic_1','time','Europe/Madrid',0,0), - (244,8,1077,456,0,0,'<p style="line-height: 18px; overflow: hidden;"><span style="color: #000000;"><strong><span class="visual_font_size_18pt">BERLIN</span></strong></span></p>',NULL,4,3600,0,0,0,0,1,0,0,0,0,'line','down','','',0,0,'default',0,0.000,0.000,0,0,'analogic_1','time','Europe/Madrid',0,0), - (245,8,143,262,0,0,'<p style="line-height: 18px; overflow: hidden;"><strong><span class="visual_font_size_18pt" style="color: #ffffff;">FRANCFORT</span></strong></p>',NULL,4,3600,0,0,0,0,1,0,0,0,0,'line','down','','',0,0,'default',0,0.000,0.000,0,0,'analogic_1','time','Europe/Madrid',0,0), - (246,8,144,341,0,0,'<p style="line-height: 18px; overflow: hidden;"><strong><span class="visual_font_size_18pt" style="color: #ffffff;">MUNICH</span></strong></p>',NULL,4,3600,0,0,0,0,1,0,0,0,0,'line','down','','',0,0,'default',0,0.000,0.000,0,0,'analogic_1','time','Europe/Madrid',0,0), - (247,8,957,827,0,0,'<p style="line-height: 18px; overflow: hidden;"><span style="color: #000000;"><strong><span class="visual_font_size_18pt">MUNICH</span></strong></span></p>',NULL,4,3600,0,0,0,0,1,0,0,0,0,'line','down','','',0,0,'default',0,0.000,0.000,0,0,'analogic_1','time','Europe/Madrid',0,0), - (248,8,795,655,0,0,'<p style="line-height: 18px; overflow: hidden;"><span style="color: #000000;"><strong><span class="visual_font_size_18pt">FRANCFORT</span></strong></span></p>',NULL,4,3600,0,0,0,0,1,0,0,0,0,'line','down','','',0,0,'default',0,0.000,0.000,0,0,'analogic_1','time','Europe/Madrid',0,0), - (249,8,143,183,0,0,'<p style="line-height: 18px; overflow: hidden;"><strong><span class="visual_font_size_18pt" style="color: #ffffff;">BERLIN</span></strong></p>',NULL,4,3600,0,0,0,0,1,0,0,0,0,'line','down','','',0,0,'default',0,0.000,0.000,0,0,'analogic_1','time','Europe/Madrid',0,0), - (250,8,1676,185,132,200,'','worldmap',5,3600,0,0,3,0,1,0,0,0,0,'line','down','','',0,0,'default',0,0.000,0.000,0,0,'analogic_1','time','Europe/Madrid',0,0), - (251,8,1677,343,132,200,'','europemap',5,3600,0,0,4,0,1,0,0,0,0,'line','down','','',0,0,'default',0,0.000,0.000,0,0,'analogic_1','time','Europe/Madrid',0,0), - (252,8,846,940,0,0,'','white',19,3600,0,0,0,0,1,0,0,0,0,'line','down','','#FFFFFF',0,0,'default',0,0.000,0.000,0,0,'digital_1','time','Europe/Madrid',0,0), - (253,9,848,944,0,0,'','white',19,3600,0,0,0,0,1,0,0,0,0,'line','down','','#FFFFFF',0,0,'default',0,0.000,0.000,0,0,'digital_1','time','Europe/Madrid',0,0), - (254,6,848,943,0,0,'','white',19,3600,0,0,0,0,1,0,0,0,0,'line','down','','#FFFFFF',0,0,'default',0,0.000,0.000,0,0,'digital_1','time','Europe/Madrid',0,0), - (255,5,846,941,0,0,'','white',19,3600,0,0,0,0,1,0,0,0,0,'line','down','','#FFFFFF',0,0,'default',0,0.000,0.000,0,0,'digital_1','time','Europe/Madrid',0,0) -; +INSERT INTO `tlayout_data` VALUES + (1,1,998,699,80,18,'','rack_hard_disk_2',0,3600,9,2,0,0,1,1,0,0,0,'line','down','','',0,0,'default',0,0.000,0.000,0,0,'analogic_1','time','Europe/Madrid',0,60), + (2,1,998,783,80,18,'','rack_hard_disk_2',0,3600,9,2,0,0,1,1,0,0,0,'line','down','','',0,0,'default',0,0.000,0.000,0,0,'analogic_1','time','Europe/Madrid',0,60), + (3,1,1016,783,80,18,'','rack_hard_disk_2',0,3600,9,2,0,0,1,1,0,0,0,'line','down','','',0,0,'default',0,0.000,0.000,0,0,'analogic_1','time','Europe/Madrid',0,60), + (4,1,1016,699,80,18,'','rack_hard_disk_2',0,3600,9,2,0,0,1,1,0,0,0,'line','down','','',0,0,'default',0,0.000,0.000,0,0,'analogic_1','time','Europe/Madrid',0,60), + (5,1,1034,783,80,18,'','rack_hard_disk_2',0,3600,9,2,0,0,1,1,0,0,0,'line','down','','',0,0,'default',0,0.000,0.000,0,0,'analogic_1','time','Europe/Madrid',0,60), + (6,1,1034,699,80,18,'','rack_hard_disk_2',0,3600,9,2,0,0,1,1,0,0,0,'line','down','','',0,0,'default',0,0.000,0.000,0,0,'analogic_1','time','Europe/Madrid',0,60), + (7,1,1052,783,80,18,'','rack_hard_disk_2',0,3600,9,2,0,0,1,1,0,0,0,'line','down','','',0,0,'default',0,0.000,0.000,0,0,'analogic_1','time','Europe/Madrid',0,60), + (8,1,1052,699,80,18,'','rack_hard_disk_2',0,3600,9,2,0,0,1,1,0,0,0,'line','down','','',0,0,'default',0,0.000,0.000,0,0,'analogic_1','time','Europe/Madrid',0,60), + (9,1,1070,783,80,18,'','rack_hard_disk_2',0,3600,9,2,0,0,1,1,0,0,0,'line','down','','',0,0,'default',0,0.000,0.000,0,0,'analogic_1','time','Europe/Madrid',0,60), + (10,1,1070,699,80,18,'','rack_hard_disk_2',0,3600,9,2,0,0,1,1,0,0,0,'line','down','','',0,0,'default',0,0.000,0.000,0,0,'analogic_1','time','Europe/Madrid',0,60), + (11,1,1088,783,80,18,'','rack_hard_disk_2',0,3600,9,2,0,0,1,1,0,0,0,'line','down','','',0,0,'default',0,0.000,0.000,0,0,'analogic_1','time','Europe/Madrid',0,60), + (12,1,1088,699,80,18,'','rack_hard_disk_2',0,3600,9,2,0,0,1,1,0,0,0,'line','down','','',0,0,'default',0,0.000,0.000,0,0,'analogic_1','time','Europe/Madrid',0,60), + (13,1,1106,699,80,18,'','rack_hard_disk_2',0,3600,9,2,0,0,1,1,0,0,0,'line','down','','',0,0,'default',0,0.000,0.000,0,0,'analogic_1','time','Europe/Madrid',0,60), + (14,1,1106,783,80,18,'','rack_hard_disk_2',0,3600,9,2,0,0,1,1,0,0,0,'line','down','','',0,0,'default',0,0.000,0.000,0,0,'analogic_1','time','Europe/Madrid',0,60), + (15,1,1124,699,80,18,'','rack_hard_disk_2',0,3600,9,2,0,0,1,1,0,0,0,'line','down','','',0,0,'default',0,0.000,0.000,0,0,'analogic_1','time','Europe/Madrid',0,60), + (16,1,1124,783,80,18,'','rack_hard_disk_2',0,3600,9,2,0,0,1,1,0,0,0,'line','down','','',0,0,'default',0,0.000,0.000,0,0,'analogic_1','time','Europe/Madrid',0,60), + (17,1,1142,699,80,18,'','rack_hard_disk_2',0,3600,9,2,0,0,1,1,0,0,0,'line','down','','',0,0,'default',0,0.000,0.000,0,0,'analogic_1','time','Europe/Madrid',0,60), + (18,1,1142,783,80,18,'','rack_hard_disk_2',0,3600,9,2,0,0,1,1,0,0,0,'line','down','','',0,0,'default',0,0.000,0.000,0,0,'analogic_1','time','Europe/Madrid',0,60), + (19,1,1160,699,80,18,'','rack_hard_disk_2',0,3600,9,2,0,0,1,1,0,0,0,'line','down','','',0,0,'default',0,0.000,0.000,0,0,'analogic_1','time','Europe/Madrid',0,60), + (20,1,1160,783,80,18,'','rack_hard_disk_2',0,3600,9,2,0,0,1,1,0,0,0,'line','down','','',0,0,'default',0,0.000,0.000,0,0,'analogic_1','time','Europe/Madrid',0,60), + (21,1,1178,699,80,18,'','rack_hard_disk_2',0,3600,9,2,0,0,1,1,0,0,0,'line','down','','',0,0,'default',0,0.000,0.000,0,0,'analogic_1','time','Europe/Madrid',0,60), + (22,1,1178,783,80,18,'','rack_hard_disk_2',0,3600,9,2,0,0,1,1,0,0,0,'line','down','','',0,0,'default',0,0.000,0.000,0,0,'analogic_1','time','Europe/Madrid',0,60), + (23,1,1196,699,80,18,'','rack_hard_disk_2',0,3600,9,2,0,0,1,1,0,0,0,'line','down','','',0,0,'default',0,0.000,0.000,0,0,'analogic_1','time','Europe/Madrid',0,60), + (24,1,1196,783,80,18,'','rack_hard_disk_2',0,3600,9,2,0,0,1,1,0,0,0,'line','down','','',0,0,'default',0,0.000,0.000,0,0,'analogic_1','time','Europe/Madrid',0,60), + (25,1,1214,699,80,18,'','rack_hard_disk_2',0,3600,9,2,0,0,1,1,0,0,0,'line','down','','',0,0,'default',0,0.000,0.000,0,0,'analogic_1','time','Europe/Madrid',0,60), + (26,1,1214,783,80,18,'','rack_hard_disk_2',0,3600,9,2,0,0,1,1,0,0,0,'line','down','','',0,0,'default',0,0.000,0.000,0,0,'analogic_1','time','Europe/Madrid',0,60), + (27,1,1232,699,80,18,'','rack_hard_disk_2',0,3600,9,2,0,0,1,1,0,0,0,'line','down','','',0,0,'default',0,0.000,0.000,0,0,'analogic_1','time','Europe/Madrid',0,60), + (28,1,1232,783,80,18,'','rack_hard_disk_2',0,3600,9,2,0,0,1,1,0,0,0,'line','down','','',0,0,'default',0,0.000,0.000,0,0,'analogic_1','time','Europe/Madrid',0,60), + (29,1,1250,699,80,18,'','rack_hard_disk_2',0,3600,9,2,0,0,1,1,0,0,0,'line','down','','',0,0,'default',0,0.000,0.000,0,0,'analogic_1','time','Europe/Madrid',0,60), + (30,1,1250,783,80,18,'','rack_hard_disk_2',0,3600,9,2,0,0,1,1,0,0,0,'line','down','','',0,0,'default',0,0.000,0.000,0,0,'analogic_1','time','Europe/Madrid',0,60), + (31,1,1268,699,80,18,'','rack_hard_disk_2',0,3600,9,2,0,0,1,1,0,0,0,'line','down','','',0,0,'default',0,0.000,0.000,0,0,'analogic_1','time','Europe/Madrid',0,60), + (32,1,1268,783,80,18,'','rack_hard_disk_2',0,3600,9,2,0,0,1,1,0,0,0,'line','down','','',0,0,'default',0,0.000,0.000,0,0,'analogic_1','time','Europe/Madrid',0,60), + (33,1,1286,699,80,18,'','rack_hard_disk_2',0,3600,9,2,0,0,1,1,0,0,0,'line','down','','',0,0,'default',0,0.000,0.000,0,0,'analogic_1','time','Europe/Madrid',0,60), + (34,1,1286,783,80,18,'','rack_hard_disk_2',0,3600,9,2,0,0,1,1,0,0,0,'line','down','','',0,0,'default',0,0.000,0.000,0,0,'analogic_1','time','Europe/Madrid',0,60), + (35,1,1286,699,80,18,'','rack_hard_disk_2',0,3600,9,2,0,0,1,1,0,0,0,'line','down','','',0,0,'default',0,0.000,0.000,0,0,'analogic_1','time','Europe/Madrid',0,60), + (36,1,1304,783,80,18,'','rack_hard_disk_2',0,3600,9,2,0,0,1,1,0,0,0,'line','down','','',0,0,'default',0,0.000,0.000,0,0,'analogic_1','time','Europe/Madrid',0,60), + (37,1,1304,699,80,18,'','rack_hard_disk_2',0,3600,9,2,0,0,1,1,0,0,0,'line','down','','',0,0,'default',0,0.000,0.000,0,0,'analogic_1','time','Europe/Madrid',0,60), + (38,1,1322,783,80,18,'','rack_hard_disk_2',0,3600,9,2,0,0,1,1,0,0,0,'line','down','','',0,0,'default',0,0.000,0.000,0,0,'analogic_1','time','Europe/Madrid',0,60), + (39,1,1322,699,80,18,'','rack_hard_disk_2',0,3600,9,2,0,0,1,1,0,0,0,'line','down','','',0,0,'default',0,0.000,0.000,0,0,'analogic_1','time','Europe/Madrid',0,60), + (40,1,1340,783,80,18,'','rack_hard_disk_2',0,3600,9,2,0,0,1,1,0,0,0,'line','down','','',0,0,'default',0,0.000,0.000,0,0,'analogic_1','time','Europe/Madrid',0,60), + (41,1,1507,260,174,29,'','rack_server_rack',0,3600,1,1,0,0,1,1,0,0,0,'line','down','','',0,0,'default',0,0.000,0.000,0,0,'analogic_1','time','Europe/Madrid',0,60), + (42,1,1536,260,174,29,'','rack_server_rack',0,3600,1,1,0,0,1,1,0,0,0,'line','down','','',0,0,'default',0,0.000,0.000,0,0,'analogic_1','time','Europe/Madrid',0,60), + (43,1,1568,260,174,29,'','rack_server_rack',0,3600,1,1,0,0,1,1,0,0,0,'line','down','','',0,0,'default',0,0.000,0.000,0,0,'analogic_1','time','Europe/Madrid',0,60), + (44,1,1599,260,174,29,'','rack_server_rack',0,3600,1,1,0,0,1,1,0,0,0,'line','down','','',0,0,'default',0,0.000,0.000,0,0,'analogic_1','time','Europe/Madrid',0,60), + (45,1,1627,260,174,29,'','rack_server_rack',0,3600,1,1,0,0,1,1,0,0,0,'line','down','','',0,0,'default',0,0.000,0.000,0,0,'analogic_1','time','Europe/Madrid',0,60), + (46,1,1656,260,174,29,'','rack_server_rack',0,3600,1,1,0,0,1,1,0,0,0,'line','down','','',0,0,'default',0,0.000,0.000,0,0,'analogic_1','time','Europe/Madrid',0,60), + (47,1,1685,260,174,29,'','rack_server_rack',0,3600,1,1,0,0,1,1,0,0,0,'line','down','','',0,0,'default',0,0.000,0.000,0,0,'analogic_1','time','Europe/Madrid',0,60), + (48,1,1714,260,174,29,'','rack_server_rack',0,3600,1,1,0,0,1,1,0,0,0,'line','down','','',0,0,'default',0,0.000,0.000,0,0,'analogic_1','time','Europe/Madrid',0,60), + (49,1,1743,260,174,29,'','rack_server_rack',0,3600,1,1,0,0,1,1,0,0,0,'line','down','','',0,0,'default',0,0.000,0.000,0,0,'analogic_1','time','Europe/Madrid',0,60), + (50,1,1772,260,174,29,'','rack_server_rack',0,3600,1,1,0,0,1,1,0,0,0,'line','down','','',0,0,'default',0,0.000,0.000,0,0,'analogic_1','time','Europe/Madrid',0,60), + (51,1,1449,260,174,29,'','rack_server_rack',0,3600,1,1,0,0,1,1,0,0,0,'line','down','','',0,0,'default',0,0.000,0.000,0,0,'analogic_1','time','Europe/Madrid',0,60), + (52,1,1800,260,174,29,'','rack_server_rack',0,3600,1,1,0,0,1,1,0,0,0,'line','down','','',0,0,'default',0,0.000,0.000,0,0,'analogic_1','time','Europe/Madrid',0,60), + (53,1,1413,243,205,426,'','rack_frame',0,3600,1,1,0,0,1,1,0,0,0,'line','down','','',0,0,'default',0,0.000,0.000,0,0,'analogic_1','time','Europe/Madrid',0,60), + (54,1,962,381,73,408,'','rack_firewall',0,3600,1,1,0,0,1,1,0,0,0,'line','down','','',0,0,'default',0,0.000,0.000,0,0,'analogic_1','time','Europe/Madrid',0,60), + (55,1,962,454,73,408,'','rack_pdu',0,3600,1,1,0,0,1,1,0,0,0,'line','down','','',0,0,'default',0,0.000,0.000,0,0,'analogic_1','time','Europe/Madrid',0,60), + (56,1,530,732,74,413,'','rack_server',0,3600,1,1,0,0,1,1,0,0,0,'line','down','','',0,0,'default',0,0.000,0.000,0,0,'analogic_1','time','Europe/Madrid',0,60), + (57,1,962,233,74,413,'','rack_server',0,3600,1,1,0,0,1,1,0,0,0,'line','down','','',0,0,'default',0,0.000,0.000,0,0,'analogic_1','time','Europe/Madrid',0,60), + (58,1,962,307,74,413,'','rack_server',0,3600,1,1,0,0,1,1,0,0,0,'line','down','','',0,0,'default',0,0.000,0.000,0,0,'analogic_1','time','Europe/Madrid',0,60), + (59,1,530,658,74,413,'','rack_server',0,3600,1,1,0,0,1,1,0,0,0,'line','down','','',0,0,'default',0,0.000,0.000,0,0,'analogic_1','time','Europe/Madrid',0,60), + (60,1,530,350,74,413,'','rack_server',0,3600,1,1,0,0,1,1,0,0,0,'line','down','','',0,0,'default',0,0.000,0.000,0,0,'analogic_1','time','Europe/Madrid',0,60), + (61,1,530,204,73,408,'','rack_psa',0,3600,1,1,0,0,1,1,0,0,0,'line','down','','',0,0,'default',0,0.000,0.000,0,0,'analogic_1','time','Europe/Madrid',0,60), + (62,1,530,277,73,408,'','rack_pdu',0,3600,1,1,0,0,1,1,0,0,0,'line','down','','',0,0,'default',0,0.000,0.000,0,0,'analogic_1','time','Europe/Madrid',0,60), + (63,1,530,585,73,408,'','rack_firewall',0,3600,1,1,0,0,1,1,0,0,0,'line','down','','',0,0,'default',0,0.000,0.000,0,0,'analogic_1','time','Europe/Madrid',0,60), + (64,1,530,424,161,411,'','rack_double_server',0,3600,1,1,0,0,1,1,0,0,0,'line','down','','',0,0,'default',0,0.000,0.000,0,0,'analogic_1','time','Europe/Madrid',0,60), + (65,1,1426,448,74,413,'','rack_server',0,3600,1,1,0,0,1,1,0,0,0,'line','down','','',0,0,'default',0,0.000,0.000,0,0,'analogic_1','time','Europe/Madrid',0,60), + (66,1,1495,540,174,29,'','rack_server_rack',0,3600,1,1,0,0,1,1,0,0,0,'line','down','','',0,0,'default',0,0.000,0.000,0,0,'analogic_1','time','Europe/Madrid',0,60), + (67,1,1423,260,174,29,'','rack_server_rack',0,3600,1,1,0,0,1,1,0,0,0,'line','down','','',0,0,'default',0,0.000,0.000,0,0,'analogic_1','time','Europe/Madrid',0,60), + (68,1,1463,540,174,29,'','rack_server_rack',0,3600,1,1,0,0,1,1,0,0,0,'line','down','','',0,0,'default',0,0.000,0.000,0,0,'analogic_1','time','Europe/Madrid',0,60), + (69,1,1433,540,174,29,'','rack_server_rack',0,3600,1,1,0,0,1,1,0,0,0,'line','down','','',0,0,'default',0,0.000,0.000,0,0,'analogic_1','time','Europe/Madrid',0,60), + (70,1,74,733,73,408,'','rack_pdu',0,3600,1,1,0,0,1,1,0,0,0,'line','down','','',0,0,'default',0,0.000,0.000,0,0,'analogic_1','time','Europe/Madrid',0,60), + (71,1,1098,701,80,18,'','rack_hard_disk',0,3600,9,2,0,0,1,1,0,0,0,'line','down','','',0,0,'default',0,0.000,0.000,0,0,'analogic_1','time','Europe/Madrid',0,60), + (72,1,1148,701,80,18,'','rack_hard_disk',0,3600,9,2,0,0,1,1,0,0,0,'line','down','','',0,0,'default',0,0.000,0.000,0,0,'analogic_1','time','Europe/Madrid',0,60), + (73,1,1340,699,80,18,'','rack_hard_disk_2',0,3600,9,2,0,0,1,1,0,0,0,'line','down','','',0,0,'default',0,0.000,0.000,0,0,'analogic_1','time','Europe/Madrid',0,60), + (74,1,1358,783,80,18,'','rack_hard_disk_2',0,3600,9,2,0,0,1,1,0,0,0,'line','down','','',0,0,'default',0,0.000,0.000,0,0,'analogic_1','time','Europe/Madrid',0,60), + (75,1,1358,699,80,18,'','rack_hard_disk_2',0,3600,9,2,0,0,1,1,0,0,0,'line','down','','',0,0,'default',0,0.000,0.000,0,0,'analogic_1','time','Europe/Madrid',0,60), + (76,1,1143,783,80,18,'','rack_hard_disk',0,3600,9,2,0,0,1,1,0,0,0,'line','down','','',0,0,'default',0,0.000,0.000,0,0,'analogic_1','time','Europe/Madrid',0,60), + (77,1,962,682,205,426,'','rack_frame',0,3600,1,1,0,0,1,1,0,0,0,'line','down','','',0,0,'default',0,0.000,0.000,0,0,'analogic_1','time','Europe/Madrid',0,60), + (78,1,1522,540,174,29,'','rack_server_rack',0,3600,1,1,0,0,1,1,0,0,0,'line','down','','',0,0,'default',0,0.000,0.000,0,0,'analogic_1','time','Europe/Madrid',0,60), + (79,1,1419,521,205,426,'','rack_frame',0,3600,1,1,0,0,1,1,0,0,0,'line','down','','',0,0,'default',0,0.000,0.000,0,0,'analogic_1','time','Europe/Madrid',0,60), + (80,1,74,278,74,413,'','rack_server',0,3600,1,1,0,0,1,1,0,0,0,'line','down','','',0,0,'default',0,0.000,0.000,0,0,'analogic_1','time','Europe/Madrid',0,60), + (81,1,74,572,161,411,'','rack_double_server',0,3600,1,1,0,0,1,1,0,0,0,'line','down','','',0,0,'default',0,0.000,0.000,0,0,'analogic_1','time','Europe/Madrid',0,60), + (82,1,1418,729,74,413,'','rack_server',0,3600,1,1,0,0,1,1,0,0,0,'line','down','','',0,0,'default',0,0.000,0.000,0,0,'analogic_1','time','Europe/Madrid',0,60), + (83,1,962,527,73,408,'','rack_switch',0,3600,1,1,0,0,1,1,0,0,0,'line','down','','',0,0,'default',0,0.000,0.000,0,0,'analogic_1','time','Europe/Madrid',0,60), + (84,1,74,352,73,408,'','rack_router',0,3600,1,1,0,0,1,1,0,0,0,'line','down','','',0,0,'default',0,0.000,0.000,0,0,'analogic_1','time','Europe/Madrid',0,60), + (85,1,962,600,74,413,'','rack_server',0,3600,1,1,0,0,1,1,0,0,0,'line','down','','',0,0,'default',0,0.000,0.000,0,0,'analogic_1','time','Europe/Madrid',0,60), + (86,1,530,806,73,408,'','rack_firewall',0,3600,1,1,0,0,1,1,0,0,0,'line','down','','',0,0,'default',0,0.000,0.000,0,0,'analogic_1','time','Europe/Madrid',0,60), + (87,1,74,425,74,413,'','rack_server',0,3600,1,1,0,0,1,1,0,0,0,'line','down','','',0,0,'default',0,0.000,0.000,0,0,'analogic_1','time','Europe/Madrid',0,60), + (88,1,74,499,73,408,'','rack_switch',0,3600,1,1,0,0,1,1,0,0,0,'line','down','','',0,0,'default',0,0.000,0.000,0,0,'analogic_1','time','Europe/Madrid',0,60), + (89,1,74,806,73,408,'','rack_psa',0,3600,1,1,0,0,1,1,0,0,0,'line','down','','',0,0,'default',0,0.000,0.000,0,0,'analogic_1','time','Europe/Madrid',0,60), + (90,1,74,204,74,413,'','rack_server',0,3600,1,1,0,0,1,1,0,0,0,'line','down','','',0,0,'default',0,0.000,0.000,0,0,'analogic_1','time','Europe/Madrid',0,60), + (91,1,1424,806,73,408,'','rack_firewall',0,3600,1,1,0,0,1,1,0,0,0,'line','down','','',0,0,'default',0,0.000,0.000,0,0,'analogic_1','time','Europe/Madrid',0,60), + (92,1,1486,907,0,0,'<p style="text-align: center; overflow: hidden;"><span class="visual_font_size_28pt" style="color: #ffffff; font-family: opensans;"><strong><span class="visual_font_size_28pt" style="color: #ffffff; font-family: opensans;">Office 8 -&nbsp;</span></strong></span><span class="visual_font_size_28pt" style="color: #ffffff; font-family: opensans;">Rack 2</span></p>','white',4,3600,1,1,0,0,1,1,0,0,0,'line','down','','',0,0,'default',0,0.000,0.000,0,0,'analogic_1','time','Europe/Madrid',0,0), + (93,1,1048,889,58,281,'<p style="text-align: center; overflow: hidden;"><span class="visual_font_size_28pt" style="color: #ffffff; font-family: opensans;"><strong><span class="visual_font_size_28pt" style="color: #ffffff; font-family: opensans;">Office 8 -&nbsp;</span></strong></span><span class="visual_font_size_28pt" style="color: #ffffff; font-family: opensans;">Rack 1</span></p>','white',4,3600,1,1,0,0,1,1,0,0,0,'line','down','','',0,0,'default',0,0.000,0.000,0,0,'analogic_1','time','Europe/Madrid',0,0), + (94,1,580,904,0,0,'<p style="text-align: center; overflow: hidden;"><span class="visual_font_size_28pt" style="color: #ffffff; font-family: opensans;"><strong><span class="visual_font_size_28pt" style="color: #ffffff; font-family: opensans;">Office 7 -&nbsp;</span></strong></span><span class="visual_font_size_28pt" style="color: #ffffff; font-family: opensans;">Rack 2</span></p>','white',4,3600,1,1,0,0,1,1,0,0,0,'line','down','','',0,0,'default',0,0.000,0.000,0,0,'analogic_1','time','Europe/Madrid',0,0), + (95,1,132,907,0,0,'<p style="text-align: center; overflow: hidden;"><span class="visual_font_size_28pt" style="color: #ffffff; font-family: opensans;"><strong><span class="visual_font_size_28pt" style="color: #ffffff; font-family: opensans;">Office 7 -&nbsp;</span></strong></span><span class="visual_font_size_28pt" style="color: #ffffff; font-family: opensans;">Rack 1</span></p>','white',4,3600,1,1,0,0,1,1,0,0,0,'line','down','','',0,0,'default',0,0.000,0.000,0,0,'analogic_1','time','Europe/Madrid',0,0), + (96,1,733,20,0,0,'<p style="overflow: hidden;"><span class="visual_font_size_48pt"><strong><span style="color: #ffffff; font-family: opensans;">OFFICE RACKS</span></strong></span></p>','white',4,3600,1,1,0,0,1,1,0,0,0,'line','down','','',0,0,'default',0,0.000,0.000,0,0,'analogic_1','time','Europe/Madrid',0,0), + (97,1,1479,260,174,29,'','rack_server_rack',0,3600,1,1,0,0,1,1,0,0,0,'line','down','','',0,0,'default',0,0.000,0.000,0,0,'analogic_1','time','Europe/Madrid',0,60), + (98,2,709,103,0,400,'','white',19,3600,0,0,0,0,1,1,0,0,0,'line','down','','#FFFFFF',0,0,'default',0,0.000,0.000,0,0,'digital_1','timedate','Europe/Madrid',0,0), + (99,2,178,481,111,111,'','status',0,3600,11556,430,0,0,1,1,0,0,0,'line','down','','',0,0,'default',0,0.000,0.000,0,0,'analogic_1','time','Europe/Madrid',0,0), + (100,2,542,481,111,111,'','status',0,3600,13,2,0,0,1,1,0,0,0,'line','down','','',0,0,'default',0,0.000,0.000,0,0,'analogic_1','time','Europe/Madrid',0,0), + (101,2,905,481,111,111,'','status',0,3600,114,11,0,0,1,1,0,0,0,'line','down','','',0,0,'default',0,0.000,0.000,0,0,'analogic_1','time','Europe/Madrid',0,0), + (102,2,1276,481,111,111,'','status',0,3600,7,1,0,0,1,1,0,0,0,'line','down','','',0,0,'default',0,0.000,0.000,0,0,'analogic_1','time','Europe/Madrid',0,0), + (103,2,1631,482,111,111,'','status',0,3600,11547,1,0,0,1,1,0,0,0,'line','down','','',0,0,'default',0,0.000,0.000,0,0,'analogic_1','time','Europe/Madrid',0,0), + (104,2,157,393,0,0,'

Backups

\n

 

','white',4,3600,0,0,0,0,1,1,0,0,0,'line','down','','',0,0,'default',0,0.000,0.000,0,0,'analogic_1','time','Europe/Madrid',0,0), + (105,2,512,382,96,172,'<p style="overflow: hidden;"><span class="visual_font_size_28pt" style="font-family: opensans; color: #ffffff;">DB Status</span></p> <p style="overflow: hidden;">&nbsp;</p>','white',4,3600,0,0,0,0,1,1,0,0,0,'line','down','','',0,0,'default',0,0.000,0.000,0,0,'analogic_1','time','Europe/Madrid',0,0), + (106,2,886,382,0,0,'

Disk slave

\n

 

','white',4,3600,0,0,0,0,1,1,0,0,0,'line','down','','',0,0,'default',0,0.000,0.000,0,0,'analogic_1','time','Europe/Madrid',0,0), + (107,2,1251,382,0,0,'

Disk /var

\n

 

','white',4,3600,0,0,0,0,1,1,0,0,0,'line','down','','',0,0,'default',0,0.000,0.000,0,0,'analogic_1','time','Europe/Madrid',0,0), + (108,2,1547,382,0,0,'<p style="line-height: 18px; overflow: hidden;"><span class="visual_font_size_28pt" style="color: #ffffff; font-family: opensans;">Authentification</span></p>','white',4,3600,0,0,0,0,1,1,0,0,0,'line','down','','',0,0,'default',0,0.000,0.000,0,0,'analogic_1','time','Europe/Madrid',0,0), + (109,2,126,820,0,0,'<p style="line-height: 18px; overflow: hidden;"><strong><span class="visual_font_size_36pt" style="font-family: opensans; color: #ffffff;">Processing</span></strong></p>','white',4,3600,0,0,0,0,1,1,0,0,0,'line','down','','',0,0,'default',0,0.000,0.000,0,0,'analogic_1','time','Europe/Madrid',0,0), + (110,2,755,820,0,0,'<p style="line-height: 18px; overflow: hidden;"><strong><span class="visual_font_size_36pt" style="font-family: opensans; color: #ffffff;">Network</span></strong></p>','white',4,3600,0,0,0,0,1,1,0,0,0,'line','down','','',0,0,'default',0,0.000,0.000,0,0,'analogic_1','time','Europe/Madrid',0,0), + (111,2,1281,820,0,0,'<p style="line-height: 18px; overflow: hidden;"><strong><span class="visual_font_size_36pt" style="color: #ffffff; font-family: opensans;">Storage</span></strong></p>','white',4,3600,0,0,0,0,1,1,0,0,0,'line','down','','',0,0,'default',0,0.000,0.000,0,0,'analogic_1','time','Europe/Madrid',0,0), + (113,3,851,932,0,0,'','white',19,3600,0,0,0,0,1,1,0,0,0,'line','down','','#FFFFFF',0,0,'default',0,0.000,0.000,0,0,'digital_1','time','Europe/Madrid',0,0), + (114,3,946,314,60,60,'','status',0,3600,0,0,4,0,1,1,0,0,0,'line','down','','',0,0,'default',0,0.000,0.000,0,0,'analogic_1','time','Europe/Madrid',0,0), + (115,3,604,351,60,60,'','status',0,3600,0,0,5,0,1,1,0,0,0,'line','down','','',0,0,'default',0,0.000,0.000,0,0,'analogic_1','time','Europe/Madrid',0,0), + (116,3,840,314,0,0,'<p style="line-height: 18px; overflow: hidden;"><span class="visual_font_size_18pt"><strong><span style="color: #ffffff;">EUROPE</span></strong></span></p>',NULL,4,3600,0,0,4,0,1,1,0,0,0,'line','down','','',0,0,'default',0,0.000,0.000,0,0,'analogic_1','time','Europe/Madrid',0,0), + (117,3,664,374,0,0,'<p style="line-height: 18px; overflow: hidden;"><span class="visual_font_size_18pt"><strong><span style="color: #ffffff;">USA</span></strong></span></p>',NULL,4,3600,0,0,5,0,1,1,0,0,0,'line','down','','',0,0,'default',0,0.000,0.000,0,0,'analogic_1','time','Europe/Madrid',0,0), + (118,3,57,182,60,60,'','status',0,3600,0,0,5,0,1,1,0,0,0,'line','down','','',0,0,'default',0,0.000,0.000,0,0,'analogic_1','time','Europe/Madrid',0,0), + (119,3,56,258,60,60,'','status',0,3600,0,0,4,0,1,1,0,0,0,'line','down','','',0,0,'default',0,0.000,0.000,0,0,'analogic_1','time','Europe/Madrid',0,0), + (121,3,138,183,0,0,'<p style="line-height: 18px; overflow: hidden;"><span class="visual_font_size_18pt"><strong><span style="color: #ffffff;">USA</span></strong></span></p>',NULL,4,3600,0,0,5,0,1,1,0,0,0,'line','down','','',0,0,'default',0,0.000,0.000,0,0,'analogic_1','time','Europe/Madrid',0,0), + (122,3,138,259,0,0,'<p style="line-height: 18px; overflow: hidden;"><span class="visual_font_size_18pt"><strong><span style="color: #ffffff;">EUROPE</span></strong></span></p>',NULL,4,3600,0,0,4,0,1,1,0,0,0,'line','down','','',0,0,'default',0,0.000,0.000,0,0,'analogic_1','time','Europe/Madrid',0,0), + (123,4,839,525,60,60,'','status',0,3600,0,0,8,0,1,1,0,0,0,'line','down','','',0,0,'default',0,0.000,0.000,0,0,'analogic_1','time','Europe/Madrid',0,0), + (124,4,699,583,60,60,'','status',0,3600,0,0,9,0,1,1,0,0,0,'line','down','','',0,0,'default',0,0.000,0.000,0,0,'analogic_1','time','Europe/Madrid',0,0), + (125,4,585,705,60,60,'','status',0,3600,0,0,6,0,1,1,0,0,0,'line','down','','',0,0,'default',0,0.000,0.000,0,0,'analogic_1','time','Europe/Madrid',0,0), + (126,4,563,754,0,0,'<p style="overflow: hidden;"><span style="color: #000000;"><strong><span class="visual_font_size_18pt">SPAIN</span></strong></span></p>',NULL,4,3600,0,0,6,0,1,1,0,0,0,'line','down','','',0,0,'default',0,0.000,0.000,0,0,'analogic_1','time','Europe/Madrid',0,0), + (127,4,681,629,0,0,'<p style="overflow: hidden;"><span style="color: #000000;"><strong><span class="visual_font_size_18pt">FRANCE</span></strong></span></p>',NULL,4,3600,0,0,9,0,1,1,0,0,0,'line','down','','',0,0,'default',0,0.000,0.000,0,0,'analogic_1','time','Europe/Madrid',0,0), + (128,4,832,576,0,0,'<p style="overflow: hidden;"><span class="visual_font_size_18pt" style="color: #000000;"><strong><span style="color: #000000;">GERMANY</span></strong></span></p>',NULL,4,3600,0,0,8,0,1,1,0,0,0,'line','down','','',0,0,'default',0,0.000,0.000,0,0,'analogic_1','time','Europe/Madrid',0,0), + (129,4,51,177,60,60,'','status',0,3600,0,0,8,0,1,1,0,0,0,'line','down','','',0,0,'default',0,0.000,0.000,0,0,'analogic_1','time','Europe/Madrid',0,0), + (130,4,124,176,0,0,'<p style="overflow: hidden;"><span class="visual_font_size_18pt" style="color: #ffffff;"><strong>GERMANY</strong></span></p>',NULL,4,3600,0,0,8,0,1,1,0,0,0,'line','down','','',0,0,'default',0,0.000,0.000,0,0,'analogic_1','time','Europe/Madrid',0,0), + (131,4,51,255,60,60,'','status',0,3600,0,0,9,0,1,1,0,0,0,'line','down','','',0,0,'default',0,0.000,0.000,0,0,'analogic_1','time','Europe/Madrid',0,0), + (132,4,127,256,0,0,'<p style="overflow: hidden;"><span style="color: #ffffff;"><strong><span class="visual_font_size_18pt">FRANCE</span></strong></span></p>',NULL,4,3600,0,0,9,0,1,1,0,0,0,'line','down','','',0,0,'default',0,0.000,0.000,0,0,'analogic_1','time','Europe/Madrid',0,0), + (133,4,51,333,60,60,'','status',0,3600,0,0,7,0,1,1,0,0,0,'line','down','','',0,0,'default',0,0.000,0.000,0,0,'analogic_1','time','Europe/Madrid',0,0), + (134,4,129,333,0,0,'<p style="overflow: hidden;"><span style="color: #ffffff;"><strong><span class="visual_font_size_18pt">SPAIN</span></strong></span></p>',NULL,4,3600,0,0,6,0,1,1,0,0,0,'line','down','','',0,0,'default',0,0.000,0.000,0,0,'analogic_1','time','Europe/Madrid',0,0), + (136,4,816,928,0,0,'','white',19,3600,0,0,0,0,1,1,0,0,0,'line','down','','#FFFFFF',0,0,'default',0,0.000,0.000,0,0,'digital_1','time','Europe/Madrid',0,0), + (137,4,1674,184,132,200,'','worldmap',5,3600,0,0,3,0,1,1,0,0,0,'line','down','','',0,0,'default',0,0.000,0.000,0,0,'analogic_1','time','Europe/Madrid',0,0), + (143,6,972,452,60,60,'','status',0,3600,0,0,7,0,1,1,0,0,0,'line','down','','',0,0,'default',0,0.000,0.000,0,0,'analogic_1','time','Europe/Madrid',0,0), + (144,6,1039,223,60,60,'','status',0,3600,0,0,11,0,1,1,0,0,0,'line','down','','',0,0,'default',0,0.000,0.000,0,0,'analogic_1','time','Europe/Madrid',0,0), + (145,6,1339,366,60,60,'','status',0,3600,0,0,10,0,1,1,0,0,0,'line','down','','',0,0,'default',0,0.000,0.000,0,0,'analogic_1','time','Europe/Madrid',0,0), + (146,6,841,699,60,60,'','status',0,3600,0,0,12,0,1,1,0,0,0,'line','down','','',0,0,'default',0,0.000,0.000,0,0,'analogic_1','time','Europe/Madrid',0,0), + (147,6,1677,183,132,200,'','worldmap',5,3600,0,0,3,0,1,1,0,0,0,'line','down','','',0,0,'default',0,0.000,0.000,0,0,'analogic_1','time','Europe/Madrid',0,0), + (148,6,1674,341,132,200,'','europemap',5,3600,0,0,4,0,1,1,0,0,0,'line','down','','',0,0,'default',0,0.000,0.000,0,0,'analogic_1','time','Europe/Madrid',0,0), + (149,6,1023,288,0,0,'<p style="line-height: 18px; overflow: hidden;"><strong><span class="visual_font_size_18pt" style="color: #000000;">BASQUE COUNTRY</span></strong></p>',NULL,4,3600,0,0,11,0,1,1,0,0,0,'line','down','','',0,0,'default',0,0.000,0.000,0,0,'analogic_1','time','Europe/Madrid',0,0), + (150,6,1324,425,0,0,'<p style="line-height: 18px; overflow: hidden;"><span style="color: #ffffff;"><strong><span class="visual_font_size_18pt">CATALONIA</span></strong></span></p>',NULL,4,3600,0,0,10,0,1,1,0,0,0,'line','down','','',0,0,'default',0,0.000,0.000,0,0,'analogic_1','time','Europe/Madrid',0,0), + (151,6,963,511,0,0,'<p style="line-height: 18px; overflow: hidden;"><strong><span class="visual_font_size_18pt" style="color: #000000;">MADRID</span></strong></p>',NULL,4,3600,0,0,7,0,1,1,0,0,0,'line','down','','',0,0,'default',0,0.000,0.000,0,0,'analogic_1','time','Europe/Madrid',0,0), + (152,6,843,749,0,0,'<p style="line-height: 18px; overflow: hidden;"><strong><span class="visual_font_size_18pt" style="color: #000000;">ANDALUSIA</span></strong></p>',NULL,4,3600,0,0,12,0,1,1,0,0,0,'line','down','','',0,0,'default',0,0.000,0.000,0,0,'analogic_1','time','Europe/Madrid',0,0), + (153,6,52,179,60,60,'','status',0,3600,0,0,11,0,1,1,0,0,0,'line','down','','',0,0,'default',0,0.000,0.000,0,0,'analogic_1','time','Europe/Madrid',0,0), + (154,6,125,184,0,0,'<p style="line-height: 18px; overflow: hidden;"><span class="visual_font_size_14pt" style="color: #ffffff;"><strong>BASQUE COUNTRY</strong></span></p>',NULL,4,3600,0,0,11,0,1,1,0,0,0,'line','down','','',0,0,'default',0,0.000,0.000,0,0,'analogic_1','time','Europe/Madrid',0,0), + (155,6,53,260,60,60,'','status',0,3600,0,0,7,0,1,1,0,0,0,'line','down','','',0,0,'default',0,0.000,0.000,0,0,'analogic_1','time','Europe/Madrid',0,0), + (156,6,132,264,0,0,'<p style="line-height: 18px; overflow: hidden;"><span class="visual_font_size_14pt" style="color: #ffffff;"><strong>MADRID</strong></span></p>',NULL,4,3600,0,0,7,0,1,1,0,0,0,'line','down','','',0,0,'default',0,0.000,0.000,0,0,'analogic_1','time','Europe/Madrid',0,0), + (157,6,52,339,60,60,'','status',0,3600,0,0,10,0,1,1,0,0,0,'line','down','','',0,0,'default',0,0.000,0.000,0,0,'analogic_1','time','Europe/Madrid',0,0), + (158,6,132,343,0,0,'<p style="line-height: 18px; overflow: hidden;"><span class="visual_font_size_14pt" style="color: #ffffff;"><strong>CATALONIA</strong></span></p>',NULL,4,3600,0,0,10,0,1,1,0,0,0,'line','down','','',0,0,'default',0,0.000,0.000,0,0,'analogic_1','time','Europe/Madrid',0,0), + (159,6,52,413,60,60,'','status',0,3600,0,0,12,0,1,1,0,0,0,'line','down','','',0,0,'default',0,0.000,0.000,0,0,'analogic_1','time','Europe/Madrid',0,0), + (160,6,132,423,0,0,'<p style="line-height: 18px; overflow: hidden;"><span class="visual_font_size_14pt" style="color: #ffffff;"><strong>ANDALUSIA</strong></span></p>',NULL,4,3600,0,0,12,0,1,1,0,0,0,'line','down','','',0,0,'default',0,0.000,0.000,0,0,'analogic_1','time','Europe/Madrid',0,0), + (161,11,761,345,60,60,'','status',0,3600,0,1,0,0,1,1,0,0,0,'line','down','','',0,0,'default',0,0.000,0.000,0,0,'analogic_1','time','Europe/Madrid',0,0), + (162,11,791,401,0,0,'<p style="margin-top:"><span class="visual_font_size_18pt"><strong>BILBAO</strong></span></p>','white',4,3600,0,0,0,0,1,1,0,0,0,'line','down','','',0,0,'default',0,0.000,0.000,0,0,'analogic_1','time','Europe/Madrid',0,0), + (163,11,53,183,60,60,'','status',0,3600,0,1,0,0,1,1,0,0,0,'line','down','','',0,0,'default',0,0.000,0.000,0,0,'analogic_1','time','Europe/Madrid',0,0), + (164,11,131,191,0,0,'<p><span class="visual_font_size_18pt" style="color: #ffffff;"><strong>BILBAO</strong></span></p>','white',4,3600,0,0,0,0,1,1,0,0,0,'line','down','','',0,0,'default',0,0.000,0.000,0,0,'analogic_1','time','Europe/Madrid',0,0), + (165,11,1675,184,132,200,'','worldmap',5,3600,0,0,3,0,1,1,0,0,0,'line','down','','',0,0,'default',0,0.000,0.000,0,0,'analogic_1','time','Europe/Madrid',0,0), + (167,11,1675,340,132,200,'','europemap',5,3600,0,0,4,0,1,1,0,0,0,'line','down','','',0,0,'default',0,0.000,0.000,0,0,'analogic_1','time','Europe/Madrid',0,0), + (168,11,1673,507,132,200,'','spainmap',5,3600,0,0,6,0,1,1,0,0,0,'line','down','','',0,0,'default',0,0.000,0.000,0,0,'analogic_1','time','Europe/Madrid',0,0), + (169,10,1075,607,60,60,'','status',0,3600,0,1,0,0,1,1,0,0,0,'line','down','','',0,0,'default',0,0.000,0.000,0,0,'analogic_1','time','Europe/Madrid',0,0), + (170,10,1012,539,60,60,'','status',0,3600,0,1,0,0,1,1,0,0,0,'line','down','','',0,0,'default',0,0.000,0.000,0,0,'analogic_1','time','Europe/Madrid',0,0), + (171,10,57,266,60,60,'','status',0,3600,0,1,0,0,1,1,0,0,0,'line','down','','',0,0,'default',0,0.000,0.000,0,0,'analogic_1','time','Europe/Madrid',0,0), + (172,10,55,183,60,60,'','status',0,3600,0,1,0,0,1,1,0,0,0,'line','down','','',0,0,'default',0,0.000,0.000,0,0,'analogic_1','time','Europe/Madrid',0,0), + (173,10,131,189,0,0,'<p style="line-height: 18px; overflow: hidden;"><span style="color: #ffffff;"><strong><span class="visual_font_size_18pt">BARCELONA</span></strong></span></p>',NULL,4,3600,0,0,0,0,1,1,0,0,0,'line','down','','',0,0,'default',0,0.000,0.000,0,0,'analogic_1','time','Europe/Madrid',0,0), + (174,10,1044,670,0,0,'<p style="line-height: 18px; overflow: hidden;"><span style="color: #ffffff;"><strong><span class="visual_font_size_18pt">BARCELONA</span></strong></span></p>',NULL,4,3600,0,0,0,0,1,1,0,0,0,'line','down','','',0,0,'default',0,0.000,0.000,0,0,'analogic_1','time','Europe/Madrid',0,0), + (175,10,1000,488,0,0,'<p style="line-height: 18px; overflow: hidden;"><span class="visual_font_size_18pt"><strong><span style="color: #ffffff;">MANRESA</span></strong></span></p>',NULL,4,3600,0,0,0,0,1,1,0,0,0,'line','down','','',0,0,'default',0,0.000,0.000,0,0,'analogic_1','time','Europe/Madrid',0,0), + (176,10,134,265,0,0,'<p style="line-height: 18px; overflow: hidden;"><span style="color: #ffffff;"><strong><span class="visual_font_size_18pt">MANRESA</span></strong></span></p>',NULL,4,3600,0,0,0,0,1,1,0,0,0,'line','down','','',0,0,'default',0,0.000,0.000,0,0,'analogic_1','time','Europe/Madrid',0,0), + (177,10,1675,184,132,200,'','worldmap',5,3600,0,0,3,0,1,1,0,0,0,'line','down','','',0,0,'default',0,0.000,0.000,0,0,'analogic_1','time','Europe/Madrid',0,0), + (178,10,1675,342,132,200,'','europemap',5,3600,0,0,4,0,1,1,0,0,0,'line','down','','',0,0,'default',0,0.000,0.000,0,0,'analogic_1','time','Europe/Madrid',0,0), + (179,10,1673,508,132,200,'','spainmap',5,3600,0,0,6,0,1,1,0,0,0,'line','down','','',0,0,'default',0,0.000,0.000,0,0,'analogic_1','time','Europe/Madrid',0,0), + (180,7,1675,184,132,200,'','worldmap',5,3600,0,0,3,0,1,1,0,0,0,'line','down','','',0,0,'default',0,0.000,0.000,0,0,'analogic_1','time','Europe/Madrid',0,0), + (181,7,1673,340,132,200,'','europemap',5,3600,0,0,4,0,1,1,0,0,0,'line','down','','',0,0,'default',0,0.000,0.000,0,0,'analogic_1','time','Europe/Madrid',0,0), + (182,7,1675,505,132,200,'','spainmap',5,3600,0,0,6,0,1,1,0,0,0,'line','down','','',0,0,'default',0,0.000,0.000,0,0,'analogic_1','time','Europe/Madrid',0,0), + (183,7,960,571,60,60,'','status',0,3600,0,1,0,0,1,1,0,0,0,'line','down','','',0,0,'default',0,0.000,0.000,0,0,'analogic_1','time','Europe/Madrid',0,0), + (184,7,1089,529,60,60,'','status',0,3600,0,1,0,0,1,1,0,0,0,'line','down','','',0,0,'default',0,0.000,0.000,0,0,'analogic_1','time','Europe/Madrid',0,0), + (185,7,865,541,60,60,'','status',0,3600,0,1,0,0,1,1,0,0,0,'line','down','','',0,0,'default',0,0.000,0.000,0,0,'analogic_1','time','Europe/Madrid',0,0), + (186,7,958,475,60,60,'','status',0,3600,0,1,0,0,1,1,0,0,0,'line','down','','',0,0,'default',0,0.000,0.000,0,0,'analogic_1','time','Europe/Madrid',0,0), + (187,7,54,182,60,60,'','status',0,3600,0,1,0,0,1,1,0,0,0,'line','down','','',0,0,'default',0,0.000,0.000,0,0,'analogic_1','time','Europe/Madrid',0,0), + (188,7,58,417,60,60,'','status',0,3600,0,1,0,0,1,1,0,0,0,'line','down','','',0,0,'default',0,0.000,0.000,0,0,'analogic_1','time','Europe/Madrid',0,0), + (189,7,55,258,60,60,'','status',0,3600,0,1,0,0,1,1,0,0,0,'line','down','','',0,0,'default',0,0.000,0.000,0,0,'analogic_1','time','Europe/Madrid',0,0), + (191,7,56,338,60,60,'','status',0,3600,0,1,0,0,1,1,0,0,0,'line','down','','',0,0,'default',0,0.000,0.000,0,0,'analogic_1','time','Europe/Madrid',0,0), + (192,7,947,631,0,0,'<p style="line-height: 18px; overflow: hidden;"><strong><span class="visual_font_size_18pt">MADRID</span></strong></p>',NULL,4,3600,0,0,0,0,1,1,0,0,0,'line','down','','',0,0,'default',0,0.000,0.000,0,0,'analogic_1','time','Europe/Madrid',0,0), + (193,7,790,598,0,0,'<p style="line-height: 18px; overflow: hidden;"><strong><span class="visual_font_size_18pt">LAS ROZAS</span></strong></p>',NULL,4,3600,0,0,0,0,1,1,0,0,0,'line','down','','',0,0,'default',0,0.000,0.000,0,0,'analogic_1','time','Europe/Madrid',0,0), + (194,7,914,429,0,0,'<p style="line-height: 18px; overflow: hidden;"><strong><span class="visual_font_size_18pt">ALCOBENDAS</span></strong></p>',NULL,4,3600,0,0,0,0,1,1,0,0,0,'line','down','','',0,0,'default',0,0.000,0.000,0,0,'analogic_1','time','Europe/Madrid',0,0), + (195,7,1065,583,0,0,'<p style="line-height: 18px; overflow: hidden; text-align: center;"><strong><span class="visual_font_size_18pt">ALCAL&Aacute; DE </span></strong></p> <p style="line-height: 18px; overflow: hidden; text-align: center;"><strong><span class="visual_font_size_18pt">HENARES</span></strong></p>',NULL,4,3600,0,0,0,0,1,1,0,0,0,'line','down','','',0,0,'default',0,0.000,0.000,0,0,'analogic_1','time','Europe/Madrid',0,0), + (196,7,134,267,0,0,'<p style="line-height: 18px; overflow: hidden;"><span class="visual_font_size_14pt" style="color: #ffffff;"><strong>ALCOBENDAS</strong></span></p>',NULL,4,3600,0,0,0,0,1,1,0,0,0,'line','down','','',0,0,'default',0,0.000,0.000,0,0,'analogic_1','time','Europe/Madrid',0,0), + (197,7,133,193,0,0,'<p style="line-height: 18px; overflow: hidden;"><span class="visual_font_size_14pt" style="color: #ffffff;"><strong>MADRID</strong></span></p>',NULL,4,3600,0,0,0,0,1,1,0,0,0,'line','down','','',0,0,'default',0,0.000,0.000,0,0,'analogic_1','time','Europe/Madrid',0,0), + (198,7,134,347,0,0,'<p style="line-height: 18px; overflow: hidden;"><span class="visual_font_size_14pt" style="color: #ffffff;"><strong>LAS ROZAS</strong></span></p>',NULL,4,3600,0,0,0,0,1,1,0,0,0,'line','down','','',0,0,'default',0,0.000,0.000,0,0,'analogic_1','time','Europe/Madrid',0,0), + (199,7,135,425,0,0,'<p style="line-height: 18px; overflow: hidden;"><span class="visual_font_size_14pt" style="color: #ffffff;"><strong>ALCAL&Aacute; DE HENARES</strong></span></p>',NULL,4,3600,0,0,0,0,1,1,0,0,0,'line','down','','',0,0,'default',0,0.000,0.000,0,0,'analogic_1','time','Europe/Madrid',0,0), + (200,12,757,537,60,60,'','status',0,3600,0,1,0,0,1,1,0,0,0,'line','down','','',0,0,'default',0,0.000,0.000,0,0,'analogic_1','time','Europe/Madrid',0,0), + (201,12,1022,656,60,60,'','status',0,3600,0,1,0,0,1,1,0,0,0,'line','down','','',0,0,'default',0,0.000,0.000,0,0,'analogic_1','time','Europe/Madrid',0,0), + (202,12,54,182,60,60,'','status',0,3600,0,1,0,0,1,1,0,0,0,'line','down','','',0,0,'default',0,0.000,0.000,0,0,'analogic_1','time','Europe/Madrid',0,0), + (203,12,54,260,60,60,'','status',0,3600,0,1,0,0,1,1,0,0,0,'line','down','','',0,0,'default',0,0.000,0.000,0,0,'analogic_1','time','Europe/Madrid',0,0), + (204,12,751,593,0,0,'<p style="line-height: 18px; overflow: hidden;"><span class="visual_font_size_18pt"><strong>SEVILLA</strong></span></p>',NULL,4,3600,0,0,0,0,1,1,0,0,0,'line','down','','',0,0,'default',0,0.000,0.000,0,0,'analogic_1','time','Europe/Madrid',0,0), + (205,12,1047,716,0,0,'<p style="line-height: 18px; overflow: hidden;"><span class="visual_font_size_18pt" style="color: #ffffff;"><strong>MALAGA</strong></span></p>',NULL,4,3600,0,0,0,0,1,1,0,0,0,'line','down','','',0,0,'default',0,0.000,0.000,0,0,'analogic_1','time','Europe/Madrid',0,0), + (206,12,133,266,0,0,'<p style="line-height: 18px; overflow: hidden;"><span class="visual_font_size_18pt" style="color: #ffffff;"><strong>MALAGA</strong></span></p>',NULL,4,3600,0,0,0,0,1,1,0,0,0,'line','down','','',0,0,'default',0,0.000,0.000,0,0,'analogic_1','time','Europe/Madrid',0,0), + (207,12,132,190,0,0,'<p style="line-height: 18px; overflow: hidden;"><span class="visual_font_size_18pt" style="color: #ffffff;"><strong>SEVILLA</strong></span></p>',NULL,4,3600,0,0,0,0,1,1,0,0,0,'line','down','','',0,0,'default',0,0.000,0.000,0,0,'analogic_1','time','Europe/Madrid',0,0), + (208,12,1675,184,132,200,'','worldmap',5,3600,0,0,3,0,1,1,0,0,0,'line','down','','',0,0,'default',0,0.000,0.000,0,0,'analogic_1','time','Europe/Madrid',0,0), + (209,12,1675,341,132,200,'','europemap',5,3600,0,0,4,0,1,1,0,0,0,'line','down','','',0,0,'default',0,0.000,0.000,0,0,'analogic_1','time','Europe/Madrid',0,0), + (210,12,1674,505,132,200,'','spainmap',5,3600,0,0,6,0,1,1,0,0,0,'line','down','','',0,0,'default',0,0.000,0.000,0,0,'analogic_1','time','Europe/Madrid',0,0), + (211,12,842,935,0,0,'',NULL,19,3600,0,0,0,0,1,1,0,0,0,'line','down','','#FFFFFF',0,0,'default',0,0.000,0.000,0,0,'digital_1','time','Europe/Madrid',0,0), + (212,7,848,941,0,0,'','white',19,3600,0,0,0,0,1,1,0,0,0,'line','down','','#FFFFFF',0,0,'default',0,0.000,0.000,0,0,'digital_1','time','Europe/Madrid',0,0), + (213,10,844,940,0,0,'','white',19,3600,0,0,0,0,1,1,0,0,0,'line','down','','#FFFFFF',0,0,'default',0,0.000,0.000,0,0,'digital_1','time','Europe/Madrid',0,0), + (214,11,847,948,0,0,'','white',19,3600,0,0,0,0,1,1,0,0,0,'line','down','','#FFFFFF',0,0,'default',0,0.000,0.000,0,0,'digital_1','time','Europe/Madrid',0,0), + (215,5,525,608,60,60,'','status',0,3600,0,1,0,0,1,1,0,0,0,'line','down','','',0,0,'default',0,0.000,0.000,0,0,'analogic_1','time','Europe/Madrid',0,0), + (216,5,1412,351,60,60,'','status',0,3600,0,1,0,0,1,1,0,0,0,'line','down','','',0,0,'default',0,0.000,0.000,0,0,'analogic_1','time','Europe/Madrid',0,0), + (217,5,966,697,60,60,'','status',0,3600,0,1,0,0,1,1,0,0,0,'line','down','','',0,0,'default',0,0.000,0.000,0,0,'analogic_1','time','Europe/Madrid',0,0), + (218,5,458,672,0,0,'<p style="line-height: 18px; overflow: hidden;"><strong><span class="visual_font_size_18pt" style="color: #ffffff;">CALIFORNIA</span></strong></p>','white',4,3600,0,0,0,0,1,1,0,0,0,'line','down','','',0,0,'default',0,0.000,0.000,0,0,'analogic_1','time','Europe/Madrid',0,0), + (219,5,954,759,0,0,'<p style="line-height: 18px; overflow: hidden;"><span style="color: #000000;"><strong><span class="visual_font_size_18pt">TEXAS</span></strong></span></p>','white',4,3600,0,0,0,0,1,1,0,0,0,'line','down','','',0,0,'default',0,0.000,0.000,0,0,'analogic_1','time','Europe/Madrid',0,0), + (220,5,1468,389,0,0,'<p style="line-height: 18px; overflow: hidden;"><span style="color: #ffffff;"><strong><span class="visual_font_size_18pt">NEW YORK</span></strong></span></p>','white',4,3600,0,0,0,0,1,1,0,0,0,'line','down','','',0,0,'default',0,0.000,0.000,0,0,'analogic_1','time','Europe/Madrid',0,0), + (221,5,56,182,60,60,'','status',0,3600,0,1,0,0,1,1,0,0,0,'line','down','','',0,0,'default',0,0.000,0.000,0,0,'analogic_1','time','Europe/Madrid',0,0), + (222,5,57,258,60,60,'','status',0,3600,0,1,0,0,1,1,0,0,0,'line','down','','',0,0,'default',0,0.000,0.000,0,0,'analogic_1','time','Europe/Madrid',0,0), + (223,5,57,338,60,60,'','status',0,3600,0,1,0,0,1,1,0,0,0,'line','down','','',0,0,'default',0,0.000,0.000,0,0,'analogic_1','time','Europe/Madrid',0,0), + (224,5,137,189,0,0,'<p style="line-height: 18px; overflow: hidden;"><strong><span class="visual_font_size_18pt" style="color: #ffffff;">CALIFORNIA</span></strong></p>','white',4,3600,0,0,0,0,1,1,0,0,0,'line','down','','',0,0,'default',0,0.000,0.000,0,0,'analogic_1','time','Europe/Madrid',0,0), + (225,5,139,264,0,0,'<p style="line-height: 18px; overflow: hidden;"><strong><span class="visual_font_size_18pt" style="color: #ffffff;">TEXAS</span></strong></p>','white',4,3600,0,0,0,0,1,1,0,0,0,'line','down','','',0,0,'default',0,0.000,0.000,0,0,'analogic_1','time','Europe/Madrid',0,0), + (226,5,138,342,0,0,'<p style="line-height: 18px; overflow: hidden;"><strong><span class="visual_font_size_18pt" style="color: #ffffff;">NUEVA YORK</span></strong></p>','white',4,3600,0,0,0,0,1,1,0,0,0,'line','down','','',0,0,'default',0,0.000,0.000,0,0,'analogic_1','time','Europe/Madrid',0,0), + (227,5,1677,189,132,200,'','worldmap',5,3600,0,0,3,0,1,1,0,0,0,'line','down','','',0,0,'default',0,0.000,0.000,0,0,'analogic_1','time','Europe/Madrid',0,0), + (228,9,907,353,60,60,'','status',0,3600,0,1,0,0,1,1,0,0,0,'line','down','','',0,0,'default',0,0.000,0.000,0,0,'analogic_1','time','Europe/Madrid',0,0), + (229,9,1068,599,60,60,'','status',0,3600,0,1,0,0,1,1,0,0,0,'line','down','','',0,0,'default',0,0.000,0.000,0,0,'analogic_1','time','Europe/Madrid',0,0), + (230,9,60,182,60,60,'','status',0,3600,0,1,0,0,1,1,0,0,0,'line','down','','',0,0,'default',0,0.000,0.000,0,0,'analogic_1','time','Europe/Madrid',0,0), + (231,9,61,258,60,60,'','status',0,3600,0,1,0,0,1,1,0,0,0,'line','down','','',0,0,'default',0,0.000,0.000,0,0,'analogic_1','time','Europe/Madrid',0,0), + (232,9,138,187,0,0,'<p style="line-height: 18px; overflow: hidden;"><span class="visual_font_size_18pt" style="color: #ffffff;"><strong>PARIS</strong></span></p>',NULL,4,3600,0,0,0,0,1,1,0,0,0,'line','down','','',0,0,'default',0,0.000,0.000,0,0,'analogic_1','time','Europe/Madrid',0,0), + (233,9,139,257,0,0,'<p style="line-height: 18px; overflow: hidden;"><span class="visual_font_size_18pt" style="color: #ffffff;"><strong>LYON</strong></span></p>',NULL,4,3600,0,0,0,0,1,1,0,0,0,'line','down','','',0,0,'default',0,0.000,0.000,0,0,'analogic_1','time','Europe/Madrid',0,0), + (234,9,1063,660,0,0,'<p style="line-height: 18px; overflow: hidden;"><span class="visual_font_size_18pt" style="color: #333333;"><strong>LYON</strong></span></p>',NULL,4,3600,0,0,0,0,1,1,0,0,0,'line','down','','',0,0,'default',0,0.000,0.000,0,0,'analogic_1','time','Europe/Madrid',0,0), + (235,9,905,416,0,0,'<p style="line-height: 18px; overflow: hidden;"><span class="visual_font_size_18pt" style="color: #333333;"><strong>PARIS</strong></span></p>',NULL,4,3600,0,0,0,0,1,1,0,0,0,'line','down','','',0,0,'default',0,0.000,0.000,0,0,'analogic_1','time','Europe/Madrid',0,0), + (236,9,1676,184,132,200,'','worldmap',5,3600,0,0,3,0,1,1,0,0,0,'line','down','','',0,0,'default',0,0.000,0.000,0,0,'analogic_1','time','Europe/Madrid',0,0), + (237,9,1673,340,132,200,'','europemap',5,3600,0,0,4,0,1,1,0,0,0,'line','down','','',0,0,'default',0,0.000,0.000,0,0,'analogic_1','time','Europe/Madrid',0,0), + (238,8,1098,397,60,60,'','status',0,3600,0,1,0,0,1,1,0,0,0,'line','down','','',0,0,'default',0,0.000,0.000,0,0,'analogic_1','time','Europe/Madrid',0,0), + (239,8,989,775,60,60,'','status',0,3600,0,1,0,0,1,1,0,0,0,'line','down','','',0,0,'default',0,0.000,0.000,0,0,'analogic_1','time','Europe/Madrid',0,0), + (240,8,824,595,60,60,'','status',0,3600,0,1,0,0,1,1,0,0,0,'line','down','','',0,0,'default',0,0.000,0.000,0,0,'analogic_1','time','Europe/Madrid',0,0), + (241,8,59,183,60,60,'','status',0,3600,0,1,0,0,1,1,0,0,0,'line','down','','',0,0,'default',0,0.000,0.000,0,0,'analogic_1','time','Europe/Madrid',0,0), + (242,8,62,335,60,60,'','status',0,3600,0,1,0,0,1,1,0,0,0,'line','down','','',0,0,'default',0,0.000,0.000,0,0,'analogic_1','time','Europe/Madrid',0,0), + (243,8,60,260,60,60,'','status',0,3600,0,1,0,0,1,1,0,0,0,'line','down','','',0,0,'default',0,0.000,0.000,0,0,'analogic_1','time','Europe/Madrid',0,0), + (244,8,1077,456,0,0,'<p style="line-height: 18px; overflow: hidden;"><span style="color: #000000;"><strong><span class="visual_font_size_18pt">BERLIN</span></strong></span></p>',NULL,4,3600,0,0,0,0,1,1,0,0,0,'line','down','','',0,0,'default',0,0.000,0.000,0,0,'analogic_1','time','Europe/Madrid',0,0), + (245,8,143,262,0,0,'<p style="line-height: 18px; overflow: hidden;"><strong><span class="visual_font_size_18pt" style="color: #ffffff;">FRANCFORT</span></strong></p>',NULL,4,3600,0,0,0,0,1,1,0,0,0,'line','down','','',0,0,'default',0,0.000,0.000,0,0,'analogic_1','time','Europe/Madrid',0,0), + (246,8,144,341,0,0,'<p style="line-height: 18px; overflow: hidden;"><strong><span class="visual_font_size_18pt" style="color: #ffffff;">MUNICH</span></strong></p>',NULL,4,3600,0,0,0,0,1,1,0,0,0,'line','down','','',0,0,'default',0,0.000,0.000,0,0,'analogic_1','time','Europe/Madrid',0,0), + (247,8,957,827,0,0,'<p style="line-height: 18px; overflow: hidden;"><span style="color: #000000;"><strong><span class="visual_font_size_18pt">MUNICH</span></strong></span></p>',NULL,4,3600,0,0,0,0,1,1,0,0,0,'line','down','','',0,0,'default',0,0.000,0.000,0,0,'analogic_1','time','Europe/Madrid',0,0), + (248,8,795,655,0,0,'<p style="line-height: 18px; overflow: hidden;"><span style="color: #000000;"><strong><span class="visual_font_size_18pt">FRANCFORT</span></strong></span></p>',NULL,4,3600,0,0,0,0,1,1,0,0,0,'line','down','','',0,0,'default',0,0.000,0.000,0,0,'analogic_1','time','Europe/Madrid',0,0), + (249,8,143,183,0,0,'<p style="line-height: 18px; overflow: hidden;"><strong><span class="visual_font_size_18pt" style="color: #ffffff;">BERLIN</span></strong></p>',NULL,4,3600,0,0,0,0,1,1,0,0,0,'line','down','','',0,0,'default',0,0.000,0.000,0,0,'analogic_1','time','Europe/Madrid',0,0), + (250,8,1676,185,132,200,'','worldmap',5,3600,0,0,3,0,1,1,0,0,0,'line','down','','',0,0,'default',0,0.000,0.000,0,0,'analogic_1','time','Europe/Madrid',0,0), + (251,8,1677,343,132,200,'','europemap',5,3600,0,0,4,0,1,1,0,0,0,'line','down','','',0,0,'default',0,0.000,0.000,0,0,'analogic_1','time','Europe/Madrid',0,0), + (252,8,846,940,0,0,'','white',19,3600,0,0,0,0,1,1,0,0,0,'line','down','','#FFFFFF',0,0,'default',0,0.000,0.000,0,0,'digital_1','time','Europe/Madrid',0,0), + (253,9,848,944,0,0,'','white',19,3600,0,0,0,0,1,1,0,0,0,'line','down','','#FFFFFF',0,0,'default',0,0.000,0.000,0,0,'digital_1','time','Europe/Madrid',0,0), + (254,6,848,943,0,0,'','white',19,3600,0,0,0,0,1,1,0,0,0,'line','down','','#FFFFFF',0,0,'default',0,0.000,0.000,0,0,'digital_1','time','Europe/Madrid',0,0), + (255,5,846,941,0,0,'','white',19,3600,0,0,0,0,1,1,0,0,0,'line','down','','#FFFFFF',0,0,'default',0,0.000,0.000,0,0,'digital_1','time','Europe/Madrid',0,0); -- -- Dumping data for table `tpen` From 266dc8ba4ab76babc1a01deda0292b9a825bc6dc Mon Sep 17 00:00:00 2001 From: Daniel Barbero Martin Date: Mon, 23 Nov 2020 16:38:06 +0100 Subject: [PATCH 078/263] fix public view link remove hash --- .../models/VisualConsole/Container.php | 21 +++++++++++++++++ .../operation/visual_console/public_view.php | 23 +++++++++++++++++++ .../operation/visual_console/view.php | 7 ++++++ 3 files changed, 51 insertions(+) diff --git a/pandora_console/include/rest-api/models/VisualConsole/Container.php b/pandora_console/include/rest-api/models/VisualConsole/Container.php index 16d72c9d95..04e2927893 100644 --- a/pandora_console/include/rest-api/models/VisualConsole/Container.php +++ b/pandora_console/include/rest-api/models/VisualConsole/Container.php @@ -88,6 +88,7 @@ final class Container extends Model 'height' => (int) $data['height'], 'backgroundURL' => static::extractBackgroundUrl($data), 'relationLineWidth' => (int) $data['relationLineWidth'], + 'hash' => static::extractHash($data), ]; } @@ -198,6 +199,22 @@ final class Container extends Model } + /** + * Extract a hash. + * + * @param array $data Unknown input data structure. + * + * @return mixed String representing hash (not empty) or null. + */ + private static function extractHash(array $data) + { + return static::notEmptyStringOr( + static::issetInArray($data, ['hash']), + null + ); + } + + /** * Extract a background color value. * @@ -279,6 +296,10 @@ final class Container extends Model ); } + $row['hash'] = md5( + $config['dbpass'].$row['id'].$config['id_user'] + ); + return \io_safe_output($row); } diff --git a/pandora_console/operation/visual_console/public_view.php b/pandora_console/operation/visual_console/public_view.php index 6cc9f817a1..f8b7c0eff9 100644 --- a/pandora_console/operation/visual_console/public_view.php +++ b/pandora_console/operation/visual_console/public_view.php @@ -177,6 +177,22 @@ $visualConsoleItems = VisualConsole::getItemsFromDB( var handleUpdate = function (prevProps, newProps) { if (!newProps) return; + //Remove spinner change VC. + document + .getElementById("visual-console-container") + .classList.remove("is-updating"); + + var div = document + .getElementById("visual-console-container") + .querySelector(".div-visual-console-spinner"); + + if (div !== null) { + var parent = div.parentElement; + if (parent !== null) { + parent.removeChild(div); + } + } + // Change the background color when the fullscreen mode is enabled. if (prevProps && prevProps.backgroundColor != newProps.backgroundColor @@ -200,17 +216,24 @@ $visualConsoleItems = VisualConsole::getItemsFromDB( var regex = /(id=|id_visual_console=|id_layout=|id_visualmap=)\d+(&?)/gi; var replacement = '$1' + newProps.id + '$2'; + var regex_hash = /(hash=)[^&]+(&?)/gi; + var replacement_hash = '$1' + newProps.hash + '$2'; // Tab links. var menuLinks = document.querySelectorAll("div#menu_tab a"); if (menuLinks !== null) { menuLinks.forEach(function (menuLink) { menuLink.href = menuLink.href.replace(regex, replacement); + menuLink.href = menuLink.href.replace( + regex_hash, + replacement_hash + ); }); } // Change the URL (if the browser has support). if ("history" in window) { var href = window.location.href.replace(regex, replacement); + href = href.replace(regex_hash, replacement_hash); window.history.replaceState({}, document.title, href); } } diff --git a/pandora_console/operation/visual_console/view.php b/pandora_console/operation/visual_console/view.php index 3b70215aa2..7f889509a0 100644 --- a/pandora_console/operation/visual_console/view.php +++ b/pandora_console/operation/visual_console/view.php @@ -470,11 +470,17 @@ ui_require_css_file('form'); var regex = /(id=|id_visual_console=|id_layout=|id_visualmap=)\d+(&?)/gi; var replacement = '$1' + newProps.id + '$2'; + var regex_hash = /(hash=)[^&]+(&?)/gi; + var replacement_hash = '$1' + newProps.hash + '$2'; // Tab links. var menuLinks = document.querySelectorAll("div#menu_tab a"); if (menuLinks !== null) { menuLinks.forEach(function (menuLink) { menuLink.href = menuLink.href.replace(regex, replacement); + menuLink.href = menuLink.href.replace( + regex_hash, + replacement_hash + ); }); } @@ -487,6 +493,7 @@ ui_require_css_file('form'); // Change the URL (if the browser has support). if ("history" in window) { var href = window.location.href.replace(regex, replacement); + href = href.replace(regex_hash, replacement_hash); window.history.replaceState({}, document.title, href); } } From f5648d734ddffa1e552a1e023161cd7dc17106a1 Mon Sep 17 00:00:00 2001 From: alejandro-campos Date: Mon, 23 Nov 2020 17:09:58 +0100 Subject: [PATCH 079/263] fixed snapshot in meta --- pandora_console/include/class/Tree.class.php | 3 ++- pandora_console/include/functions_modules.php | 3 ++- pandora_console/include/functions_ui.php | 6 ++++-- 3 files changed, 8 insertions(+), 4 deletions(-) diff --git a/pandora_console/include/class/Tree.class.php b/pandora_console/include/class/Tree.class.php index a5f754866d..9d7dbd6555 100644 --- a/pandora_console/include/class/Tree.class.php +++ b/pandora_console/include/class/Tree.class.php @@ -565,7 +565,6 @@ class Tree $module['id_module_type'] = (int) $module['id_tipo_modulo']; $module['server_type'] = (int) $module['id_modulo']; $module['status'] = $module['estado']; - $module['value'] = modules_get_agentmodule_data_for_humans($module); if (is_metaconsole()) { $module['serverID'] = $this->serverID; @@ -575,6 +574,8 @@ class Tree $module['serverID'] = false; } + $module['value'] = modules_get_agentmodule_data_for_humans($module); + if (!isset($module['value'])) { $module['value'] = modules_get_last_value($module['id']); } diff --git a/pandora_console/include/functions_modules.php b/pandora_console/include/functions_modules.php index fef8c7d302..b694412ff6 100755 --- a/pandora_console/include/functions_modules.php +++ b/pandora_console/include/functions_modules.php @@ -2361,7 +2361,8 @@ function modules_get_agentmodule_data_for_humans($module) $module['datos'], $module['id'], $module['current_interval'], - $module['module_name'] + $module['module_name'], + $module['serverID'] ? $module['serverID'] : 0 ); } } diff --git a/pandora_console/include/functions_ui.php b/pandora_console/include/functions_ui.php index ab6079fb36..cedec43a63 100755 --- a/pandora_console/include/functions_ui.php +++ b/pandora_console/include/functions_ui.php @@ -5609,7 +5609,8 @@ function ui_print_module_string_value( $value, $id_agente_module, $current_interval, - $module_name=null + $module_name=null, + $server_id=0 ) { global $config; @@ -5654,6 +5655,7 @@ function ui_print_module_string_value( 'last_data' => $value, 'interval' => $current_interval, 'module_name' => $module_name, + 'id_node' => $server_id ? $server_id : 0, ] ); $salida = ui_get_snapshot_image($link, $is_snapshot).'  '; @@ -5761,7 +5763,7 @@ function ui_get_snapshot_link($params, $only_params=false) $params = array_merge($default_params, $params); // First parameter of js winopeng_var. - $page = $config['homeurl'].'/operation/agentes/snapshot_view.php'; + $page = ui_get_full_url('operation/agentes/snapshot_view.php', false, false, false); $url = $page.'?id='.$params['id_module'].'&label='.rawurlencode(urlencode(io_safe_output($params['module_name']))).'&id_node='.$params['id_node']; From a767b6c13374e729a1afd084e50acc7c15e22e48 Mon Sep 17 00:00:00 2001 From: fbsanchez Date: Mon, 23 Nov 2020 20:16:11 +0100 Subject: [PATCH 080/263] Fixes in history db settings and improved pandora_db tool --- pandora_console/godmode/setup/performance.php | 7 +-- pandora_server/util/pandora_db.pl | 54 ++++++++++++++++--- 2 files changed, 51 insertions(+), 10 deletions(-) diff --git a/pandora_console/godmode/setup/performance.php b/pandora_console/godmode/setup/performance.php index 096defb664..bc79631697 100644 --- a/pandora_console/godmode/setup/performance.php +++ b/pandora_console/godmode/setup/performance.php @@ -64,7 +64,7 @@ if ($update_config == 1 && $config['history_db_enabled'] == 1) { $historical_string_purge = get_parameter('historical_string_purge', 0); $history_connect = @mysql_db_process_sql( - 'SELECT 1 FROM tconfig', + 'DESCRIBE tconfig', 'affected_rows', $config['history_db_connection'], false @@ -390,8 +390,8 @@ if ($config['history_db_enabled'] == 1) { $config_history = false; if ($config['history_db_connection']) { - $history_connect = @mysql_db_process_sql( - 'SELECT 1 FROM tconfig', + $history_connect = mysql_db_process_sql( + 'DESCRIBE tconfig', 'affected_rows', $config['history_db_connection'], false @@ -408,6 +408,7 @@ if ($config['history_db_enabled'] == 1) { if (isset($config_history_array) && is_array($config_history_array)) { foreach ($config_history_array as $key => $value) { $config_history[$value['token']] = $value['value']; + $config_history = true; } } } else { diff --git a/pandora_server/util/pandora_db.pl b/pandora_server/util/pandora_db.pl index 2c55dbf933..4cac97dd34 100755 --- a/pandora_server/util/pandora_db.pl +++ b/pandora_server/util/pandora_db.pl @@ -581,6 +581,28 @@ sub pandora_init_pdb ($) { help_screen () if ($conf->{'_pandora_path'} eq ''); } +######################################################################## +# Prepares conf read from historical database settings. +######################################################################## +sub pandoradb_load_history_conf($) { + my $dbh = shift; + + my @options = get_db_rows($dbh, 'SELECT * FROM `tconfig`'); + + my %options = map + { + '_' . $_->{'token'} => $_->{'value'} + } @options; + + $options{'_days_autodisable_deletion'} = 0 unless defined ($options{'_days_autodisable_deletion'}); + $options{'_num_past_special_days'} = 0 unless defined($options{'_num_past_special_days'}); + $options{'_delete_old_network_matrix'} = 0 unless defined($options{'_delete_old_network_matrix'}); + $options{'_delete_old_messages'} = 0 unless defined($options{'_delete_old_messages'}); + $options{'_netflow_max_lifetime'} = 0 unless defined($options{'_netflow_max_lifetime'}); + $options{'claim_back_snmp_modules'} = 0 unless defined($options{'claim_back_snmp_modules'}); + + return \%options; +} ######################################################################## # Read external configuration file. @@ -651,7 +673,7 @@ sub pandora_load_config_pdb ($) { $conf->{'_metaconsole_events_history'} = get_db_value ($dbh, "SELECT value FROM tconfig WHERE token = 'metaconsole_events_history'"); $conf->{'_netflow_max_lifetime'} = get_db_value ($dbh, "SELECT value FROM tconfig WHERE token = 'netflow_max_lifetime'"); $conf->{'_netflow_nfexpire'} = get_db_value ($dbh, "SELECT value FROM tconfig WHERE token = 'netflow_nfexpire'"); - $conf->{'_netflow_path'} = get_db_value ($dbh, "SELECT value FROM tconfig WHERE token = 'netflow_path'"); + $conf->{'_netflow_path'} = get_db_value ($dbh, "SELECT value FROM tconfig WHERE token = 'netflow_path'"); $conf->{'_delete_notinit'} = get_db_value ($dbh, "SELECT value FROM tconfig WHERE token = 'delete_notinit'"); $conf->{'_session_timeout'} = get_db_value ($dbh, "SELECT value FROM tconfig WHERE token = 'session_timeout'"); @@ -726,7 +748,7 @@ sub pandora_checkdb_consistency { # 1. Check for modules that do not have tagente_estado but have # tagente_module #------------------------------------------------------------------- - if (defined($conf->{'_delete_notinit'}) && $conf->{'_delete_notinit'} == 1) { + if (defined($conf->{'_delete_notinit'}) && $conf->{'_delete_notinit'} eq "1") { log_message ('CHECKDB', "Deleting not-init data."); my @modules = get_db_rows ($dbh, 'SELECT id_agente_modulo, id_agente @@ -956,7 +978,7 @@ sub pandora_delete_old_module_data { sub pandora_delete_old_export_data { my ($dbh, $ulimit_timestamp) = @_; - log_message ('PURGE', "Deleting old export data from tserver_export_data\n"); + log_message ('PURGE', "Deleting old export data from tserver_export_data"); while((my $rc = db_delete_limit ($dbh, 'tserver_export_data', 'UNIX_TIMESTAMP(timestamp) < ?', $SMALL_OPERATION_STEP, $ulimit_timestamp)) ne '0E0') { print "RC:$rc\n"; usleep (10000); @@ -982,7 +1004,7 @@ sub pandora_delete_old_session_data { $ulimit_timestamp = time() - $session_timeout; } - log_message ('PURGE', "Deleting old session data from tsessions_php\n"); + log_message ('PURGE', "Deleting old session data from tsessions_php"); while(db_delete_limit ($dbh, 'tsessions_php', 'last_active < ?', $SMALL_OPERATION_STEP, $ulimit_timestamp) ne '0E0') { usleep (10000); }; @@ -1076,8 +1098,9 @@ if ($conf{'_force'} == 0 && pandora_is_master(\%conf) == 0) { exit 1; } -# Get a lock -my $lock = db_get_lock ($dbh, 'pandora_db'); +# Get a lock on dbname. +my $lock_name = $conf{'dbname'}; +my $lock = db_get_lock ($dbh, $lock_name); if ($lock == 0 && $conf{'_force'} == 0) { log_message ('', " [*] Another instance of DB Tool seems to be running.\n\n"); exit 1; @@ -1086,9 +1109,26 @@ if ($lock == 0 && $conf{'_force'} == 0) { # Main pandoradb_main(\%conf, $dbh, $history_dbh); +# history_dbh is unset in pandoradb_main if not in use. +if (defined($history_dbh)) { + log_message('', " [>] DB Tool running on historical database.\n"); + my $h_conf = pandoradb_load_history_conf($history_dbh); + + # Keep base settings. + $h_conf->{'_onlypurge'} = $conf{'_onlypurge'}; + + # Re-launch maintenance process for historical database. + pandoradb_main( + $h_conf, + $history_dbh, + undef + ); + +} + # Release the lock if ($lock == 1) { - db_release_lock ($dbh, 'pandora_db'); + db_release_lock ($dbh, $lock_name); } # Cleanup and exit From 04f4aee7eb2a19cb57031dfe186043a7e978388c Mon Sep 17 00:00:00 2001 From: fbsanchez Date: Tue, 24 Nov 2020 10:51:27 +0100 Subject: [PATCH 081/263] Export product VERSION from Tools.pm --- pandora_server/lib/PandoraFMS/Tools.pm | 1 + 1 file changed, 1 insertion(+) diff --git a/pandora_server/lib/PandoraFMS/Tools.pm b/pandora_server/lib/PandoraFMS/Tools.pm index 4c9683c888..98ddf09c09 100755 --- a/pandora_server/lib/PandoraFMS/Tools.pm +++ b/pandora_server/lib/PandoraFMS/Tools.pm @@ -91,6 +91,7 @@ our @EXPORT = qw( $DEVNULL $OS $OS_VERSION + $VERSION RECOVERED_ALERT FIRED_ALERT MODULE_NORMAL From 2428558a39a8d5d57829a971903403df3e319a01 Mon Sep 17 00:00:00 2001 From: Daniel Barbero Martin Date: Tue, 24 Nov 2020 10:59:42 +0100 Subject: [PATCH 082/263] fix bugs vc serialez module string donuts and bars charts --- .../rest-api/models/VisualConsole/Items/DonutGraph.php | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/pandora_console/include/rest-api/models/VisualConsole/Items/DonutGraph.php b/pandora_console/include/rest-api/models/VisualConsole/Items/DonutGraph.php index 65f671d405..f73806967e 100644 --- a/pandora_console/include/rest-api/models/VisualConsole/Items/DonutGraph.php +++ b/pandora_console/include/rest-api/models/VisualConsole/Items/DonutGraph.php @@ -167,10 +167,12 @@ final class DonutGraph extends Item ); } else { $src = 'images/console/signes/wrong_donut_graph.png'; - if (\is_metaconsole() === true && $metaconsoleId !== null) { + if (\is_metaconsole() === true) { $src = '../../'.$src; } + $src = ui_get_full_url($src); + $style = 'width:'.$width.'px; height:'.$height.'px;'; $data['html'] = ''; } From af6bab49e9c03eaa78cc96a03b70198350eda4d7 Mon Sep 17 00:00:00 2001 From: Daniel Barbero Martin Date: Tue, 24 Nov 2020 12:04:25 +0100 Subject: [PATCH 083/263] fix visual view pure CV --- pandora_console/include/styles/visual_maps.css | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/pandora_console/include/styles/visual_maps.css b/pandora_console/include/styles/visual_maps.css index 7c4809eeeb..b222b2a68a 100644 --- a/pandora_console/include/styles/visual_maps.css +++ b/pandora_console/include/styles/visual_maps.css @@ -8,6 +8,23 @@ div#vc-controls { position: fixed; top: 30px; right: 20px; + width: 350px; + background-color: #ececec; + display: flex; + flex-direction: column; + justify-content: center; + align-items: center; + padding: 5px; + border-radius: 3px; +} + +div#vc-controls div#menu_tab { + margin: 0px; +} + +div#vc-controls ul.white-box-content { + background-color: #ececec; + border: 0px; } div#vc-controls div.vc-title, From 7931f171447572b2e8ddd5be9c6511fa87d0185f Mon Sep 17 00:00:00 2001 From: alejandro-campos Date: Tue, 24 Nov 2020 14:08:37 +0100 Subject: [PATCH 084/263] fixed selector --- pandora_console/godmode/massive/massive_edit_modules.php | 1 + 1 file changed, 1 insertion(+) diff --git a/pandora_console/godmode/massive/massive_edit_modules.php b/pandora_console/godmode/massive/massive_edit_modules.php index 1948ba4fa0..8bf6817310 100755 --- a/pandora_console/godmode/massive/massive_edit_modules.php +++ b/pandora_console/godmode/massive/massive_edit_modules.php @@ -1138,6 +1138,7 @@ $table->data['edit1'][1] = ''; ); $array_os = [ + '' => __('No change'), 'inherited' => __('Inherited'), 'linux' => __('Linux'), 'windows' => __('Windows'), From e2bdb4858a53970fda7c0b2b51433f93c0393af9 Mon Sep 17 00:00:00 2001 From: alejandro-campos Date: Tue, 24 Nov 2020 16:13:28 +0100 Subject: [PATCH 085/263] included not init modules in total count --- pandora_console/include/functions_reporting.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pandora_console/include/functions_reporting.php b/pandora_console/include/functions_reporting.php index 479adc67c8..6ffdf374bd 100755 --- a/pandora_console/include/functions_reporting.php +++ b/pandora_console/include/functions_reporting.php @@ -10688,7 +10688,7 @@ function reporting_tiny_stats( if (isset($counts_info['total_count'])) { $not_init = isset($counts_info['notinit_count']) ? $counts_info['notinit_count'] : 0; - $total_count = ($counts_info['total_count'] - $not_init); + $total_count = $counts_info['total_count']; $stats[] = [ 'name' => 'total_count', 'count' => $total_count, From fba3bbbc93d077cc6b2c251e2e37063221a9e9eb Mon Sep 17 00:00:00 2001 From: Daniel Barbero Martin Date: Tue, 24 Nov 2020 16:56:41 +0100 Subject: [PATCH 086/263] fix dashboard widget groups status add secondary groups --- .../include/functions_reporting.php | 195 +++++++++--------- 1 file changed, 94 insertions(+), 101 deletions(-) diff --git a/pandora_console/include/functions_reporting.php b/pandora_console/include/functions_reporting.php index 479adc67c8..404c4bcaa8 100755 --- a/pandora_console/include/functions_reporting.php +++ b/pandora_console/include/functions_reporting.php @@ -9314,7 +9314,6 @@ function reporting_get_group_stats_resume($id_group=0, $access='AR') $data['monitor_unknown'] = 0; $data['monitor_ok'] = 0; $data['monitor_bad'] = 0; - // Critical + Unknown + Warning $data['monitor_warning'] = 0; $data['monitor_critical'] = 0; $data['monitor_not_normal'] = 0; @@ -9340,7 +9339,7 @@ function reporting_get_group_stats_resume($id_group=0, $access='AR') $cur_time = get_system_time(); - // Check for access credentials using check_acl. More overhead, much safer + // Check for access credentials using check_acl. More overhead, much safer. if (!check_acl($config['id_user'], $id_group, $access)) { return $data; } @@ -9397,113 +9396,107 @@ function reporting_get_group_stats_resume($id_group=0, $access='AR') // Realtime stats, done by PHP Console // ------------------------------------------------------------------- } else { - if (!empty($id_group)) { - // check tags for user - $tags = db_get_value('tags', 'tusuario_perfil', 'id_usuario', $config['id_user']); - if ($tags) { - $tags_sql = " AND tae.id_agente_modulo IN ( SELECT id_agente_modulo - FROM ttag_module - WHERE id_tag IN ($tags) ) "; - } else { - $tags_sql = ''; - } - + if (empty($id_group) === false) { if (is_array($id_group)) { $id_group = implode(',', $id_group); } - // for stats modules - $sql = "SELECT tg.id_grupo as id, tg.nombre as name, - SUM(tae.estado=0) as monitor_ok, - SUM(tae.estado=1) as monitor_critical, - SUM(tae.estado=2) as monitor_warning, - SUM(tae.estado=3) as monitor_unknown, - SUM(tae.estado=4) as monitor_not_init, - COUNT(tae.estado) as monitor_total - - FROM - tagente_estado tae, - tagente ta, - tagente_modulo tam, - tgrupo tg - - WHERE 1=1 - AND tae.id_agente = ta.id_agente - AND tae.id_agente_modulo = tam.id_agente_modulo - AND ta.id_grupo = tg.id_grupo - AND tam.disabled = 0 - AND ta.disabled = 0 - AND ta.id_grupo IN ($id_group) $tags_sql - GROUP BY tg.id_grupo;"; - $data_array = db_get_all_rows_sql($sql); - - $data = $data_array[0]; - - // Get alerts configured, except disabled - $data['monitor_alerts'] += groups_monitor_alerts($group_array); - - // Get alert configured currently FIRED, except disabled - $data['monitor_alerts_fired'] += groups_monitor_fired_alerts($group_array); - - // for stats agents - $sql = "SELECT tae.id_agente id_agente, tg.id_grupo id_grupo, - SUM(tae.estado=0) as monitor_agent_ok, - SUM(tae.estado=1) as monitor_agent_critical, - SUM(tae.estado=2) as monitor_agent_warning, - SUM(tae.estado=3) as monitor_agent_unknown, - SUM(tae.estado=4) as monitor_agent_not_init, - COUNT(tae.estado) as monitor_agent_total + $agent_table = (is_metaconsole() === true) ? 'tmetaconsole_agent' : 'tagente'; + $agent_secondary_table = (is_metaconsole() === true) ? 'tmetaconsole_agent_secondary_group' : 'tagent_secondary_group'; + $sql = sprintf( + 'SELECT tg.id_grupo AS id_group, + IF (SUM(modules_total) IS NULL,0,SUM(modules_total)) AS modules, + IF (SUM(modules_ok) IS NULL,0,SUM(modules_ok)) AS normal, + IF (SUM(modules_critical) IS NULL,0,SUM(modules_critical)) AS critical, + IF (SUM(modules_warning) IS NULL,0,SUM(modules_warning)) AS warning, + IF (SUM(modules_unknown) IS NULL,0,SUM(modules_unknown)) AS unknown, + IF (SUM(modules_not_init) IS NULL,0,SUM(modules_not_init)) AS `non-init`, + IF (SUM(alerts_fired) IS NULL,0,SUM(alerts_fired)) AS alerts_fired, + IF (SUM(agents_total) IS NULL,0,SUM(agents_total)) AS agents, + IF (SUM(agents_unknown) IS NULL,0,SUM(agents_unknown)) AS agents_unknown, + IF (SUM(agents_critical) IS NULL,0,SUM(agents_critical)) AS agents_critical, + IF (SUM(agents_warnings) IS NULL,0,SUM(agents_warnings)) AS agents_warnings, + IF (SUM(agents_not_init) IS NULL,0,SUM(agents_not_init)) AS agents_not_init, + IF (SUM(agents_normal) IS NULL,0,SUM(agents_normal)) AS agents_normal, + UNIX_TIMESTAMP() AS utimestamp FROM - tagente_estado tae, - tagente ta, - tagente_modulo tam, - tgrupo tg - - WHERE 1=1 - AND tae.id_agente = ta.id_agente - AND tae.id_agente_modulo = tam.id_agente_modulo - AND ta.id_grupo = tg.id_grupo - AND tam.disabled = 0 - AND ta.disabled = 0 - AND ta.id_grupo IN ($id_group) $tags_sql - GROUP BY tae.id_agente;"; - $data_array_2 = db_get_all_rows_sql($sql); + ( + SELECT SUM(ta.normal_count) AS modules_ok, + SUM(ta.critical_count) AS modules_critical, + SUM(ta.warning_count) AS modules_warning, + SUM(ta.unknown_count) AS modules_unknown, + SUM(ta.notinit_count) AS modules_not_init, + SUM(ta.total_count) AS modules_total, + SUM(ta.fired_count) AS alerts_fired, + SUM(IF(ta.critical_count > 0, 1, 0)) AS agents_critical, + SUM(IF(ta.warning_count > 0 AND ta.critical_count = 0, 1, 0)) AS agents_warnings, + SUM(IF(ta.total_count = ta.notinit_count OR ta.total_count = 0, 1, 0)) AS agents_not_init, + SUM(IF(ta.critical_count = 0 AND ta.warning_count = 0 AND ta.unknown_count = 0 AND ta.normal_count > 0, 1, 0)) AS agents_normal, + SUM(IF(ta.critical_count = 0 AND ta.warning_count = 0 AND ta.unknown_count > 0, 1, 0)) AS agents_unknown, + COUNT(ta.id_agente) AS agents_total, + ta.id_grupo AS g + FROM %s ta + WHERE ta.disabled = 0 + AND ta.id_grupo IN (%s) + GROUP BY g + UNION ALL + SELECT SUM(ta.normal_count) AS modules_ok, + SUM(ta.critical_count) AS modules_critical, + SUM(ta.warning_count) AS modules_warning, + SUM(ta.unknown_count) AS modules_unknown, + SUM(ta.notinit_count) AS modules_not_init, + SUM(ta.total_count) AS modules_total, + SUM(ta.fired_count) AS alerts_fired, + SUM(IF(ta.critical_count > 0, 1, 0)) AS agents_critical, + SUM(IF(ta.warning_count > 0 AND ta.critical_count = 0, 1, 0)) AS agents_warnings, + SUM(IF(ta.total_count = ta.notinit_count OR ta.total_count = 0, 1, 0)) AS agents_not_init, + SUM(IF(ta.critical_count = 0 AND ta.warning_count = 0 AND ta.unknown_count = 0 AND ta.normal_count > 0, 1, 0)) AS agents_normal, + SUM(IF(ta.critical_count = 0 AND ta.warning_count = 0 AND ta.unknown_count > 0, 1, 0)) AS agents_unknown, + COUNT(ta.id_agente) AS agents_total, + tasg.id_group AS g + FROM %s ta + LEFT JOIN %s tasg + ON ta.id_agente = tasg.id_agent + WHERE ta.disabled = 0 + AND (ta.id_grupo IN (%s) OR tasg.id_group IN (%s)) + GROUP BY g + ) counters + RIGHT JOIN tgrupo tg + ON counters.g = tg.id_grupo + WHERE tg.id_grupo IN (%s) + GROUP BY tg.id_grupo', + $agent_table, + $id_group, + $agent_table, + $agent_secondary_table, + $id_group, + $id_group, + $id_group + ); - if (is_array($data_array_2) || is_object($data_array_2)) { - foreach ($data_array_2 as $key => $value) { - if ($value['monitor_agent_critical'] != 0) { - $data['agent_critical'] ++; - } else if ($value['monitor_agent_critical'] == 0 && $value['monitor_agent_warning'] != 0) { - $data['agent_warning'] ++; - } else if ($value['monitor_agent_critical'] == 0 && $value['monitor_agent_warning'] == 0 - && $value['monitor_agent_unknown'] != 0 - ) { - $data['agent_unknown'] ++; - } else if ($value['monitor_agent_critical'] == 0 && $value['monitor_agent_warning'] == 0 - && $value['monitor_agent_unknown'] == 0 && $value['monitor_agent_ok'] != 0 - ) { - $data['agent_ok'] ++; - } else if ($value['monitor_agent_critical'] == 0 && $value['monitor_agent_warning'] == 0 - && $value['monitor_agent_unknown'] == 0 && $value['monitor_agent_ok'] == 0 - && $value['monitor_agent_not_init'] != 0 - ) { - $data['agent_not_init'] ++; - } - - $data['total_agents'] ++; - } - } - - // Get total count of monitors for this group, except disabled. - $data['monitor_checks'] = ($data['monitor_not_init'] + $data['monitor_unknown'] + $data['monitor_warning'] + $data['monitor_critical'] + $data['monitor_ok']); - - // Calculate not_normal monitors - $data['monitor_not_normal'] += ($data['monitor_checks'] - $data['monitor_ok']); + $group_stat = db_get_all_rows_sql($sql); + $data = [ + 'monitor_checks' => (int) $group_stat[0]['modules'], + 'monitor_alerts' => (int) groups_monitor_alerts($group_array), + 'monitor_alerts_fired' => (int) $group_stat[0]['alerts_fired'], + 'monitor_alerts_fire_count' => (int) $group_stat[0]['alerts_fired'], + 'monitor_ok' => (int) $group_stat[0]['normal'], + 'monitor_warning' => (int) $group_stat[0]['warning'], + 'monitor_critical' => (int) $group_stat[0]['critical'], + 'monitor_unknown' => (int) $group_stat[0]['unknown'], + 'monitor_not_init' => (int) $group_stat[0]['non-init'], + 'agent_not_init' => (int) $group_stat[0]['agents_not_init'], + 'agent_unknown' => (int) $group_stat[0]['agents_unknown'], + 'agent_ok' => (int) $group_stat[0]['agents_normal'], + 'agent_warning' => (int) $group_stat[0]['agents_warnings'], + 'agent_critical' => (int) $group_stat[0]['agents_critical'], + 'total_checks' => (int) $group_stat[0]['modules'], + 'total_alerts' => (int) groups_monitor_alerts($group_array), + 'total_agents' => (int) $group_stat[0]['agents'], + 'utimestamp' => (int) $group_stat[0]['utimestamp'], + ]; } - - // Get total count of monitors for this group, except disabled. - $data['monitor_checks'] = ($data['monitor_not_init'] + $data['monitor_unknown'] + $data['monitor_warning'] + $data['monitor_critical'] + $data['monitor_ok']); } if ($data['monitor_unknown'] > 0 && $data['monitor_checks'] > 0) { From 73f931d51648b3c50ee3a825d296d09daa07d9c3 Mon Sep 17 00:00:00 2001 From: marcos Date: Wed, 25 Nov 2020 10:50:06 +0100 Subject: [PATCH 087/263] show and hide 2fa swtich --- pandora_console/godmode/setup/setup_auth.php | 23 +++++++++++--------- 1 file changed, 13 insertions(+), 10 deletions(-) diff --git a/pandora_console/godmode/setup/setup_auth.php b/pandora_console/godmode/setup/setup_auth.php index e8c4ccbfad..b97f857a37 100644 --- a/pandora_console/godmode/setup/setup_auth.php +++ b/pandora_console/godmode/setup/setup_auth.php @@ -334,17 +334,20 @@ echo ''; $( document ).ready(function() { //For change autocreate remote users - $('input[type=checkbox][name=double_auth_enabled]').change(function () { - if ($('input[type=checkbox][name=double_auth_enabled]:checked').val() == 1) { - $('#table1-2FA_all_users').show(); - } - else { - $('#table1-2FA_all_users').hide(); - $('input[type=checkbox][name=2FA_all_users][value=0]').prop('checked', false); - } + $('input[type=checkbox][name=double_auth_enabled]').change(function () { + if ($('input[type=checkbox][name=double_auth_enabled]:checked').val() == 1) { + $('#table1-2FA_all_users').show(); + $('#table3-2FA_all_users').show(); + $('#table5-2FA_all_users').show(); + } + else { + $('input[type=checkbox][name=2FA_all_users][value=1]').prop('checked', false); + $('#table1-2FA_all_users').hide(); + $('#table3-2FA_all_users').hide(); + $('#table5-2FA_all_users').hide(); + } }).change(); - }); - + }); $('#auth').on('change', function(){ type_auth = $('#auth').val(); $.ajax({ From 2e950af548ac67ea40a73dc8d5f653af80e3eafd Mon Sep 17 00:00:00 2001 From: Daniel Barbero Martin Date: Wed, 25 Nov 2020 11:33:36 +0100 Subject: [PATCH 088/263] fix dashboard VC --- .../Dashboard/Widgets/maps_made_by_user.php | 41 ++++++++++--------- 1 file changed, 21 insertions(+), 20 deletions(-) diff --git a/pandora_console/include/lib/Dashboard/Widgets/maps_made_by_user.php b/pandora_console/include/lib/Dashboard/Widgets/maps_made_by_user.php index f480c45389..89fb8b6c6c 100644 --- a/pandora_console/include/lib/Dashboard/Widgets/maps_made_by_user.php +++ b/pandora_console/include/lib/Dashboard/Widgets/maps_made_by_user.php @@ -368,26 +368,27 @@ class MapsMadeByUser extends Widget // of the visual consoles. $output .= ''; - // Store servers timezone offset to be retrieved from js + // Store servers timezone offset to be retrieved from js. set_js_value('timezone_offset', date('Z', time())); } -extensions_add_operation_menu_option(__('Realtime graphs'), 'estado', null, 'v1r1', 'view'); +extensions_add_operation_menu_option( + __('Realtime graphs'), + 'estado', + null, + 'v1r1', + 'view' +); extensions_add_main_function('pandora_realtime_graphs'); $db = null; diff --git a/pandora_console/extensions/realtime_graphs/ajax.php b/pandora_console/extensions/realtime_graphs/ajax.php index dea94a5753..a09ef9bb29 100644 --- a/pandora_console/extensions/realtime_graphs/ajax.php +++ b/pandora_console/extensions/realtime_graphs/ajax.php @@ -69,15 +69,34 @@ switch ($graph) { case 'snmp_interface': case 'snmp_module': - $snmp_address = $_POST['snmp_address']; - $snmp_community = $_POST['snmp_community']; - $snmp_ver = $_POST['snmp_ver']; - $snmp_oid = $_POST['snmp_oid']; + $snmp_address = get_parameter('snmp_address', ''); + $snmp_community = get_parameter('snmp_community', ''); + $snmp_ver = get_parameter('snmp_ver', ''); + $snmp_oid = get_parameter('snmp_oid', ''); + $snmp3_auth_user = get_parameter('snmp3_auth_user', ''); + $snmp3_security_level = get_parameter('snmp3_security_level', ''); + $snmp3_auth_method = get_parameter('snmp3_auth_method', ''); + $snmp3_auth_pass = get_parameter('snmp3_auth_pass', ''); + $snmp3_privacy_method = get_parameter('snmp3_privacy_method', ''); + $snmp3_privacy_pass = get_parameter('snmp3_privacy_pass', ''); if (empty($snmp_address) || empty($snmp_oid)) { $data = 0; } else { - $data = get_snmpwalk($snmp_address, $snmp_ver, $snmp_community, '', '', '', '', '', '', 0, $snmp_oid); + $data = get_snmpwalk( + $snmp_address, + $snmp_ver, + $snmp_community, + $snmp3_auth_user, + $snmp3_security_level, + $snmp3_auth_method, + $snmp3_auth_pass, + $snmp3_privacy_method, + $snmp3_privacy_pass, + 0, + $snmp_oid, + $snmp_port + ); $data_index = array_keys($data); $graph_title = $data_index[0]; if (!empty($data)) { diff --git a/pandora_console/extensions/realtime_graphs/realtime_graphs.js b/pandora_console/extensions/realtime_graphs/realtime_graphs.js index b5d6c1eaa8..088f31dbf6 100644 --- a/pandora_console/extensions/realtime_graphs/realtime_graphs.js +++ b/pandora_console/extensions/realtime_graphs/realtime_graphs.js @@ -1,3 +1,4 @@ +/* global $, get_php_value */ (function() { var numberOfPoints = 100; var refresh = parseInt($("#refresh").val()); @@ -14,7 +15,7 @@ container: $("#chartLegend") }, xaxis: { - tickFormatter: function(timestamp, axis) { + tickFormatter: function(timestamp) { var date = new Date(timestamp * 1000); var server_timezone_offset = get_php_value("timezone_offset"); @@ -39,7 +40,7 @@ } }, yaxis: { - tickFormatter: function(value, axis) { + tickFormatter: function(value) { return shortNumber(roundToTwo(value)); } }, @@ -66,10 +67,16 @@ data: { graph: $("#graph :selected").val(), graph_title: $("#graph :selected").html(), - snmp_community: $("#text-snmp_community").val(), - snmp_oid: $("#text-snmp_oid").val(), - snmp_ver: $("#snmp_version :selected").val(), - snmp_address: $("#text-ip_target").val(), + snmp_community: $("#text-community").val(), + snmp_oid: $("#text-starting_oid").val(), + snmp_ver: $("#snmp_browser_version").val(), + snmp_address: $("#text-target_ip").val(), + snmp3_auth_user: $("#text-snmp3_browser_auth_user").val(), + snmp3_security_level: $("#snmp3_browser_security_level").val(), + snmp3_auth_method: $("#snmp3_browser_auth_method").val(), + snmp3_auth_pass: $("#password-snmp3_browser_auth_pass").val(), + snmp3_privacy_method: $("#snmp3_browser_privacy_method").val(), + snmp3_privacy_pass: $("#password-snmp3_browser_privacy_pass").val(), refresh: refresh }, success: function(serie) { @@ -87,7 +94,7 @@ } if (data.length === 0) { - for (i = 0; i < numberOfPoints; i++) { + for (var i = 0; i < numberOfPoints; i++) { var step = i * (refresh / 1000); serie.data.unshift([timestamp - step, 0]); } @@ -123,7 +130,7 @@ var data = plot.getData(); if (data.length === 0) return; - for (i = 0; i < data[0].data.length; i++) { + for (var i = 0; i < data[0].data.length; i++) { data[0].data[i][1] = 0; } if (incremental) lastIncVal = null; diff --git a/pandora_console/include/functions_modules.php b/pandora_console/include/functions_modules.php index e28c255f9c..b28cde4936 100755 --- a/pandora_console/include/functions_modules.php +++ b/pandora_console/include/functions_modules.php @@ -3395,16 +3395,16 @@ function recursive_get_dt_from_modules_tree(&$f_modules, $modules, $deep) /** * @brief Get the button with the link to open realtime stats into a new window - * Only to native (not satellite discovered) snmp modules. + * Only to native (not satellite discovered) snmp modules. * - * @param array With all the module info - * @return string All the HTML code to paint the button + * @param array $module With all the module info. + * @return string Link to chart. */ function get_module_realtime_link_graph($module) { global $config; - // Sometimes some parameters are renamed + // Sometimes some parameters are renamed. if (!isset($module['id_tipo_modulo'])) { $module['id_tipo_modulo'] = $module['module_type']; } @@ -3413,36 +3413,51 @@ function get_module_realtime_link_graph($module) $module['nombre'] = $module['module_name']; } - // Avoid to show on metaconsole + // Avoid to show on metaconsole. if (is_metaconsole()) { return ''; } - // Realtime graph is an extension and it should be enabled + // Realtime graph is an extension and it should be enabled. if (!extensions_is_enabled_extension('realtime_graphs.php')) { return ''; } - // Only to remote_snmp, remote_snmp_proc. snmp_snmp_inc - if ($module['id_tipo_modulo'] != 15 && $module['id_tipo_modulo'] != 16 && $module['id_tipo_modulo'] != 18) { + // Only to remote_snmp, remote_snmp_proc. snmp_snmp_inc. + if ($module['id_tipo_modulo'] != 15 + && $module['id_tipo_modulo'] != 16 + && $module['id_tipo_modulo'] != 18 + ) { return ''; } - // Only version 1, 2 and 2c - if ($module['tcp_send'] != '1' && $module['tcp_send'] != '2' && $module['tcp_send'] != '2c') { + // Only version 1, 2, 2c and 3 + if ($module['tcp_send'] != '1' + && $module['tcp_send'] != '2' + && $module['tcp_send'] != '2c' + && $module['tcp_send'] != '3' + ) { return ''; } $params = [ - 'graph' => 'snmp_module', - 'agent_alias' => urlencode(modules_get_agentmodule_agent_alias($module['id_agente_modulo'])), - 'module_name' => urlencode($module['nombre']), - 'snmp_address' => $module['ip_target'], - 'snmp_community' => urlencode($module['snmp_community']), - 'snmp_oid' => $module['snmp_oid'], - 'snmp_ver' => $module['tcp_send'], - 'hide_header' => 1, - 'rel_path' => '../../', + 'graph' => 'snmp_module', + 'agent_alias' => urlencode( + modules_get_agentmodule_agent_alias($module['id_agente_modulo']) + ), + 'module_name' => urlencode($module['nombre']), + 'target_ip' => $module['ip_target'], + 'community' => urlencode($module['snmp_community']), + 'starting_oid' => urlencode($module['snmp_oid']), + 'snmp_browser_version' => urlencode($module['tcp_send']), + 'snmp3_auth_user' => urlencode($module['plugin_user']), + 'snmp3_security_level' => urlencode($module['custom_string_3']), + 'snmp3_auth_method' => urlencode($module['plugin_parameters']), + 'snmp3_auth_pass' => urlencode($module['plugin_pass']), + 'snmp3_privacy_method' => urlencode($module['custom_string_1']), + 'snmp3_privacy_pass' => urlencode($module['custom_string_2']), + 'hide_header' => 1, + 'rel_path' => '../../', ]; // Incremental type if ($module['id_tipo_modulo'] == 16) { @@ -3456,9 +3471,13 @@ function get_module_realtime_link_graph($module) $link = substr($link, 0, -1); - $win_handle = 'realtime_'.dechex(crc32($module['id_agente_modulo'].$module['nombre'])); + $win_handle = 'realtime_'; + $win_handle .= dechex( + crc32($module['id_agente_modulo'].$module['nombre']) + ); - $link_button = ''.html_print_image( + $link_button = ''; + $link_button .= html_print_image( 'images/realtime_shortcut.png', true, [ @@ -3466,7 +3485,8 @@ function get_module_realtime_link_graph($module) 'alt' => '', 'title' => __('Realtime SNMP graph'), ] - ).''; + ); + $link_button .= ''; return $link_button; } diff --git a/pandora_console/include/functions_snmp_browser.php b/pandora_console/include/functions_snmp_browser.php index c4ef3b05ab..3f66c790ae 100644 --- a/pandora_console/include/functions_snmp_browser.php +++ b/pandora_console/include/functions_snmp_browser.php @@ -669,18 +669,29 @@ function snmp_browser_print_oid( /** - * Print the div that contains the SNMP browser. + * Print browser container. * - * @param bool return The result is printed if set to true or returned if set to false. - * @param string width Width of the SNMP browser. Units must be specified. - * @param string height Height of the SNMP browser. Units must be specified. - * @param string display CSS display value for the container div. Set to none to hide the div. + * @param boolean $return The result is printed + * if set to true or returned if set to false. + * @param string $width Width of the SNMP browser. + * Units must be specified. + * @param string $height Height of the SNMP browser. + * Units must be specified. + * @param string $display CSS display value for the + * container div. Set to none to hide the div. + * @param boolean $show_massive_buttons Massive buttons. * - * @return string The container div. + * @return string html. */ -function snmp_browser_print_container($return=false, $width='100%', $height='60%', $display='', $show_massive_buttons=false) -{ - // Target selection +function snmp_browser_print_container( + $return=false, + $width='100%', + $height='60%', + $display='', + $show_massive_buttons=false +) { + $snmp_version = get_parameter('snmp_browser_version', '2c'); + // Target selection. $table = new stdClass(); $table->width = '100%'; $table->class = 'databox filters'; @@ -754,12 +765,36 @@ function snmp_browser_print_container($return=false, $width='100%', $height='60% } } - $table->data[1][1] = ''.__('Server to execute').'   '; - $table->data[1][1] .= html_print_select($servers_to_exec, 'server_to_exec', '', '', '', '', true); + $table->data[1][1] = ''; + $table->data[1][1] .= __('Server to execute'); + $table->data[1][1] .= '   '; + $table->data[1][1] .= html_print_select( + $servers_to_exec, + 'server_to_exec', + '', + '', + '', + '', + true + ); - $table->data[1][2] = html_print_button(__('Browse'), 'browse', false, 'snmpBrowse()', 'class="sub search" style="margin-top:0px;"', true); + $table->data[1][2] = html_print_button( + __('Browse'), + 'browse', + false, + 'snmpBrowse()', + 'class="sub search" style="margin-top:0px;"', + true + ); + + // SNMP v3 options. + $snmp3_auth_user = get_parameter('snmp3_auth_user', ''); + $snmp3_security_level = get_parameter('snmp3_security_level', 'authNoPriv'); + $snmp3_auth_method = get_parameter('snmp3_auth_method', 'MD5'); + $snmp3_auth_pass = get_parameter('snmp3_auth_pass', ''); + $snmp3_privacy_method = get_parameter('snmp3_privacy_method', 'AES'); + $snmp3_privacy_pass = get_parameter('snmp3_privacy_pass', ''); - // SNMP v3 options $table3 = new stdClass(); $table3->width = '100%'; @@ -767,18 +802,69 @@ function snmp_browser_print_container($return=false, $width='100%', $height='60% $table3->valign[1] = ''; $table3->data[2][1] = ''.__('Auth user').''; - $table3->data[2][2] = html_print_input_text('snmp3_browser_auth_user', '', '', 15, 60, true); + $table3->data[2][2] = html_print_input_text( + 'snmp3_browser_auth_user', + $snmp3_auth_user, + '', + 15, + 60, + true + ); + $table3->data[2][3] = ''.__('Auth password').''; - $table3->data[2][4] = html_print_input_password('snmp3_browser_auth_pass', '', '', 15, 60, true); - $table3->data[2][4] .= html_print_input_hidden_extended('active_snmp_v3', 0, 'active_snmp_v3_fsb', true); + $table3->data[2][4] = html_print_input_password( + 'snmp3_browser_auth_pass', + $snmp3_auth_pass, + '', + 15, + 60, + true + ); + + $table3->data[2][4] .= html_print_input_hidden_extended( + 'active_snmp_v3', + 0, + 'active_snmp_v3_fsb', + true + ); $table3->data[5][0] = ''.__('Privacy method').''; - $table3->data[5][1] = html_print_select(['DES' => __('DES'), 'AES' => __('AES')], 'snmp3_browser_privacy_method', '', '', '', '', true); + $table3->data[5][1] = html_print_select( + [ + 'DES' => __('DES'), + 'AES' => __('AES'), + ], + 'snmp3_browser_privacy_method', + $snmp3_privacy_method, + '', + '', + '', + true + ); + $table3->data[5][2] = ''.__('Privacy pass').''; - $table3->data[5][3] = html_print_input_password('snmp3_browser_privacy_pass', '', '', 15, 60, true); + $table3->data[5][3] = html_print_input_password( + 'snmp3_browser_privacy_pass', + $snmp3_privacy_pass, + '', + 15, + 60, + true + ); $table3->data[6][0] = ''.__('Auth method').''; - $table3->data[6][1] = html_print_select(['MD5' => __('MD5'), 'SHA' => __('SHA')], 'snmp3_browser_auth_method', '', '', '', '', true); + $table3->data[6][1] = html_print_select( + [ + 'MD5' => __('MD5'), + 'SHA' => __('SHA'), + ], + 'snmp3_browser_auth_method', + $snmp3_auth_method, + '', + '', + '', + true + ); $table3->data[6][2] = ''.__('Security level').''; $table3->data[6][3] = html_print_select( [ @@ -787,29 +873,92 @@ function snmp_browser_print_container($return=false, $width='100%', $height='60% 'authPriv' => __('Auth and privacy method'), ], 'snmp3_browser_security_level', - '', + $snmp3_security_level, '', '', '', true ); - // Search tools + // Search tools. $table2 = new stdClass(); $table2->width = '100%'; $table2->class = 'databox filters'; $table2->size = []; $table2->data = []; - $table2->data[0][0] = html_print_input_text('search_text', '', '', 25, 0, true); - $table2->data[0][0] .= ''.html_print_image('images/zoom.png', true, ['title' => __('Search'), 'style' => 'vertical-align: middle;', 'onclick' => 'searchText();']).''; - $table2->data[0][1] = ' '.''.html_print_image('images/go_first.png', true, ['title' => __('First match'), 'style' => 'vertical-align: middle;', 'onclick' => 'searchFirstMatch();']).''; - $table2->data[0][1] .= ' '.''.html_print_image('images/go_previous.png', true, ['title' => __('Previous match'), 'style' => 'vertical-align: middle;', 'onclick' => 'searchPrevMatch();']).''; - $table2->data[0][1] .= ' '.''.html_print_image('images/go_next.png', true, ['title' => __('Next match'), 'style' => 'vertical-align: middle;', 'onclick' => 'searchNextMatch();']).''; - $table2->data[0][1] .= ' '.''.html_print_image('images/go_last.png', true, ['title' => __('Last match'), 'style' => 'vertical-align: middle;', 'onclick' => 'searchLastMatch();']).''; + $table2->data[0][0] = html_print_input_text( + 'search_text', + '', + '', + 25, + 0, + true + ); + $table2->data[0][0] .= ''; + $table2->data[0][0] .= html_print_image( + 'images/zoom.png', + true, + [ + 'title' => __('Search'), + 'style' => 'vertical-align: middle;', + 'onclick' => 'searchText();', + ] + ); + $table2->data[0][0] .= ''; + + $table2->data[0][1] = ' '; + $table2->data[0][1] .= ''; + $table2->data[0][1] .= html_print_image( + 'images/go_first.png', + true, + [ + 'title' => __('First match'), + 'style' => 'vertical-align: middle;', + 'onclick' => 'searchFirstMatch();', + ] + ); + $table2->data[0][1] .= ''; + $table2->data[0][1] .= ' '; + $table2->data[0][1] .= ''; + $table2->data[0][1] .= html_print_image( + 'images/go_previous.png', + true, + [ + 'title' => __('Previous match'), + 'style' => 'vertical-align: middle;', + 'onclick' => 'searchPrevMatch();', + ] + ); + $table2->data[0][1] .= ''; + $table2->data[0][1] .= ' '; + $table2->data[0][1] .= ''; + $table2->data[0][1] .= html_print_image( + 'images/go_next.png', + true, + [ + 'title' => __('Next match'), + 'style' => 'vertical-align: middle;', + 'onclick' => 'searchNextMatch();', + ] + ); + $table2->data[0][1] .= ''; + $table2->data[0][1] .= ' '; + $table2->data[0][1] .= ''; + $table2->data[0][1] .= html_print_image( + 'images/go_last.png', + true, + [ + 'title' => __('Last match'), + 'style' => 'vertical-align: middle;', + 'onclick' => 'searchLastMatch();', + ] + ); + $table2->data[0][1] .= ''; $table2->cellstyle[0][1] = 'text-align:center;'; - $table2->data[0][2] = ' '.''.html_print_image( + $table2->data[0][2] = ' '; + $table2->data[0][2] .= ''.html_print_image( 'images/expand.png', true, [ @@ -817,8 +966,20 @@ function snmp_browser_print_container($return=false, $width='100%', $height='60% 'style' => 'vertical-align: middle;', 'onclick' => 'expandAll();', ] - ).''; - $table2->data[0][2] .= ' '.''.html_print_image('images/collapse.png', true, ['title' => __('Collapse the tree'), 'style' => 'vertical-align: middle;', 'onclick' => 'collapseAll();']).''; + ); + $table2->data[0][2] .= ''; + $table2->data[0][2] .= ' '; + $table2->data[0][2] .= ''; + $table2->data[0][2] .= html_print_image( + 'images/collapse.png', + true, + [ + 'title' => __('Collapse the tree'), + 'style' => 'vertical-align: middle;', + 'onclick' => 'collapseAll();', + ] + ); + $table2->data[0][2] .= ''; $table2->cellstyle[0][2] = 'text-align:center;'; // This extra div that can be handled by jquery's dialog. @@ -828,7 +989,7 @@ function snmp_browser_print_container($return=false, $width='100%', $height='60% $output .= html_print_table($table, true); $output .= ''; - if (!isset($snmp_version)) { + if (isset($snmp_version) === false) { $snmp_version = null; } @@ -838,40 +999,61 @@ function snmp_browser_print_container($return=false, $width='100%', $height='60% $output .= ''; $output .= '
'; - $output .= ui_toggle(html_print_table($table2, true), __('Search options'), '', '', true, true); + $output .= ui_toggle( + html_print_table($table2, true), + __('Search options'), + '', + '', + true, + true + ); $output .= '
'; - // SNMP tree container + // SNMP tree container. $output .= '
'; $output .= html_print_input_hidden('search_count', 0, true); $output .= html_print_input_hidden('search_index', -1, true); - // Save some variables for javascript functions - $output .= html_print_input_hidden('ajax_url', ui_get_full_url('ajax.php'), true); - $output .= html_print_input_hidden('search_matches_translation', __('Search matches'), true); + // Save some variables for javascript functions. + $output .= html_print_input_hidden( + 'ajax_url', + ui_get_full_url('ajax.php'), + true + ); + $output .= html_print_input_hidden( + 'search_matches_translation', + __('Search matches'), + true + ); $output .= ''; $output .= ''; $output .= '
'; $output .= '
'; - $output .= '
'; + $output .= ''; $output .= '
'; $output .= ''; $output .= ''; if ($show_massive_buttons) { $output .= ''; + $output .= ''; } if ($return) { diff --git a/pandora_console/operation/agentes/realtime_win.php b/pandora_console/operation/agentes/realtime_win.php index 93c8c85ea2..dbf9cf9f71 100644 --- a/pandora_console/operation/agentes/realtime_win.php +++ b/pandora_console/operation/agentes/realtime_win.php @@ -1,18 +1,31 @@ '; echo ''; - ui_print_error_message(__('There was a problem connecting with the node')); + ui_print_error_message( + __('There was a problem connecting with the node') + ); echo ''; echo ''; exit; @@ -43,7 +58,9 @@ if (is_metaconsole() && !empty($server_id)) { $user_language = get_user_language($config['id_user']); if (file_exists('../../include/languages/'.$user_language.'.mo')) { - $l10n = new gettext_reader(new CachedFileReader('../../include/languages/'.$user_language.'.mo')); + $l10n = new gettext_reader( + new CachedFileReader('../../include/languages/'.$user_language.'.mo') + ); $l10n->load_tables(); } @@ -53,7 +70,7 @@ echo ' 0) { $query = ui_get_url_refresh(false); @@ -70,7 +87,7 @@ echo ' Date: Mon, 30 Nov 2020 14:45:13 +0100 Subject: [PATCH 126/263] Modified query for show only available alerts --- pandora_console/include/functions_tactical.php | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/pandora_console/include/functions_tactical.php b/pandora_console/include/functions_tactical.php index 4e29dda423..7be3771f8f 100644 --- a/pandora_console/include/functions_tactical.php +++ b/pandora_console/include/functions_tactical.php @@ -427,6 +427,8 @@ function tactical_monitor_alerts($group_array, $strict_user=false, $id_group_str $sql = "SELECT COUNT(talert_template_modules.id) FROM talert_template_modules, tagente_modulo, tagente_estado, tagente WHERE tagente.id_grupo IN $group_clause_strict AND tagente_modulo.id_agente = tagente.id_agente + AND tagente.disabled = 0 + AND talert_template_modules.disabled = 0 AND tagente_estado.id_agente_modulo = tagente_modulo.id_agente_modulo AND talert_template_modules.id_agent_module = tagente_modulo.id_agente_modulo"; $count = db_get_sql($sql); @@ -437,6 +439,8 @@ function tactical_monitor_alerts($group_array, $strict_user=false, $id_group_str "SELECT COUNT(talert_template_modules.id) FROM talert_template_modules, tagente_modulo, tagente_estado, tagente WHERE tagente.id_grupo IN $group_clause AND tagente_modulo.id_agente = tagente.id_agente + AND tagente.disabled = 0 + AND talert_template_modules.disabled = 0 AND tagente_estado.id_agente_modulo = tagente_modulo.id_agente_modulo AND talert_template_modules.id_agent_module = tagente_modulo.id_agente_modulo" ); From 734341bf2d1c57321ff7741cfe43de7794f7b5bb Mon Sep 17 00:00:00 2001 From: Luis Date: Mon, 30 Nov 2020 14:52:03 +0100 Subject: [PATCH 127/263] Ent 6148 new report permissions --- .../reporting_builder.item_editor.php | 196 ++++++++++++++++-- .../godmode/reporting/reporting_builder.php | 16 ++ .../godmode/users/configure_user.php | 2 + pandora_console/godmode/users/user_list.php | 47 +++++ pandora_console/include/constants.php | 3 + pandora_console/include/functions_html.php | 60 ++++-- .../include/functions_reporting.php | 129 ++++++++++++ .../include/functions_reporting_html.php | 118 +++++++++++ pandora_console/include/functions_reports.php | 31 +-- pandora_console/include/functions_users.php | 33 ++- .../javascript/multiselect_filtered.js | 8 +- .../include/styles/multiselect_filtered.css | 19 +- pandora_console/include/styles/pandora.css | 7 + 13 files changed, 613 insertions(+), 56 deletions(-) diff --git a/pandora_console/godmode/reporting/reporting_builder.item_editor.php b/pandora_console/godmode/reporting/reporting_builder.item_editor.php index c2dc30cf22..c635773ce7 100755 --- a/pandora_console/godmode/reporting/reporting_builder.item_editor.php +++ b/pandora_console/godmode/reporting/reporting_builder.item_editor.php @@ -156,6 +156,11 @@ $agent_max_value = true; $agent_min_value = true; $uncompressed_module = true; +// Users. +$id_users = []; +$users_groups = []; +$select_by_group = false; + $nothing = __('Local metaconsole'); $nothing_value = 0; @@ -752,6 +757,22 @@ switch ($action) { $top_n_value = $item['top_n_value']; break; + case 'permissions_report': + $description = $item['description']; + $es = json_decode($item['external_source'], true); + $id_users = array_combine( + array_values($es['id_users']), + array_values($es['id_users']) + ); + + if (isset($id_users[0]) && $id_users[0] == 0) { + $id_users[0] = __('None'); + } + + $users_groups = $es['users_groups']; + $select_by_group = $es['select_by_group']; + break; + default: // It's not possible. break; @@ -1046,23 +1067,6 @@ $class = 'databox filters'; -
- - - - + + + + + + + + + + + + + + + +
- - - -
+ + + +
+ + + $user) { + $select_users[$user['id_user']] = $user['id_user']; + } + + $input_data = [ + 'type' => 'select_multiple_filtered', + 'class' => 'w80p mw600px', + 'name' => 'id_users', + 'return' => 0, + 'available' => array_diff( + $select_users, + $id_users + ), + 'selected' => $id_users, + 'group_filter' => [ + 'page' => 'godmode/users/user_list', + 'method' => 'get_users_by_group', + 'nothing' => __('Unnasigned group'), + 'nothing_value' => -1, + 'id' => $id_users, + ], + 'texts' => [ + 'title-left' => 'Available users', + 'title-right' => 'Selected users', + 'filter-item' => 'Filter user name', + ], + 'sections' => [ + 'filters' => 1, + 'item-selected-filters' => 0, + ], + ]; + + html_print_input($input_data, 'div', true); + ?> +
+ + + +
".__('Please checked a custom interval option.').'

'; echo ''; +echo "'; + +echo "'; + ui_require_javascript_file( 'pandora_inventory', ENTERPRISE_DIR.'/include/javascript/' @@ -3936,6 +4043,20 @@ $(document).ready (function () { }); }); + $("#checkbox-select_by_group").change(function () { + var select_by_group = $('#checkbox-select_by_group').prop('checked'); + + if(select_by_group == true) { + $("#row_users").hide(); + $("#row_profiles_group").show(); + + } else { + $("#row_users").show(); + $("#row_profiles_group").hide(); + + } + }); + $("#checkbox-fullscale").change(function(e){ if(e.target.checked === true) { $("#graph_render").prop('disabled', 'disabled'); @@ -3999,6 +4120,16 @@ $(document).ready (function () { return false; } break; + case 'permissions_report': + if ($("#checkbox-select_by_group").prop("checked") && $("select#users_groups>option:selected").val() == undefined) { + dialog_message('#message_no_group'); + return false; + } + if ($("#checkbox-select_by_group").prop("checked") == false && $("select#selected-select-id_users>option:selected").val() == 0) { + dialog_message('#message_no_user'); + return false; + } + break; default: break; } @@ -4120,6 +4251,18 @@ $(document).ready (function () { return false; } break; + + case 'permissions_report': + if ($("#checkbox-select_by_group").prop("checked") && $("select#users_groups>option:selected").val() == undefined) { + dialog_message('#message_no_group'); + return false; + } + if ($("#checkbox-select_by_group").prop("checked") == false && $("select#selected-select-id_users>option:selected").val() == 0) { + dialog_message('#message_no_user'); + return false; + } + break; + default: break; } @@ -5006,6 +5149,10 @@ function chooseType() { $("#row_select_fields2").hide(); $("#row_select_fields3").hide(); $("#row_uncompressed_module").hide(); + $("#row_users").hide(); + $("#row_profiles_group").hide(); + $("#row_select_by_group").hide(); + // SLA list default state. $("#sla_list").hide(); @@ -5690,7 +5837,21 @@ function chooseType() { $("#row_period").show(); $("#row_quantity").show(); break; + + case 'permissions_report': + $("#row_description").show(); + $("#row_users").show(); + $("#row_profiles_group").show(); + $("#row_select_by_group").show(); + + if($("#checkbox-select_by_group").prop("checked")) { + $("#row_users").hide(); + } else { + $("#row_profiles_group").hide(); + } + } + switch (type) { case 'event_report_agent': case 'simple_graph': @@ -5779,4 +5940,5 @@ function dialog_message(message_id) { } }); } + diff --git a/pandora_console/godmode/reporting/reporting_builder.php b/pandora_console/godmode/reporting/reporting_builder.php index 724be2b1d5..a550e9768a 100755 --- a/pandora_console/godmode/reporting/reporting_builder.php +++ b/pandora_console/godmode/reporting/reporting_builder.php @@ -2054,6 +2054,14 @@ switch ($action) { } break; + case 'permissions_report': + $es['id_users'] = get_parameter('selected-select-id_users', 0); + $es['users_groups'] = get_parameter('users_groups', 0); + $es['select_by_group'] = get_parameter('select_by_group', 0); + $description = get_parameter('description'); + $values['external_source'] = json_encode($es); + break; + default: // Default. break; @@ -2669,6 +2677,14 @@ switch ($action) { } break; + case 'permissions_report': + $es['id_users'] = get_parameter('selected-select-id_users'); + $es['users_groups'] = get_parameter('users_groups', 0); + $es['select_by_group'] = get_parameter('select_by_group', 0); + $description = get_parameter('description'); + $values['external_source'] = json_encode($es); + break; + default: // Default. break; diff --git a/pandora_console/godmode/users/configure_user.php b/pandora_console/godmode/users/configure_user.php index d13e9bc227..f9618b64a1 100644 --- a/pandora_console/godmode/users/configure_user.php +++ b/pandora_console/godmode/users/configure_user.php @@ -196,6 +196,8 @@ if (is_ajax()) { } } + + $tab = get_parameter('tab', 'user'); if ($id) { diff --git a/pandora_console/godmode/users/user_list.php b/pandora_console/godmode/users/user_list.php index 783c22c8af..77ca07acce 100644 --- a/pandora_console/godmode/users/user_list.php +++ b/pandora_console/godmode/users/user_list.php @@ -33,6 +33,53 @@ if (! check_acl($config['id_user'], 0, 'UM')) { exit; } +if (is_ajax()) { + $method = get_parameter('method'); + $group_id = get_parameter('group_id'); + $group_recursion = (bool) get_parameter('group_recursion', 0); + $return_all = false; + + if ($group_id == -1) { + $sql = 'SELECT tusuario.id_user FROM tusuario + LEFT OUTER JOIN tusuario_perfil + ON tusuario.id_user = tusuario_perfil.id_usuario + WHERE tusuario_perfil.id_usuario IS NULL'; + + $users = io_safe_output(db_get_all_rows_sql($sql)); + + foreach ($users as $key => $user) { + $ret_user[$user['id_user']] = $user['id_user']; + } + + echo json_encode($ret_user); + return; + } + + if ($group_id == 0) { + $users = io_safe_output(db_get_all_rows_filter('tusuario', [], 'id_user')); + foreach ($users as $key => $user) { + $ret_user[$user['id_user']] = $user['id_user']; + } + + echo json_encode($ret_user); + return; + } + + if ($method === 'get_users_by_group') { + if ($group_recursion === true) { + $group_id = groups_get_children_ids($group_id); + } + + $users_id = io_safe_output(users_get_user_users(false, 'AR', false, null, $group_id)); + foreach ($users_id as $key => $user_id) { + $ret_id[$user_id] = $user_id; + } + + echo json_encode($ret_id); + return; + } +} + $sortField = get_parameter('sort_field'); $sort = get_parameter('sort', 'none'); $tab = get_parameter('tab', 'user'); diff --git a/pandora_console/include/constants.php b/pandora_console/include/constants.php index 258b6e805c..66b784d712 100644 --- a/pandora_console/include/constants.php +++ b/pandora_console/include/constants.php @@ -457,6 +457,9 @@ define('REPORT_OLD_TYPE_SUMATORY', 10); define('REPORT_GENERAL_NOT_GROUP_BY_AGENT', 0); define('REPORT_GENERAL_GROUP_BY_AGENT', 1); +define('REPORT_PERMISSIONS_NOT_GROUP_BY_GROUP', 0); +define('REPORT_PERMISSIONS_GROUP_BY_GROUP', 1); + define('REPORTING_CUSTOM_GRAPH_LEGEND_EACH_MODULE_VERTICAL_SIZE', 15); // POLICIES. diff --git a/pandora_console/include/functions_html.php b/pandora_console/include/functions_html.php index bda3a759e0..5929d25069 100644 --- a/pandora_console/include/functions_html.php +++ b/pandora_console/include/functions_html.php @@ -913,28 +913,45 @@ function html_print_select_multiple_filtered( $sections = []; } - if (empty($sections['filters']) === true) { + if (isset($sections['filters']) === false) { $sections['filters'] = 1; } - if (empty($sections['group-filter']) === true) { + // Show/hide all left/rigth sfilters. + if (isset($sections['item-selected-filters']) === false) { + $sections['item-selected-filters'] = 1; + } + + if (isset($sections['item-available-filters']) === false) { + $sections['item-available-filters'] = 1; + } + + if (isset($sections['group-filter']) == false) { $sections['group-filter'] = 1; } - if (empty($sections['item-available-filter']) === true) { + if (isset($sections['item-available-filter']) === false) { $sections['item-available-filter'] = 1; } - if (empty($sections['item-selected-filter']) === true) { + if (isset($sections['item-selected-filter']) === false) { $sections['item-selected-filter'] = 1; } - if (empty($group_filter) === true) { + if (isset($group_filter) === false) { $sections['group-filter'] = 0; } + if (isset($group_filter['nothing']) === false) { + $group_filter['nothing'] = ''; + } + + if (isset($group_filter['nothing_value']) === false) { + $group_filter['nothing_value'] = 0; + } + // Main container. - $output = '
'; + $output = '
'; // Left box. $output .= '
'; @@ -943,6 +960,7 @@ function html_print_select_multiple_filtered( // Filtering. if (isset($sections['filters']) === true && $sections['filters'] === 1 + && $sections['item-available-filters'] === 1 ) { // Filtering. if (isset($sections['group-filter']) === true @@ -962,6 +980,7 @@ function html_print_select_multiple_filtered( $output .= html_print_input( [ + 'input_class' => 'flex-row-vcenter', 'label' => __('Filter group'), 'name' => 'id-group-available-select-'.$rid, 'returnAllGroup' => true, @@ -969,16 +988,20 @@ function html_print_select_multiple_filtered( 'type' => 'select_groups', 'return' => true, 'script' => $reload_content, + 'nothing' => $group_filter['nothing'], + 'nothing_value' => $group_filter['nothing_value'], ] ); $output .= html_print_input( [ - 'label' => __('Group recursion'), - 'name' => 'id-group-recursion-available-select-'.$rid, - 'type' => 'checkbox', - 'script' => $reload_content, - 'return' => true, + 'label' => __('Group recursion'), + 'input_class' => 'flex-row-vcenter', + 'name' => 'id-group-recursion-available-select-'.$rid, + 'id' => 'checkbox-id-group-recursion-available-select-'.$rid, + 'type' => 'switch', + 'onchange' => $reload_content, + 'return' => true, ] ); @@ -1087,6 +1110,7 @@ function html_print_select_multiple_filtered( // Filtering. if (isset($sections['filters']) === true && $sections['filters'] === 1 + && $sections['item-selected-filters'] ) { if (isset($sections['group-filter']) === true && $sections['group-filter'] === 1 @@ -1105,6 +1129,7 @@ function html_print_select_multiple_filtered( $output .= html_print_input( [ + 'input_class' => 'flex-row-vcenter', 'label' => __('Filter group'), 'name' => 'id-group-selected-select-'.$rid, 'returnAllGroup' => true, @@ -1117,11 +1142,12 @@ function html_print_select_multiple_filtered( $output .= html_print_input( [ - 'label' => __('Group recursion'), - 'name' => 'id-group-recursion-selected-select-'.$rid, - 'type' => 'checkbox', - 'script' => $reload_content, - 'return' => true, + 'input_class' => 'flex-row-vcenter', + 'label' => __('Group recursion'), + 'name' => 'id-group-recursion-selected-select-'.$rid, + 'type' => 'checkbox', + 'script' => $reload_content, + 'return' => true, ] ); @@ -1152,7 +1178,7 @@ function html_print_select_multiple_filtered( 'label' => __($texts['filter-item']), 'name' => 'filter-item-selected-'.$rid, 'onKeyUp' => $f, - 'input_class' => 'filter w100p', + 'input_class' => 'flex-row-vcenter filter w100p', 'size' => 20, 'type' => 'text', 'return' => true, diff --git a/pandora_console/include/functions_reporting.php b/pandora_console/include/functions_reporting.php index 479adc67c8..3d72dcb5ca 100755 --- a/pandora_console/include/functions_reporting.php +++ b/pandora_console/include/functions_reporting.php @@ -694,6 +694,13 @@ function reporting_make_reporting_data( ); break; + case 'permissions_report': + $report['contents'][] = reporting_permissions( + $report, + $content + ); + break; + case 'group_report': $report['contents'][] = reporting_group_report( $report, @@ -3577,6 +3584,128 @@ function reporting_last_value($report, $content) } +function reporting_permissions($report, $content) +{ + global $config; + + $return['type'] = 'permissions_report'; + + if (empty($content['name'])) { + $content['name'] = __('Permissions report'); + } + + $return['title'] = $content['name']; + $return['landscape'] = $content['landscape']; + $return['pagebreak'] = $content['pagebreak']; + $return['description'] = $content['description']; + $return['date'] = reporting_get_date_text($report, $content); + + $external_source = io_safe_input(json_decode($content['external_source'], true)); + $id_users = $external_source['id_users']; + $id_groups = $external_source['users_groups']; + + // Select subtype: group by user or by group. + $select_by_group = $external_source['select_by_group']; + $return['subtype'] = (int) $select_by_group; + + if ($select_by_group === REPORT_PERMISSIONS_NOT_GROUP_BY_GROUP) { + foreach ($id_users as $id_user) { + $user_name = db_get_value_filter('fullname', 'tusuario', ['id_user' => $id_user]); + + $sql = sprintf( + "SELECT name, id_grupo FROM tusuario_perfil + INNER JOIN tperfil ON tperfil.id_perfil = tusuario_perfil.id_perfil + WHERE tusuario_perfil.id_usuario like '%s'", + $id_user + ); + + $profiles = db_get_all_rows_sql($sql); + + $user_profiles = []; + if (empty($profiles)) { + $user_profiles[] = __('The user doesn\'t have any assigned profile/group'); + } else { + foreach ($profiles as $user_profile) { + $user_profiles[] = $user_profile['name'].' / '.groups_get_name($user_profile['id_grupo'], true); + } + } + + $data[] = [ + 'user_id' => io_safe_output($id_user), + 'user_name' => io_safe_output($user_name), + 'user_profiles' => io_safe_output($user_profiles), + ]; + } + } else { + $group_users = []; + foreach ($id_groups as $id_group) { + if ($id_group === '') { + $group_name = __('Unnasigned group'); + $sql = 'SELECT tusuario.id_user, tusuario.fullname FROM tusuario + LEFT OUTER JOIN tusuario_perfil + ON tusuario.id_user = tusuario_perfil.id_usuario + WHERE tusuario_perfil.id_usuario IS NULL'; + $group_users = db_get_all_rows_sql($sql); + $group_name = __('Unassigned group'); + } else { + $group_name = groups_get_name($id_group, true); + $group_users = users_get_user_users( + $id_group, + 'AR', + false, + [ + 'id_user', + 'fullname', + + ], + [$id_group] + ); + } + + $row['users'] = []; + foreach ($group_users as $user) { + $id_user = $user['id_user']; + + // Get user fullanme. + $row['users'][$id_user]['fullname'] = $user['fullname']; + + if ($id_group === '') { + $row['users'][$id_user]['profiles'][] = __('The user doesn\'t have any assigned profile/group'); + } else { + // Incluide id group = 0 for 'All' profile. + $sql = sprintf( + "SELECT id_perfil FROM tusuario_perfil + WHERE id_usuario LIKE '%s' + AND ( + id_grupo LIKE '%s' + OR id_grupo LIKE '0' + )", + $id_user, + $id_group + ); + + $user_profiles_id = db_get_all_rows_sql($sql); + + foreach ($user_profiles_id as $profile_id) { + $row['users'][$id_user]['profiles'][] = profile_get_name($profile_id['id_perfil']); + } + } + } + + $data[] = [ + 'group_name' => $group_name, + 'users' => $row['users'], + ]; + } + } + + $return['data'] = []; + $return['data'] = $data; + + return reporting_check_structure_content($return); +} + + function reporting_group_configuration($report, $content) { global $config; diff --git a/pandora_console/include/functions_reporting_html.php b/pandora_console/include/functions_reporting_html.php index c501c8526f..48a09385d5 100644 --- a/pandora_console/include/functions_reporting_html.php +++ b/pandora_console/include/functions_reporting_html.php @@ -236,6 +236,10 @@ function reporting_html_print_report($report, $mini=false, $report_info=1) reporting_html_log($table, $item); break; + case 'permissions_report': + reporting_html_permissions($table, $item); + break; + case 'availability_graph': reporting_html_availability_graph($table, $item); break; @@ -5649,3 +5653,117 @@ function reporting_html_planned_downtimes_table($planned_downtimes) return $downtimes_table; } + + +function reporting_html_permissions($table, $item, $pdf=0) +{ + global $config; + + $table1 = new stdClass(); + $table1->width = '100%'; + + $table1->style[0] = 'text-align: left;vertical-align: top;min-width: 100px;'; + $table1->class = 'databox data'; + $table1->cellpadding = 1; + $table1->cellspacing = 1; + $table1->styleTable = 'overflow: wrap; table-layout: fixed;'; + + if ($item['subtype'] === REPORT_PERMISSIONS_NOT_GROUP_BY_GROUP) { + $table1->style[0] = 'text-align: left;vertical-align: top;min-width: 100px;'; + $table1->style[1] = 'text-align: left;vertical-align: top;min-width: 100px;'; + $table1->style[2] = 'text-align: left;vertical-align: top; min-width: 100px'; + + $table1->head = [ + __('User ID'), + __('Full name'), + __('Permissions'), + ]; + + $table1->headstyle[0] = 'text-align: left'; + $table1->headstyle[1] = 'text-align: left'; + $table1->headstyle[2] = 'text-align: left'; + } + + if ($item['subtype'] === REPORT_PERMISSIONS_GROUP_BY_GROUP) { + $table1->style[0] = 'text-align: left;vertical-align: top;min-width: 150px;'; + $table1->style[1] = 'text-align: left;vertical-align: top;min-width: 150px;'; + $table1->style[2] = 'text-align: left;vertical-align: top;min-width: 150px;'; + $table1->style[3] = 'text-align: left;vertical-align: top;min-width: 150px;'; + + $table1->headstyle[0] = 'text-align: left'; + $table1->headstyle[1] = 'text-align: left'; + $table1->headstyle[2] = 'text-align: left'; + $table1->headstyle[3] = 'text-align: left'; + + $table1->head = [ + __('Group'), + __('User ID'), + __('Full name'), + __('Permissions'), + ]; + } + + $table1->data = []; + + foreach ($item['data'] as $data) { + if ($item['subtype'] === REPORT_PERMISSIONS_NOT_GROUP_BY_GROUP) { + $profile_group_name = ''; + foreach ($data['user_profiles'] as $user_profile) { + $profile_group_name .= $user_profile.'
'; + } + + $row = [ + $data['user_id'], + $data['user_name'], + $profile_group_name, + ]; + } + + if ($item['subtype'] === REPORT_PERMISSIONS_GROUP_BY_GROUP) { + $user_profile_id_users = ''; + $user_profile_name = ''; + $user_profile_users_name = ''; + $group_name = $data['group_name'].'
'; + + foreach ($data['users'] as $user => $user_data) { + $user_profile_id_users .= $user.'
'; + $user_profile_users_name .= $user_data['fullname'].'
'; + + foreach ($user_data['profiles'] as $profile) { + $user_profile_id_users .= '
'; + $user_profile_users_name .= '
'; + $user_profile_name .= $profile.'
'; + } + + $user_profile_name .= '
'; + } + + $row = [ + $group_name, + $user_profile_id_users, + $user_profile_users_name, + $user_profile_name, + ]; + } + + $table1->data[] = $row; + + if ($pdf !== 0) { + $table1->data[] = '
'; + } + } + + if ($pdf === 0) { + $table->colspan['permissions']['cell'] = 3; + $table->cellstyle['permissions']['cell'] = 'text-align: center;'; + $table->data['permissions']['cell'] = html_print_table( + $table1, + true + ); + } else { + return html_print_table( + $table1, + true + ); + } +} diff --git a/pandora_console/include/functions_reports.php b/pandora_console/include/functions_reports.php index 130fe4deb0..878ded7968 100755 --- a/pandora_console/include/functions_reports.php +++ b/pandora_console/include/functions_reports.php @@ -54,7 +54,7 @@ function reports_get_report($id_report, $filter=false, $fields=false) return false; } - if (! is_array($filter)) { + if (!is_array($filter)) { $filter = []; } @@ -72,7 +72,7 @@ function reports_get_report($id_report, $filter=false, $fields=false) $report = db_get_row_filter('treport', $filter, $fields); - if (! check_acl($config['id_user'], $report['id_group'], 'RR')) { + if (!check_acl($config['id_user'], $report['id_group'], 'RR')) { return false; } @@ -104,16 +104,16 @@ function reports_get_reports( ) { global $config; - if (! is_array($filter)) { + if (!is_array($filter)) { $filter = []; } - /* - if (!is_user_admin ($config["id_user"])) + /* + if (!is_user_admin ($config["id_user"])) $filter[] = sprintf ('private = 0 OR (private = 1 AND id_user = "%s")', $config['id_user']); - */ - $filter['hidden'] = 0; + */ + $filter['hidden'] = 0; if (is_array($fields)) { $fields[] = 'id_group'; $fields[] = 'id_user'; @@ -146,7 +146,7 @@ function reports_get_reports( } if ($config['id_user'] != $report['id_user'] - && ! check_acl($config['id_user'], $report['id_group'], $privileges) + && !check_acl($config['id_user'], $report['id_group'], $privileges) ) { continue; } @@ -173,7 +173,7 @@ function reports_create_report($name, $id_group, $values=false) { global $config; - if (! is_array($values)) { + if (!is_array($values)) { $values = []; } @@ -246,7 +246,7 @@ function reports_get_content($id_report_content, $filter=false, $fields=false) return false; } - if (! is_array($filter)) { + if (!is_array($filter)) { $filter = []; } @@ -293,7 +293,7 @@ function reports_create_content($id_report, $values) return false; } - if (! is_array($values)) { + if (!is_array($values)) { return false; } @@ -341,7 +341,7 @@ function reports_get_contents($id_report, $filter=false, $fields=false) return []; } - if (! is_array($filter)) { + if (!is_array($filter)) { $filter = []; } @@ -546,7 +546,7 @@ function reports_delete_content($id_report_content) function get_report_name($type, $template=false) { $types = reports_get_report_types($template); - if (! isset($types[$type])) { + if (!isset($types[$type])) { return __('Unknown'); } @@ -890,5 +890,10 @@ function reports_get_report_types($template=false, $not_editor=false) ]; } + $types['permissions_report'] = [ + 'optgroup' => __('Permissions report'), + 'name' => __('Permissions report'), + ]; + return $types; } diff --git a/pandora_console/include/functions_users.php b/pandora_console/include/functions_users.php index 7b310889ac..73e7340838 100755 --- a/pandora_console/include/functions_users.php +++ b/pandora_console/include/functions_users.php @@ -616,7 +616,8 @@ function users_get_user_users( $id_user=false, $privilege='AR', $returnAllGroup=true, - $fields=null + $fields=null, + $filter_group=[] ) { global $config; @@ -625,8 +626,12 @@ function users_get_user_users( $user_users = []; $array_user_group = []; - foreach ($user_groups as $id_user_group => $name_user_group) { - $array_user_group[] = $id_user_group; + if (empty($filter_group)) { + foreach ($user_groups as $id_user_group => $name_user_group) { + $array_user_group[] = $id_user_group; + } + } else { + $array_user_group = $filter_group; } $group_users = groups_get_users($array_user_group, false, $returnAllGroup); @@ -831,3 +836,25 @@ function users_get_user_profile($id_user) return $user_profiles; } + + +/** + * Obtiene una matriz con la informacion de cada usuario que pertenece a un grupo + * + * @param string User id + * @return array Return . + */ +function users_get_users_group_by_group($id_group) +{ + $sql = sprintf( + "SELECT tusuario.* FROM tusuario + LEFT JOIN tusuario_perfil ON tusuario_perfil.id_usuario = tusuario.id_user + AND tusuario_perfil.id_grupo = '%s' + GROUP BY tusuario_perfil.id_usuario", + $id_group + ); + + $users = db_get_all_rows_sql($sql); + + return $users; +} diff --git a/pandora_console/include/javascript/multiselect_filtered.js b/pandora_console/include/javascript/multiselect_filtered.js index 27135c6843..7976158740 100644 --- a/pandora_console/include/javascript/multiselect_filtered.js +++ b/pandora_console/include/javascript/multiselect_filtered.js @@ -113,7 +113,9 @@ function reloadContent(id, url, options, side, noneStr) { data.side = side; data.group_recursion = $("#checkbox-id-group-recursion-" + current).prop( "checked" - ); + ) + ? 1 + : 0; data.group_id = $("#id-group-" + current).val(); $.ajax({ @@ -139,8 +141,8 @@ function reloadContent(id, url, options, side, noneStr) { for (var [value, label] of items) { if ( - $("#" + opposite + " option[value=" + value + "]").length == 0 && - $("#tmp-" + current + " option[value=" + value + "]").length == 0 + $("#" + opposite + " option[value='" + value + "']").length == 0 && + $("#tmp-" + current + " option[value='" + value + "']").length == 0 ) { // Does not exist in opposite box nor is filtered. $("#" + current).append(new Option(label, value)); diff --git a/pandora_console/include/styles/multiselect_filtered.css b/pandora_console/include/styles/multiselect_filtered.css index a938422456..c1eebbe657 100644 --- a/pandora_console/include/styles/multiselect_filtered.css +++ b/pandora_console/include/styles/multiselect_filtered.css @@ -2,9 +2,6 @@ flex: 1 1 200px; min-width: 200px; } -.multi-select-container * { - flex: 1 1 auto; -} .multi-select.flex-column { justify-content: space-between; @@ -16,10 +13,12 @@ .multi-select-container.flex-column { flex-wrap: nowrap; + align-self: flex-end; } .arrows-container { width: 50px; + margin-bottom: 65px; } .multi-select-container .filter input, @@ -40,8 +39,11 @@ .group-filter.flex-row-vcenter div, .item-filter.flex-row-vcenter div { + display: flex; flex: 1 1 auto; width: 100%; + justify-content: space-between; + align-items: center; } .multi-select-container .title { @@ -58,3 +60,14 @@ .multi-select-container.flex-column div input { margin-left: 1em; } + +.multi-select-container + .select2-container--default + .select2-selection--single + .select2-selection__rendered { + line-height: inherit !important; +} + +.p-slider { + margin: auto !important; +} diff --git a/pandora_console/include/styles/pandora.css b/pandora_console/include/styles/pandora.css index d8b4777fbb..bc743e9c77 100644 --- a/pandora_console/include/styles/pandora.css +++ b/pandora_console/include/styles/pandora.css @@ -612,6 +612,13 @@ select:-internal-list-box { align-items: center; } +.flex-row-end { + display: flex; + flex-direction: row; + flex-wrap: wrap; + align-items: flex-end; +} + .flex-space-around { justify-content: space-around; } From 54d5529599e7b28b34268dba004cd2dca21e2dfd Mon Sep 17 00:00:00 2001 From: fbsanchez Date: Mon, 30 Nov 2020 17:50:36 +0100 Subject: [PATCH 128/263] removed duplicated methods (unified in HTML class) added minor improvements --- .../godmode/wizards/Wizard.main.php | 501 +----------------- pandora_console/include/class/HTML.class.php | 36 +- pandora_console/include/functions_ui.php | 7 +- pandora_console/include/styles/wizard.css | 18 + 4 files changed, 64 insertions(+), 498 deletions(-) diff --git a/pandora_console/godmode/wizards/Wizard.main.php b/pandora_console/godmode/wizards/Wizard.main.php index 9af64a7c39..2ca0373687 100644 --- a/pandora_console/godmode/wizards/Wizard.main.php +++ b/pandora_console/godmode/wizards/Wizard.main.php @@ -27,7 +27,12 @@ */ // Begin. +global $config; +require_once $config['homedir'].'/vendor/autoload.php'; +require_once $config['homedir'].'/include/class/HTML.class.php'; + +use \HTML; /** * Global Wizard generic class. Needs to be inherited. @@ -423,281 +428,6 @@ class Wizard } - /** - * Print a block of inputs. - * Example, using direct to 'anidate' inputs directly to wrapper: - * [ - * 'wrapper' => 'div', - * 'block_id' => 'example_id', - * 'class' => 'your class', - * 'direct' => 1, - * 'block_content' => [ - * [ - * 'arguments' => [ - * 'label' => __('Sugesstion'), - * 'type' => 'button', - * 'attributes' => 'class="sub ok btn_sug"', - * 'name' => 'option_1', - * 'id' => 'option_1', - * 'script' => 'change_option1()', - * ], - * ], - * [ - * 'arguments' => [ - * 'label' => __('Something is not quite right'), - * 'type' => 'button', - * 'attributes' => 'class="sub ok btn_something"', - * 'name' => 'option_2', - * 'id' => 'option_2', - * 'script' => 'change_option2()', - * ], - * ], - * ], - * ]. - * - * @param array $input Definition of target block to be printed. - * @param boolean $return Return as string or direct output. - * @param boolean $direct Avoid encapsulation if input print is direct. - * - * @return string HTML content. - */ - public function printBlock( - array $input, - bool $return=false, - bool $direct=false - ) { - $output = ''; - if ($input['hidden'] == 1) { - $class = ' hidden'; - } else { - $class = ''; - } - - if (isset($input['class']) === true) { - $class = $input['class'].$class; - } - - if (is_array($input['block_content']) === true) { - $direct = (bool) $input['direct']; - $toggle = (bool) $input['toggle']; - - // Print independent block of inputs. - $output .= '
  • '; - - if ($input['wrapper']) { - $output .= '<'.$input['wrapper'].' id="'.$input['block_id'].'" class="'.$class.'">'; - } - - if (!$direct) { - // Avoid encapsulation if input is direct => 1. - $output .= '
      '; - } - - $html = ''; - - foreach ($input['block_content'] as $in) { - $html .= $this->printBlock( - $in, - $return, - (bool) $direct - ); - } - - if ($toggle === true) { - $output .= ui_print_toggle( - [ - 'name' => (isset($input['toggle_name']) ? $input['toggle_name'] : 'toggle_'.uniqid()), - 'content' => $html, - 'title' => $input['toggle_title'], - 'id' => $input['toggle_id'], - 'hidden_default' => $input['toggle_hidden_default'], - 'return' => (isset($input['toggle_return']) ? $input['toggle_return'] : true), - 'toggle_class' => $input['toggle_toggle_class'], - 'main_class' => $input['toggle_main_class'], - 'container_class' => $input['toggle_container_class'], - 'img_a' => $input['toggle_img_a'], - 'img_b' => $input['toggle_img_b'], - 'clean' => (isset($input['toggle_clean']) ? $input['toggle_clean'] : true), - ] - ); - } else { - $output .= $html; - } - - // Close block. - if (!$direct) { - $output .= '
    '; - } - - if ($input['wrapper']) { - $output .= ''; - } - - $output .= '
  • '; - } else { - if ($input['arguments']['type'] != 'hidden' - && $input['arguments']['type'] != 'hidden_extended' - ) { - if (!$direct) { - $output .= '
  • '; - } - - $output .= ''; - $output .= $this->printInput($input['arguments']); - // Allow dynamic content. - $output .= $input['extra']; - if (!$direct) { - $output .= '
  • '; - } - } else { - $output .= $this->printInput($input['arguments']); - // Allow dynamic content. - $output .= $input['extra']; - } - } - - if ($return === false) { - echo $output; - } - - return $output; - } - - - /** - * Print a block of inputs with grid format. - * - * @param array $input Definition of target block to be printed. - * @param boolean $return Return as string or direct output. - * - * @return string HTML content. - */ - public function printBlockAsGrid(array $input, bool $return=false) - { - $output = ''; - if ($input['hidden'] == 1) { - $class = ' hidden'; - } else { - $class = ''; - } - - if (isset($input['class']) === true) { - $class = $input['class'].$class; - } - - if (is_array($input['block_content']) === true) { - // Print independent block of inputs. - $output .= '
    '; - $output .= '
      '; - foreach ($input['block_content'] as $input) { - $output .= $this->printBlockAsGrid($input, $return); - } - - $output .= '
    '; - } else { - if ($input['arguments']['type'] != 'hidden' - && $input['arguments']['type'] != 'hidden_extended' - ) { - $id = ''; - if ($input['id']) { - $id = $input['id']; - } - - if ($input['arguments']['inline'] != 'true') { - $output .= '
    '; - } else { - $output .= '
    '; - } - - $output .= '
    '; - $output .= $input['label']; - $output .= '
    '; - - if ($input['arguments']['type'] == 'text' || $input['arguments']['type'] == 'text_extended') { - $output .= '
    '; - $output .= $this->printInput($input['arguments']); - $output .= '
    '; - } else if ($input['arguments']['inline'] == 'true') { - $output .= '
    '; - $output .= $this->printInput($input['arguments']); - $output .= '
    '; - } else { - $output .= $this->printInput($input['arguments']); - } - - // Allow dynamic content. - $output .= $input['extra']; - $output .= '
    '; - } else { - $output .= $this->printInput($input['arguments']); - // Allow dynamic content. - $output .= $input['extra']; - } - } - - if ($return === false) { - echo $output; - } - - return $output; - } - - - /** - * Print a block of inputs as a list element. - * - * @param array $input Definition of target block to be printed. - * @param boolean $return Return as string or direct output. - * - * @return string HTML content. - */ - public function printBlockAsList(array $input, bool $return=false) - { - $output = ''; - if ($input['hidden'] == 1) { - $class = ' hidden'; - } else { - $class = ''; - } - - if (isset($input['class']) === true) { - $class = $input['class'].$class; - } - - if (is_array($input['block_content']) === true) { - // Print independent block of inputs. - $output .= '
  • '; - $output .= '
      '; - foreach ($input['block_content'] as $input) { - $output .= $this->printBlockAsList($input, $return); - } - - $output .= '
  • '; - } else { - if ($input['arguments']['type'] != 'hidden' - && $input['arguments']['type'] != 'hidden_extended' - ) { - $output .= '
  • '; - $output .= ''; - $output .= $this->printInput($input['arguments']); - // Allow dynamic content. - $output .= $input['extra']; - $output .= '
  • '; - } else { - $output .= $this->printInput($input['arguments']); - // Allow dynamic content. - $output .= $input['extra']; - } - } - - if ($return === false) { - echo $output; - } - - return $output; - } - - /** * Print a form. * @@ -712,67 +442,7 @@ class Wizard bool $return=false, bool $print_white_box=false ) { - $form = $data['form']; - $inputs = $data['inputs']; - $js = $data['js']; - $rawjs = $data['js_block']; - $cb_function = $data['cb_function']; - $cb_args = $data['cb_args']; - - $output_head = '
    '; - - if ($return === false) { - echo $output_head; - } - - try { - if (isset($cb_function) === true) { - call_user_func_array( - $cb_function, - (isset($cb_args) === true) ? $cb_args : [] - ); - } - } catch (Exception $e) { - error_log('Error executing wizard callback: ', $e->getMessage()); - } - - $output_submit = ''; - $output = ''; - - if ($print_white_box === true) { - $output .= '
    '; - } - - $output .= '
      '; - - foreach ($inputs as $input) { - if ($input['arguments']['type'] != 'submit') { - $output .= $this->printBlock($input, true); - } else { - $output_submit .= $this->printBlock($input, true); - } - } - - $output .= '
    '; - - if ($print_white_box === true) { - $output .= '
    '; - } - - $output .= '
      '.$output_submit.'
    '; - $output .= '
    '; - $output .= ''; - if ($rawjs) { - $output .= $rawjs; - } - - if ($return === false) { - echo $output; - } - - return $output_head.$output; - + return HTML::printForm($data, $return, $print_white_box); } @@ -786,112 +456,7 @@ class Wizard */ public function printFormAsGrid(array $data, bool $return=false) { - $form = $data['form']; - - $rows = $data['rows']; - - $js = $data['js']; - $rawjs = $data['js_block']; - $cb_function = $data['cb_function']; - $cb_args = $data['cb_args']; - - $output_head = '
    '; - - if ($return === false) { - echo $output_head; - } - - try { - if (isset($cb_function) === true) { - call_user_func_array( - $cb_function, - (isset($cb_args) === true) ? $cb_args : [] - ); - } - } catch (Exception $e) { - error_log('Error executing wizard callback: ', $e->getMessage()); - } - - $output_submit = ''; - $output = ''; - - $first_block_printed = false; - - if (is_array($rows)) { - foreach ($rows as $row) { - if ($row['new_form_block'] == true) { - if ($first_block_printed === true) { - // If first form block has been placed, then close it before starting a new one. - $output .= '
    '; - $output .= '
    '; - } else { - $output .= '
    '; - } - - $first_block_printed = true; - } - - $row_output = '
    '; - - foreach ($row['columns'] as $column) { - $width = isset($column['width']) ? 'width: '.$column['width'].';' : 'width: 100%;'; - $padding_left = isset($column['padding-left']) ? 'padding-left: '.$column['padding-left'].';' : 'padding-left: 0;'; - $padding_right = isset($column['padding-right']) ? 'padding-right: '.$column['padding-right'].';' : 'padding-right: 0;'; - $extra_styles = isset($column['style']) ? $column['style'] : ''; - $class = isset($column['class']) ? $column['class'] : ''; - - $row_output .= '
    '; - - foreach ($column['inputs'] as $input) { - if (is_array($input)) { - if ($input['arguments']['type'] != 'submit') { - $row_output .= $this->printBlockAsGrid($input, true); - } else { - $output_submit .= $this->printBlockAsGrid($input, true); - } - } else { - $row_output .= $input; - } - } - - $row_output .= '
    '; - } - - if (isset($row['toggle'])) { - $output .= ui_print_toggle( - [ - 'content' => $row_output, - 'name' => $row['toggle_label'], - 'hidden_default' => ! (bool) $row['toggle'], - 'return' => true, - ] - ); - } else { - $output .= $row_output; - } - - $output .= '
    '; - } - } - - $output .= '
    '; - - $output .= '
      '.$output_submit.'
    '; - $output .= ''; - $output .= ''; - if ($rawjs) { - $output .= $rawjs; - } - - if ($return === false) { - echo $output; - } - - return $output_head.$output; - + return HTML::printFormAsGrid($data, $return); } @@ -905,57 +470,7 @@ class Wizard */ public function printFormAsList(array $data, bool $return=false) { - $form = $data['form']; - $inputs = $data['inputs']; - $js = $data['js']; - $rawjs = $data['js_block']; - $cb_function = $data['cb_function']; - $cb_args = $data['cb_args']; - - $output_head = '
    '; - - if ($return === false) { - echo $output_head; - } - - try { - if (isset($cb_function) === true) { - call_user_func_array( - $cb_function, - (isset($cb_args) === true) ? $cb_args : [] - ); - } - } catch (Exception $e) { - error_log('Error executing wizard callback: ', $e->getMessage()); - } - - $output = '
    '; - $output .= '
      '; - - foreach ($inputs as $input) { - if ($input['arguments']['type'] != 'submit') { - $output .= $this->printBlockAsList($input, true); - } else { - $output_submit .= $this->printBlockAsList($input, true); - } - } - - $output .= '
    '; - $output .= '
    '; - $output .= '
      '.$output_submit.'
    '; - $output .= '
    '; - $output .= ''; - if ($rawjs) { - $output .= $rawjs; - } - - if ($return === false) { - echo $output; - } - - return $output_head.$output; - + return HTML::printFormAsList($data, $return); } diff --git a/pandora_console/include/class/HTML.class.php b/pandora_console/include/class/HTML.class.php index 4382896fc3..1560b9ff28 100644 --- a/pandora_console/include/class/HTML.class.php +++ b/pandora_console/include/class/HTML.class.php @@ -610,9 +610,15 @@ class HTML } if (is_array($input['block_content']) === true) { + if (empty($input['label']) === false) { + $output .= '
    '; + $output .= $input['label']; + $output .= '
    '; + } + // Print independent block of inputs. - $output .= '
  • '; $output .= '
      '; + $output .= '
    • '; foreach ($input['block_content'] as $input) { $output .= self::printBlockAsGrid($input, $return); } @@ -717,7 +723,7 @@ class HTML if (is_array($input['block_content']) === true) { // Print independent block of inputs. $output .= '
    • '; - $output .= '
        '; + $output .= '
          '; foreach ($input['block_content'] as $input) { $output .= self::printBlockAsList($input, $return); } @@ -908,10 +914,34 @@ class HTML $output .= ' style="'.$width.$padding_left.$padding_right; $output .= $extra_styles.'">'; + // Toggle option. foreach ($column['inputs'] as $input) { if (is_array($input)) { if ($input['arguments']['type'] != 'submit') { - $output .= self::printBlockAsGrid($input, true); + if ($input['toggle'] === true) { + $output .= ui_print_toggle( + [ + 'name' => (isset($input['toggle_name']) ? $input['toggle_name'] : 'toggle_'.uniqid()), + 'title' => $input['toggle_title'], + 'id' => $input['toggle_id'], + 'hidden_default' => $input['toggle_hidden_default'], + 'content' => self::printBlockAsGrid( + $input, + true + ), + 'return' => true, + 'name' => (isset($input['toggle_name']) ? $input['toggle_name'] : 'toggle_'.uniqid()), + 'toggle_class' => $input['toggle_toggle_class'], + 'main_class' => $input['toggle_main_class'], + 'container_class' => $input['toggle_container_class'], + 'img_a' => $input['toggle_img_a'], + 'img_b' => $input['toggle_img_b'], + 'clean' => (isset($input['toggle_clean']) ? $input['toggle_clean'] : true), + ] + ); + } else { + $output .= self::printBlockAsGrid($input, true); + } } else { $output_submit .= self::printBlockAsGrid($input, true); } diff --git a/pandora_console/include/functions_ui.php b/pandora_console/include/functions_ui.php index 2506b3e051..f1b41fff99 100755 --- a/pandora_console/include/functions_ui.php +++ b/pandora_console/include/functions_ui.php @@ -3803,7 +3803,11 @@ function ui_toggle( if ($clean === false) { $header_class = 'white_table_graph_header'; } else { - $main_class = ''; + if ($main_class == 'box-shadow white_table_graph') { + // Default value, clean class. + $main_class = ''; + } + $container_class = 'white-box-content-clean'; } @@ -3839,7 +3843,6 @@ function ui_toggle( ); } - $output .= '  '; $output .= ''.$name.''; } else { $output .= $name; diff --git a/pandora_console/include/styles/wizard.css b/pandora_console/include/styles/wizard.css index 0ef0df5ee6..d20513ef8d 100644 --- a/pandora_console/include/styles/wizard.css +++ b/pandora_console/include/styles/wizard.css @@ -11,12 +11,26 @@ ul.wizard { ul.wizard li { padding-bottom: 10px; padding-top: 10px; +} + +ul.wizard.inline li { display: flex; flex-direction: row; align-items: center; justify-content: flex-start; } +.list .edit_discovery_input { + display: flex; + align-items: center; + flex-direction: row; + justify-content: space-between; +} + +.list .label_select { + margin-bottom: 0; +} + ul.wizard li > label:not(.p-switch) { width: 250px; vertical-align: top; @@ -34,6 +48,10 @@ ul.wizard li > textarea { display: none; } +.indented { + margin-left: 1em; +} + .wizard .indented { margin-left: 1em; } From e0266b53ba32e3113a9bab2a95e2bebbbb2fe73f Mon Sep 17 00:00:00 2001 From: artica Date: Tue, 1 Dec 2020 01:00:46 +0100 Subject: [PATCH 129/263] Auto-updated build strings. --- pandora_agents/unix/DEBIAN/control | 2 +- pandora_agents/unix/DEBIAN/make_deb_package.sh | 2 +- pandora_agents/unix/pandora_agent | 2 +- pandora_agents/unix/pandora_agent.redhat.spec | 2 +- pandora_agents/unix/pandora_agent.spec | 2 +- pandora_agents/unix/pandora_agent_installer | 2 +- pandora_agents/win32/installer/pandora.mpi | 2 +- pandora_agents/win32/pandora.cc | 2 +- pandora_agents/win32/versioninfo.rc | 2 +- pandora_console/DEBIAN/control | 2 +- pandora_console/DEBIAN/make_deb_package.sh | 2 +- pandora_console/include/config_process.php | 2 +- pandora_console/install.php | 2 +- pandora_console/pandora_console.redhat.spec | 2 +- pandora_console/pandora_console.rhel7.spec | 2 +- pandora_console/pandora_console.spec | 2 +- pandora_server/DEBIAN/control | 2 +- pandora_server/DEBIAN/make_deb_package.sh | 2 +- pandora_server/lib/PandoraFMS/Config.pm | 2 +- pandora_server/lib/PandoraFMS/PluginTools.pm | 2 +- pandora_server/pandora_server.redhat.spec | 2 +- pandora_server/pandora_server.spec | 2 +- pandora_server/pandora_server_installer | 2 +- pandora_server/util/pandora_db.pl | 2 +- pandora_server/util/pandora_manage.pl | 2 +- 25 files changed, 25 insertions(+), 25 deletions(-) diff --git a/pandora_agents/unix/DEBIAN/control b/pandora_agents/unix/DEBIAN/control index ac65800a20..f282e0cf96 100644 --- a/pandora_agents/unix/DEBIAN/control +++ b/pandora_agents/unix/DEBIAN/control @@ -1,5 +1,5 @@ package: pandorafms-agent-unix -Version: 7.0NG.750-201130 +Version: 7.0NG.750-201201 Architecture: all Priority: optional Section: admin diff --git a/pandora_agents/unix/DEBIAN/make_deb_package.sh b/pandora_agents/unix/DEBIAN/make_deb_package.sh index 2a3e9af4f9..d916788748 100644 --- a/pandora_agents/unix/DEBIAN/make_deb_package.sh +++ b/pandora_agents/unix/DEBIAN/make_deb_package.sh @@ -14,7 +14,7 @@ # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. -pandora_version="7.0NG.750-201130" +pandora_version="7.0NG.750-201201" echo "Test if you has the tools for to make the packages." whereis dpkg-deb | cut -d":" -f2 | grep dpkg-deb > /dev/null diff --git a/pandora_agents/unix/pandora_agent b/pandora_agents/unix/pandora_agent index 65215d3e8c..d6e6da535a 100755 --- a/pandora_agents/unix/pandora_agent +++ b/pandora_agents/unix/pandora_agent @@ -46,7 +46,7 @@ my $Sem = undef; my $ThreadSem = undef; use constant AGENT_VERSION => '7.0NG.750'; -use constant AGENT_BUILD => '201130'; +use constant AGENT_BUILD => '201201'; # Agent log default file size maximum and instances use constant DEFAULT_MAX_LOG_SIZE => 600000; diff --git a/pandora_agents/unix/pandora_agent.redhat.spec b/pandora_agents/unix/pandora_agent.redhat.spec index 8e4006b2fb..c259ced047 100644 --- a/pandora_agents/unix/pandora_agent.redhat.spec +++ b/pandora_agents/unix/pandora_agent.redhat.spec @@ -3,7 +3,7 @@ # %define name pandorafms_agent_unix %define version 7.0NG.750 -%define release 201130 +%define release 201201 Summary: Pandora FMS Linux agent, PERL version Name: %{name} diff --git a/pandora_agents/unix/pandora_agent.spec b/pandora_agents/unix/pandora_agent.spec index cf7a3bc749..2f41863aa8 100644 --- a/pandora_agents/unix/pandora_agent.spec +++ b/pandora_agents/unix/pandora_agent.spec @@ -3,7 +3,7 @@ # %define name pandorafms_agent_unix %define version 7.0NG.750 -%define release 201130 +%define release 201201 Summary: Pandora FMS Linux agent, PERL version Name: %{name} diff --git a/pandora_agents/unix/pandora_agent_installer b/pandora_agents/unix/pandora_agent_installer index 36e9bb11b7..b6cb60cb45 100755 --- a/pandora_agents/unix/pandora_agent_installer +++ b/pandora_agents/unix/pandora_agent_installer @@ -10,7 +10,7 @@ # ********************************************************************** PI_VERSION="7.0NG.750" -PI_BUILD="201130" +PI_BUILD="201201" OS_NAME=`uname -s` FORCE=0 diff --git a/pandora_agents/win32/installer/pandora.mpi b/pandora_agents/win32/installer/pandora.mpi index ac934c8c08..779dafc5c2 100644 --- a/pandora_agents/win32/installer/pandora.mpi +++ b/pandora_agents/win32/installer/pandora.mpi @@ -186,7 +186,7 @@ UpgradeApplicationID {} Version -{201130} +{201201} ViewReadme {Yes} diff --git a/pandora_agents/win32/pandora.cc b/pandora_agents/win32/pandora.cc index a5bf5f61b6..5ff45d4e3d 100644 --- a/pandora_agents/win32/pandora.cc +++ b/pandora_agents/win32/pandora.cc @@ -30,7 +30,7 @@ using namespace Pandora; using namespace Pandora_Strutils; #define PATH_SIZE _MAX_PATH+1 -#define PANDORA_VERSION ("7.0NG.750(Build 201130)") +#define PANDORA_VERSION ("7.0NG.750(Build 201201)") string pandora_path; string pandora_dir; diff --git a/pandora_agents/win32/versioninfo.rc b/pandora_agents/win32/versioninfo.rc index 95a72f3223..d7902c37dc 100644 --- a/pandora_agents/win32/versioninfo.rc +++ b/pandora_agents/win32/versioninfo.rc @@ -11,7 +11,7 @@ BEGIN VALUE "LegalCopyright", "Artica ST" VALUE "OriginalFilename", "PandoraAgent.exe" VALUE "ProductName", "Pandora FMS Windows Agent" - VALUE "ProductVersion", "(7.0NG.750(Build 201130))" + VALUE "ProductVersion", "(7.0NG.750(Build 201201))" VALUE "FileVersion", "1.0.0.0" END END diff --git a/pandora_console/DEBIAN/control b/pandora_console/DEBIAN/control index b12b783921..4d584831da 100644 --- a/pandora_console/DEBIAN/control +++ b/pandora_console/DEBIAN/control @@ -1,5 +1,5 @@ package: pandorafms-console -Version: 7.0NG.750-201130 +Version: 7.0NG.750-201201 Architecture: all Priority: optional Section: admin diff --git a/pandora_console/DEBIAN/make_deb_package.sh b/pandora_console/DEBIAN/make_deb_package.sh index 56d64760d3..364b93bb74 100644 --- a/pandora_console/DEBIAN/make_deb_package.sh +++ b/pandora_console/DEBIAN/make_deb_package.sh @@ -14,7 +14,7 @@ # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. -pandora_version="7.0NG.750-201130" +pandora_version="7.0NG.750-201201" package_pear=0 package_pandora=1 diff --git a/pandora_console/include/config_process.php b/pandora_console/include/config_process.php index 79ca8cf182..7ad88456ef 100644 --- a/pandora_console/include/config_process.php +++ b/pandora_console/include/config_process.php @@ -20,7 +20,7 @@ /** * Pandora build version and version */ -$build_version = 'PC201130'; +$build_version = 'PC201201'; $pandora_version = 'v7.0NG.750'; // Do not overwrite default timezone set if defined. diff --git a/pandora_console/install.php b/pandora_console/install.php index de9f4636d4..38fd478750 100644 --- a/pandora_console/install.php +++ b/pandora_console/install.php @@ -129,7 +129,7 @@
          [ qw() ] ); diff --git a/pandora_server/pandora_server.redhat.spec b/pandora_server/pandora_server.redhat.spec index fe223e5266..f82992ebc8 100644 --- a/pandora_server/pandora_server.redhat.spec +++ b/pandora_server/pandora_server.redhat.spec @@ -3,7 +3,7 @@ # %define name pandorafms_server %define version 7.0NG.750 -%define release 201130 +%define release 201201 Summary: Pandora FMS Server Name: %{name} diff --git a/pandora_server/pandora_server.spec b/pandora_server/pandora_server.spec index 8f86ffbf89..e329e84cd8 100644 --- a/pandora_server/pandora_server.spec +++ b/pandora_server/pandora_server.spec @@ -3,7 +3,7 @@ # %define name pandorafms_server %define version 7.0NG.750 -%define release 201130 +%define release 201201 Summary: Pandora FMS Server Name: %{name} diff --git a/pandora_server/pandora_server_installer b/pandora_server/pandora_server_installer index 575d144bd0..1396e29cf3 100755 --- a/pandora_server/pandora_server_installer +++ b/pandora_server/pandora_server_installer @@ -9,7 +9,7 @@ # ********************************************************************** PI_VERSION="7.0NG.750" -PI_BUILD="201130" +PI_BUILD="201201" MODE=$1 if [ $# -gt 1 ]; then diff --git a/pandora_server/util/pandora_db.pl b/pandora_server/util/pandora_db.pl index 5922eed398..ae356313fd 100755 --- a/pandora_server/util/pandora_db.pl +++ b/pandora_server/util/pandora_db.pl @@ -35,7 +35,7 @@ use PandoraFMS::Config; use PandoraFMS::DB; # version: define current version -my $version = "7.0NG.750 PS201130"; +my $version = "7.0NG.750 PS201201"; # Pandora server configuration my %conf; diff --git a/pandora_server/util/pandora_manage.pl b/pandora_server/util/pandora_manage.pl index 84ccb95554..a3ed71fc91 100755 --- a/pandora_server/util/pandora_manage.pl +++ b/pandora_server/util/pandora_manage.pl @@ -36,7 +36,7 @@ use Encode::Locale; Encode::Locale::decode_argv; # version: define current version -my $version = "7.0NG.750 PS201130"; +my $version = "7.0NG.750 PS201201"; # save program name for logging my $progname = basename($0); From 75e070ebb05ab41d7f60f725b38e98953582b4f7 Mon Sep 17 00:00:00 2001 From: Junichi Satoh Date: Tue, 1 Dec 2020 16:46:09 +0900 Subject: [PATCH 130/263] Fixed UM permission problems. --- pandora_console/godmode/users/configure_user.php | 6 +++--- pandora_console/godmode/users/user_list.php | 12 ++++-------- pandora_console/include/functions_profile.php | 2 +- pandora_console/include/functions_users.php | 4 ++++ 4 files changed, 12 insertions(+), 12 deletions(-) diff --git a/pandora_console/godmode/users/configure_user.php b/pandora_console/godmode/users/configure_user.php index f9618b64a1..2e0cdd8d7d 100644 --- a/pandora_console/godmode/users/configure_user.php +++ b/pandora_console/godmode/users/configure_user.php @@ -759,13 +759,13 @@ if (!users_is_admin() && $config['id_user'] != $id && !$new_user) { $sql = sprintf( "SELECT tusuario_perfil.* FROM tusuario_perfil INNER JOIN tperfil ON tperfil.id_perfil = tusuario_perfil.id_perfil - WHERE id_usuario like '%s' AND id_grupo IN (%s) AND user_management = 0", - $id, + WHERE id_usuario like '%s' AND id_grupo IN (%s) AND user_management = 1", + $config['id_user'], $group_um_string ); $result = db_get_all_rows_sql($sql); - if ($result == false || $user_info['is_admin']) { + if ($result == false && $user_info['is_admin'] == false) { db_pandora_audit( 'ACL Violation', 'Trying to access User Management' diff --git a/pandora_console/godmode/users/user_list.php b/pandora_console/godmode/users/user_list.php index 77ca07acce..31c6b3c119 100644 --- a/pandora_console/godmode/users/user_list.php +++ b/pandora_console/godmode/users/user_list.php @@ -491,12 +491,6 @@ foreach ($info as $user_id => $user_info) { foreach ($group_um as $key => $value) { if (isset($user_profiles_aux[$key])) { $user_profiles[$key] = $user_profiles_aux[$key]; - if ($user_profiles_aux[$key]['user_management'] == 1) { - $user_info['edit'] = 0; - } else { - $user_info['edit'] = 1; - } - unset($user_profiles_aux[$key]); } } @@ -593,8 +587,10 @@ foreach ($info as $user_id => $user_info) { $total_profile++; } - - $data[4] .= '
          '; + if (isset($user_info['not_delete'])) { + $data[4] .= __('Other profiles are also assigned.') . ui_print_help_tip(__('Other profiles you cannot manage are also assigned. These profiles are not shown. You cannot enable/disable or delete this user.'), true); + } + $data[4] .= '
  • '; } else { $data[4] .= __('The user doesn\'t have any assigned profile/group'); } diff --git a/pandora_console/include/functions_profile.php b/pandora_console/include/functions_profile.php index 4406fd72b6..e55a37ddf8 100644 --- a/pandora_console/include/functions_profile.php +++ b/pandora_console/include/functions_profile.php @@ -234,7 +234,7 @@ function profile_print_profile_table($id) $sql = sprintf( "SELECT tusuario_perfil.* FROM tusuario_perfil INNER JOIN tperfil ON tperfil.id_perfil = tusuario_perfil.id_perfil - WHERE id_usuario like '%s' AND id_grupo IN (%s) AND user_management = 0", + WHERE id_usuario like '%s' AND id_grupo IN (%s)", $id, $group_um_string ); diff --git a/pandora_console/include/functions_users.php b/pandora_console/include/functions_users.php index 73e7340838..7929edeaab 100755 --- a/pandora_console/include/functions_users.php +++ b/pandora_console/include/functions_users.php @@ -752,6 +752,10 @@ function users_get_groups_UM($id_user) foreach ($groups as $key => $group) { if (!isset($return[$group['id_grupo']]) || (isset($return[$group['id_grupo']]) && $group['user_management'] != 0)) { $return[$group['id_grupo']] = $group['user_management']; + $children = groups_get_children($group['id_grupo'], false, 'UM', false); + foreach ($children as $key => $child_group) { + $return[$child_group['id_grupo']] = $group['user_management']; + } if ($group['id_grupo'] == '0') { $return['group_all'] = $group['id_grupo']; } From 250a378773e9c1c06c259b7eb03bdb72d790206e Mon Sep 17 00:00:00 2001 From: Jose Gonzalez Date: Tue, 1 Dec 2020 11:52:12 +0100 Subject: [PATCH 131/263] Fixed for show better results --- pandora_console/include/class/AgentWizard.class.php | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/pandora_console/include/class/AgentWizard.class.php b/pandora_console/include/class/AgentWizard.class.php index ff78c525bf..68bbbee0ee 100644 --- a/pandora_console/include/class/AgentWizard.class.php +++ b/pandora_console/include/class/AgentWizard.class.php @@ -2408,8 +2408,8 @@ class AgentWizard extends HTML } } - // If value comes Null, must return a empty value. - $value = ($value ?? ''); + // If value comes empty, must return a "Empty" value for view it in console. + $value = (empty($value) === true) ? ''.__('Empty').'' : $value; return $value; } @@ -3332,7 +3332,7 @@ class AgentWizard extends HTML } else { preg_match('/\.\d+$/', $key, $index); $tmp = explode(': ', $oid_unit); - $output[$index[0]] = $tmp[1]; + $output[$index[0]] = ($tmp[1] ?? ''); } } } From aeb7a7e9c6686ab1c83869009799a3f47a003a66 Mon Sep 17 00:00:00 2001 From: Luis Date: Tue, 1 Dec 2020 11:56:15 +0100 Subject: [PATCH 132/263] Add decimal separator to all pandora csv --- .../godmode/setup/setup_visuals.php | 2 +- .../include/class/ModuleTemplates.class.php | 20 ++++++++++++++++--- pandora_console/include/functions.php | 16 +++++++++++---- .../include/graphs/export_data.php | 12 ++++++++++- 4 files changed, 41 insertions(+), 9 deletions(-) diff --git a/pandora_console/godmode/setup/setup_visuals.php b/pandora_console/godmode/setup/setup_visuals.php index 8c07381452..d1230f8c35 100755 --- a/pandora_console/godmode/setup/setup_visuals.php +++ b/pandora_console/godmode/setup/setup_visuals.php @@ -1482,7 +1482,7 @@ $row++; '.' => '.', ',' => ',', ]; - $table_other->data[$row][0] = __('CSV decimal separator').ui_print_help_tip(__('Only for csv reports'), true); + $table_other->data[$row][0] = __('CSV decimal separator'); $table_other->data[$row][1] = html_print_select($decimal_separator, 'csv_decimal_separator', $config['csv_decimal_separator'], '', '', '', true, false, false); $row++; diff --git a/pandora_console/include/class/ModuleTemplates.class.php b/pandora_console/include/class/ModuleTemplates.class.php index f9573b6c0e..8d89519486 100644 --- a/pandora_console/include/class/ModuleTemplates.class.php +++ b/pandora_console/include/class/ModuleTemplates.class.php @@ -409,6 +409,8 @@ class ModuleTemplates extends HTML case 'export': global $config; + enterprise_include_once('include/functions_reporting_csv.php'); + $id_network_profile = safe_int($this->id_np); if (empty($id_network_profile)) { return false; @@ -500,7 +502,7 @@ class ModuleTemplates extends HTML } // Then print the first line (row names) - echo '"'.implode('","', $row_names).'"'; + echo '"'.implode('"'.$config['csv_divider'].'"', $row_names).'"'; echo "\n"; // Then print the rest of the data. Encapsulate in quotes in case we have comma's in any of the descriptions @@ -509,7 +511,19 @@ class ModuleTemplates extends HTML unset($row[$bad_key]); } - echo '"'.implode('","', $row).'"'; + if ($config['csv_decimal_separator'] !== '.') { + foreach ($row as $name => $data) { + if (is_numeric($data)) { + // Get the number of decimals, if > 0, format dec comma. + $decimals = strlen(substr(strrchr($data, '.'), 1)); + if ($decimals !== 0) { + $row[$name] = csv_format_numeric((float) $data, $decimals, true); + } + } + } + } + + echo '"'.implode('"'.$config['csv_divider'].'"', $row).'"'; echo "\n"; } @@ -845,7 +859,7 @@ class ModuleTemplates extends HTML $row['id_np'], '', true, - ['title' => 'Export to CSV'] + ['title' => 'Export tdaso CSV'] ); $data[3] = ''.html_print_image('images/cross.png', true, ['title' => __('Delete')]).''; $data[3] .= ''.html_print_image('images/csv.png', true, ['title' => __('Export to CSV')]).''; diff --git a/pandora_console/include/functions.php b/pandora_console/include/functions.php index b1c537c6aa..f79c56b764 100644 --- a/pandora_console/include/functions.php +++ b/pandora_console/include/functions.php @@ -3904,17 +3904,23 @@ function series_type_graph_array($data, $show_elements_graph) $data_return['legend'][$key] .= __('Min:').remove_right_zeros( number_format( $value['min'], - $config['graph_precision'] + $config['graph_precision'], + $config['csv_decimal_separator'], + $config['csv_decimal_separator'] == ',' ? '.' : ',' ) ).' '.__('Max:').remove_right_zeros( number_format( $value['max'], - $config['graph_precision'] + $config['graph_precision'], + $config['csv_decimal_separator'], + $config['csv_decimal_separator'] == ',' ? '.' : ',' ) ).' '._('Avg:').remove_right_zeros( number_format( $value['avg'], - $config['graph_precision'] + $config['graph_precision'], + $config['csv_decimal_separator'], + $config['csv_decimal_separator'] == ',' ? '.' : ',' ) ).' '.$str; @@ -3978,7 +3984,8 @@ function series_type_graph_array($data, $show_elements_graph) $data_return['legend'][$key] .= remove_right_zeros( number_format( $value['avg'], - $config['graph_precision'] + $config['graph_precision'], + $config['csv_decimal_separator'] ) ).' '.$str; } @@ -5911,4 +5918,5 @@ function send_test_email( } return $result; + } diff --git a/pandora_console/include/graphs/export_data.php b/pandora_console/include/graphs/export_data.php index 8aca5c04b5..cab03220d5 100644 --- a/pandora_console/include/graphs/export_data.php +++ b/pandora_console/include/graphs/export_data.php @@ -15,6 +15,7 @@ // This file only uses data retrieved in a request. require_once '../../include/config.php'; require_once '../../include/functions.php'; +enterprise_include_once('include/functions_reporting_csv.php'); global $config; @@ -49,7 +50,10 @@ $filename = io_safe_output($filename); * ); */ $output_csv = function ($data, $filename) { - $separator = (string) get_parameter('separator', ';'); + global $config; + + $separator = (string) $config['csv_divider']; + $excel_encoding = (bool) get_parameter('excel_encoding', false); // CSV Output @@ -68,9 +72,15 @@ $output_csv = function ($data, $filename) { throw new Exception(__('An error occured exporting the data')); } + // Get key for item value. + $value_key = array_search('value', $items['head']); + $head_line = implode($separator, $items['head']); echo $head_line."\n"; foreach ($items['data'] as $item) { + // Find value and replace csv decimal separator. + $item[$value_key] = csv_format_numeric($item[$value_key]); + $item = str_replace('--> '.__('Selected'), '', $item); $line = implode($separator, $item); From 2b52b3beef946ca5bbc580cd5179611571e7e90b Mon Sep 17 00:00:00 2001 From: Daniel Maya Date: Tue, 1 Dec 2020 12:29:53 +0100 Subject: [PATCH 133/263] Show data type --- pandora_console/operation/agentes/status_monitor.php | 8 -------- 1 file changed, 8 deletions(-) diff --git a/pandora_console/operation/agentes/status_monitor.php b/pandora_console/operation/agentes/status_monitor.php index db1e052ab9..4ef70c71e7 100644 --- a/pandora_console/operation/agentes/status_monitor.php +++ b/pandora_console/operation/agentes/status_monitor.php @@ -593,10 +593,6 @@ $table->data[2][5] = html_print_input_text('min_hours_status', $min_hours_val, ' $table->data[3][0] = 'data[3][0] .= 'style="display:none"'; -} - $table->data[3][0] .= '>'.__('Data type').''; @@ -671,10 +667,6 @@ switch ($moduletype) { $a = db_get_all_rows_sql($sql); $table->data[3][1] .= '