diff --git a/pandora_console/composer.json b/pandora_console/composer.json index acc6a3e352..208e2d3d14 100644 --- a/pandora_console/composer.json +++ b/pandora_console/composer.json @@ -10,5 +10,16 @@ "require": { "mpdf/mpdf": "^7.1", "swiftmailer/swiftmailer": "^6.0" + }, + "autoload": { + "psr-4": { + "Models\\": "include/rest-api/models", + "Enterprise\\Models\\": "enterprise/include/rest-api/models" + } + }, + "autoload-dev": { + "psr-4": { + "Tests\\": "tests/" + } } } diff --git a/pandora_console/godmode/reporting/visual_console_builder.editor.js b/pandora_console/godmode/reporting/visual_console_builder.editor.js index 6744d2fb2d..03841a342e 100755 --- a/pandora_console/godmode/reporting/visual_console_builder.editor.js +++ b/pandora_console/godmode/reporting/visual_console_builder.editor.js @@ -1090,10 +1090,11 @@ function readFields() { var text = tinymce.get("text-label").getContent(); values["label"] = text; + values["percentile_label_color"] = $( + "input[name=percentile_label_color]" + ).val(); + if ($("input[name=percentile_label]").val().length > 0) { - values["percentile_label_color"] = $( - "input[name=percentile_label_color]" - ).val(); values["label"] = "data[$row][1] = html_print_select($values, 'vc_refr', $config['vc_refr'], '', 'N/A', 0, true, false, false); -$row++; - $table_behaviour->data[$row][0] = __('Paginated module view'); $table_behaviour->data[$row][1] = html_print_checkbox_switch( 'paginate_module', @@ -908,6 +904,24 @@ $row++; $table_vc->size[0] = '50%'; $table_vc->data = []; + // Remove when the new view reaches rock solid stability. + $table_vc->data[$row][0] = __('Legacy Visual Console View'); + $table_vc->data[$row][0] .= ui_print_help_tip( + __('To use the old view when using the Visual Console visor'), + true + ); + $table_vc->data[$row][1] = html_print_checkbox_switch( + 'legacy_vc', + 1, + (bool) $config['legacy_vc'], + true + ); + $row++; + + $table_vc->data[$row][0] = __('Default interval for refresh on Visual Console').ui_print_help_tip(__('This interval will affect to Visual Console pages'), true); + $table_vc->data[$row][1] = html_print_select($values, 'vc_refr', (int) $config['vc_refr'], '', 'N/A', 0, true, false, false); + $row++; + $vc_favourite_view_array[0] = __('Classic view'); $vc_favourite_view_array[1] = __('View of favorites'); $table_vc->data[$row][0] = __('Type of view of visual consoles').ui_print_help_tip(__('Allows you to directly display the list of favorite visual consoles'), true); @@ -918,12 +932,8 @@ $row++; $table_vc->data[$row][1] = ""; $row++; - if (empty($config['vc_line_thickness'])) { - $config['vc_line_thickness'] = 2; - } - $table_vc->data[$row][0] = __('Default line thickness for the Visual Console').ui_print_help_tip(__('This interval will affect to the lines between elements on the Visual Console'), true); - $table_vc->data[$row][1] = html_print_input_text('vc_line_thickness', $config['vc_line_thickness'], '', 5, 5, true); + $table_vc->data[$row][1] = html_print_input_text('vc_line_thickness', (int) $config['vc_line_thickness'], '', 5, 5, true); echo '
'; diff --git a/pandora_console/include/functions_agents.php b/pandora_console/include/functions_agents.php index 67ad7b949c..fd5e922e32 100644 --- a/pandora_console/include/functions_agents.php +++ b/pandora_console/include/functions_agents.php @@ -328,14 +328,21 @@ function agents_get_alerts_simple($id_agent=false, $filter='', $options=false, $ * * By default, it will return all the agents where the user has reading access. * - * @param array filter options in an indexed array. See - * db_format_array_where_clause_sql() - * @param array Fields to get. - * @param string Access needed in the agents groups. - * @param array $order The order of agents, by default is upward for field nombre. - * @param boolean $return Whether to return array with agents or false, or sql string statement + * @param array $filter Filter options in an indexed array. + * See db_format_array_where_clause_sql(). + * @param array $fields DB fields to get. + * @param string $access ACL level needed in the agents groups. + * @param array $order The order of agents, by default is upward + * for field nombre. + * @param boolean $return Whether to return array with agents or + * the sql string statement. + * @param boolean $disabled_agent Whether to return only the enabled agents + * or not. + * @param boolean $use_meta_table Whether to use the regular or the meta table + * to retrieve the agents. * - * @return mixed An array with all alerts defined for an agent or false in case no allowed groups are specified. + * @return mixed An array with all alerts defined for an agent + * or false in case no allowed groups are specified. */ function agents_get_agents( $filter=false, @@ -346,7 +353,8 @@ function agents_get_agents( 'order' => 'ASC', ], $return=false, - $disabled_agent=0 + $disabled_agent=0, + $use_meta_table=false ) { global $config; @@ -563,11 +571,15 @@ function agents_get_agents( ); } + $table_name = ($use_meta_table === true) ? 'tmetaconsole_agent' : 'tagente'; $sql = sprintf( 'SELECT DISTINCT %s - FROM tagente LEFT JOIN tagent_secondary_group ON tagent_secondary_group.id_agent=tagente.id_agente + FROM `%s` tagente + LEFT JOIN tagent_secondary_group + ON tagent_secondary_group.id_agent=tagente.id_agente WHERE %s %s', implode(',', $fields), + $table_name, $where, $order ); diff --git a/pandora_console/include/functions_config.php b/pandora_console/include/functions_config.php index 7b3c4b632b..0200331e5b 100644 --- a/pandora_console/include/functions_config.php +++ b/pandora_console/include/functions_config.php @@ -980,7 +980,11 @@ function config_update_config() $error_update[] = __('Custom support url'); } - if (!config_update_value('vc_refr', get_parameter('vc_refr'))) { + if (!config_update_value('legacy_vc', (int) get_parameter('legacy_vc'))) { + $error_update[] = __('Use the legacy Visual Console'); + } + + if (!config_update_value('vc_refr', (int) get_parameter('vc_refr'))) { $error_update[] = __('Default interval for refresh on Visual Console'); } @@ -2415,10 +2419,18 @@ function config_process_config() config_update_value('dbtype', 'mysql'); } + if (!isset($config['legacy_vc'])) { + config_update_value('legacy_vc', 0); + } + if (!isset($config['vc_refr'])) { config_update_value('vc_refr', 300); } + if (!isset($config['vc_line_thickness'])) { + config_update_value('vc_line_thickness', 2); + } + if (!isset($config['agent_size_text_small'])) { config_update_value('agent_size_text_small', 18); } diff --git a/pandora_console/include/functions_visual_map.php b/pandora_console/include/functions_visual_map.php index 5344eb15ee..603afe9c90 100755 --- a/pandora_console/include/functions_visual_map.php +++ b/pandora_console/include/functions_visual_map.php @@ -3961,38 +3961,54 @@ function visual_map_translate_module_status($module_status) * layouts), and makes an AND operation to be sure that all the items * are OK. If any of them is down, then result is down (0) * - * @param int Id of the layout - * @param array Information about the status calculation of the item - * @param int Depth (for recursion control) + * @param integer $layout_id Id of the layout. + * @param array $status_data Information about the status calculation of the + * item. + * @param integer $depth Depth (for recursion control). * - * @return boolean The status of the given layout. True if it's OK, false if not. + * @return integer The status of the given layout. */ function visual_map_get_layout_status($layout_id, $status_data=[], $depth=0) { global $config; - // TODO: Implement this limit into the setup + // TODO: Implement this limit into the setup. if ($depth > 10) { return VISUAL_MAP_STATUS_UNKNOWN; } - $layout_items = db_get_all_rows_filter('tlayout_data', ['id_layout' => $layout_id]); + $layout_items = db_get_all_rows_filter( + 'tlayout_data', + ['id_layout' => $layout_id] + ); if ($layout_items === false) { return VISUAL_MAP_STATUS_UNKNOWN; } - // Check for valid items to retrieve the status for + // Check for valid items to retrieve the status for. $valid_layout_items = []; foreach ($layout_items as $layout_item_data) { if (($layout_item_data['type'] == GROUP_ITEM && !empty($layout_item_data['id_group']) - && check_acl($config['id_user'], $layout_item_data['id_group'], 'VR') - && check_acl($config['id_user'], $layout_item_data['element_group'], 'VR')) + && check_acl( + $config['id_user'], + $layout_item_data['id_group'], + 'VR' + ) + && check_acl( + $config['id_user'], + $layout_item_data['element_group'], + 'VR' + )) || ((!empty($layout_item_data['id_layout_linked']) || !empty($layout_item_data['id_agente_modulo']) || !empty($layout_item_data['id_agent'])) - && check_acl($config['id_user'], $layout_item_data['element_group'], 'VR')) + && check_acl( + $config['id_user'], + $layout_item_data['element_group'], + 'VR' + )) ) { $valid_layout_items[] = $layout_item_data; } @@ -4002,7 +4018,7 @@ function visual_map_get_layout_status($layout_id, $status_data=[], $depth=0) return VISUAL_MAP_STATUS_UNKNOWN; } - // Sort by node id to reduce the number of connections + // Sort by node id to reduce the number of connections. if (is_metaconsole()) { sort_by_column($valid_layout_items, 'id_metaconsole'); } @@ -4021,14 +4037,14 @@ function visual_map_get_layout_status($layout_id, $status_data=[], $depth=0) if (empty($node_id) && $meta_connected_to) { metaconsole_restore_db(); - // Restore db connection + // Restore db connection. $meta_connected_to = null; - } else if (!empty($node_id) && ( empty($meta_connected_to) - || $meta_connected_to != $node_id) + } else if (!empty($node_id) + && (empty($meta_connected_to) || $meta_connected_to != $node_id) ) { if (!empty($meta_connected_to)) { metaconsole_restore_db(); - // Restore db connection + // Restore db connection. } $connection = metaconsole_get_connection_by_id($node_id); @@ -4042,64 +4058,80 @@ function visual_map_get_layout_status($layout_id, $status_data=[], $depth=0) $status = VISUAL_MAP_STATUS_NORMAL; - $ent_element_status = enterprise_hook('enterprise_visual_map_get_status_element', [$layoutData]); + $ent_element_status = enterprise_hook( + 'enterprise_visual_map_get_status_element', + [$layoutData] + ); if ($ent_element_status === ENTERPRISE_NOT_HOOK) { $ent_element_status = false; } - // Enterprise element if ($ent_element_status !== false) { + // Enterprise element. $status = $ent_element_status; - } - // Other - else { + } else { + // Other. switch ($layout_item_data['type']) { case STATIC_GRAPH: case PERCENTILE_BAR: case PERCENTILE_BUBBLE: case CIRCULAR_PROGRESS_BAR: case CIRCULAR_INTERIOR_PROGRESS_BAR: - // Linked layout if (!empty($layout_item_data['id_layout_linked'])) { - $status = visual_map_get_layout_status($layout_item_data['id_layout_linked'], $layout_item_data, ($depth + 1)); - } - // Module - else if (!empty($layout_item_data['id_agente_modulo'])) { - $module_status = modules_get_agentmodule_status($layout_item_data['id_agente_modulo']); - $status = visual_map_translate_module_status($module_status); - } - // Agent - else if (!empty($layout_item_data['id_agent'])) { - $agent_status = agents_get_status($layout_item_data['id_agent'], true); - $status = visual_map_translate_agent_status($agent_status); - } - // Unknown - else { + // Linked layout. + $status = visual_map_get_layout_status( + $layout_item_data['id_layout_linked'], + $layout_item_data, + ($depth + 1) + ); + } else if (!empty($layout_item_data['id_agente_modulo'])) { + // Module. + $module_status = modules_get_agentmodule_status( + $layout_item_data['id_agente_modulo'] + ); + $status = visual_map_translate_module_status( + $module_status + ); + } else if (!empty($layout_item_data['id_agent'])) { + // Agent. + $agent_status = agents_get_status( + $layout_item_data['id_agent'], + true + ); + $status = visual_map_translate_agent_status( + $agent_status + ); + } else { + // Unknown. $status = VISUAL_MAP_STATUS_UNKNOWN; } break; case GROUP_ITEM: - $group_status = groups_get_status($layout_item_data['id_group']); + $group_status = groups_get_status( + $layout_item_data['id_group'] + ); $status = visual_map_translate_agent_status($group_status); break; default: - // If it's a graph, a progress bar or a data tag, ALWAYS report status OK - // (=0) to avoid confussions here. + // If it's a graph, a progress bar or a data tag, + // ALWAYS report status OK (=0) to avoid confussions here. $status = VISUAL_MAP_STATUS_NORMAL; break; } } - // When the status calculation type is 'default', only one critical element is required to - // set the layout status as critical, so we can return the critical status right now. - if ($status_data['linked_layout_status_type'] === 'default' && ( $status == VISUAL_MAP_STATUS_CRITICAL_BAD + // When the status calculation type is 'default', only one critical + // element is required to set the layout status as critical, so we can + // return the critical status right now. + if ($status_data['linked_layout_status_type'] === 'default' + && ($status == VISUAL_MAP_STATUS_CRITICAL_BAD || $status == VISUAL_MAP_STATUS_CRITICAL_ALERT) ) { if (is_metaconsole() && $meta_connected_to) { + // Restore db connection. metaconsole_restore_db(); - // Restore db connection } return $status; @@ -4113,11 +4145,11 @@ function visual_map_get_layout_status($layout_id, $status_data=[], $depth=0) } if (is_metaconsole() && $meta_connected_to) { + // Restore db connection. metaconsole_restore_db(); - // Restore db connection } - // Status calculation + // Status calculation. switch ($status_data['linked_layout_status_type']) { default: case 'default': @@ -4149,7 +4181,7 @@ function visual_map_get_layout_status($layout_id, $status_data=[], $depth=0) if ($num_items_critical > 0 && ((($num_items_critical_alert + $num_items_critical) * 100) / $num_items) >= $weight ) { - return $num_items_critical_alert > 0 ? VISUAL_MAP_STATUS_CRITICAL_ALERT : VISUAL_MAP_STATUS_CRITICAL_BAD; + return ($num_items_critical_alert > 0) ? VISUAL_MAP_STATUS_CRITICAL_ALERT : VISUAL_MAP_STATUS_CRITICAL_BAD; } else if ($num_items_warning > 0 && (($num_items_warning * 100) / $num_items) >= $weight ) { @@ -4525,3 +4557,43 @@ function visual_map_get_color_cloud_element($data) '; + continue; + } + + preg_match('/.*.css$/', $file, $match, PREG_OFFSET_CAPTURE); + if (empty($match) === false) { + $url = $baseUrl.$vcClientPath.'/'.$match[0][0]; + echo ''; + } + } + + closedir($dh); + } + } +} diff --git a/pandora_console/include/functions_visual_map_editor.php b/pandora_console/include/functions_visual_map_editor.php index 151014cd01..90da456ae1 100755 --- a/pandora_console/include/functions_visual_map_editor.php +++ b/pandora_console/include/functions_visual_map_editor.php @@ -688,7 +688,7 @@ function visual_map_editor_print_item_palette($visualConsole_id, $background) 'percentile_item', 'datos', ]; - $form_items['percentile_item_row_6']['html'] = ''.__('Label color').' + $form_items['percentile_item_row_6']['html'] = ''.__('Value color').' '.html_print_input_text_extended( 'percentile_label_color', '#ffffff', diff --git a/pandora_console/include/graphs/flot/pandora.flot.js b/pandora_console/include/graphs/flot/pandora.flot.js index dd6fb0645e..8fc7073365 100644 --- a/pandora_console/include/graphs/flot/pandora.flot.js +++ b/pandora_console/include/graphs/flot/pandora.flot.js @@ -2133,7 +2133,7 @@ function pandoraFlotArea( var plot = $.plot($("#" + graph_id), datas, options); // Re-calculate the graph height with the legend height - if (dashboard || vconsole) { + if (dashboard) { $acum = 0; if (dashboard) $acum = 35; var hDiff = diff --git a/pandora_console/include/javascript/pandora.js b/pandora_console/include/javascript/pandora.js index c30736cd8b..b7dd0dc48e 100644 --- a/pandora_console/include/javascript/pandora.js +++ b/pandora_console/include/javascript/pandora.js @@ -1748,7 +1748,9 @@ function round_with_decimals(value, multiplier) { if (typeof multiplier === "undefined") multiplier = 1; // Return non numeric types without modification - if (typeof value !== "number") return value; + if (typeof value !== "number" || Number.isNaN(value)) { + return value; + } if (value * multiplier == 0) return 0; if (Math.abs(value) * multiplier >= 1) { diff --git a/pandora_console/include/javascript/pandora_visual_console.js b/pandora_console/include/javascript/pandora_visual_console.js index 06209e092f..cd21dd77d0 100755 --- a/pandora_console/include/javascript/pandora_visual_console.js +++ b/pandora_console/include/javascript/pandora_visual_console.js @@ -1,3 +1,268 @@ +// TODO: Add Artica ST header. +/* globals jQuery, VisualConsole */ + +/* + * ********************* + * * New VC functions. * + * ********************* + */ + +/** + * Generate a Visual Console client. + * @param {HTMLElement} container Node which will be used to contain the VC. + * @param {object} props VC container properties. + * @param {object[]} items List of item definitions. + * @param {string | null} baseUrl Base URL to perform API requests. + * @param {number | null} updateInterval Time in milliseconds between VC updates. + * @param {function | null} onUpdate Callback which will be execuded when the Visual Console. + * is updated. It will receive two arguments with the old and the new Visual Console's + * data structure. + * @return {VisualConsole | null} The Visual Console instance or a null value. + */ +// eslint-disable-next-line no-unused-vars +function createVisualConsole( + container, + props, + items, + baseUrl, + updateInterval, + onUpdate +) { + var visualConsole = null; + var linkedVCRequest = null; + var updateVCRequest = null; + + if (container == null || props == null || items == null) return null; + if (baseUrl == null) baseUrl = ""; + + // Code which will be executed between intervals. + var intervalRef = null; + var stopInterval = function() { + if (intervalRef !== null) window.clearInterval(intervalRef); + }; + var startInterval = function() { + if (updateInterval == null || updateInterval <= 0) return; + stopInterval(); + + intervalRef = window.setInterval(function() { + if (updateVCRequest !== null) updateVCRequest.abort(); + updateVCRequest = loadVisualConsoleData( + baseUrl, + visualConsole.props.id, + function(error, data) { + if (error) { + console.log( + "[ERROR]", + "[VISUAL-CONSOLE-CLIENT]", + "[API]", + error.message + ); + return; + } + + // Replace Visual Console. + if (data != null && data.props != null && data.items != null) { + try { + var props = + typeof data.props === "string" + ? JSON.parse(data.props) + : data.props; + var items = + typeof data.items === "string" + ? JSON.parse(data.items) + : data.items; + + var prevProps = visualConsole.props; + // Update the data structure. + visualConsole.props = props; + // Update the items. + visualConsole.updateElements(items); + // Emit the VC update event. + if (onUpdate) onUpdate(prevProps, visualConsole.props); + } catch (ignored) {} // eslint-disable-line no-empty + } + } + ); + }, updateInterval); + }; + + // Initialize the Visual Console. + try { + visualConsole = new VisualConsole(container, props, items); + // VC Item clicked. + visualConsole.onClick(function(e) { + // Override the link to another VC if it isn't on remote console. + if ( + e.data && + e.data.linkedLayoutId != null && + e.data.linkedLayoutId > 0 && + e.data.link != null && + e.data.link.length > 0 && + (e.data.metaconsoleId == null || e.data.metaconsoleId === 0) + ) { + // Stop the current link behavior. + e.nativeEvent.preventDefault(); + + // Fetch and update the old VC with the new. + if (linkedVCRequest !== null) linkedVCRequest.abort(); + linkedVCRequest = loadVisualConsoleData( + baseUrl, + e.data.linkedLayoutId, + function(error, data) { + if (error) { + console.log( + "[ERROR]", + "[VISUAL-CONSOLE-CLIENT]", + "[API]", + error.message + ); + return; + } + + // Replace Visual Console. + if (data != null && data.props != null && data.items != null) { + // Cancel the old VC updates. + stopInterval(); + + try { + var props = + typeof data.props === "string" + ? JSON.parse(data.props) + : data.props; + var items = + typeof data.items === "string" + ? JSON.parse(data.items) + : data.items; + + if (updateVCRequest !== null) updateVCRequest.abort(); + // Save the old props. + var prevProps = visualConsole.props; + // Update the data structure. + visualConsole.props = props; + // Update the items. + visualConsole.updateElements(items); + // Emit the VC update event. + if (onUpdate) onUpdate(prevProps, visualConsole.props); + } catch (ignored) {} // eslint-disable-line no-empty + + // Restart the updates. + startInterval(); + } + } + ); + } + }); + + // Start an interval to update the Visual Console. + startInterval(); + } catch (error) { + console.log("[ERROR]", "[VISUAL-CONSOLE-CLIENT]", error.message); + } + + return visualConsole; +} + +/** + * Fetch a Visual Console's structure and its items. + * @param {string} baseUrl Base URL to build the API path. + * @param {number} vcId Identifier of the Visual Console. + * @param {function} callback Function to be executed on request success or fail. + * On success, the function will receive an object with the next properties: + * - `props`: object with the Visual Console's data structure. + * - `items`: array of data structures of the Visual Console's items. + * @return {Object} Cancellable. Object which include and .abort([statusText]) function. + */ +// eslint-disable-next-line no-unused-vars +function loadVisualConsoleData(baseUrl, vcId, callback) { + // var apiPath = baseUrl + "/include/rest-api"; + var apiPath = baseUrl + "/ajax.php"; + var vcJqXHR = null; + var itemsJqXHR = null; + + // Initialize the final result. + var result = { + props: null, + items: null + }; + + // Cancel the ajax requests. + var abort = function(textStatus) { + if (textStatus == null) textStatus = "abort"; + + // -- XMLHttpRequest.readyState -- + // Value State Description + // 0 UNSENT Client has been created. open() not called yet. + // 4 DONE The operation is complete. + + if (vcJqXHR.readyState !== 0 && vcJqXHR.readyState !== 4) + vcJqXHR.abort(textStatus); + if (itemsJqXHR.readyState !== 0 && itemsJqXHR.readyState !== 4) + itemsJqXHR.abort(textStatus); + }; + + // Check if the required data is complete. + var checkResult = function() { + return result.props !== null && result.items !== null; + }; + + // Failed request handler. + var handleFail = function(jqXHR, textStatus, errorThrown) { + abort(); + // Manually aborted or not. + if (textStatus === "abort") { + callback(); + } else { + var error = new Error(errorThrown); + error.request = jqXHR; + callback(error); + } + }; + + // Curried function which handle success. + var handleSuccess = function(key) { + // Actual request handler. + return function(data) { + result[key] = data; + if (checkResult()) callback(null, result); + }; + }; + + // Visual Console container request. + vcJqXHR = jQuery + // .get(apiPath + "/visual-consoles/" + vcId, null, "json") + .get( + apiPath, + { + page: "include/rest-api/index", + getVisualConsole: 1, + visualConsoleId: vcId + }, + "json" + ) + .done(handleSuccess("props")) + .fail(handleFail); + // Visual Console items request. + itemsJqXHR = jQuery + // .get(apiPath + "/visual-consoles/" + vcId + "/items", null, "json") + .get( + apiPath, + { + page: "include/rest-api/index", + getVisualConsoleItems: 1, + visualConsoleId: vcId + }, + "json" + ) + .done(handleSuccess("items")) + .fail(handleFail); + + // Abortable. + return { + abort: abort + }; +} + +// TODO: Delete the functions below when you can. /************************************** These functions require jQuery library **************************************/ diff --git a/pandora_console/include/rest-api/index.php b/pandora_console/include/rest-api/index.php new file mode 100644 index 0000000000..10f87488c7 --- /dev/null +++ b/pandora_console/include/rest-api/index.php @@ -0,0 +1,49 @@ + $visualConsoleId]); + $visualConsoleData = $visualConsole->toArray(); + $groupId = $visualConsoleData['groupId']; + + // 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'); + + if (!$aclRead && !$aclWrite && !$aclManage) { + db_pandora_audit( + 'ACL Violation', + 'Trying to access visual console without group access' + ); + exit; + } + + echo $visualConsole; +} else if ($getVisualConsoleItems === true) { + $vcItems = VisualConsole::getItemsFromDB($visualConsoleId, $aclUserGroups); + echo '['.implode($vcItems, ',').']'; +} + +exit; diff --git a/pandora_console/include/rest-api/models/Model.php b/pandora_console/include/rest-api/models/Model.php new file mode 100644 index 0000000000..78ef90468e --- /dev/null +++ b/pandora_console/include/rest-api/models/Model.php @@ -0,0 +1,231 @@ +validateData($unknownData); + $this->data = $this->decode($unknownData); + // Sort alphabetically. + ksort($this->data, (SORT_NATURAL | SORT_FLAG_CASE)); + } + + + /** + * Instance the class with the unknown input data. + * + * @param array $data Unknown data structure. + * + * @return self Instance of the model. + */ + public static function fromArray(array $data) + { + // The reserved word static refers to the invoked class at runtime. + return new static($data); + } + + + /** + * Obtain a data structure from the database using a filter. + * + * @param array $filter Filter to retrieve the modeled element. + * + * @return array The modeled element data structure stored into the DB. + * @throws \Exception When the data cannot be retrieved from the DB. + * + * @abstract + */ + abstract protected static function fetchDataFromDB(array $filter); + + + /** + * Obtain a model's instance from the database using a filter. + * + * @param array $filter Filter to retrieve the modeled element. + * + * @return self A modeled element's instance. + */ + public static function fromDB(array $filter): self + { + // The reserved word static refers to the invoked class at runtime. + return static::fromArray(static::fetchDataFromDB($filter)); + } + + + /** + * JSON representation of the model. + * + * @return string + */ + public function toArray(): array + { + return $this->data; + } + + + /** + * JSON representation of the model. + * + * @return string + */ + public function toJson(): string + { + return \json_encode($this->data); + } + + + /** + * Text representation of the model. + * + * @return string + */ + public function __toString(): string + { + return $this->toJson(); + } + + + /* + * ------------- + * - UTILITIES - + * ------------- + */ + + + /** + * From a unknown value, it will try to extract a valid boolean value. + * + * @param mixed $value Unknown input. + * + * @return boolean Valid boolean value. + */ + protected static function parseBool($value): bool + { + if (\is_bool($value) === true) { + return $value; + } else if (\is_numeric($value) === true) { + return $value > 0; + } else if (\is_string($value) === true) { + return $value === '1' || $value === 'true'; + } else { + return false; + } + } + + + /** + * Return a not empty string or a default value from a unknown value. + * + * @param mixed $val Input value. + * @param mixed $def Default value. + * + * @return mixed A valid string (not empty) extracted from the input + * or the default value. + */ + protected static function notEmptyStringOr($val, $def) + { + return (\is_string($val) === true && strlen($val) > 0) ? $val : $def; + } + + + /** + * Return a valid integer or a default value from a unknown value. + * + * @param mixed $val Input value. + * @param mixed $def Default value. + * + * @return mixed A valid int extracted from the input or the default value. + */ + protected static function parseIntOr($val, $def) + { + return (is_numeric($val) === true) ? (int) $val : $def; + } + + + /** + * Return a valid float or a default value from a unknown value. + * + * @param mixed $val Input value. + * @param mixed $def Default value. + * + * @return mixed A valid float extracted from the input or the + * default value. + */ + protected static function parseFloatOr($val, $def) + { + return (is_numeric($val) === true) ? (float) $val : $def; + } + + + /** + * Get a value from a dictionary from a possible pool of keys. + * + * @param array $dict Input array. + * @param array $keys Possible keys. + * + * @return mixed The first value found with the pool of keys or null. + */ + protected static function issetInArray(array $dict, array $keys) + { + foreach ($keys as $key => $value) { + if (isset($dict[$value]) === true) { + return $dict[$value]; + } + } + + return null; + } + + +} diff --git a/pandora_console/include/rest-api/models/VisualConsole/Container.php b/pandora_console/include/rest-api/models/VisualConsole/Container.php new file mode 100644 index 0000000000..720fa2f556 --- /dev/null +++ b/pandora_console/include/rest-api/models/VisualConsole/Container.php @@ -0,0 +1,396 @@ + (int) $data['id'], + 'name' => $data['name'], + 'groupId' => static::extractGroupId($data), + 'backgroundImage' => static::extractBackgroundImage($data), + 'backgroundColor' => static::extractBackgroundColor($data), + 'isFavorite' => static::extractFavorite($data), + 'width' => (int) $data['width'], + 'height' => (int) $data['height'], + 'backgroundURL' => static::extractBackgroundUrl($data), + 'relationLineWidth' => (int) $data['relationLineWidth'], + ]; + } + + + /** + * Extract a group Id value. + * + * @param array $data Unknown input data structure. + * + * @return integer Valid identifier of a group. + * + * @throws \InvalidArgumentException When a valid group Id can't be found. + */ + private static function extractGroupId(array $data): int + { + $groupId = static::parseIntOr( + static::issetInArray($data, ['id_group', 'groupId']), + null + ); + + if ($groupId === null || $groupId < 0) { + throw new \InvalidArgumentException( + 'the group Id property is required and should be integer' + ); + } + + return $groupId; + } + + + /** + * Extract a image name value. + * + * @param array $data Unknown input data structure. + * + * @return mixed String representing the image name (not empty) or null. + */ + private static function extractBackgroundImage(array $data) + { + $backgroundImage = static::notEmptyStringOr( + static::issetInArray($data, ['background', 'backgroundURL']), + null + ); + + return ($backgroundImage === 'None.png') ? null : $backgroundImage; + } + + + /** + * Extract a image url value. + * + * @param array $data Unknown input data structure. + * + * @return mixed String representing the image url (not empty) or null. + */ + private static function extractBackgroundUrl(array $data) + { + return static::notEmptyStringOr( + static::issetInArray($data, ['backgroundURL']), + null + ); + } + + + /** + * Extract a background color value. + * + * @param array $data Unknown input data structure. + * + * @return mixed String representing the color (not empty) or null. + */ + private static function extractBackgroundColor(array $data) + { + return static::notEmptyStringOr( + static::issetInArray( + $data, + [ + 'backgroundColor', + 'background_color', + ] + ), + null + ); + } + + + /** + * Extract the "is favorite" switch value. + * + * @param array $data Unknown input data structure. + * + * @return boolean If the item is favorite or not. + */ + private static function extractFavorite(array $data): bool + { + return static::parseBool( + static::issetInArray($data, ['is_favourite', 'isFavorite']) + ); + } + + + /** + * Obtain a container data structure from the database using a filter. + * + * @param array $filter Filter of the Visual Console. + * + * @return self A Visual Console Container instance. + * @throws \Exception When the data cannot be retrieved from the DB. + * + * @override Model::fetchDataFromDB. + */ + protected static function fetchDataFromDB(array $filter) + { + // Due to this DB call, this function cannot be unit tested without + // a proper mock. + $row = \db_get_row_filter('tlayout', $filter); + + if ($row === false) { + throw new \Exception('error fetching the data from the DB'); + } + + // Load side libraries. + global $config; + include_once $config['homedir'].'/include/functions_io.php'; + include_once $config['homedir'].'/include/functions_ui.php'; + + // Clean HTML entities. + $row = \io_safe_output($row); + + $row['relationLineWidth'] = (int) $config['vc_line_thickness']; + + $backgroundUrl = static::extractBackgroundUrl($row); + $backgroundImage = static::extractBackgroundImage($row); + + if ($backgroundUrl === null && $backgroundImage !== null) { + $backgroundPath = 'images/console/background/'.$backgroundImage; + + $width = (int) $row['width']; + $height = (int) $row['height']; + + if ($width > 0 && $height > 0) { + $q = [ + 'getFile' => 1, + 'thumb' => 1, + 'thumb_size' => $width.'x'.$height, + 'file' => '../../'.$backgroundPath, + ]; + $row['backgroundURL'] = ui_get_full_url( + 'include/Image/image_functions.php?'.http_build_query($q), + false, + false, + false + ); + } else { + $row['backgroundURL'] = ui_get_full_url( + $backgroundPath, + false, + false, + false + ); + } + } + + return \io_safe_output($row); + } + + + /** + * Obtain a item's class. + * + * @param integer $type Type of the item of the Visual Console. + * + * @return mixed A reference to the item's class. + */ + public static function getItemClass(int $type) + { + switch ($type) { + case STATIC_GRAPH: + return Items\StaticGraph::class; + + case MODULE_GRAPH: + return Items\ModuleGraph::class; + + case SIMPLE_VALUE: + case SIMPLE_VALUE_MAX: + case SIMPLE_VALUE_MIN: + case SIMPLE_VALUE_AVG: + return Items\SimpleValue::class; + + case PERCENTILE_BAR: + case PERCENTILE_BUBBLE: + case CIRCULAR_PROGRESS_BAR: + case CIRCULAR_INTERIOR_PROGRESS_BAR: + return Items\Percentile::class; + + case LABEL: + return Items\Label::class; + + case ICON: + return Items\Icon::class; + + // Enterprise item. It may not exist. + case SERVICE: + return \class_exists('\Enterprise\Models\VisualConsole\Items\Service') ? \Enterprise\Models\VisualConsole\Items\Service::class : Item::class; + + case GROUP_ITEM: + return Items\Group::class; + + case BOX_ITEM: + return Items\Box::class; + + case LINE_ITEM: + return Items\Line::class; + + case AUTO_SLA_GRAPH: + return Items\EventsHistory::class; + + case DONUT_GRAPH: + return Items\DonutGraph::class; + + case BARS_GRAPH: + return Items\BarsGraph::class; + + case CLOCK: + return Items\Clock::class; + + case COLOR_CLOUD: + return Items\ColorCloud::class; + + default: + return Item::class; + } + } + + + /** + * Obtain a list of items which belong to the Visual Console. + * + * @param integer $layoutId Identifier of the Visual Console. + * @param array $groupsFilter Groups can access user. + * + * @return array A list of items. + * @throws \Exception When the data cannot be retrieved from the DB. + */ + public static function getItemsFromDB( + int $layoutId, + array $groupsFilter=[] + ): array { + // Default filter. + $filter = ['id_layout' => $layoutId]; + $fields = [ + 'id', + 'type', + ]; + + // Override the filter if the groups filter is not empty. + if (count($groupsFilter) > 0) { + // Filter group for elements groups. + $filter = []; + $filter[] = \db_format_array_where_clause_sql( + [ + 'id_layout' => $layoutId, + 'element_group' => $groupsFilter, + ] + ); + + // Filter groups for type groups. + // Only true condition if type is GROUP_ITEM. + $filter[] = '('.\db_format_array_where_clause_sql( + [ + 'type' => GROUP_ITEM, + 'id_group' => $groupsFilter, + ] + ).')'; + } + + $rows = \db_get_all_rows_filter( + 'tlayout_data', + $filter, + $fields, + 'OR' + ); + + if ($rows === false) { + $rows = []; + } + + $items = []; + + foreach ($rows as $data) { + $itemId = (int) $data['id']; + $itemType = (int) $data['type']; + $class = static::getItemClass($itemType); + + try { + array_push($items, $class::fromDB(['id' => $itemId])); + } catch (\Throwable $e) { + // TODO: Log this? + } + } + + return $items; + } + + +} diff --git a/pandora_console/include/rest-api/models/VisualConsole/Item.php b/pandora_console/include/rest-api/models/VisualConsole/Item.php new file mode 100644 index 0000000000..122a9549b6 --- /dev/null +++ b/pandora_console/include/rest-api/models/VisualConsole/Item.php @@ -0,0 +1,1107 @@ + (int) $data['id'], + 'type' => (int) $data['type'], + 'label' => static::extractLabel($data), + 'labelPosition' => static::extractLabelPosition($data), + 'isLinkEnabled' => static::extractIsLinkEnabled($data), + 'isOnTop' => static::extractIsOnTop($data), + 'parentId' => static::extractParentId($data), + 'aclGroupId' => static::extractAclGroupId($data), + 'width' => (int) $data['width'], + 'height' => (int) $data['height'], + 'x' => static::extractX($data), + 'y' => static::extractY($data), + ]; + + if (static::$useLinkedModule === true) { + $decodedData = array_merge( + $decodedData, + static::extractLinkedModule($data) + ); + } else if (static::$useLinkedAgent === true) { + $decodedData = array_merge( + $decodedData, + static::extractLinkedAgent($data) + ); + } + + if (static::$useLinkedVisualConsole === true) { + $decodedData = array_merge( + $decodedData, + static::extractLinkedVisualConsole($data) + ); + } + + if (static::$useHtmlOutput === true) { + $decodedData['encodedHtml'] = static::extractEncodedHtml($data); + } + + // Conditionally add the item link. + if ($decodedData['isLinkEnabled'] === true) { + $decodedData['link'] = static::notEmptyStringOr( + static::issetInArray($data, ['link']), + null + ); + } + + return $decodedData; + } + + + /** + * Extract x y axis value. + * + * @param array $data Unknown input data structure. + * + * @return integer Valid x axis position of the item. + */ + private static function extractX(array $data): int + { + return static::parseIntOr( + static::issetInArray($data, ['x', 'pos_x']), + 0 + ); + } + + + /** + * Extract a y axis value. + * + * @param array $data Unknown input data structure. + * + * @return integer Valid y axis position of the item. + */ + private static function extractY(array $data): int + { + return static::parseIntOr( + static::issetInArray($data, ['y', 'pos_y']), + 0 + ); + } + + + /** + * Extract a group Id (for ACL) value. + * + * @param array $data Unknown input data structure. + * + * @return integer Valid identifier of a group. + */ + private static function extractAclGroupId(array $data) + { + return static::parseIntOr( + static::issetInArray($data, ['id_group', 'aclGroupId']), + null + ); + } + + + /** + * Extract a parent Id value. + * + * @param array $data Unknown input data structure. + * + * @return integer Valid identifier of the item's parent. + */ + private static function extractParentId(array $data) + { + return static::parseIntOr( + static::issetInArray($data, ['parentId', 'parent_item']), + null + ); + } + + + /** + * Extract the "is on top" switch value. + * + * @param array $data Unknown input data structure. + * + * @return boolean If the item is on top or not. + */ + private static function extractIsOnTop(array $data): bool + { + return static::parseBool( + static::issetInArray($data, ['isOnTop', 'show_on_top']) + ); + } + + + /** + * Extract the "is link enabled" switch value. + * + * @param array $data Unknown input data structure. + * + * @return boolean If the item has the link enabled or not. + */ + private static function extractIsLinkEnabled(array $data): bool + { + return static::parseBool( + static::issetInArray($data, ['isLinkEnabled', 'enable_link']) + ); + } + + + /** + * Extract a label value. + * + * @param array $data Unknown input data structure. + * + * @return mixed String representing the label (not empty) or null. + */ + private static function extractLabel(array $data) + { + return static::notEmptyStringOr( + static::issetInArray($data, ['label']), + null + ); + } + + + /** + * Extract a label position value. + * + * @param array $data Unknown input data structure. + * + * @return mixed One string of up|right|left|down. down by default. + */ + private static function extractLabelPosition(array $data): string + { + $labelPosition = static::notEmptyStringOr( + static::issetInArray($data, ['labelPosition', 'label_position']), + null + ); + + switch ($labelPosition) { + case 'up': + case 'right': + case 'left': + return $labelPosition; + + default: + return 'down'; + } + } + + + /** + * Extract an agent Id value. + * + * @param array $data Unknown input data structure. + * + * @return integer Valid identifier of an agent. + */ + private static function extractAgentId(array $data) + { + return static::parseIntOr( + static::issetInArray($data, ['agentId', 'id_agent', 'id_agente']), + null + ); + } + + + /** + * Extract an module Id value. + * + * @param array $data Unknown input data structure. + * + * @return integer Valid identifier of a module. + */ + private static function extractModuleId(array $data) + { + return static::parseIntOr( + static::issetInArray( + $data, + [ + 'moduleId', + 'id_agente_modulo', + 'id_modulo', + ] + ), + null + ); + } + + + /** + * Extract an metaconsole node Id value. + * + * @param array $data Unknown input data structure. + * + * @return integer Valid identifier of a metaconsole node. + */ + private static function extractMetaconsoleId(array $data) + { + return static::parseIntOr( + static::issetInArray($data, ['metaconsoleId', 'id_metaconsole']), + null + ); + } + + + /** + * Extract the values of a linked agent. + * + * @param array $data Unknown input data structure. + * + * @return array Data structure of the linked agent info. + * + * @example [ + * 'metaconsoleId' => 1, + * 'agentId' => 2, + * 'agentName' => 'Foo', + * ] + * @example [ + * 'agentId' => 20, + * 'agentName' => 'Bar', + * ] + * @example [ + * 'agentId' => null, + * 'agentName' => null, + * ] + */ + protected static function extractLinkedAgent(array $data): array + { + $agentData = []; + + // We should add the metaconsole Id if we can. + $metaconsoleId = static::extractMetaconsoleId($data); + if ($metaconsoleId !== null && $metaconsoleId <= 0) { + $agentData['metaconsoleId'] = null; + } else { + $agentData['metaconsoleId'] = $metaconsoleId; + } + + // The agent Id should be a valid int or a null value. + $agentData['agentId'] = static::extractAgentId($data); + + // The agent name should be a valid string or a null value. + $agentData['agentName'] = static::notEmptyStringOr( + static::issetInArray($data, ['agentName', 'agent_name']), + null + ); + + // The agent alias should be a valid string or a null value. + $agentData['agentAlias'] = static::notEmptyStringOr( + static::issetInArray($data, ['agentAlias', 'agent_alias']), + null + ); + + // The agent description should be a valid string or a null value. + $agentData['agentDescription'] = static::notEmptyStringOr( + static::issetInArray($data, ['agentDescription', 'agent_description']), + null + ); + + // The agent address should be a valid string or a null value. + $agentData['agentAddress'] = static::notEmptyStringOr( + static::issetInArray($data, ['agentAddress', 'agent_address']), + null + ); + + return $agentData; + } + + + /** + * Extract the values of a linked module. + * + * @param array $data Unknown input data structure. + * + * @return array Data structure of the linked module info. + * + * @example [ + * 'metaconsoleId' => 1, + * 'agentId' => 2, + * 'agentName' => 'Foo', + * 'moduleId' => 1, + * 'moduleName' => 'cpu', + * ] + * @example [ + * 'agentId' => 4, + * 'agentName' => 'Bar', + * 'moduleId' => null, + * 'moduleName' => null, + * ] + * @example [ + * 'agentId' => null, + * 'agentName' => null, + * 'moduleId' => null, + * 'moduleName' => null, + * ] + */ + protected static function extractLinkedModule(array $data): array + { + // Initialize the data with the agent data and then expand it. + $moduleData = static::extractLinkedAgent($data); + + // The module Id should be a valid int or a null value. + $moduleData['moduleId'] = static::extractModuleId($data); + + // The module name should be a valid string or a null value. + $moduleData['moduleName'] = static::notEmptyStringOr( + static::issetInArray($data, ['moduleName', 'module_name']), + null + ); + + // The module description should be a valid string or a null value. + $moduleData['moduleDescription'] = static::notEmptyStringOr( + static::issetInArray($data, ['moduleDescription', 'module_description']), + null + ); + + return $moduleData; + } + + + /** + * Extract the values of a linked visual console. + * + * @param array $data Unknown input data structure. + * + * @return array Data structure of the linked visual console info. + * + * @example [ + * 'metaconsoleId' => 2, + * 'linkedLayoutId' => 12, + * 'linkedLayoutAgentId' => 48, + * 'linkedLayoutStatusType' => 'default', + * ] + * @example [ + * 'linkedLayoutId' => 11, + * 'linkedLayoutAgentId' => null, + * 'linkedLayoutStatusType' => 'weight', + * 'linkedLayoutStatusTypeWeight' => 80, + * ] + * @example [ + * 'metaconsoleId' => 2, + * 'linkedLayoutId' => 10, + * 'linkedLayoutAgentId' => 48, + * 'linkedLayoutStatusType' => 'service', + * 'linkedLayoutStatusTypeWarningThreshold' => 50, + * 'linkedLayoutStatusTypeCriticalThreshold' => 80, + * ] + */ + private static function extractLinkedVisualConsole(array $data): array + { + $vcData = []; + + // We should add the metaconsole Id if we can. If not, + // it doesn't have to be into the structure. + $metaconsoleId = static::extractMetaconsoleId($data); + if ($metaconsoleId !== null) { + $vcData['metaconsoleId'] = $metaconsoleId; + } + + // The linked vc Id should be a valid int or a null value. + $vcData['linkedLayoutId'] = static::parseIntOr( + static::issetInArray($data, ['linkedLayoutId', 'id_layout_linked']), + null + ); + + // The linked vc agent Id should be a valid int or a null value. + $vcData['linkedLayoutAgentId'] = static::parseIntOr( + static::issetInArray( + $data, + [ + 'linkedLayoutAgentId', + 'linked_layout_node_id', + ] + ), + null + ); + + // The linked vc status type should be a enum value. + $linkedLayoutStatusType = static::notEmptyStringOr( + static::issetInArray( + $data, + [ + 'linkedLayoutStatusType', + 'linked_layout_status_type', + ] + ), + null + ); + + // Extract data for the calculation of the linked visual console status. + switch ($linkedLayoutStatusType) { + case 'default': + default: + $vcData['linkedLayoutStatusType'] = 'default'; + break; + + case 'weight': + $vcData['linkedLayoutStatusType'] = 'weight'; + $vcData['linkedLayoutStatusTypeWeight'] = static::parseIntOr( + static::issetInArray( + $data, + [ + 'linkedLayoutStatusTypeWeight', + 'id_layout_linked_weight', + ] + ), + 0 + ); + break; + + case 'service': + $vcData['linkedLayoutStatusType'] = 'service'; + $vcData['linkedLayoutStatusTypeWarningThreshold'] = static::parseIntOr( + static::issetInArray( + $data, + [ + 'linkedLayoutStatusTypeWarningThreshold', + 'linked_layout_status_as_service_warning', + ] + ), + 0 + ); + $vcData['linkedLayoutStatusTypeCriticalThreshold'] = static::parseIntOr( + static::issetInArray( + $data, + [ + 'linkedLayoutStatusTypeCriticalThreshold', + 'linked_layout_status_as_service_critical', + ] + ), + 0 + ); + break; + } + + return $vcData; + } + + + /** + * Extract a encoded HTML representation of the item. + * + * @param array $data Unknown input data structure. + * + * @return string The HTML representation in base64 encoding. + */ + private static function extractEncodedHtml(array $data): string + { + if (isset($data['encodedHtml']) === true) { + return $data['encodedHtml']; + } else if (isset($data['html']) === true) { + return base64_encode($data['html']); + } + + return ''; + } + + + /** + * Fetch a vc item data structure from the database using a filter. + * + * @param array $filter Filter of the Visual Console Item. + * + * @return array The Visual Console Item data structure stored into the DB. + * @throws \Exception When the data cannot be retrieved from the DB. + * + * @override Model::fetchDataFromDB. + */ + protected static function fetchDataFromDB(array $filter): array + { + // Load side libraries. + global $config; + include_once $config['homedir'].'/include/functions_io.php'; + + // Due to this DB call, this function cannot be unit tested without + // a proper mock. + $row = \db_get_row_filter('tlayout_data', $filter); + + if ($row === false) { + throw new \Exception('error fetching the data from the DB'); + } + + // Load side libraries. + global $config; + include_once $config['homedir'].'/include/functions_io.php'; + + // Clean up to two levels of HTML entities. + $row = \io_safe_output(\io_safe_output($row)); + + /* + * Retrieve extra data. + */ + + // The linked module includes the agent data. + if (static::$useLinkedModule === true) { + $row = array_merge($row, static::fetchModuleDataFromDB($row)); + } else if (static::$useLinkedAgent === true) { + $row = array_merge($row, static::fetchAgentDataFromDB($row)); + } + + // Build the item link if needed. + if (static::extractIsLinkEnabled($row) === true) { + $row['link'] = static::buildLink($row); + } + + return $row; + } + + + /** + * Fetch a data structure of an agent from the database using the + * vs item's data. + * + * @param array $itemData Visual Console Item's data structure. + * + * @return array The agent data structure stored into the DB. + * + * @throws \InvalidArgumentException When the input agent Id is invalid. + * @throws \Exception When the data cannot be retrieved from the DB. + */ + protected static function fetchAgentDataFromDB(array $itemData): array + { + $agentData = []; + + // We should add the metaconsole Id if we can. + $metaconsoleId = static::extractMetaconsoleId($itemData); + + // Can't fetch an agent with an invalid Id. + $agentId = static::extractAgentId($itemData); + if ($agentId === null) { + throw new \InvalidArgumentException('invalid agent Id'); + } + + // Staticgraph don't need to have an agent. + if ($agentId === 0) { + return $agentData; + } + + if (\is_metaconsole() && $metaconsoleId === null) { + throw new \InvalidArgumentException('missing metaconsole node Id'); + } + + $agent = false; + + if (\is_metaconsole()) { + $sql = sprintf( + 'SELECT nombre, alias, direccion, comentarios + FROM tmetaconsole_agent + WHERE id_tagente = %s and id_tmetaconsole_setup = %s', + $agentId, + $metaconsoleId + ); + } else { + $sql = sprintf( + 'SELECT nombre, alias, direccion, comentarios + FROM tagente + WHERE id_agente = %s', + $agentId + ); + } + + $agent = \db_get_row_sql($sql); + + if ($agent === false) { + throw new \Exception('error fetching the data from the DB'); + } + + // The agent name should be a valid string or a null value. + $agentData['agentName'] = $agent['nombre']; + $agentData['agentAlias'] = $agent['alias']; + $agentData['agentDescription'] = $agent['comentarios']; + $agentData['agentAddress'] = $agent['direccion']; + + return $agentData; + } + + + /** + * Fetch a data structure of an module from the database using the + * vs item's data. + * + * @param array $itemData Visual Console Item's data structure. + * + * @return array The module data structure stored into the DB. + * @throws \InvalidArgumentException When the input module Id is invalid. + * @throws \Exception When the data cannot be retrieved from the DB. + */ + protected static function fetchModuleDataFromDB(array $itemData): array + { + // Load side libraries. + if (\is_metaconsole()) { + \enterprise_include_once('include/functions_metaconsole.php'); + } + + // Initialize with the agent data. + $moduleData = static::fetchAgentDataFromDB($itemData); + + // Can't fetch an module with a invalid Id. + $moduleId = static::extractModuleId($itemData); + if ($moduleId === null) { + throw new \InvalidArgumentException('invalid module Id'); + } + + // Staticgraph don't need to have a module. + if ($moduleId === 0) { + return $moduleData; + } + + // We should add the metaconsole Id if we can. + $metaconsoleId = static::extractMetaconsoleId($itemData); + + if (\is_metaconsole() && $metaconsoleId === null) { + throw new \InvalidArgumentException('missing metaconsole node Id'); + } + + $moduleName = false; + + // Connect to node. + if (\is_metaconsole() + && \metaconsole_connect(null, $metaconsoleId) !== NOERR + ) { + throw new \InvalidArgumentException( + 'error connecting to the node' + ); + } + + $sql = sprintf( + 'SELECT nombre, descripcion + FROM tagente_modulo + WHERE id_agente_modulo = %s', + $moduleId + ); + + $moduleName = \db_get_row_sql($sql); + + // Restore connection. + if (\is_metaconsole()) { + \metaconsole_restore_db(); + } + + if ($moduleName === false) { + throw new \Exception('error fetching the data from the DB'); + } + + $moduleData['moduleName'] = $moduleName['nombre']; + $moduleData['moduleDescription'] = $moduleName['descripcion']; + + return $moduleData; + } + + + /** + * Generate a link to something related with the item. + * + * @param array $data Visual Console Item's data structure. + * + * @return mixed The link or a null value. + * @throws \Exception Not really. It's controlled. + */ + protected static function buildLink(array $data) + { + global $config; + + // Load side libraries. + if (\is_metaconsole()) { + \enterprise_include_once('include/functions_metaconsole.php'); + \enterprise_include_once('meta/include/functions_ui_meta.php'); + } + + $linkedVisualConsole = static::extractLinkedVisualConsole($data); + $linkedModule = static::extractLinkedModule($data); + $linkedAgent = static::extractLinkedAgent($data); + + $baseUrl = $config['homeurl'].'index.php'; + + // TODO: There's a feature to get the link from the label. + if (static::$useLinkedVisualConsole === true + && $linkedVisualConsole['linkedLayoutId'] !== null + && $linkedVisualConsole['linkedLayoutId'] > 0 + ) { + // Linked Visual Console. + $vcId = $linkedVisualConsole['linkedLayoutId']; + // The layout can be from another node. + $metaconsoleId = $linkedVisualConsole['metaconsoleId']; + + if (empty($metaconsoleId) === true && \is_metaconsole()) { + /* + * A Visual Console from this console. + * We are in a metaconsole. + */ + + return $baseUrl.'?'.http_build_query( + [ + 'sec' => 'screen', + 'sec2' => 'screens/screens', + 'action' => 'visualmap', + 'id_visualmap' => $vcId, + 'pure' => (int) $config['pure'], + ] + ); + } else if (empty($metaconsoleId) === true && !\is_metaconsole()) { + /* + * A Visual Console from this console. + * We are in a regular console. + */ + + return $baseUrl.'?'.http_build_query( + [ + 'sec' => 'network', + 'sec2' => 'operation/visual_console/view', + 'id' => $vcId, + 'pure' => (int) $config['pure'], + ] + ); + } else if (\is_metaconsole() && \can_user_access_node()) { + /* + * A Visual Console from a meta node. + * We are in a metaconsole. + */ + + try { + $node = \metaconsole_get_connection_by_id( + $metaconsoleId + ); + return \ui_meta_get_node_url( + $node, + 'network', + // TODO: Link to a public view. + 'operation/visual_console/view', + [], + // No autologin from the public view. + !$config['public_view'] + ); + } catch (\Throwable $ignored) { + return null; + } + } + } else { + if (static::$useLinkedModule === true + && $linkedModule['moduleId'] !== null + && $linkedModule['moduleId'] > 0 + ) { + // Module Id. + $moduleId = $linkedModule['moduleId']; + // The module can be from another node. + $metaconsoleId = $linkedModule['metaconsoleId']; + + if (empty($metaconsoleId) === true) { + /* + * A module from this console. + */ + + // Check if the module is from a service. + $serviceId = (int) \db_get_value_filter( + 'custom_integer_1', + 'tagente_modulo', + [ + 'id_agente_modulo' => $moduleId, + 'prediction_module' => 1, + ] + ); + + if (empty($serviceId) === false) { + // A service. + $queryParams = [ + 'sec' => 'services', + 'sec2' => 'enterprise/operation/services/services', + 'id_service' => $serviceId, + ]; + } else { + // A regular module. + $queryParams = [ + 'sec' => 'view', + 'sec2' => 'operation/agentes/status_monitor', + 'id_module' => $moduleId, + ]; + } + + return $baseUrl.'?'.http_build_query($queryParams); + } else if (\is_metaconsole() && \can_user_access_node()) { + /* + * A module from a meta node. + * We are in a metaconsole. + */ + + try { + $node = \metaconsole_get_connection_by_id( + $metaconsoleId + ); + + // Connect to node. + if (\metaconsole_connect($node) !== NOERR) { + // Will be catched below. + throw new \Exception( + 'error connecting to the node' + ); + } + + // Check if the module is a service. + $serviceId = (int) \db_get_value_filter( + 'custom_integer_1', + 'tagente_modulo', + [ + 'id_agente_modulo' => $moduleId, + 'prediction_module' => 1, + ] + ); + + // Restore connection. + \metaconsole_restore_db(); + + if (empty($serviceId) === false) { + // A service. + return \ui_meta_get_node_url( + $node, + 'services', + 'enterprise/operation/services/services', + ['id_service' => $serviceId], + // No autologin from the public view. + !$config['public_view'] + ); + } else { + // A regular module. + return \ui_meta_get_node_url( + $node, + 'view', + 'operation/agentes/status_monitor', + ['id_module' => $moduleId], + // No autologin from the public view. + !$config['public_view'] + ); + } + } catch (\Throwable $ignored) { + return null; + } + } + } else if (static::$useLinkedAgent === true + && $linkedAgent['agentId'] !== null + && $linkedAgent['agentId'] > 0 + ) { + // Linked agent. + // Agent Id. + $agentId = $linkedAgent['agentId']; + // The agent can be from another node. + $metaconsoleId = $linkedAgent['metaconsoleId']; + + if (empty($metaconsoleId) === true) { + /* + * An agent from this console. + * We are in a regular console. + */ + + return $baseUrl.'?'.http_build_query( + [ + 'sec' => 'estado', + 'sec2' => 'operation/agentes/ver_agente', + 'id_agente' => $agentId, + ] + ); + } else if (\is_metaconsole() && \can_user_access_node()) { + /* + * An agent from a meta node. + * We are in a metaconsole. + */ + + try { + $node = \metaconsole_get_connection_by_id( + $metaconsoleId + ); + return \ui_meta_get_node_url( + $node, + 'estado', + 'operation/agentes/ver_agente', + ['id_agente' => $moduleId], + // No autologin from the public view. + !$config['public_view'] + ); + } catch (\Throwable $ignored) { + return null; + } + } + } + } + + return null; + } + + +} diff --git a/pandora_console/include/rest-api/models/VisualConsole/Items/BarsGraph.php b/pandora_console/include/rest-api/models/VisualConsole/Items/BarsGraph.php new file mode 100644 index 0000000000..4482b4d53d --- /dev/null +++ b/pandora_console/include/rest-api/models/VisualConsole/Items/BarsGraph.php @@ -0,0 +1,346 @@ +extractGridColor($data); + $return['backgroundColor'] = $this->extractBackgroundColor($data); + $return['typeGraph'] = $this->extractTypeGraph($data); + return $return; + } + + + /** + * Extract a grid color value. + * + * @param array $data Unknown input data structure. + * + * @return mixed String representing the grid color (not empty) or null. + */ + private function extractGridColor(array $data): string + { + return static::notEmptyStringOr( + static::issetInArray($data, ['gridColor', 'border_color']), + '#000000' + ); + } + + + /** + * Extract a background color value. + * + * @param array $data Unknown input data structure. + * + * @return string One of 'white', 'black' or 'transparent'. + * 'white' by default. + */ + private function extractBackgroundColor(array $data): string + { + $backgroundColor = static::notEmptyStringOr( + static::issetInArray($data, ['backgroundColor', 'image']), + null + ); + + switch ($backgroundColor) { + case 'black': + case 'transparent': + return $backgroundColor; + + default: + return 'white'; + } + } + + + /** + * Extract a type graph value. + * + * @param array $data Unknown input data structure. + * + * @return string One of 'vertical' or 'horizontal'. 'vertical' by default. + */ + private function extractTypeGraph(array $data): string + { + $typeGraph = static::notEmptyStringOr( + static::issetInArray($data, ['typeGraph', 'type_graph']), + null + ); + + switch ($typeGraph) { + case 'horizontal': + return 'horizontal'; + + default: + return 'vertical'; + } + } + + + /** + * Fetch a vc item data structure from the database using a filter. + * + * @param array $filter Filter of the Visual Console Item. + * + * @return array The Visual Console Item data structure stored into the DB. + * @throws \InvalidArgumentException When an agent Id cannot be found. + * + * @override Item::fetchDataFromDB. + */ + protected static function fetchDataFromDB(array $filter): array + { + // Due to this DB call, this function cannot be unit tested without + // a proper mock. + $data = parent::fetchDataFromDB($filter); + + /* + * Retrieve extra data. + */ + + // Load config. + global $config; + + // Load side libraries. + include_once $config['homedir'].'/include/functions_ui.php'; + include_once $config['homedir'].'/include/functions_visual_map.php'; + include_once $config['homedir'].'/include/graphs/fgraph.php'; + + if (is_metaconsole()) { + \enterprise_include_once('include/functions_metaconsole.php'); + } + + // Extract needed properties. + $gridColor = static::extractGridColor($data); + $backGroundColor = static::extractBackgroundColor($data); + $typeGraph = static::extractTypeGraph($data); + + // Get the linked agent and module Ids. + $linkedModule = static::extractLinkedModule($data); + $agentId = $linkedModule['agentId']; + $moduleId = $linkedModule['moduleId']; + $metaconsoleId = $linkedModule['metaconsoleId']; + + if ($agentId === null) { + throw new \InvalidArgumentException('missing agent Id'); + } + + if ($moduleId === null) { + throw new \InvalidArgumentException('missing module Id'); + } + + // Add colors that will use the graphics. + $color = []; + + $color[0] = [ + 'border' => '#000000', + 'color' => $config['graph_color1'], + 'alpha' => CHART_DEFAULT_ALPHA, + ]; + $color[1] = [ + 'border' => '#000000', + 'color' => $config['graph_color2'], + 'alpha' => CHART_DEFAULT_ALPHA, + ]; + $color[2] = [ + 'border' => '#000000', + 'color' => $config['graph_color3'], + 'alpha' => CHART_DEFAULT_ALPHA, + ]; + $color[3] = [ + 'border' => '#000000', + 'color' => $config['graph_color4'], + 'alpha' => CHART_DEFAULT_ALPHA, + ]; + $color[4] = [ + 'border' => '#000000', + 'color' => $config['graph_color5'], + 'alpha' => CHART_DEFAULT_ALPHA, + ]; + $color[5] = [ + 'border' => '#000000', + 'color' => $config['graph_color6'], + 'alpha' => CHART_DEFAULT_ALPHA, + ]; + $color[6] = [ + 'border' => '#000000', + 'color' => $config['graph_color7'], + 'alpha' => CHART_DEFAULT_ALPHA, + ]; + $color[7] = [ + 'border' => '#000000', + 'color' => $config['graph_color8'], + 'alpha' => CHART_DEFAULT_ALPHA, + ]; + $color[8] = [ + 'border' => '#000000', + 'color' => $config['graph_color9'], + 'alpha' => CHART_DEFAULT_ALPHA, + ]; + $color[9] = [ + 'border' => '#000000', + 'color' => $config['graph_color10'], + 'alpha' => CHART_DEFAULT_ALPHA, + ]; + $color[11] = [ + 'border' => '#000000', + 'color' => COL_GRAPH9, + 'alpha' => CHART_DEFAULT_ALPHA, + ]; + $color[12] = [ + 'border' => '#000000', + 'color' => COL_GRAPH10, + 'alpha' => CHART_DEFAULT_ALPHA, + ]; + $color[13] = [ + 'border' => '#000000', + 'color' => COL_GRAPH11, + 'alpha' => CHART_DEFAULT_ALPHA, + ]; + $color[14] = [ + 'border' => '#000000', + 'color' => COL_GRAPH12, + 'alpha' => CHART_DEFAULT_ALPHA, + ]; + $color[15] = [ + 'border' => '#000000', + 'color' => COL_GRAPH13, + 'alpha' => CHART_DEFAULT_ALPHA, + ]; + + // Maybe connect to node. + $nodeConnected = false; + if (\is_metaconsole() === true && $metaconsoleId !== null) { + $nodeConnected = \metaconsole_connect( + null, + $metaconsoleId + ) === NOERR; + + if ($nodeConnected === false) { + throw new \InvalidArgumentException( + 'error connecting to the node' + ); + } + } + + $moduleData = \get_bars_module_data($moduleId); + + $waterMark = [ + 'file' => $config['homedir'].'/images/logo_vertical_water.png', + 'url' => \ui_get_full_url( + 'images/logo_vertical_water.png', + false, + false, + false + ), + ]; + + if ((int) $data['width'] === 0 || (int) $data['height'] === 0) { + $width = 400; + $height = 400; + } else { + $width = (int) $data['width']; + $height = (int) $data['height']; + } + + if ($typeGraph === 'horizontal') { + $graph = \hbar_graph( + $moduleData, + $width, + $height, + $color, + [], + [], + \ui_get_full_url( + 'images/image_problem_area.png', + false, + false, + false + ), + '', + '', + $waterMark, + $config['fontpath'], + 6, + '', + 0, + $config['homeurl'], + $backGroundColor, + $gridColor + ); + } else { + $graph = \vbar_graph( + $moduleData, + $width, + $height, + $color, + [], + [], + \ui_get_full_url( + 'images/image_problem_area.png', + false, + false, + false + ), + '', + '', + $waterMark, + $config['fontpath'], + 6, + '', + 0, + $config['homeurl'], + $backGroundColor, + true, + false, + $gridColor + ); + } + + // Restore connection. + if ($nodeConnected === true) { + \metaconsole_restore_db(); + } + + $data['html'] = $graph; + + return $data; + } + + +} diff --git a/pandora_console/include/rest-api/models/VisualConsole/Items/Box.php b/pandora_console/include/rest-api/models/VisualConsole/Items/Box.php new file mode 100644 index 0000000000..144518ebb1 --- /dev/null +++ b/pandora_console/include/rest-api/models/VisualConsole/Items/Box.php @@ -0,0 +1,85 @@ +extractBorderWidth($data); + $boxData['borderColor'] = $this->extractBorderColor($data); + $boxData['fillColor'] = $this->extractFillColor($data); + return $boxData; + } + + + /** + * Extract a border width value. + * + * @param array $data Unknown input data structure. + * + * @return integer Valid border width. 0 by default. + */ + private function extractBorderWidth(array $data): int + { + return static::parseIntOr( + static::issetInArray($data, ['borderWidth', 'border_width']), + 0 + ); + } + + + /** + * Extract a border color value. + * + * @param array $data Unknown input data structure. + * + * @return mixed String representing the border color (not empty) or null. + */ + private function extractBorderColor(array $data) + { + return static::notEmptyStringOr( + static::issetInArray($data, ['borderColor', 'border_color']), + null + ); + } + + + /** + * Extract a fill color value. + * + * @param array $data Unknown input data structure. + * + * @return mixed String representing the fill color (not empty) or null. + */ + private function extractFillColor(array $data) + { + return static::notEmptyStringOr( + static::issetInArray($data, ['fillColor', 'fill_color']), + null + ); + } + + +} diff --git a/pandora_console/include/rest-api/models/VisualConsole/Items/Clock.php b/pandora_console/include/rest-api/models/VisualConsole/Items/Clock.php new file mode 100644 index 0000000000..ba72e0c30c --- /dev/null +++ b/pandora_console/include/rest-api/models/VisualConsole/Items/Clock.php @@ -0,0 +1,154 @@ +getOffset( + $dateTimeUtc + ); + } catch (\Throwable $e) { + throw new \InvalidArgumentException($e->getMessage()); + } + + // $clockData['showClockTimezone'] = static::parseBool( + // static::issetInArray($data, ['showClockTimezone']) + // ); + // TODO: Remove the true by default when added into the editor. + $clockData['showClockTimezone'] = true; + $clockData['color'] = static::extractColor($data); + return $clockData; + } + + + /** + * Extract a clock type value. + * + * @param array $data Unknown input data structure. + * + * @return string One of 'digital' or 'analogic'. 'analogic' by default. + */ + private static function extractClockType(array $data): string + { + $clockType = static::notEmptyStringOr( + static::issetInArray($data, ['clockType', 'clock_animation']), + null + ); + + switch ($clockType) { + case 'digital': + case 'digital_1': + return 'digital'; + + default: + return 'analogic'; + } + } + + + /** + * Extract a clock format value. + * + * @param array $data Unknown input data structure. + * + * @return string One of 'time' or 'datetime'. 'datetime' by default. + */ + private static function extractClockFormat(array $data): string + { + $clockFormat = static::notEmptyStringOr( + static::issetInArray($data, ['clockFormat', 'time_format']), + null + ); + + switch ($clockFormat) { + case 'time': + return 'time'; + + default: + return 'datetime'; + } + } + + + /** + * Extract a clock timezone value. + * + * @param array $data Unknown input data structure. + * + * @return string + * @throws \InvalidArgumentException When a valid clock timezone cannot be + * extracted. + */ + private static function extractClockTimezone(array $data): string + { + $clockTimezone = static::notEmptyStringOr( + static::issetInArray($data, ['clockTimezone', 'timezone']), + null + ); + + if ($clockTimezone === null) { + throw new \InvalidArgumentException( + 'the clockTimezone property is required and should be string' + ); + } + + return $clockTimezone; + } + + + /** + * Extract the color value. + * + * @param array $data Unknown input data structure. + * + * @return mixed returns a color or null. + */ + private static function extractColor(array $data) + { + return static::notEmptyStringOr( + static::issetInArray($data, ['color', 'fill_color']), + null + ); + } + + +} diff --git a/pandora_console/include/rest-api/models/VisualConsole/Items/ColorCloud.php b/pandora_console/include/rest-api/models/VisualConsole/Items/ColorCloud.php new file mode 100644 index 0000000000..807d8fb3e9 --- /dev/null +++ b/pandora_console/include/rest-api/models/VisualConsole/Items/ColorCloud.php @@ -0,0 +1,281 @@ +decode. + */ + protected function decode(array $data): array + { + $decodedData = parent::decode($data); + $decodedData['type'] = COLOR_CLOUD; + $decodedData['label'] = null; + $decodedData['defaultColor'] = static::extractDefaultColor($data); + $decodedData['colorRanges'] = static::extractColorRanges($data); + $decodedData['color'] = static::notEmptyStringOr( + static::issetInArray($data, ['color']), + null + ); + + return $decodedData; + } + + + /** + * Extract the default color value. + * + * @param array $data Unknown input data structure. + * + * @return string Default color. + * @throws \InvalidArgumentException If the default color cannot be + * extracted. + */ + private static function extractDefaultColor(array $data): string + { + if (isset($data['defaultColor'])) { + $defaultColor = static::notEmptyStringOr( + $data['defaultColor'], + null + ); + + if ($defaultColor === null) { + throw new \InvalidArgumentException( + 'the default color property is required and should be a not empty string' + ); + } + + return $defaultColor; + } else { + $dynamicData = static::extractDynamicData($data); + return $dynamicData['defaultColor']; + } + } + + + /** + * Extract a list of color ranges. + * + * @param array $data Unknown input data structure. + * + * @return array Color ranges list. + * @throws \InvalidArgumentException If any of the color ranges is invalid. + */ + private static function extractColorRanges(array $data): array + { + if (isset($data['colorRanges']) && \is_array($data['colorRanges'])) { + // Validate the color ranges. + foreach ($data['colorRanges'] as $colorRange) { + if (\is_numeric($colorRange['fromValue']) === false + || \is_numeric($colorRange['toValue']) === false + || static::notEmptyStringOr($colorRange['color'], null) === null + ) { + throw new \InvalidArgumentException('invalid color range'); + } + } + + return $data['colorRanges']; + } else if (isset($data['label']) === true) { + $dynamicData = static::extractDynamicData($data); + return $dynamicData['colorRanges']; + } else { + return []; + } + } + + + /** + * Extract a dynamic data structure from the 'label' field. + * + * @param array $data Unknown input data structure. + * + * @return array Dynamic data structure. + * @throws \InvalidArgumentException If the structure cannot be built. + * + * @example [ + * 'defaultColor' => '#FFF', + * 'colorRanges' => [ + * [ + * 'fromValue' => 50.0, + * 'toValue' => 150.5, + * 'color' => '#000', + * ], + * [ + * 'fromValue' => 200.0, + * 'toValue' => 300.5, + * 'color' => '#F0F0F0', + * ], + * ] + * ] + */ + private static function extractDynamicData(array $data): array + { + $dynamicDataEncoded = static::notEmptyStringOr($data['label'], null); + + if ($dynamicDataEncoded === null) { + throw new \InvalidArgumentException('dynamic data not found'); + } + + $result = []; + + try { + $dynamicData = \json_decode($dynamicDataEncoded, true); + + $result['defaultColor'] = $dynamicData['default_color']; + $result['colorRanges'] = []; + + if (\is_array($dynamicData['color_ranges']) === true) { + foreach ($dynamicData['color_ranges'] as $colorRange) { + if (\is_numeric($colorRange['from_value']) === true + && \is_numeric($colorRange['to_value']) === true + && static::notEmptyStringOr( + $colorRange['color'], + null + ) !== null + ) { + $result['colorRanges'][] = [ + 'color' => $colorRange['color'], + 'fromValue' => (float) $colorRange['from_value'], + 'toValue' => (float) $colorRange['to_value'], + ]; + } + } + } + } catch (\Throwable $e) { + throw new \InvalidArgumentException('invalid dynamic data'); + } + + return $result; + } + + + /** + * Fetch a vc item data structure from the database using a filter. + * + * @param array $filter Filter of the Visual Console Item. + * + * @return array The Visual Console Item data structure stored into the DB. + * @throws \InvalidArgumentException When an agent Id cannot be found. + * + * @override Item::fetchDataFromDB. + */ + protected static function fetchDataFromDB(array $filter): array + { + // Due to this DB call, this function cannot be unit tested without + // a proper mock. + $data = parent::fetchDataFromDB($filter); + + /* + * Retrieve extra data. + */ + + // Load side libraries. + global $config; + include_once $config['homedir'].'/include/functions_modules.php'; + if (is_metaconsole()) { + \enterprise_include_once('include/functions_metaconsole.php'); + } + + // Get the linked module Id. + $linkedModule = static::extractLinkedModule($data); + $moduleId = $linkedModule['moduleId']; + $metaconsoleId = $linkedModule['metaconsoleId']; + + if ($moduleId === null) { + throw new \InvalidArgumentException('missing module Id'); + } + + $dynamicData = static::extractDynamicData($data); + // Set the initial color. + $data['color'] = $dynamicData['defaultColor']; + + // Search for a matching color range. + if (empty($dynamicData['colorRanges']) === false) { + // Connect to node. + $nodeConnected = false; + if (\is_metaconsole() === true && $metaconsoleId !== null) { + $nodeConnected = \metaconsole_connect( + null, + $metaconsoleId + ) === NOERR; + + if ($nodeConnected === false) { + throw new \InvalidArgumentException( + 'error connecting to the node' + ); + } + } + + // Fetch module value. + $value = false; + if ($metaconsoleId === null + || ($metaconsoleId !== null && $nodeConnected) + ) { + $value = \modules_get_last_value($moduleId); + } + + // Restore connection. + if ($nodeConnected === true) { + \metaconsole_restore_db(); + } + + // Value found. + if ($value !== false) { + /* + * TODO: It would be ok to give support to string values in the + * future? + * + * It can be done by matching the range value with the value + * if it is a string. I think the function to retrieve the value + * only supports numeric values. + */ + + $value = (float) $value; + foreach ($dynamicData['colorRanges'] as $colorRange) { + if ($colorRange['fromValue'] <= $value + && $colorRange['toValue'] >= $value + ) { + // Range matched. Use the range color. + $data['color'] = $colorRange['color']; + break; + } + } + } + } + + return $data; + } + + +} diff --git a/pandora_console/include/rest-api/models/VisualConsole/Items/DonutGraph.php b/pandora_console/include/rest-api/models/VisualConsole/Items/DonutGraph.php new file mode 100644 index 0000000000..1adc948da2 --- /dev/null +++ b/pandora_console/include/rest-api/models/VisualConsole/Items/DonutGraph.php @@ -0,0 +1,193 @@ +'; + } + + return $data; + } + + +} diff --git a/pandora_console/include/rest-api/models/VisualConsole/Items/EventsHistory.php b/pandora_console/include/rest-api/models/VisualConsole/Items/EventsHistory.php new file mode 100644 index 0000000000..fab34c26bf --- /dev/null +++ b/pandora_console/include/rest-api/models/VisualConsole/Items/EventsHistory.php @@ -0,0 +1,128 @@ + $groupId, + 'status' => AGENT_STATUS_CRITICAL, + ], + ['COUNT(*) AS total'], + 'AR', + false, + false, + true, + $isMetaconsole + ); + $numCritical = $agentsCritical[0]['total']; + $agentsWarning = \agents_get_agents( + [ + 'id_grupo' => $groupId, + 'status' => AGENT_STATUS_WARNING, + ], + ['COUNT(*) AS total'], + 'AR', + false, + false, + true, + $isMetaconsole + ); + $numWarning = $agentsWarning[0]['total']; + $agentsUnknown = \agents_get_agents( + [ + 'id_grupo' => $groupId, + 'status' => AGENT_STATUS_UNKNOWN, + ], + ['COUNT(*) AS total'], + 'AR', + false, + false, + true, + $isMetaconsole + ); + $numUnknown = $agentsUnknown[0]['total']; + $agentsOk = \agents_get_agents( + [ + 'id_grupo' => $groupId, + 'status' => AGENT_STATUS_OK, + ], + ['COUNT(*) AS total'], + 'AR', + false, + false, + true, + $isMetaconsole + ); + $numNormal = $agentsOk[0]['total']; + + $numTotal = ($numCritical + $numWarning + $numUnknown + $numNormal); + $agentStats = [ + 'critical' => ($numCritical / $numTotal * 100), + 'warning' => ($numWarning / $numTotal * 100), + 'normal' => ($numNormal / $numTotal * 100), + 'unknown' => ($numUnknown / $numTotal * 100), + ]; + + $groupName = \groups_get_name($groupId, true); + $data['html'] = static::printStatsTable( + $groupName, + $agentStats, + (int) $data['width'], + (int) $data['height'] + ); + } else { + if (\is_metaconsole()) { + $groupFilter = $groupId; + if ($groupId === 0) { + $groupFilter = implode( + ',', + array_keys(\users_get_groups()) + ); + } + + $sql = sprintf( + 'SELECT + SUM(fired_count) AS fired, + SUM(critical_count) AS critical, + SUM(warning_count) AS warning, + SUM(unknown_count) AS unknown + FROM tmetaconsole_agent + LEFT JOIN tmetaconsole_agent_secondary_group tasg + ON id_agente = tasg.id_agent + WHERE id_grupo IN (%s) + OR tasg.id_group IN (%s)', + $groupFilter, + $groupFilter + ); + + $countStatus = \db_get_row_sql($sql); + + if ($countStatus['fired'] > 0) { + $status = AGENT_STATUS_ALERT_FIRED; + } else if ($countStatus['critical'] > 0) { + $status = AGENT_STATUS_CRITICAL; + } else if ($countStatus['warning'] > 0) { + $status = AGENT_STATUS_WARNING; + } else if ($countStatus['unknown'] > 0) { + $status = AGENT_STATUS_UNKNOWN; + } else { + $status = AGENT_STATUS_NORMAL; + } + } else { + // Get the status img src. + $status = \groups_get_status($groupId); + } + + $imagePath = \visual_map_get_image_status_element($data, $status); + $data['statusImageSrc'] = \ui_get_full_url( + $imagePath, + false, + false, + false + ); + + // If the width or the height are equal to 0 we will extract them + // from the real image size. + $width = (int) $data['width']; + $height = (int) $data['height']; + if ($width === 0 || $height === 0) { + // TODO: This will be the default behaviour after we finish the + // builder. Don't delete this code. + // $sizeImage = getimagesize($config['homedir'].'/'.$imagePath); + // $data['width'] = $sizeImage[0]; + // $data['height'] = $sizeImage[1]; + $sizeImage = getimagesize($config['homedir'].'/'.$imagePath); + $imageHeight = $sizeImage[1]; + + if ($width === 0) { + $data['width'] = 70; + } + + if ($height === 0) { + $data['height'] = ($imageHeight > 70) ? 70 : $imageHeight; + } + } + + $data['html'] = ''; + } + + return $data; + } + + + /** + * HTML representation for the agent stats of a group. + * + * @param string $groupName Group name. + * @param array $agentStats Data structure with the agent statistics. + * @param integer $width Width. + * @param integer $height Height. + * + * @return string HTML representation. + */ + private static function printStatsTable( + string $groupName, + array $agentStats, + int $width=520, + int $height=80 + ): string { + $width = ($width > 0) ? $width : 520; + $height = ($height > 0) ? $height : 80; + + $tableStyle = \join( + [ + 'width:'.$width.'px;', + 'height:'.$height.'px;', + 'text-align:center;', + ] + ); + $headStyle = \join( + [ + 'text-align:center;', + 'background-color:#9d9ea0;', + 'color:black;', + 'font-weight:bold;', + ] + ); + $valueStyle = \join( + [ + 'margin-left: 2%;', + 'color: #FFF;', + 'font-size: 12px;', + 'display: inline;', + 'background-color: #FC4444;', + 'position: relative;', + 'height: 80%;', + 'width: 9.4%;', + 'height: 80%;', + 'border-radius: 2px;', + 'text-align: center;', + 'padding: 5px;', + ] + ); + $nameStyle = \join( + [ + 'background-color: white;', + 'color: black;', + 'font-size: 12px;', + 'display: inline;', + 'display: inline;', + 'position:relative;', + 'width: 9.4%;', + 'height: 80%;', + 'border-radius: 2px;', + 'text-align: center;', + 'padding: 5px;', + ] + ); + + $html = ''; + $html .= ''; + $html .= ''; + $html .= ''; + $html .= ''; + $html .= ''; + $html .= ''; + $html .= '
'.$groupName.'
'; + + // Critical. + $html .= '
'; + $html .= \number_format($agentStats['critical']).'%'; + $html .= '
'; + $html .= '
'.__('Critical').'
'; + // Warning. + $html .= '
'; + $html .= \number_format($agentStats['warning']).'%'; + $html .= '
'; + $html .= '
'.__('Warning').'
'; + // Normal. + $html .= '
'; + $html .= \number_format($agentStats['normal']).'%'; + $html .= '
'; + $html .= '
'.__('Normal').'
'; + // Unknown. + $html .= '
'; + $html .= \number_format($agentStats['unknown']).'%'; + $html .= '
'; + $html .= '
'.__('Unknown').'
'; + + $html .= '
'; + + return $html; + } + + + /** + * Generate a link to something related with the item. + * + * @param array $data Visual Console Item's data structure. + * + * @return mixed The link or a null value. + * + * @override Item::buildLink. + */ + protected static function buildLink(array $data) + { + // This will return the link to a linked VC if this item has one. + $link = parent::buildLink($data); + if ($link !== null) { + return $link; + } + + global $config; + + $groupId = static::extractGroupId($data); + $baseUrl = $config['homeurl'].'index.php'; + + if (\is_metaconsole()) { + return $baseUrl.'?'.http_build_query( + [ + 'sec' => 'monitoring', + 'sec2' => 'operation/tree', + 'group_id' => $groupId, + ] + ); + } + + return $baseUrl.'?'.http_build_query( + [ + 'sec' => 'estado', + 'sec2' => 'operation/agentes/estado_agente', + 'group_id' => $groupId, + ] + ); + } + + +} diff --git a/pandora_console/include/rest-api/models/VisualConsole/Items/Icon.php b/pandora_console/include/rest-api/models/VisualConsole/Items/Icon.php new file mode 100644 index 0000000000..29605ffb4f --- /dev/null +++ b/pandora_console/include/rest-api/models/VisualConsole/Items/Icon.php @@ -0,0 +1,144 @@ + 70) ? 70 : $imageHeight; + } + } + + return $data; + } + + +} diff --git a/pandora_console/include/rest-api/models/VisualConsole/Items/Label.php b/pandora_console/include/rest-api/models/VisualConsole/Items/Label.php new file mode 100644 index 0000000000..41c9c6c6d1 --- /dev/null +++ b/pandora_console/include/rest-api/models/VisualConsole/Items/Label.php @@ -0,0 +1,64 @@ +validateData. + */ + protected function validateData(array $data): void + { + parent::validateData($data); + if (static::notEmptyStringOr(static::issetInArray($data, ['label']), null) === null) { + throw new \InvalidArgumentException( + 'the label property is required and should be a not empty string' + ); + } + } + + + /** + * Returns a valid representation of the model. + * + * @param array $data Input data. + * + * @return array Data structure representing the model. + * + * @overrides Item->decode. + */ + protected function decode(array $data): array + { + $return = parent::decode($data); + $return['type'] = LABEL; + return $return; + } + + +} diff --git a/pandora_console/include/rest-api/models/VisualConsole/Items/Line.php b/pandora_console/include/rest-api/models/VisualConsole/Items/Line.php new file mode 100644 index 0000000000..fd6f64b4cb --- /dev/null +++ b/pandora_console/include/rest-api/models/VisualConsole/Items/Line.php @@ -0,0 +1,209 @@ +validateData. + */ + protected function validateData(array $data): void + { + if (isset($data['id']) === false + || \is_numeric($data['id']) === false + ) { + throw new \InvalidArgumentException( + 'the Id property is required and should be integer' + ); + } + + if (isset($data['type']) === false + || \is_numeric($data['type']) === false + ) { + throw new \InvalidArgumentException( + 'the Id property is required and should be integer' + ); + } + } + + + /** + * Returns a valid representation of the model. + * + * @param array $data Input data. + * + * @return array Data structure representing the model. + * + * @overrides Model->decode. + */ + protected function decode(array $data): array + { + return [ + 'id' => (int) $data['id'], + 'type' => LINE_ITEM, + 'startX' => static::extractStartX($data), + 'startY' => static::extractStartY($data), + 'endX' => static::extractEndX($data), + 'endY' => static::extractEndY($data), + 'isOnTop' => static::extractIsOnTop($data), + 'borderWidth' => static::extractBorderWidth($data), + 'borderColor' => static::extractBorderColor($data), + ]; + } + + + /** + * Extract a x axis value. + * + * @param array $data Unknown input data structure. + * + * @return integer Valid x axis of the start position of the line. + */ + private static function extractStartX(array $data): int + { + return static::parseIntOr( + static::issetInArray($data, ['startX', 'pos_x']), + 0 + ); + } + + + /** + * Extract a y axis value. + * + * @param array $data Unknown input data structure. + * + * @return integer Valid y axis of the start position of the line. + */ + private static function extractStartY(array $data): int + { + return static::parseIntOr( + static::issetInArray($data, ['startY', 'pos_y']), + 0 + ); + } + + + /** + * Extract a x axis value. + * + * @param array $data Unknown input data structure. + * + * @return integer Valid x axis of the end position of the line. + */ + private static function extractEndX(array $data): int + { + return static::parseIntOr( + static::issetInArray($data, ['endX', 'width']), + 0 + ); + } + + + /** + * Extract a y axis value. + * + * @param array $data Unknown input data structure. + * + * @return integer Valid y axis of the end position of the line. + */ + private static function extractEndY(array $data): int + { + return static::parseIntOr( + static::issetInArray($data, ['endY', 'height']), + 0 + ); + } + + + /** + * Extract a conditional value which tells if the item has visual priority. + * + * @param array $data Unknown input data structure. + * + * @return boolean If the item is on top or not. + */ + private static function extractIsOnTop(array $data): bool + { + return static::parseBool( + static::issetInArray($data, ['isOnTop', 'show_on_top']) + ); + } + + + /** + * Extract a border width value. + * + * @param array $data Unknown input data structure. + * + * @return integer Valid border width. 0 by default and minimum value. + */ + private static function extractBorderWidth(array $data): int + { + $borderWidth = static::parseIntOr( + static::issetInArray($data, ['borderWidth', 'border_width']), + 0 + ); + + return ($borderWidth >= 0) ? $borderWidth : 0; + } + + + /** + * Extract a border color value. + * + * @param array $data Unknown input data structure. + * + * @return mixed String representing the border color (not empty) or null. + */ + private static function extractBorderColor(array $data) + { + return static::notEmptyStringOr( + static::issetInArray($data, ['borderColor', 'border_color']), + null + ); + } + + + /** + * Obtain a vc item data structure from the database using a filter. + * + * @param array $filter Filter of the Visual Console Item. + * + * @return array The Visual Console line data structure stored into the DB. + * @throws \Exception When the data cannot be retrieved from the DB. + * + * @override Model::fetchDataFromDB. + */ + protected static function fetchDataFromDB(array $filter): array + { + // Due to this DB call, this function cannot be unit tested without + // a proper mock. + $row = \db_get_row_filter('tlayout_data', $filter); + + if ($row === false) { + throw new \Exception('error fetching the data from the DB'); + } + + return $row; + } + + +} diff --git a/pandora_console/include/rest-api/models/VisualConsole/Items/ModuleGraph.php b/pandora_console/include/rest-api/models/VisualConsole/Items/ModuleGraph.php new file mode 100644 index 0000000000..0465c3cb50 --- /dev/null +++ b/pandora_console/include/rest-api/models/VisualConsole/Items/ModuleGraph.php @@ -0,0 +1,262 @@ + $period, + 'width' => $data['width'], + 'height' => $data['height'], + 'title' => '', + 'unit_name' => null, + 'show_alerts' => false, + 'only_image' => $imageOnly, + 'vconsole' => true, + 'backgroundColor' => $backgroundType, + ]; + + $paramsCombined = [ + 'id_graph' => $customGraphId, + 'stacked' => $graph['stacked'], + 'summatory' => $graph['summatory_series'], + 'average' => $graph['average_series'], + 'modules_series' => $graph['modules_series'], + ]; + + $data['html'] = \graphic_combined_module( + false, + $params, + $paramsCombined + ); + } else { + // Module graph. + if ($moduleId === null) { + throw new \InvalidArgumentException('missing module Id'); + } + + $params = [ + 'agent_module_id' => $moduleId, + 'period' => $period, + 'show_events' => false, + 'width' => $data['width'], + 'height' => $data['height'], + 'title' => \modules_get_agentmodule_name($moduleId), + 'unit' => \modules_get_unit($moduleId), + 'only_image' => $imageOnly, + 'menu' => false, + 'backgroundColor' => $backgroundType, + 'type_graph' => $graphType, + 'vconsole' => true, + ]; + + $data['html'] = \grafico_modulo_sparse($params); + } + + // Restore connection. + if ($nodeConnected === true) { + \metaconsole_restore_db(); + } + + return $data; + } + + +} diff --git a/pandora_console/include/rest-api/models/VisualConsole/Items/Percentile.php b/pandora_console/include/rest-api/models/VisualConsole/Items/Percentile.php new file mode 100644 index 0000000000..3ce365195b --- /dev/null +++ b/pandora_console/include/rest-api/models/VisualConsole/Items/Percentile.php @@ -0,0 +1,249 @@ +validateData. + */ + protected function validateData(array $data): void + { + parent::validateData($data); + if (isset($data['value']) === false) { + throw new \InvalidArgumentException( + 'the value property is required and should be string' + ); + } + } + + + /** + * Returns a valid representation of the model. + * + * @param array $data Input data. + * + * @return array Data structure representing the model. + * + * @overrides Item->decode. + */ + protected function decode(array $data): array + { + $return = parent::decode($data); + $return['type'] = SIMPLE_VALUE; + $return['processValue'] = static::extractProcessValue($data); + $return['valueType'] = static::extractValueType($data); + $return['value'] = $data['value']; + + if ($return['processValue'] !== 'none') { + $return['period'] = static::extractPeriod($data); + } + + // Clear the size, as this element always have a dynamic size. + $return['width'] = 0; + $return['height'] = 0; + + return $return; + } + + + /** + * Extract a process value. + * + * @param array $data Unknown input data structure. + * + * @return string One of 'none', 'avg', 'max' or 'min'. 'none' by default. + */ + private static function extractProcessValue(array $data): string + { + if (isset($data['processValue'])) { + switch ($data['processValue']) { + case 'none': + case 'avg': + case 'max': + case 'min': + return $data['processValue']; + + default: + return 'none'; + } + } else { + switch ($data['type']) { + case SIMPLE_VALUE_MAX: + return 'max'; + + case SIMPLE_VALUE_MIN: + return 'min'; + + case SIMPLE_VALUE_AVG: + return 'avg'; + + default: + return 'none'; + } + } + } + + + /** + * Extract the value of period. + * + * @param array $data Unknown input data structure. + * + * @return integer The period in seconds. 0 is the minimum value. + */ + private static function extractPeriod(array $data): int + { + $period = static::parseIntOr( + static::issetInArray($data, ['period']), + 0 + ); + return ($period >= 0) ? $period : 0; + } + + + /** + * Extract a value type. + * + * @param array $data Unknown input data structure. + * + * @return string One of 'string' or 'image'. 'string' by default. + */ + private static function extractValueType(array $data): string + { + switch ($data['valueType']) { + case 'string': + case 'image': + return $data['valueType']; + + default: + return 'string'; + } + } + + + /** + * Fetch a vc item data structure from the database using a filter. + * + * @param array $filter Filter of the Visual Console Item. + * + * @return array The Visual Console Item data structure stored into the DB. + * @throws \InvalidArgumentException When a module Id cannot be found. + * + * @override Item::fetchDataFromDB. + */ + protected static function fetchDataFromDB(array $filter): array + { + // Due to this DB call, this function cannot be unit tested without + // a proper mock. + $data = parent::fetchDataFromDB($filter); + + /* + * Retrieve extra data. + */ + + // Load side libraries. + global $config; + include_once $config['homedir'].'/include/functions_visual_map.php'; + if (is_metaconsole()) { + \enterprise_include_once('include/functions_metaconsole.php'); + } + + // Get the linked module Id. + $linkedModule = static::extractLinkedModule($data); + $moduleId = static::parseIntOr($linkedModule['moduleId'], null); + $metaconsoleId = static::parseIntOr( + $linkedModule['metaconsoleId'], + null + ); + + if ($moduleId === null) { + throw new \InvalidArgumentException('missing module Id'); + } + + // Maybe connect to node. + $nodeConnected = false; + if (\is_metaconsole() === true && $metaconsoleId !== null) { + $nodeConnected = \metaconsole_connect( + null, + $metaconsoleId + ) === NOERR; + + if ($nodeConnected === false) { + throw new \InvalidArgumentException( + 'error connecting to the node' + ); + } + } + + // Get the formatted value. + $value = \visual_map_get_simple_value( + $data['type'], + $moduleId, + static::extractPeriod($data) + ); + + // Restore connection. + if ($nodeConnected === true) { + \metaconsole_restore_db(); + } + + // Some modules are image based. Extract the base64 image if needed. + $matches = []; + if (\preg_match('/src=\"(data:image.*)"/', $value, $matches) === 1) { + $data['valueType'] = 'image'; + $data['value'] = $matches[1]; + } else { + $data['valueType'] = 'string'; + $data['value'] = $value; + } + + return $data; + } + + +} diff --git a/pandora_console/include/rest-api/models/VisualConsole/Items/StaticGraph.php b/pandora_console/include/rest-api/models/VisualConsole/Items/StaticGraph.php new file mode 100644 index 0000000000..0f68841686 --- /dev/null +++ b/pandora_console/include/rest-api/models/VisualConsole/Items/StaticGraph.php @@ -0,0 +1,256 @@ +decode. + */ + protected function decode(array $data): array + { + $return = parent::decode($data); + $return['type'] = STATIC_GRAPH; + $return['imageSrc'] = static::extractImageSrc($data); + $return['showLastValueTooltip'] = static::extractShowLastValueTooltip( + $data + ); + $return['statusImageSrc'] = static::notEmptyStringOr( + static::issetInArray($data, ['statusImageSrc']), + null + ); + $return['lastValue'] = static::notEmptyStringOr( + static::issetInArray($data, ['lastValue']), + null + ); + + return $return; + } + + + /** + * Extract a image src value. + * + * @param array $data Unknown input data structure. + * + * @return mixed String representing the image url (not empty) or null. + * + * @throws \InvalidArgumentException When a valid image src can't be found. + */ + private static function extractImageSrc(array $data): string + { + $imageSrc = static::notEmptyStringOr( + static::issetInArray($data, ['imageSrc', 'image']), + null + ); + + if ($imageSrc === null) { + throw new \InvalidArgumentException( + 'the image src property is required and should be a non empty string' + ); + } + + return $imageSrc; + } + + + /** + * Extract the value of showLastValueTooltip and + * return 'default', 'enabled' or 'disabled'. + * + * @param array $data Unknown input data structure. + * + * @return string + */ + private static function extractShowLastValueTooltip(array $data): string + { + $showLastValueTooltip = static::notEmptyStringOr( + static::issetInArray($data, ['showLastValueTooltip']), + null + ); + + if ($showLastValueTooltip === null) { + $showLastValueTooltip = static::parseIntOr( + static::issetInArray($data, ['show_last_value']), + null + ); + switch ($showLastValueTooltip) { + case 1: + return 'enabled'; + + case 2: + return 'disabled'; + + default: + return 'default'; + } + } else { + switch ($showLastValueTooltip) { + case 'enabled': + return 'enabled'; + + case 'disabled': + return 'disabled'; + + default: + return 'default'; + } + } + } + + + /** + * Fetch a vc item data structure from the database using a filter. + * + * @param array $filter Filter of the Visual Console Item. + * + * @return array The Visual Console Item data structure stored into the DB. + * @throws \InvalidArgumentException When an agent Id cannot be found. + * + * @override Item::fetchDataFromDB. + */ + protected static function fetchDataFromDB(array $filter): array + { + // Due to this DB call, this function cannot be unit tested without + // a proper mock. + $data = parent::fetchDataFromDB($filter); + + /* + * Retrieve extra data. + */ + + // Load side libraries. + global $config; + include_once $config['homedir'].'/include/functions_ui.php'; + include_once $config['homedir'].'/include/functions_io.php'; + include_once $config['homedir'].'/include/functions_visual_map.php'; + include_once $config['homedir'].'/include/functions_modules.php'; + if (is_metaconsole()) { + \enterprise_include_once('include/functions_metaconsole.php'); + } + + // Get the linked module Id. + $linkedModule = static::extractLinkedModule($data); + $moduleId = $linkedModule['moduleId']; + $metaconsoleId = $linkedModule['metaconsoleId']; + + if ($moduleId === null) { + throw new \InvalidArgumentException('missing module Id'); + } + + // Get the img src. + // There's no need to connect to the metaconsole before searching for + // the image status cause the function itself does that for us. + $imagePath = \visual_map_get_image_status_element($data); + $data['statusImageSrc'] = \ui_get_full_url( + $imagePath, + false, + false, + false + ); + + // If the width or the height are equal to 0 we will extract them + // from the real image size. + $width = (int) $data['width']; + $height = (int) $data['height']; + if ($width === 0 || $height === 0) { + // TODO: This will be the default behaviour after we finish the + // builder. Don't delete this code. + // $sizeImage = getimagesize($config['homedir'].'/'.$imagePath); + // $data['width'] = $sizeImage[0]; + // $data['height'] = $sizeImage[1]; + // Default value. Will be replaced by a dynamic image size + // calculation after the phase 3. + $sizeImage = getimagesize($config['homedir'].'/'.$imagePath); + $imageHeight = $sizeImage[1]; + + if ($width === 0) { + $data['width'] = 70; + } + + if ($height === 0) { + $data['height'] = ($imageHeight > 70) ? 70 : $imageHeight; + } + } + + // Get last value. + $showLastValueTooltip = static::extractShowLastValueTooltip($data); + if ($showLastValueTooltip !== 'disabled' && $moduleId > 0) { + // Maybe connect to node. + $nodeConnected = false; + if (\is_metaconsole() === true && $metaconsoleId !== null) { + $nodeConnected = \metaconsole_connect( + null, + $metaconsoleId + ) === NOERR; + + if ($nodeConnected === false) { + throw new \InvalidArgumentException( + 'error connecting to the node' + ); + } + } + + $imgTitle = ''; + + $unit = \trim(\io_safe_output(\modules_get_unit($moduleId))); + $value = \modules_get_last_value($moduleId); + + $isBooleanModule = \modules_is_boolean($moduleId); + if (!$isBooleanModule + || ($isBooleanModule && $showLastValueTooltip !== 'default') + ) { + if (is_numeric($value)) { + $imgTitle .= __('Last value: ').\remove_right_zeros($value); + } else { + $imgTitle .= __('Last value: ').$value; + } + + if (empty($unit) === false && empty($imgTitle) === false) { + $imgTitle .= ' '.$unit; + } + + $data['lastValue'] = $imgTitle; + } + + // Restore connection. + if ($nodeConnected === true) { + \metaconsole_restore_db(); + } + } + + return $data; + } + + +} diff --git a/pandora_console/include/visual-console-client/alarm-clock.ttf b/pandora_console/include/visual-console-client/alarm-clock.ttf new file mode 100644 index 0000000000..9e9b593459 Binary files /dev/null and b/pandora_console/include/visual-console-client/alarm-clock.ttf differ diff --git a/pandora_console/include/visual-console-client/vc.main.css b/pandora_console/include/visual-console-client/vc.main.css new file mode 100644 index 0000000000..5f5aecfbac --- /dev/null +++ b/pandora_console/include/visual-console-client/vc.main.css @@ -0,0 +1,89 @@ +#visual-console-container { + margin: 0px auto; + position: relative; + background-repeat: no-repeat; + background-size: contain; +} + +.visual-console-item { + position: absolute; + display: -webkit-box; + display: -ms-flexbox; + display: flex; + -webkit-box-orient: initial; + -webkit-box-direction: initial; + -ms-flex-direction: initial; + flex-direction: initial; + justify-items: center; + -webkit-box-align: center; + -ms-flex-align: center; + align-items: center; + -webkit-user-select: text; + -moz-user-select: text; + -ms-user-select: text; + user-select: text; +} + +@font-face { + font-family: Alarm Clock; + src: url(alarm-clock.ttf); +} + +/* Digital clock */ + +.visual-console-item .digital-clock { + display: -webkit-box; + display: -ms-flexbox; + display: flex; + -webkit-box-orient: vertical; + -webkit-box-direction: normal; + -ms-flex-direction: column; + flex-direction: column; + -webkit-box-pack: center; + -ms-flex-pack: center; + justify-content: center; + justify-items: center; + -ms-flex-line-pack: center; + align-content: center; + -webkit-box-align: center; + -ms-flex-align: center; + align-items: center; +} + +.visual-console-item .digital-clock > span { + font-family: "Alarm Clock", "Courier New", Courier, monospace; + font-size: 50px; + + /* To improve legibility */ + -webkit-font-smoothing: antialiased; + -moz-osx-font-smoothing: grayscale; + text-rendering: optimizeLegibility; + text-shadow: rgba(0, 0, 0, 0.01) 0 0 1px; +} + +.visual-console-item .digital-clock > span.date { + font-size: 25px; +} + +.visual-console-item .digital-clock > span.timezone { + font-size: 25px; +} + +/* Analog clock */ + +.visual-console-item .analogic-clock .hour-hand { + -webkit-animation: rotate-hour 43200s infinite linear; + animation: rotate-hour 43200s infinite linear; +} + +.visual-console-item .analogic-clock .minute-hand { + -webkit-animation: rotate-minute 3600s infinite linear; + animation: rotate-minute 3600s infinite linear; +} + +.visual-console-item .analogic-clock .second-hand { + -webkit-animation: rotate-second 60s infinite linear; + animation: rotate-second 60s infinite linear; +} + +/*# sourceMappingURL=vc.main.css.map*/ diff --git a/pandora_console/include/visual-console-client/vc.main.css.map b/pandora_console/include/visual-console-client/vc.main.css.map new file mode 100644 index 0000000000..eb03f5c6ba --- /dev/null +++ b/pandora_console/include/visual-console-client/vc.main.css.map @@ -0,0 +1 @@ +{"version":3,"sources":["webpack:///main.css","webpack:///styles.css"],"names":[],"mappings":"AAAA;EACE,gBAAgB;EAChB,kBAAkB;EAClB,4BAA4B;EAC5B,wBAAwB;AAC1B;;AAEA;EACE,kBAAkB;EAClB,oBAAa;EAAb,oBAAa;EAAb,aAAa;EACb,2BAAuB;EAAvB,8BAAuB;MAAvB,2BAAuB;UAAvB,uBAAuB;EACvB,qBAAqB;EACrB,yBAAmB;MAAnB,sBAAmB;UAAnB,mBAAmB;EACnB,yBAAiB;KAAjB,sBAAiB;MAAjB,qBAAiB;UAAjB,iBAAiB;AACnB;;ACdA;EACE,wBAAwB;EACxB,0BAA2B;AAC7B;;AAEA,kBAAkB;;AAElB;EACE,oBAAa;EAAb,oBAAa;EAAb,aAAa;EACb,4BAAsB;EAAtB,6BAAsB;MAAtB,0BAAsB;UAAtB,sBAAsB;EACtB,wBAAuB;MAAvB,qBAAuB;UAAvB,uBAAuB;EACvB,qBAAqB;EACrB,0BAAqB;MAArB,qBAAqB;EACrB,yBAAmB;MAAnB,sBAAmB;UAAnB,mBAAmB;AACrB;;AAEA;EACE,6DAA6D;EAC7D,eAAe;;EAEf,0BAA0B;EAC1B,mCAAmC;EACnC,kCAAkC;EAClC,kCAAkC;EAClC,wCAAwC;AAC1C;;AAEA;EACE,eAAe;AACjB;;AAEA;EACE,eAAe;AACjB;;AAEA,iBAAiB;;AAEjB;EACE,qDAA6C;UAA7C,6CAA6C;AAC/C;;AAEA;EACE,sDAA8C;UAA9C,8CAA8C;AAChD;;AAEA;EACE,oDAA4C;UAA5C,4CAA4C;AAC9C","file":"vc.main.css","sourcesContent":["#visual-console-container {\n margin: 0px auto;\n position: relative;\n background-repeat: no-repeat;\n background-size: contain;\n}\n\n.visual-console-item {\n position: absolute;\n display: flex;\n flex-direction: initial;\n justify-items: center;\n align-items: center;\n user-select: text;\n}\n","@font-face {\n font-family: Alarm Clock;\n src: url(./alarm-clock.ttf);\n}\n\n/* Digital clock */\n\n.visual-console-item .digital-clock {\n display: flex;\n flex-direction: column;\n justify-content: center;\n justify-items: center;\n align-content: center;\n align-items: center;\n}\n\n.visual-console-item .digital-clock > span {\n font-family: \"Alarm Clock\", \"Courier New\", Courier, monospace;\n font-size: 50px;\n\n /* To improve legibility */\n -webkit-font-smoothing: antialiased;\n -moz-osx-font-smoothing: grayscale;\n text-rendering: optimizeLegibility;\n text-shadow: rgba(0, 0, 0, 0.01) 0 0 1px;\n}\n\n.visual-console-item .digital-clock > span.date {\n font-size: 25px;\n}\n\n.visual-console-item .digital-clock > span.timezone {\n font-size: 25px;\n}\n\n/* Analog clock */\n\n.visual-console-item .analogic-clock .hour-hand {\n animation: rotate-hour 43200s infinite linear;\n}\n\n.visual-console-item .analogic-clock .minute-hand {\n animation: rotate-minute 3600s infinite linear;\n}\n\n.visual-console-item .analogic-clock .second-hand {\n animation: rotate-second 60s infinite linear;\n}\n"],"sourceRoot":""} \ No newline at end of file diff --git a/pandora_console/include/visual-console-client/vc.main.min.js b/pandora_console/include/visual-console-client/vc.main.min.js new file mode 100644 index 0000000000..ff0e3e29c0 --- /dev/null +++ b/pandora_console/include/visual-console-client/vc.main.min.js @@ -0,0 +1,2 @@ +!function(t){var e={};function n(i){if(e[i])return e[i].exports;var r=e[i]={i:i,l:!1,exports:{}};return t[i].call(r.exports,r,r.exports,n),r.l=!0,r.exports}n.m=t,n.c=e,n.d=function(t,e,i){n.o(t,e)||Object.defineProperty(t,e,{enumerable:!0,get:i})},n.r=function(t){"undefined"!=typeof Symbol&&Symbol.toStringTag&&Object.defineProperty(t,Symbol.toStringTag,{value:"Module"}),Object.defineProperty(t,"__esModule",{value:!0})},n.t=function(t,e){if(1&e&&(t=n(t)),8&e)return t;if(4&e&&"object"==typeof t&&t&&t.__esModule)return t;var i=Object.create(null);if(n.r(i),Object.defineProperty(i,"default",{enumerable:!0,value:t}),2&e&&"string"!=typeof t)for(var r in t)n.d(i,r,function(e){return t[e]}.bind(null,r));return i},n.n=function(t){var e=t&&t.__esModule?function(){return t.default}:function(){return t};return n.d(e,"a",e),e},n.o=function(t,e){return Object.prototype.hasOwnProperty.call(t,e)},n.p="",n(n.s=9)}([function(t,e,n){"use strict";n.d(e,"i",function(){return r}),n.d(e,"h",function(){return s}),n.d(e,"n",function(){return o}),n.d(e,"f",function(){return a}),n.d(e,"g",function(){return c}),n.d(e,"j",function(){return u}),n.d(e,"m",function(){return h}),n.d(e,"e",function(){return p}),n.d(e,"d",function(){return _}),n.d(e,"k",function(){return f}),n.d(e,"a",function(){return d}),n.d(e,"b",function(){return y}),n.d(e,"c",function(){return m}),n.d(e,"l",function(){return b});var i=function(){return(i=Object.assign||function(t){for(var e,n=1,i=arguments.length;n0&&!isNaN(parseInt(t))?parseInt(t):e}function s(t,e){return"number"==typeof t?t:"string"==typeof t&&t.length>0&&!isNaN(parseFloat(t))?parseFloat(t):e}function o(t){return null==t||0===t.length}function a(t,e){return"string"==typeof t&&t.length>0?t:e}function c(t){return"boolean"==typeof t?t:"number"==typeof t?t>0:"string"==typeof t&&("1"===t||"true"===t)}function l(t,e,n){void 0===n&&(n=" "),"number"==typeof t&&(t=""+t),"number"==typeof n&&(n=""+n);var i=e-t.length;if(0===i)return t;if(i<0)return t.substr(Math.abs(i));if(i===n.length)return""+n+t;if(i0){var n=document.createElement("table"),i=document.createElement("tr"),r=document.createElement("tr"),s=document.createElement("tr"),o=document.createElement("td");switch(o.innerHTML=e,i.append(o),n.append(r,i,s),n.style.textAlign="center",this.props.labelPosition){case"up":case"down":this.props.width>0&&(n.style.width=this.props.width+"px",n.style.height=null);break;case"left":case"right":this.props.height>0&&(n.style.width=null,n.style.height=this.props.height+"px")}t.append(n)}return t},t.prototype.getLabelWithMacrosReplaced=function(){var t=this.props;return Object(i.l)([{macro:"_date_",value:Object(i.b)(new Date)},{macro:"_time_",value:Object(i.c)(new Date)},{macro:"_agent_",value:null!=t.agentAlias?t.agentAlias:""},{macro:"_agentdescription_",value:null!=t.agentDescription?t.agentDescription:""},{macro:"_address_",value:null!=t.agentAddress?t.agentAddress:""},{macro:"_module_",value:null!=t.moduleName?t.moduleName:""},{macro:"_moduledescription_",value:null!=t.moduleDescription?t.moduleDescription:""}],this.props.label||"")},t.prototype.updateDomElement=function(t){t.innerHTML=this.createDomElement().innerHTML},Object.defineProperty(t.prototype,"props",{get:function(){return s({},this.itemProps)},set:function(t){var e=this.props;this.itemProps=t,this.shouldBeUpdated(e,t)&&this.render(e)},enumerable:!0,configurable:!0}),t.prototype.shouldBeUpdated=function(t,e){return t!==e},t.prototype.render=function(t){void 0===t&&(t=null),this.updateDomElement(this.childElementRef),t&&!this.positionChanged(t,this.props)||this.moveElement(this.props.x,this.props.y),t&&!this.sizeChanged(t,this.props)||this.resizeElement(this.props.width,this.props.height);var e=this.labelElementRef.innerHTML,n=this.createLabelDomElement().innerHTML;if(e!==n&&(this.labelElementRef.innerHTML=n),t&&t.labelPosition===this.props.labelPosition||this.changeLabelPosition(this.props.labelPosition),t&&(t.isLinkEnabled!==this.props.isLinkEnabled||this.props.isLinkEnabled&&t.link!==this.props.link)){var i=this.createContainerDomElement();i.innerHTML=this.elementRef.innerHTML,null!==this.elementRef.parentNode&&this.elementRef.parentNode.replaceChild(i,this.elementRef),this.elementRef=i}},t.prototype.remove=function(){this.removeEventManager.emit({data:this.props}),this.disposables.forEach(function(t){try{t.dispose()}catch(t){}}),this.elementRef.remove()},t.prototype.positionChanged=function(t,e){return t.x!==e.x||t.y!==e.y},t.prototype.changeLabelPosition=function(t){switch(t){case"up":this.elementRef.style.flexDirection="column-reverse";break;case"left":this.elementRef.style.flexDirection="row-reverse";break;case"right":this.elementRef.style.flexDirection="row";break;case"down":default:this.elementRef.style.flexDirection="column"}var e=this.labelElementRef.getElementsByTagName("table"),n=e.length>0?e.item(0):null;if(n)switch(this.props.labelPosition){case"up":case"down":this.props.width>0&&(n.style.width=this.props.width+"px",n.style.height=null);break;case"left":case"right":this.props.height>0&&(n.style.width=null,n.style.height=this.props.height+"px")}},t.prototype.moveElement=function(t,e){this.elementRef.style.left=t+"px",this.elementRef.style.top=e+"px"},t.prototype.move=function(t,e){this.moveElement(t,e),this.itemProps=s({},this.props,{x:t,y:e})},t.prototype.sizeChanged=function(t,e){return t.width!==e.width||t.height!==e.height},t.prototype.resizeElement=function(t,e){this.childElementRef.style.width=t>0?t+"px":null,this.childElementRef.style.height=e>0?e+"px":null},t.prototype.resize=function(t,e){this.resizeElement(t,e),this.itemProps=s({},this.props,{width:t,height:e})},t.prototype.onClick=function(t){var e=this.clickEventManager.on(t);return this.disposables.push(e),e},t.prototype.onRemove=function(t){var e=this.removeEventManager.on(t);return this.disposables.push(e),e},t}();e.a=c},function(t,e,n){"use strict";var i=function(){return function(){var t=this;this.listeners=[],this.listenersOncer=[],this.on=function(e){return t.listeners.push(e),{dispose:function(){return t.off(e)}}},this.once=function(e){t.listenersOncer.push(e)},this.off=function(e){var n=t.listeners.indexOf(e);n>-1&&t.listeners.splice(n,1)},this.emit=function(e){t.listeners.forEach(function(t){return t(e)}),t.listenersOncer.forEach(function(t){return t(e)}),t.listenersOncer=[]},this.pipe=function(e){return t.on(function(t){return e.emit(t)})}}}();e.a=i},function(module,__webpack_exports__,__webpack_require__){"use strict";__webpack_require__.d(__webpack_exports__,"b",function(){return eventsHistoryPropsDecoder});var _lib__WEBPACK_IMPORTED_MODULE_0__=__webpack_require__(0),_Item__WEBPACK_IMPORTED_MODULE_1__=__webpack_require__(1),__extends=(extendStatics=function(t,e){return(extendStatics=Object.setPrototypeOf||{__proto__:[]}instanceof Array&&function(t,e){t.__proto__=e}||function(t,e){for(var n in e)e.hasOwnProperty(n)&&(t[n]=e[n])})(t,e)},function(t,e){function n(){this.constructor=t}extendStatics(t,e),t.prototype=null===e?Object.create(e):(n.prototype=e.prototype,new n)}),extendStatics,__assign=function(){return(__assign=Object.assign||function(t){for(var e,n=1,i=arguments.length;n0){var y=document.createElementNS(t,"text");y.setAttribute("text-anchor","middle"),y.setAttribute("font-size","8"),y.setAttribute("transform","translate(30 50) rotate(90)"),y.setAttribute("fill",i),y.textContent=d,_.append(y)}var m=document.createElementNS(t,"g");m.setAttribute("class","marks");var b=document.createElementNS(t,"g");b.setAttribute("class","mark"),b.setAttribute("transform","translate(50 50)");var v=document.createElementNS(t,"line");v.setAttribute("x1","36"),v.setAttribute("y1","0"),v.setAttribute("x2","46"),v.setAttribute("y2","0"),v.setAttribute("stroke",i),v.setAttribute("stroke-width","5");var x=document.createElementNS(t,"line");x.setAttribute("x1","36"),x.setAttribute("y1","0"),x.setAttribute("x2","46"),x.setAttribute("y2","0"),x.setAttribute("stroke",e),x.setAttribute("stroke-width","1"),b.append(v,x),m.append(b);for(var g=1;g<60;g++){var E=document.createElementNS(t,"line");E.setAttribute("y1","0"),E.setAttribute("y2","0"),E.setAttribute("stroke",i),E.setAttribute("transform","translate(50 50) rotate("+6*g+")"),g%5==0?(E.setAttribute("x1","38"),E.setAttribute("x2","46"),E.setAttribute("stroke-width",g%15==0?"2":"1")):(E.setAttribute("x1","42"),E.setAttribute("x2","46"),E.setAttribute("stroke-width","0.5")),m.append(E)}var O=document.createElementNS(t,"g");O.setAttribute("class","hour-hand"),O.setAttribute("transform","translate(50 50)");var w=document.createElementNS(t,"line");w.setAttribute("class","hour-hand-a"),w.setAttribute("x1","0"),w.setAttribute("y1","0"),w.setAttribute("x2","30"),w.setAttribute("y2","0"),w.setAttribute("stroke",o),w.setAttribute("stroke-width","4"),w.setAttribute("stroke-linecap","round");var T=document.createElementNS(t,"line");T.setAttribute("class","hour-hand-b"),T.setAttribute("x1","0"),T.setAttribute("y1","0"),T.setAttribute("x2","29.9"),T.setAttribute("y2","0"),T.setAttribute("stroke",s),T.setAttribute("stroke-width","3.1"),T.setAttribute("stroke-linecap","round"),O.append(w,T);var A=document.createElementNS(t,"g");A.setAttribute("class","minute-hand"),A.setAttribute("transform","translate(50 50)");var k=document.createElementNS(t,"line");k.setAttribute("class","minute-hand-a"),k.setAttribute("x1","0"),k.setAttribute("y1","0"),k.setAttribute("x2","40"),k.setAttribute("y2","0"),k.setAttribute("stroke",o),k.setAttribute("stroke-width","2"),k.setAttribute("stroke-linecap","round");var P=document.createElementNS(t,"line");P.setAttribute("class","minute-hand-b"),P.setAttribute("x1","0"),P.setAttribute("y1","0"),P.setAttribute("x2","39.9"),P.setAttribute("y2","0"),P.setAttribute("stroke",s),P.setAttribute("stroke-width","1.5"),P.setAttribute("stroke-linecap","round");var j=document.createElementNS(t,"circle");j.setAttribute("r","3"),j.setAttribute("fill",s),A.append(k,P,j);var M=document.createElementNS(t,"g");M.setAttribute("class","second-hand"),M.setAttribute("transform","translate(50 50)");var S=document.createElementNS(t,"line");S.setAttribute("x1","0"),S.setAttribute("y1","0"),S.setAttribute("x2","46"),S.setAttribute("y2","0"),S.setAttribute("stroke",a),S.setAttribute("stroke-width","1"),S.setAttribute("stroke-linecap","round");var I=document.createElementNS(t,"circle");I.setAttribute("r","2"),I.setAttribute("fill",a),M.append(S,I);var D=document.createElementNS(t,"circle");D.setAttribute("cx","50"),D.setAttribute("cy","50"),D.setAttribute("r","0.3"),D.setAttribute("fill",s);var N=this.getOriginDate(),L=N.getSeconds(),R=N.getMinutes(),C=6*L,B=6*R+L/60*6,W=30*N.getHours()+R/60*30;return O.setAttribute("transform","translate(50 50) rotate("+W+")"),A.setAttribute("transform","translate(50 50) rotate("+B+")"),M.setAttribute("transform","translate(50 50) rotate("+C+")"),p.append(_,m,O,A,M,D),p.setAttribute("transform","rotate(-90)"),h.innerHTML="\n \n ",h.append(p),h},e.prototype.createDigitalClock=function(){var t=document.createElement("div");t.className="digital-clock";var e=this.getElementSize().width,n=6/this.props.clockTimezone.length,i=20*e/100,s=10*e/100,o=Math.min(20*n*e/100,e/100*10),a=this.getOriginDate();if("datetime"===this.props.clockFormat){var c=document.createElement("span");c.className="date",c.textContent=Object(r.b)(a,"default"),c.style.fontSize=s+"px",this.props.color&&(c.style.color=this.props.color),t.append(c)}var l=document.createElement("span");l.className="time",l.textContent=Object(r.c)(a),l.style.fontSize=i+"px",this.props.color&&(l.style.color=this.props.color),t.append(l);var u=this.getHumanTimezone();if(u.length>0){var h=document.createElement("span");h.className="timezone",h.textContent=u,h.style.fontSize=o+"px",this.props.color&&(h.style.color=this.props.color),t.append(h)}return t},e.prototype.getOriginDate=function(t){void 0===t&&(t=null);var e=t||new Date,n=1e3*this.props.clockTimezoneOffset,i=60*e.getTimezoneOffset()*1e3,r=e.getTime()+n+i;return new Date(r)},e.prototype.getHumanTimezone=function(t){void 0===t&&(t=this.props.clockTimezone);var e=t.split("/")[1];return(void 0===e?"":e).replace("_"," ")},e.prototype.getElementSize=function(t,e){switch(void 0===t&&(t=this.props.width),void 0===e&&(e=this.props.height),this.props.clockType){case"analogic":var n=100;return t>0&&e>0?n=Math.min(t,e):t>0?n=t:e>0&&(n=e),{width:n,height:n};case"digital":return t>0&&e>0?e=t/20?e=t/2:e>0?t=2*e:(t=100,e=50),{width:t,height:e};default:throw new Error("invalid clock type.")}},e.TICK_INTERVAL=1e3,e}(s.a),M=function(){var t=function(e,n){return(t=Object.setPrototypeOf||{__proto__:[]}instanceof Array&&function(t,e){t.__proto__=e}||function(t,e){for(var n in e)e.hasOwnProperty(n)&&(t[n]=e[n])})(e,n)};return function(e,n){function i(){this.constructor=e}t(e,n),e.prototype=null===n?Object.create(n):(i.prototype=n.prototype,new i)}}(),S=function(){return(S=Object.assign||function(t){for(var e,n=1,i=arguments.length;n0){t.style.borderStyle="solid";var e=Math.min(this.props.width,this.props.height)/2,n=Math.min(this.props.borderWidth,e);t.style.borderWidth=n+"px",this.props.borderColor&&(t.style.borderColor=this.props.borderColor)}return t},e}(s.a),N=function(){var t=function(e,n){return(t=Object.setPrototypeOf||{__proto__:[]}instanceof Array&&function(t,e){t.__proto__=e}||function(t,e){for(var n in e)e.hasOwnProperty(n)&&(t[n]=e[n])})(e,n)};return function(e,n){function i(){this.constructor=e}t(e,n),e.prototype=null===n?Object.create(n):(i.prototype=n.prototype,new i)}}(),L=function(){return(L=Object.assign||function(t){for(var e,n=1,i=arguments.length;n0&&(n=Object(r.l)([{macro:/\(?_VALUE_\)?/i,value:n}],i)),t.innerHTML=n}return t},e.prototype.createLabelDomElement=function(){var t=document.createElement("div");return t.className="visual-console-item-label",t},e}(s.a),X=n(3),Z=Math.PI,Q=2*Z,J=Q-1e-6;function $(){this._x0=this._y0=this._x1=this._y1=null,this._=""}function tt(){return new $}$.prototype=tt.prototype={constructor:$,moveTo:function(t,e){this._+="M"+(this._x0=this._x1=+t)+","+(this._y0=this._y1=+e)},closePath:function(){null!==this._x1&&(this._x1=this._x0,this._y1=this._y0,this._+="Z")},lineTo:function(t,e){this._+="L"+(this._x1=+t)+","+(this._y1=+e)},quadraticCurveTo:function(t,e,n,i){this._+="Q"+ +t+","+ +e+","+(this._x1=+n)+","+(this._y1=+i)},bezierCurveTo:function(t,e,n,i,r,s){this._+="C"+ +t+","+ +e+","+ +n+","+ +i+","+(this._x1=+r)+","+(this._y1=+s)},arcTo:function(t,e,n,i,r){t=+t,e=+e,n=+n,i=+i,r=+r;var s=this._x1,o=this._y1,a=n-t,c=i-e,l=s-t,u=o-e,h=l*l+u*u;if(r<0)throw new Error("negative radius: "+r);if(null===this._x1)this._+="M"+(this._x1=t)+","+(this._y1=e);else if(h>1e-6)if(Math.abs(u*a-c*l)>1e-6&&r){var p=n-s,_=i-o,f=a*a+c*c,d=p*p+_*_,y=Math.sqrt(f),m=Math.sqrt(h),b=r*Math.tan((Z-Math.acos((f+h-d)/(2*y*m)))/2),v=b/m,x=b/y;Math.abs(v-1)>1e-6&&(this._+="L"+(t+v*l)+","+(e+v*u)),this._+="A"+r+","+r+",0,0,"+ +(u*p>l*_)+","+(this._x1=t+x*a)+","+(this._y1=e+x*c)}else this._+="L"+(this._x1=t)+","+(this._y1=e);else;},arc:function(t,e,n,i,r,s){t=+t,e=+e;var o=(n=+n)*Math.cos(i),a=n*Math.sin(i),c=t+o,l=e+a,u=1^s,h=s?i-r:r-i;if(n<0)throw new Error("negative radius: "+n);null===this._x1?this._+="M"+c+","+l:(Math.abs(this._x1-c)>1e-6||Math.abs(this._y1-l)>1e-6)&&(this._+="L"+c+","+l),n&&(h<0&&(h=h%Q+Q),h>J?this._+="A"+n+","+n+",0,1,"+u+","+(t-o)+","+(e-a)+"A"+n+","+n+",0,1,"+u+","+(this._x1=c)+","+(this._y1=l):h>1e-6&&(this._+="A"+n+","+n+",0,"+ +(h>=Z)+","+u+","+(this._x1=t+n*Math.cos(r))+","+(this._y1=e+n*Math.sin(r))))},rect:function(t,e,n,i){this._+="M"+(this._x0=this._x1=+t)+","+(this._y0=this._y1=+e)+"h"+ +n+"v"+ +i+"h"+-n+"Z"},toString:function(){return this._}};var et=tt,nt=function(t){return function(){return t}},it=Math.abs,rt=Math.atan2,st=Math.cos,ot=Math.max,at=Math.min,ct=Math.sin,lt=Math.sqrt,ut=1e-12,ht=Math.PI,pt=ht/2,_t=2*ht;function ft(t){return t>=1?pt:t<=-1?-pt:Math.asin(t)}function dt(t){return t.innerRadius}function yt(t){return t.outerRadius}function mt(t){return t.startAngle}function bt(t){return t.endAngle}function vt(t){return t&&t.padAngle}function xt(t,e,n,i,r,s,o){var a=t-n,c=e-i,l=(o?s:-s)/lt(a*a+c*c),u=l*c,h=-l*a,p=t+u,_=e+h,f=n+u,d=i+h,y=(p+f)/2,m=(_+d)/2,b=f-p,v=d-_,x=b*b+v*v,g=r-s,E=p*d-f*_,O=(v<0?-1:1)*lt(ot(0,g*g*x-E*E)),w=(E*v-b*O)/x,T=(-E*b-v*O)/x,A=(E*v+b*O)/x,k=(-E*b+v*O)/x,P=w-y,j=T-m,M=A-y,S=k-m;return P*P+j*j>M*M+S*S&&(w=A,T=k),{cx:w,cy:T,x01:-u,y01:-h,x11:w*(r/g-1),y11:T*(r/g-1)}}var gt=function(){var t=dt,e=yt,n=nt(0),i=null,r=mt,s=bt,o=vt,a=null;function c(){var c,l,u,h=+t.apply(this,arguments),p=+e.apply(this,arguments),_=r.apply(this,arguments)-pt,f=s.apply(this,arguments)-pt,d=it(f-_),y=f>_;if(a||(a=c=et()),put)if(d>_t-ut)a.moveTo(p*st(_),p*ct(_)),a.arc(0,0,p,_,f,!y),h>ut&&(a.moveTo(h*st(f),h*ct(f)),a.arc(0,0,h,f,_,y));else{var m,b,v=_,x=f,g=_,E=f,O=d,w=d,T=o.apply(this,arguments)/2,A=T>ut&&(i?+i.apply(this,arguments):lt(h*h+p*p)),k=at(it(p-h)/2,+n.apply(this,arguments)),P=k,j=k;if(A>ut){var M=ft(A/h*ct(T)),S=ft(A/p*ct(T));(O-=2*M)>ut?(g+=M*=y?1:-1,E-=M):(O=0,g=E=(_+f)/2),(w-=2*S)>ut?(v+=S*=y?1:-1,x-=S):(w=0,v=x=(_+f)/2)}var I=p*st(v),D=p*ct(v),N=h*st(E),L=h*ct(E);if(k>ut){var R,C=p*st(x),B=p*ct(x),W=h*st(g),H=h*ct(g);if(d1?0:u<-1?ht:Math.acos(u))/2),V=lt(R[0]*R[0]+R[1]*R[1]);P=at(k,(h-V)/(q-1)),j=at(k,(p-V)/(q+1))}}w>ut?j>ut?(m=xt(W,H,I,D,p,j,y),b=xt(C,B,N,L,p,j,y),a.moveTo(m.cx+m.x01,m.cy+m.y01),jut&&O>ut?P>ut?(m=xt(N,L,C,B,h,-P,y),b=xt(I,D,W,H,h,-P,y),a.lineTo(m.cx+m.x01,m.cy+m.y01),P0)for(var i,r=t[0],s=e[0],o=t[n]-r,a=e[n]-s,c=-1;++c<=n;)i=c/n,this._basis.point(this._beta*t[c]+(1-this._beta)*(r+i*o),this._beta*e[c]+(1-this._beta)*(s+i*a));this._x=this._y=null,this._basis.lineEnd()},point:function(t,e){this._x.push(+t),this._y.push(+e)}};(function t(e){function n(t){return 1===e?new jt(t):new It(t,e)}return n.beta=function(e){return t(+e)},n})(.85);function Dt(t,e,n){t._context.bezierCurveTo(t._x1+t._k*(t._x2-t._x0),t._y1+t._k*(t._y2-t._y0),t._x2+t._k*(t._x1-e),t._y2+t._k*(t._y1-n),t._x2,t._y2)}function Nt(t,e){this._context=t,this._k=(1-e)/6}Nt.prototype={areaStart:function(){this._line=0},areaEnd:function(){this._line=NaN},lineStart:function(){this._x0=this._x1=this._x2=this._y0=this._y1=this._y2=NaN,this._point=0},lineEnd:function(){switch(this._point){case 2:this._context.lineTo(this._x2,this._y2);break;case 3:Dt(this,this._x1,this._y1)}(this._line||0!==this._line&&1===this._point)&&this._context.closePath(),this._line=1-this._line},point:function(t,e){switch(t=+t,e=+e,this._point){case 0:this._point=1,this._line?this._context.lineTo(t,e):this._context.moveTo(t,e);break;case 1:this._point=2,this._x1=t,this._y1=e;break;case 2:this._point=3;default:Dt(this,t,e)}this._x0=this._x1,this._x1=this._x2,this._x2=t,this._y0=this._y1,this._y1=this._y2,this._y2=e}};(function t(e){function n(t){return new Nt(t,e)}return n.tension=function(e){return t(+e)},n})(0);function Lt(t,e){this._context=t,this._k=(1-e)/6}Lt.prototype={areaStart:kt,areaEnd:kt,lineStart:function(){this._x0=this._x1=this._x2=this._x3=this._x4=this._x5=this._y0=this._y1=this._y2=this._y3=this._y4=this._y5=NaN,this._point=0},lineEnd:function(){switch(this._point){case 1:this._context.moveTo(this._x3,this._y3),this._context.closePath();break;case 2:this._context.lineTo(this._x3,this._y3),this._context.closePath();break;case 3:this.point(this._x3,this._y3),this.point(this._x4,this._y4),this.point(this._x5,this._y5)}},point:function(t,e){switch(t=+t,e=+e,this._point){case 0:this._point=1,this._x3=t,this._y3=e;break;case 1:this._point=2,this._context.moveTo(this._x4=t,this._y4=e);break;case 2:this._point=3,this._x5=t,this._y5=e;break;default:Dt(this,t,e)}this._x0=this._x1,this._x1=this._x2,this._x2=t,this._y0=this._y1,this._y1=this._y2,this._y2=e}};(function t(e){function n(t){return new Lt(t,e)}return n.tension=function(e){return t(+e)},n})(0);function Rt(t,e){this._context=t,this._k=(1-e)/6}Rt.prototype={areaStart:function(){this._line=0},areaEnd:function(){this._line=NaN},lineStart:function(){this._x0=this._x1=this._x2=this._y0=this._y1=this._y2=NaN,this._point=0},lineEnd:function(){(this._line||0!==this._line&&3===this._point)&&this._context.closePath(),this._line=1-this._line},point:function(t,e){switch(t=+t,e=+e,this._point){case 0:this._point=1;break;case 1:this._point=2;break;case 2:this._point=3,this._line?this._context.lineTo(this._x2,this._y2):this._context.moveTo(this._x2,this._y2);break;case 3:this._point=4;default:Dt(this,t,e)}this._x0=this._x1,this._x1=this._x2,this._x2=t,this._y0=this._y1,this._y1=this._y2,this._y2=e}};(function t(e){function n(t){return new Rt(t,e)}return n.tension=function(e){return t(+e)},n})(0);function Ct(t,e,n){var i=t._x1,r=t._y1,s=t._x2,o=t._y2;if(t._l01_a>ut){var a=2*t._l01_2a+3*t._l01_a*t._l12_a+t._l12_2a,c=3*t._l01_a*(t._l01_a+t._l12_a);i=(i*a-t._x0*t._l12_2a+t._x2*t._l01_2a)/c,r=(r*a-t._y0*t._l12_2a+t._y2*t._l01_2a)/c}if(t._l23_a>ut){var l=2*t._l23_2a+3*t._l23_a*t._l12_a+t._l12_2a,u=3*t._l23_a*(t._l23_a+t._l12_a);s=(s*l+t._x1*t._l23_2a-e*t._l12_2a)/u,o=(o*l+t._y1*t._l23_2a-n*t._l12_2a)/u}t._context.bezierCurveTo(i,r,s,o,t._x2,t._y2)}function Bt(t,e){this._context=t,this._alpha=e}Bt.prototype={areaStart:function(){this._line=0},areaEnd:function(){this._line=NaN},lineStart:function(){this._x0=this._x1=this._x2=this._y0=this._y1=this._y2=NaN,this._l01_a=this._l12_a=this._l23_a=this._l01_2a=this._l12_2a=this._l23_2a=this._point=0},lineEnd:function(){switch(this._point){case 2:this._context.lineTo(this._x2,this._y2);break;case 3:this.point(this._x2,this._y2)}(this._line||0!==this._line&&1===this._point)&&this._context.closePath(),this._line=1-this._line},point:function(t,e){if(t=+t,e=+e,this._point){var n=this._x2-t,i=this._y2-e;this._l23_a=Math.sqrt(this._l23_2a=Math.pow(n*n+i*i,this._alpha))}switch(this._point){case 0:this._point=1,this._line?this._context.lineTo(t,e):this._context.moveTo(t,e);break;case 1:this._point=2;break;case 2:this._point=3;default:Ct(this,t,e)}this._l01_a=this._l12_a,this._l12_a=this._l23_a,this._l01_2a=this._l12_2a,this._l12_2a=this._l23_2a,this._x0=this._x1,this._x1=this._x2,this._x2=t,this._y0=this._y1,this._y1=this._y2,this._y2=e}};(function t(e){function n(t){return e?new Bt(t,e):new Nt(t,0)}return n.alpha=function(e){return t(+e)},n})(.5);function Wt(t,e){this._context=t,this._alpha=e}Wt.prototype={areaStart:kt,areaEnd:kt,lineStart:function(){this._x0=this._x1=this._x2=this._x3=this._x4=this._x5=this._y0=this._y1=this._y2=this._y3=this._y4=this._y5=NaN,this._l01_a=this._l12_a=this._l23_a=this._l01_2a=this._l12_2a=this._l23_2a=this._point=0},lineEnd:function(){switch(this._point){case 1:this._context.moveTo(this._x3,this._y3),this._context.closePath();break;case 2:this._context.lineTo(this._x3,this._y3),this._context.closePath();break;case 3:this.point(this._x3,this._y3),this.point(this._x4,this._y4),this.point(this._x5,this._y5)}},point:function(t,e){if(t=+t,e=+e,this._point){var n=this._x2-t,i=this._y2-e;this._l23_a=Math.sqrt(this._l23_2a=Math.pow(n*n+i*i,this._alpha))}switch(this._point){case 0:this._point=1,this._x3=t,this._y3=e;break;case 1:this._point=2,this._context.moveTo(this._x4=t,this._y4=e);break;case 2:this._point=3,this._x5=t,this._y5=e;break;default:Ct(this,t,e)}this._l01_a=this._l12_a,this._l12_a=this._l23_a,this._l01_2a=this._l12_2a,this._l12_2a=this._l23_2a,this._x0=this._x1,this._x1=this._x2,this._x2=t,this._y0=this._y1,this._y1=this._y2,this._y2=e}};(function t(e){function n(t){return e?new Wt(t,e):new Lt(t,0)}return n.alpha=function(e){return t(+e)},n})(.5);function Ht(t,e){this._context=t,this._alpha=e}Ht.prototype={areaStart:function(){this._line=0},areaEnd:function(){this._line=NaN},lineStart:function(){this._x0=this._x1=this._x2=this._y0=this._y1=this._y2=NaN,this._l01_a=this._l12_a=this._l23_a=this._l01_2a=this._l12_2a=this._l23_2a=this._point=0},lineEnd:function(){(this._line||0!==this._line&&3===this._point)&&this._context.closePath(),this._line=1-this._line},point:function(t,e){if(t=+t,e=+e,this._point){var n=this._x2-t,i=this._y2-e;this._l23_a=Math.sqrt(this._l23_2a=Math.pow(n*n+i*i,this._alpha))}switch(this._point){case 0:this._point=1;break;case 1:this._point=2;break;case 2:this._point=3,this._line?this._context.lineTo(this._x2,this._y2):this._context.moveTo(this._x2,this._y2);break;case 3:this._point=4;default:Ct(this,t,e)}this._l01_a=this._l12_a,this._l12_a=this._l23_a,this._l01_2a=this._l12_2a,this._l12_2a=this._l23_2a,this._x0=this._x1,this._x1=this._x2,this._x2=t,this._y0=this._y1,this._y1=this._y2,this._y2=e}};(function t(e){function n(t){return e?new Ht(t,e):new Rt(t,0)}return n.alpha=function(e){return t(+e)},n})(.5);function zt(t){this._context=t}zt.prototype={areaStart:kt,areaEnd:kt,lineStart:function(){this._point=0},lineEnd:function(){this._point&&this._context.closePath()},point:function(t,e){t=+t,e=+e,this._point?this._context.lineTo(t,e):(this._point=1,this._context.moveTo(t,e))}};function Ut(t){return t<0?-1:1}function Kt(t,e,n){var i=t._x1-t._x0,r=e-t._x1,s=(t._y1-t._y0)/(i||r<0&&-0),o=(n-t._y1)/(r||i<0&&-0),a=(s*r+o*i)/(i+r);return(Ut(s)+Ut(o))*Math.min(Math.abs(s),Math.abs(o),.5*Math.abs(a))||0}function Gt(t,e){var n=t._x1-t._x0;return n?(3*(t._y1-t._y0)/n-e)/2:e}function qt(t,e,n){var i=t._x0,r=t._y0,s=t._x1,o=t._y1,a=(s-i)/3;t._context.bezierCurveTo(i+a,r+a*e,s-a,o-a*n,s,o)}function Vt(t){this._context=t}function Ft(t){this._context=new Yt(t)}function Yt(t){this._context=t}function Xt(t){this._context=t}function Zt(t){var e,n,i=t.length-1,r=new Array(i),s=new Array(i),o=new Array(i);for(r[0]=0,s[0]=2,o[0]=t[0]+2*t[1],e=1;e=0;--e)r[e]=(o[e]-r[e+1])/s[e];for(s[i-1]=(t[i]+r[i-1])/2,e=0;e=0&&(this._t=1-this._t,this._line=1-this._line)},point:function(t,e){switch(t=+t,e=+e,this._point){case 0:this._point=1,this._line?this._context.lineTo(t,e):this._context.moveTo(t,e);break;case 1:this._point=2;default:if(this._t<=0)this._context.lineTo(this._x,e),this._context.lineTo(t,e);else{var n=this._x*(1-this._t)+t*this._t;this._context.lineTo(n,this._y),this._context.lineTo(n,e)}}this._x=t,this._y=e}};var Jt=function(){var t=function(e,n){return(t=Object.setPrototypeOf||{__proto__:[]}instanceof Array&&function(t,e){t.__proto__=e}||function(t,e){for(var n in e)e.hasOwnProperty(n)&&(t[n]=e[n])})(e,n)};return function(e,n){function i(){this.constructor=e}t(e,n),e.prototype=null===n?Object.create(n):(i.prototype=n.prototype,new i)}}(),$t=function(){return($t=Object.assign||function(t){for(var e,n=1,i=arguments.length;n0){var h=document.createElementNS(ie,"tspan");h.setAttribute("x","0"),h.setAttribute("dy","1em"),h.textContent=""+this.props.value;var p=document.createElementNS(ie,"tspan");p.setAttribute("x","0"),p.setAttribute("dy","1em"),p.textContent=""+this.props.unit,u.append(h,p),u.setAttribute("transform","translate(50 33)")}else u.textContent=""+this.props.value,u.setAttribute("transform","translate(50 50)");else u.textContent=e+"%",u.setAttribute("transform","translate(50 50)");i.append(u)}return n.append(i),n},e.prototype.getProgress=function(){var t=this.props.minValue||0,e=this.props.maxValue||100,n=this.props.value||100;return n<=t?0:n>=e?100:(n-t)/(e-t)*100},e}(s.a),se=n(2),oe=n(4),ae=n(5),ce=n(6),le=function(){var t=function(e,n){return(t=Object.setPrototypeOf||{__proto__:[]}instanceof Array&&function(t,e){t.__proto__=e}||function(t,e){for(var n in e)e.hasOwnProperty(n)&&(t[n]=e[n])})(e,n)};return function(e,n){function i(){this.constructor=e}t(e,n),e.prototype=null===n?Object.create(n):(i.prototype=n.prototype,new i)}}(),ue=function(){return(ue=Object.assign||function(t){for(var e,n=1,i=arguments.length;n(value: any, defaultValue: T): number | T {\n if (typeof value === \"number\") return value;\n if (typeof value === \"string\" && value.length > 0 && !isNaN(parseInt(value)))\n return parseInt(value);\n else return defaultValue;\n}\n\n/**\n * Return a number or a default value from a raw value.\n * @param value Raw value from which we will try to extract a valid number.\n * @param defaultValue Default value to use if we cannot extract a valid number.\n * @return A valid number or the default value.\n */\n// eslint-disable-next-line @typescript-eslint/no-explicit-any\nexport function parseFloatOr(value: any, defaultValue: T): number | T {\n if (typeof value === \"number\") return value;\n if (\n typeof value === \"string\" &&\n value.length > 0 &&\n !isNaN(parseFloat(value))\n )\n return parseFloat(value);\n else return defaultValue;\n}\n\n/**\n * Check if a string exists and it's not empty.\n * @param value Value to check.\n * @return The check result.\n */\nexport function stringIsEmpty(value?: string | null): boolean {\n return value == null || value.length === 0;\n}\n\n/**\n * Return a not empty string or a default value from a raw value.\n * @param value Raw value from which we will try to extract a non empty string.\n * @param defaultValue Default value to use if we cannot extract a non empty string.\n * @return A non empty string or the default value.\n */\n// eslint-disable-next-line @typescript-eslint/no-explicit-any\nexport function notEmptyStringOr(value: any, defaultValue: T): string | T {\n return typeof value === \"string\" && value.length > 0 ? value : defaultValue;\n}\n\n/**\n * Return a boolean from a raw value.\n * @param value Raw value from which we will try to extract the boolean.\n * @return A valid boolean value. false by default.\n */\n// eslint-disable-next-line @typescript-eslint/no-explicit-any\nexport function parseBoolean(value: any): boolean {\n if (typeof value === \"boolean\") return value;\n else if (typeof value === \"number\") return value > 0;\n else if (typeof value === \"string\") return value === \"1\" || value === \"true\";\n else return false;\n}\n\n/**\n * Pad the current string with another string (multiple times, if needed)\n * until the resulting string reaches the given length.\n * The padding is applied from the start (left) of the current string.\n * @param value Text that needs to be padded.\n * @param length Length of the returned text.\n * @param pad Text to add.\n * @return Padded text.\n */\nexport function leftPad(\n value: string | number,\n length: number,\n pad: string | number = \" \"\n): string {\n if (typeof value === \"number\") value = `${value}`;\n if (typeof pad === \"number\") pad = `${pad}`;\n\n const diffLength = length - value.length;\n if (diffLength === 0) return value;\n if (diffLength < 0) return value.substr(Math.abs(diffLength));\n\n if (diffLength === pad.length) return `${pad}${value}`;\n if (diffLength < pad.length) return `${pad.substring(0, diffLength)}${value}`;\n\n const repeatTimes = Math.floor(diffLength / pad.length);\n const restLength = diffLength - pad.length * repeatTimes;\n\n let newPad = \"\";\n for (let i = 0; i < repeatTimes; i++) newPad += pad;\n\n if (restLength === 0) return `${newPad}${value}`;\n return `${newPad}${pad.substring(0, restLength)}${value}`;\n}\n\n/* Decoders */\n\n/**\n * Build a valid typed object from a raw object.\n * @param data Raw object.\n * @return An object representing the position.\n */\nexport function positionPropsDecoder(data: UnknownObject): Position {\n return {\n x: parseIntOr(data.x, 0),\n y: parseIntOr(data.y, 0)\n };\n}\n\n/**\n * Build a valid typed object from a raw object.\n * @param data Raw object.\n * @return An object representing the size.\n * @throws Will throw a TypeError if the width and height are not valid numbers.\n */\nexport function sizePropsDecoder(data: UnknownObject): Size | never {\n if (\n data.width == null ||\n isNaN(parseInt(data.width)) ||\n data.height == null ||\n isNaN(parseInt(data.height))\n ) {\n throw new TypeError(\"invalid size.\");\n }\n\n return {\n width: parseInt(data.width),\n height: parseInt(data.height)\n };\n}\n\n/**\n * Build a valid typed object from a raw object.\n * @param data Raw object.\n * @return An object representing the agent properties.\n */\nexport function agentPropsDecoder(data: UnknownObject): WithAgentProps {\n const agentProps: WithAgentProps = {\n agentId: parseIntOr(data.agent, null),\n agentName: notEmptyStringOr(data.agentName, null),\n agentAlias: notEmptyStringOr(data.agentAlias, null),\n agentDescription: notEmptyStringOr(data.agentDescription, null),\n agentAddress: notEmptyStringOr(data.agentAddress, null)\n };\n\n return data.metaconsoleId != null\n ? {\n metaconsoleId: data.metaconsoleId,\n ...agentProps // Object spread: http://es6-features.org/#SpreadOperator\n }\n : agentProps;\n}\n\n/**\n * Build a valid typed object from a raw object.\n * @param data Raw object.\n * @return An object representing the module and agent properties.\n */\nexport function modulePropsDecoder(data: UnknownObject): WithModuleProps {\n return {\n moduleId: parseIntOr(data.moduleId, null),\n moduleName: notEmptyStringOr(data.moduleName, null),\n moduleDescription: notEmptyStringOr(data.moduleDescription, null),\n ...agentPropsDecoder(data) // Object spread: http://es6-features.org/#SpreadOperator\n };\n}\n\n/**\n * Build a valid typed object from a raw object.\n * @param data Raw object.\n * @return An object representing the linked visual console properties.\n * @throws Will throw a TypeError if the status calculation properties are invalid.\n */\nexport function linkedVCPropsDecoder(\n data: UnknownObject\n): LinkedVisualConsoleProps | never {\n // Object destructuring: http://es6-features.org/#ObjectMatchingShorthandNotation\n const {\n metaconsoleId,\n linkedLayoutId: id,\n linkedLayoutAgentId: agentId\n } = data;\n\n let linkedLayoutStatusProps: LinkedVisualConsolePropsStatus = {\n linkedLayoutStatusType: \"default\"\n };\n switch (data.linkedLayoutStatusType) {\n case \"weight\": {\n const weight = parseIntOr(data.linkedLayoutStatusTypeWeight, null);\n if (weight == null)\n throw new TypeError(\"invalid status calculation properties.\");\n\n if (data.linkedLayoutStatusTypeWeight)\n linkedLayoutStatusProps = {\n linkedLayoutStatusType: \"weight\",\n linkedLayoutStatusTypeWeight: weight\n };\n break;\n }\n case \"service\": {\n const warningThreshold = parseIntOr(\n data.linkedLayoutStatusTypeWarningThreshold,\n null\n );\n const criticalThreshold = parseIntOr(\n data.linkedLayoutStatusTypeCriticalThreshold,\n null\n );\n if (warningThreshold == null || criticalThreshold == null) {\n throw new TypeError(\"invalid status calculation properties.\");\n }\n\n linkedLayoutStatusProps = {\n linkedLayoutStatusType: \"service\",\n linkedLayoutStatusTypeWarningThreshold: warningThreshold,\n linkedLayoutStatusTypeCriticalThreshold: criticalThreshold\n };\n break;\n }\n }\n\n const linkedLayoutBaseProps = {\n linkedLayoutId: parseIntOr(id, null),\n linkedLayoutAgentId: parseIntOr(agentId, null),\n ...linkedLayoutStatusProps // Object spread: http://es6-features.org/#SpreadOperator\n };\n\n return metaconsoleId != null\n ? {\n metaconsoleId,\n ...linkedLayoutBaseProps // Object spread: http://es6-features.org/#SpreadOperator\n }\n : linkedLayoutBaseProps;\n}\n\n/**\n * To get a CSS rule with the most used prefixes.\n * @param ruleName Name of the CSS rule.\n * @param ruleValue Value of the CSS rule.\n * @return An array of rules with the prefixes applied.\n */\nexport function prefixedCssRules(\n ruleName: string,\n ruleValue: string\n): string[] {\n const rule = `${ruleName}: ${ruleValue};`;\n return [\n `-webkit-${rule}`,\n `-moz-${rule}`,\n `-ms-${rule}`,\n `-o-${rule}`,\n `${rule}`\n ];\n}\n\n/**\n * Decode a base64 string.\n * @param input Data encoded using base64.\n * @return Decoded data.\n */\nexport function decodeBase64(input: string): string {\n return decodeURIComponent(escape(window.atob(input)));\n}\n\n/**\n * Generate a date representation with the format 'd/m/Y'.\n * @param initialDate Date to be used instead of a generated one.\n * @param locale Locale to use if localization is required and available.\n * @example 24/02/2020.\n * @return Date representation.\n */\nexport function humanDate(date: Date, locale: string | null = null): string {\n if (locale && Intl && Intl.DateTimeFormat) {\n // Format using the user locale.\n const options: Intl.DateTimeFormatOptions = {\n day: \"2-digit\",\n month: \"2-digit\",\n year: \"numeric\"\n };\n return Intl.DateTimeFormat(locale, options).format(date);\n } else {\n // Use getDate, getDay returns the week day.\n const day = leftPad(date.getDate(), 2, 0);\n // The getMonth function returns the month starting by 0.\n const month = leftPad(date.getMonth() + 1, 2, 0);\n const year = leftPad(date.getFullYear(), 4, 0);\n\n // Format: 'd/m/Y'.\n return `${day}/${month}/${year}`;\n }\n}\n\n/**\n * Generate a time representation with the format 'hh:mm:ss'.\n * @param initialDate Date to be used instead of a generated one.\n * @example 01:34:09.\n * @return Time representation.\n */\nexport function humanTime(date: Date): string {\n const hours = leftPad(date.getHours(), 2, 0);\n const minutes = leftPad(date.getMinutes(), 2, 0);\n const seconds = leftPad(date.getSeconds(), 2, 0);\n\n return `${hours}:${minutes}:${seconds}`;\n}\n\ninterface Macro {\n macro: string | RegExp;\n value: string;\n}\n/**\n * Replace the macros of a text.\n * @param macros List of macros and their replacements.\n * @param text Text in which we need to replace the macros.\n */\nexport function replaceMacros(macros: Macro[], text: string): string {\n return macros.reduce(\n (acc, { macro, value }) => acc.replace(macro, value),\n text\n );\n}\n","import { Position, Size, UnknownObject, WithModuleProps } from \"./types\";\nimport {\n sizePropsDecoder,\n positionPropsDecoder,\n parseIntOr,\n parseBoolean,\n notEmptyStringOr,\n replaceMacros,\n humanDate,\n humanTime\n} from \"./lib\";\nimport TypedEvent, { Listener, Disposable } from \"./TypedEvent\";\n\n// Enum: https://www.typescriptlang.org/docs/handbook/enums.html.\nexport const enum ItemType {\n STATIC_GRAPH = 0,\n MODULE_GRAPH = 1,\n SIMPLE_VALUE = 2,\n PERCENTILE_BAR = 3,\n LABEL = 4,\n ICON = 5,\n SIMPLE_VALUE_MAX = 6,\n SIMPLE_VALUE_MIN = 7,\n SIMPLE_VALUE_AVG = 8,\n PERCENTILE_BUBBLE = 9,\n SERVICE = 10,\n GROUP_ITEM = 11,\n BOX_ITEM = 12,\n LINE_ITEM = 13,\n AUTO_SLA_GRAPH = 14,\n CIRCULAR_PROGRESS_BAR = 15,\n CIRCULAR_INTERIOR_PROGRESS_BAR = 16,\n DONUT_GRAPH = 17,\n BARS_GRAPH = 18,\n CLOCK = 19,\n COLOR_CLOUD = 20\n}\n\n// Base item properties. This interface should be extended by the item implementations.\nexport interface ItemProps extends Position, Size {\n readonly id: number;\n readonly type: ItemType;\n label: string | null;\n labelPosition: \"up\" | \"right\" | \"down\" | \"left\";\n isLinkEnabled: boolean;\n link: string | null;\n isOnTop: boolean;\n parentId: number | null;\n aclGroupId: number | null;\n}\n\n// FIXME: Fix type compatibility.\nexport interface ItemClickEvent {\n // data: Props;\n data: UnknownObject;\n nativeEvent: Event;\n}\n\n// FIXME: Fix type compatibility.\nexport interface ItemRemoveEvent {\n // data: Props;\n data: UnknownObject;\n}\n\n/**\n * Extract a valid enum value from a raw label positi9on value.\n * @param labelPosition Raw value.\n */\nconst parseLabelPosition = (\n labelPosition: any // eslint-disable-line @typescript-eslint/no-explicit-any\n): ItemProps[\"labelPosition\"] => {\n switch (labelPosition) {\n case \"up\":\n case \"right\":\n case \"down\":\n case \"left\":\n return labelPosition;\n default:\n return \"down\";\n }\n};\n\n/**\n * Build a valid typed object from a raw object.\n * This will allow us to ensure the type safety.\n *\n * @param data Raw object.\n * @return An object representing the item props.\n * @throws Will throw a TypeError if some property\n * is missing from the raw object or have an invalid type.\n */\nexport function itemBasePropsDecoder(data: UnknownObject): ItemProps | never {\n if (data.id == null || isNaN(parseInt(data.id))) {\n throw new TypeError(\"invalid id.\");\n }\n if (data.type == null || isNaN(parseInt(data.type))) {\n throw new TypeError(\"invalid type.\");\n }\n\n return {\n id: parseInt(data.id),\n type: parseInt(data.type),\n label: notEmptyStringOr(data.label, null),\n labelPosition: parseLabelPosition(data.labelPosition),\n isLinkEnabled: parseBoolean(data.isLinkEnabled),\n link: notEmptyStringOr(data.link, null),\n isOnTop: parseBoolean(data.isOnTop),\n parentId: parseIntOr(data.parentId, null),\n aclGroupId: parseIntOr(data.aclGroupId, null),\n ...sizePropsDecoder(data), // Object spread. It will merge the properties of the two objects.\n ...positionPropsDecoder(data) // Object spread. It will merge the properties of the two objects.\n };\n}\n\n/**\n * Base class of the visual console items. Should be extended to use its capabilities.\n */\nabstract class VisualConsoleItem {\n // Properties of the item.\n private itemProps: Props;\n // Reference to the DOM element which will contain the item.\n public elementRef: HTMLElement;\n public readonly labelElementRef: HTMLElement;\n // Reference to the DOM element which will contain the view of the item which extends this class.\n protected readonly childElementRef: HTMLElement;\n // Event manager for click events.\n private readonly clickEventManager = new TypedEvent>();\n // Event manager for remove events.\n private readonly removeEventManager = new TypedEvent<\n ItemRemoveEvent\n >();\n // List of references to clean the event listeners.\n private readonly disposables: Disposable[] = [];\n\n /**\n * To create a new element which will be inside the item box.\n * @return Item.\n */\n protected abstract createDomElement(): HTMLElement;\n\n public constructor(props: Props) {\n this.itemProps = props;\n\n /*\n * Get a HTMLElement which represents the container box\n * of the Visual Console item. This element will manage\n * all the common things like click events, show a border\n * when hovered, etc.\n */\n this.elementRef = this.createContainerDomElement();\n this.labelElementRef = this.createLabelDomElement();\n\n /*\n * Get a HTMLElement which represents the custom view\n * of the Visual Console item. This element will be\n * different depending on the item implementation.\n */\n this.childElementRef = this.createDomElement();\n\n // Insert the elements into the container.\n this.elementRef.append(this.childElementRef, this.labelElementRef);\n\n // Resize element.\n this.resizeElement(props.width, props.height);\n // Set label position.\n this.changeLabelPosition(props.labelPosition);\n }\n\n /**\n * To create a new box for the visual console item.\n * @return Item box.\n */\n private createContainerDomElement(): HTMLElement {\n let box;\n if (this.props.isLinkEnabled) {\n box = document.createElement(\"a\");\n box as HTMLAnchorElement;\n if (this.props.link) box.href = this.props.link;\n } else {\n box = document.createElement(\"div\");\n box as HTMLDivElement;\n }\n\n box.className = \"visual-console-item\";\n box.style.zIndex = this.props.isOnTop ? \"2\" : \"1\";\n box.style.left = `${this.props.x}px`;\n box.style.top = `${this.props.y}px`;\n box.onclick = e =>\n this.clickEventManager.emit({ data: this.props, nativeEvent: e });\n\n return box;\n }\n\n /**\n * To create a new label for the visual console item.\n * @return Item label.\n */\n protected createLabelDomElement(): HTMLElement {\n const element = document.createElement(\"div\");\n element.className = \"visual-console-item-label\";\n // Add the label if it exists.\n const label = this.getLabelWithMacrosReplaced();\n if (label.length > 0) {\n // Ugly table we need to use to replicate the legacy style.\n const table = document.createElement(\"table\");\n const row = document.createElement(\"tr\");\n const emptyRow1 = document.createElement(\"tr\");\n const emptyRow2 = document.createElement(\"tr\");\n const cell = document.createElement(\"td\");\n\n cell.innerHTML = label;\n row.append(cell);\n table.append(emptyRow1, row, emptyRow2);\n table.style.textAlign = \"center\";\n\n // Change the table size depending on its position.\n switch (this.props.labelPosition) {\n case \"up\":\n case \"down\":\n if (this.props.width > 0) {\n table.style.width = `${this.props.width}px`;\n table.style.height = null;\n }\n break;\n case \"left\":\n case \"right\":\n if (this.props.height > 0) {\n table.style.width = null;\n table.style.height = `${this.props.height}px`;\n }\n break;\n }\n\n // element.innerHTML = this.props.label;\n element.append(table);\n }\n\n return element;\n }\n\n /**\n * Return the label stored into the props with some macros replaced.\n */\n protected getLabelWithMacrosReplaced(): string {\n // We assert that the props may have some needed properties.\n const props = this.props as Partial;\n\n return replaceMacros(\n [\n {\n macro: \"_date_\",\n value: humanDate(new Date())\n },\n {\n macro: \"_time_\",\n value: humanTime(new Date())\n },\n {\n macro: \"_agent_\",\n value: props.agentAlias != null ? props.agentAlias : \"\"\n },\n {\n macro: \"_agentdescription_\",\n value: props.agentDescription != null ? props.agentDescription : \"\"\n },\n {\n macro: \"_address_\",\n value: props.agentAddress != null ? props.agentAddress : \"\"\n },\n {\n macro: \"_module_\",\n value: props.moduleName != null ? props.moduleName : \"\"\n },\n {\n macro: \"_moduledescription_\",\n value: props.moduleDescription != null ? props.moduleDescription : \"\"\n }\n ],\n this.props.label || \"\"\n );\n }\n\n /**\n * To update the content element.\n * @return Item.\n */\n protected updateDomElement(element: HTMLElement): void {\n element.innerHTML = this.createDomElement().innerHTML;\n }\n\n /**\n * Public accessor of the `props` property.\n * @return Properties.\n */\n public get props(): Props {\n return { ...this.itemProps }; // Return a copy.\n }\n\n /**\n * Public setter of the `props` property.\n * If the new props are different enough than the\n * stored props, a render would be fired.\n * @param newProps\n */\n public set props(newProps: Props) {\n const prevProps = this.props;\n // Update the internal props.\n this.itemProps = newProps;\n\n // From this point, things which rely on this.props can access to the changes.\n\n // Check if we should re-render.\n if (this.shouldBeUpdated(prevProps, newProps)) this.render(prevProps);\n }\n\n /**\n * To compare the previous and the new props and returns a boolean value\n * in case the difference is meaningfull enough to perform DOM changes.\n *\n * Here, the only comparision is done by reference.\n *\n * Override this function to perform a different comparision depending on the item needs.\n *\n * @param prevProps\n * @param newProps\n * @return Whether the difference is meaningful enough to perform DOM changes or not.\n */\n protected shouldBeUpdated(prevProps: Props, newProps: Props): boolean {\n return prevProps !== newProps;\n }\n\n /**\n * To recreate or update the HTMLElement which represents the item into the DOM.\n * @param prevProps If exists it will be used to only perform DOM updates instead of a full replace.\n */\n public render(prevProps: Props | null = null): void {\n this.updateDomElement(this.childElementRef);\n\n // Move box.\n if (!prevProps || this.positionChanged(prevProps, this.props)) {\n this.moveElement(this.props.x, this.props.y);\n }\n // Resize box.\n if (!prevProps || this.sizeChanged(prevProps, this.props)) {\n this.resizeElement(this.props.width, this.props.height);\n }\n // Change label.\n const oldLabelHtml = this.labelElementRef.innerHTML;\n const newLabelHtml = this.createLabelDomElement().innerHTML;\n if (oldLabelHtml !== newLabelHtml) {\n this.labelElementRef.innerHTML = newLabelHtml;\n }\n // Change label position.\n if (!prevProps || prevProps.labelPosition !== this.props.labelPosition) {\n this.changeLabelPosition(this.props.labelPosition);\n }\n // Change link.\n if (\n prevProps &&\n (prevProps.isLinkEnabled !== this.props.isLinkEnabled ||\n (this.props.isLinkEnabled && prevProps.link !== this.props.link))\n ) {\n const container = this.createContainerDomElement();\n container.innerHTML = this.elementRef.innerHTML;\n\n if (this.elementRef.parentNode !== null) {\n this.elementRef.parentNode.replaceChild(container, this.elementRef);\n }\n\n // Changed the reference to the main element. It's ugly, but needed.\n this.elementRef = container;\n }\n }\n\n /**\n * To remove the event listeners and the elements from the DOM.\n */\n public remove(): void {\n // Call the remove event.\n this.removeEventManager.emit({ data: this.props });\n // Event listeners.\n this.disposables.forEach(disposable => {\n try {\n disposable.dispose();\n } catch (ignored) {} // eslint-disable-line no-empty\n });\n // VisualConsoleItem DOM element.\n this.elementRef.remove();\n }\n\n /**\n * Compare the previous and the new position and return\n * a boolean value in case the position changed.\n * @param prevPosition\n * @param newPosition\n * @return Whether the position changed or not.\n */\n protected positionChanged(\n prevPosition: Position,\n newPosition: Position\n ): boolean {\n return prevPosition.x !== newPosition.x || prevPosition.y !== newPosition.y;\n }\n\n /**\n * Move the label around the item content.\n * @param position Label position.\n */\n protected changeLabelPosition(position: Props[\"labelPosition\"]): void {\n switch (position) {\n case \"up\":\n this.elementRef.style.flexDirection = \"column-reverse\";\n break;\n case \"left\":\n this.elementRef.style.flexDirection = \"row-reverse\";\n break;\n case \"right\":\n this.elementRef.style.flexDirection = \"row\";\n break;\n case \"down\":\n default:\n this.elementRef.style.flexDirection = \"column\";\n break;\n }\n\n // Ugly table to show the label as its legacy counterpart.\n const tables = this.labelElementRef.getElementsByTagName(\"table\");\n const table = tables.length > 0 ? tables.item(0) : null;\n // Change the table size depending on its position.\n if (table) {\n switch (this.props.labelPosition) {\n case \"up\":\n case \"down\":\n if (this.props.width > 0) {\n table.style.width = `${this.props.width}px`;\n table.style.height = null;\n }\n break;\n case \"left\":\n case \"right\":\n if (this.props.height > 0) {\n table.style.width = null;\n table.style.height = `${this.props.height}px`;\n }\n break;\n }\n }\n }\n\n /**\n * Move the DOM container.\n * @param x Horizontal axis position.\n * @param y Vertical axis position.\n */\n protected moveElement(x: number, y: number): void {\n this.elementRef.style.left = `${x}px`;\n this.elementRef.style.top = `${y}px`;\n }\n\n /**\n * Update the position into the properties and move the DOM container.\n * @param x Horizontal axis position.\n * @param y Vertical axis position.\n */\n public move(x: number, y: number): void {\n this.moveElement(x, y);\n this.itemProps = {\n ...this.props, // Object spread: http://es6-features.org/#SpreadOperator\n x,\n y\n };\n }\n\n /**\n * Compare the previous and the new size and return\n * a boolean value in case the size changed.\n * @param prevSize\n * @param newSize\n * @return Whether the size changed or not.\n */\n protected sizeChanged(prevSize: Size, newSize: Size): boolean {\n return (\n prevSize.width !== newSize.width || prevSize.height !== newSize.height\n );\n }\n\n /**\n * Resize the DOM content container.\n * @param width\n * @param height\n */\n protected resizeElement(width: number, height: number): void {\n // The most valuable size is the content size.\n this.childElementRef.style.width = width > 0 ? `${width}px` : null;\n this.childElementRef.style.height = height > 0 ? `${height}px` : null;\n }\n\n /**\n * Update the size into the properties and resize the DOM container.\n * @param width\n * @param height\n */\n public resize(width: number, height: number): void {\n this.resizeElement(width, height);\n this.itemProps = {\n ...this.props, // Object spread: http://es6-features.org/#SpreadOperator\n width,\n height\n };\n }\n\n /**\n * To add an event handler to the click of the linked visual console elements.\n * @param listener Function which is going to be executed when a linked console is clicked.\n */\n public onClick(listener: Listener>): Disposable {\n /*\n * The '.on' function returns a function which will clean the event\n * listener when executed. We store all the 'dispose' functions to\n * call them when the item should be cleared.\n */\n const disposable = this.clickEventManager.on(listener);\n this.disposables.push(disposable);\n\n return disposable;\n }\n\n /**\n * To add an event handler to the removal of the item.\n * @param listener Function which is going to be executed when a item is removed.\n */\n public onRemove(listener: Listener>): Disposable {\n /*\n * The '.on' function returns a function which will clean the event\n * listener when executed. We store all the 'dispose' functions to\n * call them when the item should be cleared.\n */\n const disposable = this.removeEventManager.on(listener);\n this.disposables.push(disposable);\n\n return disposable;\n }\n}\n\nexport default VisualConsoleItem;\n","export interface Listener {\n (event: T): void;\n}\n\nexport interface Disposable {\n dispose: () => void;\n}\n\n/** passes through events as they happen. You will not get events from before you start listening */\nexport default class TypedEvent {\n private listeners: Listener[] = [];\n private listenersOncer: Listener[] = [];\n\n public on = (listener: Listener): Disposable => {\n this.listeners.push(listener);\n return {\n dispose: () => this.off(listener)\n };\n };\n\n public once = (listener: Listener): void => {\n this.listenersOncer.push(listener);\n };\n\n public off = (listener: Listener): void => {\n const callbackIndex = this.listeners.indexOf(listener);\n if (callbackIndex > -1) this.listeners.splice(callbackIndex, 1);\n };\n\n public emit = (event: T): void => {\n /** Update any general listeners */\n this.listeners.forEach(listener => listener(event));\n\n /** Clear the `once` queue */\n this.listenersOncer.forEach(listener => listener(event));\n this.listenersOncer = [];\n };\n\n public pipe = (te: TypedEvent): Disposable => this.on(e => te.emit(e));\n}\n","import { UnknownObject, WithModuleProps } from \"../types\";\nimport {\n modulePropsDecoder,\n parseIntOr,\n decodeBase64,\n stringIsEmpty\n} from \"../lib\";\nimport Item, { ItemType, ItemProps, itemBasePropsDecoder } from \"../Item\";\n\nexport type EventsHistoryProps = {\n type: ItemType.AUTO_SLA_GRAPH;\n maxTime: number | null;\n html: string;\n} & ItemProps &\n WithModuleProps;\n\n/**\n * Build a valid typed object from a raw object.\n * This will allow us to ensure the type safety.\n *\n * @param data Raw object.\n * @return An object representing the events history props.\n * @throws Will throw a TypeError if some property\n * is missing from the raw object or have an invalid type.\n */\nexport function eventsHistoryPropsDecoder(\n data: UnknownObject\n): EventsHistoryProps | never {\n if (stringIsEmpty(data.html) && stringIsEmpty(data.encodedHtml)) {\n throw new TypeError(\"missing html content.\");\n }\n\n return {\n ...itemBasePropsDecoder(data), // Object spread. It will merge the properties of the two objects.\n type: ItemType.AUTO_SLA_GRAPH,\n maxTime: parseIntOr(data.maxTime, null),\n html: !stringIsEmpty(data.html)\n ? data.html\n : decodeBase64(data.encodedHtml),\n ...modulePropsDecoder(data) // Object spread. It will merge the properties of the two objects.\n };\n}\n\nexport default class EventsHistory extends Item {\n protected createDomElement(): HTMLElement {\n const element = document.createElement(\"div\");\n element.className = \"events-history\";\n element.innerHTML = this.props.html;\n\n // Hack to execute the JS after the HTML is added to the DOM.\n const scripts = element.getElementsByTagName(\"script\");\n for (let i = 0; i < scripts.length; i++) {\n if (scripts[i].src.length === 0) {\n setTimeout(() => {\n try {\n eval(scripts[i].innerHTML.trim());\n } catch (ignored) {} // eslint-disable-line no-empty\n }, 0);\n }\n }\n\n return element;\n }\n\n protected updateDomElement(element: HTMLElement): void {\n element.innerHTML = this.props.html;\n\n // Hack to execute the JS after the HTML is added to the DOM.\n const aux = document.createElement(\"div\");\n aux.innerHTML = this.props.html;\n const scripts = aux.getElementsByTagName(\"script\");\n for (let i = 0; i < scripts.length; i++) {\n if (scripts[i].src.length === 0) {\n eval(scripts[i].innerHTML.trim());\n }\n }\n }\n}\n","import {\n LinkedVisualConsoleProps,\n UnknownObject,\n WithModuleProps\n} from \"../types\";\nimport {\n linkedVCPropsDecoder,\n modulePropsDecoder,\n decodeBase64,\n stringIsEmpty\n} from \"../lib\";\nimport Item, { ItemType, ItemProps, itemBasePropsDecoder } from \"../Item\";\n\nexport type DonutGraphProps = {\n type: ItemType.DONUT_GRAPH;\n html: string;\n} & ItemProps &\n WithModuleProps &\n LinkedVisualConsoleProps;\n\n/**\n * Build a valid typed object from a raw object.\n * This will allow us to ensure the type safety.\n *\n * @param data Raw object.\n * @return An object representing the donut graph props.\n * @throws Will throw a TypeError if some property\n * is missing from the raw object or have an invalid type.\n */\nexport function donutGraphPropsDecoder(\n data: UnknownObject\n): DonutGraphProps | never {\n if (stringIsEmpty(data.html) && stringIsEmpty(data.encodedHtml)) {\n throw new TypeError(\"missing html content.\");\n }\n\n return {\n ...itemBasePropsDecoder(data), // Object spread. It will merge the properties of the two objects.\n type: ItemType.DONUT_GRAPH,\n html: !stringIsEmpty(data.html)\n ? data.html\n : decodeBase64(data.encodedHtml),\n ...modulePropsDecoder(data), // Object spread. It will merge the properties of the two objects.\n ...linkedVCPropsDecoder(data) // Object spread. It will merge the properties of the two objects.\n };\n}\n\nexport default class DonutGraph extends Item {\n protected createDomElement(): HTMLElement {\n const element = document.createElement(\"div\");\n element.className = \"donut-graph\";\n element.innerHTML = this.props.html;\n\n // Hack to execute the JS after the HTML is added to the DOM.\n const scripts = element.getElementsByTagName(\"script\");\n for (let i = 0; i < scripts.length; i++) {\n setTimeout(() => {\n if (scripts[i].src.length === 0) eval(scripts[i].innerHTML.trim());\n }, 0);\n }\n\n return element;\n }\n\n protected updateDomElement(element: HTMLElement): void {\n element.innerHTML = this.props.html;\n\n // Hack to execute the JS after the HTML is added to the DOM.\n const aux = document.createElement(\"div\");\n aux.innerHTML = this.props.html;\n const scripts = aux.getElementsByTagName(\"script\");\n for (let i = 0; i < scripts.length; i++) {\n if (scripts[i].src.length === 0) {\n eval(scripts[i].innerHTML.trim());\n }\n }\n }\n}\n","import { UnknownObject, WithModuleProps } from \"../types\";\nimport { modulePropsDecoder, decodeBase64, stringIsEmpty } from \"../lib\";\nimport Item, { ItemType, ItemProps, itemBasePropsDecoder } from \"../Item\";\n\nexport type BarsGraphProps = {\n type: ItemType.BARS_GRAPH;\n html: string;\n} & ItemProps &\n WithModuleProps;\n\n/**\n * Build a valid typed object from a raw object.\n * This will allow us to ensure the type safety.\n *\n * @param data Raw object.\n * @return An object representing the bars graph props.\n * @throws Will throw a TypeError if some property\n * is missing from the raw object or have an invalid type.\n */\nexport function barsGraphPropsDecoder(\n data: UnknownObject\n): BarsGraphProps | never {\n if (stringIsEmpty(data.html) && stringIsEmpty(data.encodedHtml)) {\n throw new TypeError(\"missing html content.\");\n }\n\n return {\n ...itemBasePropsDecoder(data), // Object spread. It will merge the properties of the two objects.\n type: ItemType.BARS_GRAPH,\n html: !stringIsEmpty(data.html)\n ? data.html\n : decodeBase64(data.encodedHtml),\n ...modulePropsDecoder(data) // Object spread. It will merge the properties of the two objects.\n };\n}\n\nexport default class BarsGraph extends Item {\n protected createDomElement(): HTMLElement {\n const element = document.createElement(\"div\");\n element.className = \"bars-graph\";\n element.innerHTML = this.props.html;\n\n // Hack to execute the JS after the HTML is added to the DOM.\n const scripts = element.getElementsByTagName(\"script\");\n for (let i = 0; i < scripts.length; i++) {\n setTimeout(() => {\n if (scripts[i].src.length === 0) eval(scripts[i].innerHTML.trim());\n }, 0);\n }\n\n return element;\n }\n\n protected updateDomElement(element: HTMLElement): void {\n element.innerHTML = this.props.html;\n\n // Hack to execute the JS after the HTML is added to the DOM.\n const aux = document.createElement(\"div\");\n aux.innerHTML = this.props.html;\n const scripts = aux.getElementsByTagName(\"script\");\n for (let i = 0; i < scripts.length; i++) {\n if (scripts[i].src.length === 0) {\n eval(scripts[i].innerHTML.trim());\n }\n }\n }\n}\n","import {\n LinkedVisualConsoleProps,\n UnknownObject,\n WithModuleProps\n} from \"../types\";\nimport {\n linkedVCPropsDecoder,\n modulePropsDecoder,\n decodeBase64,\n stringIsEmpty\n} from \"../lib\";\nimport Item, { ItemType, ItemProps, itemBasePropsDecoder } from \"../Item\";\n\nexport type ModuleGraphProps = {\n type: ItemType.MODULE_GRAPH;\n html: string;\n} & ItemProps &\n WithModuleProps &\n LinkedVisualConsoleProps;\n\n/**\n * Build a valid typed object from a raw object.\n * This will allow us to ensure the type safety.\n *\n * @param data Raw object.\n * @return An object representing the module graph props.\n * @throws Will throw a TypeError if some property\n * is missing from the raw object or have an invalid type.\n */\nexport function moduleGraphPropsDecoder(\n data: UnknownObject\n): ModuleGraphProps | never {\n if (stringIsEmpty(data.html) && stringIsEmpty(data.encodedHtml)) {\n throw new TypeError(\"missing html content.\");\n }\n\n return {\n ...itemBasePropsDecoder(data), // Object spread. It will merge the properties of the two objects.\n type: ItemType.MODULE_GRAPH,\n html: !stringIsEmpty(data.html)\n ? data.html\n : decodeBase64(data.encodedHtml),\n ...modulePropsDecoder(data), // Object spread. It will merge the properties of the two objects.\n ...linkedVCPropsDecoder(data) // Object spread. It will merge the properties of the two objects.\n };\n}\n\nexport default class ModuleGraph extends Item {\n /**\n * @override Item.resizeElement.\n * Resize the DOM content container.\n * We need to override the resize function cause this item's height\n * is larger than the configured and the graph is over the label.\n * @param width\n * @param height\n */\n protected resizeElement(width: number): void {\n super.resizeElement(width, 0);\n }\n\n protected createDomElement(): HTMLElement {\n const element = document.createElement(\"div\");\n element.className = \"module-graph\";\n element.innerHTML = this.props.html;\n\n // Remove the overview graph.\n const legendP = element.getElementsByTagName(\"p\");\n for (let i = 0; i < legendP.length; i++) {\n legendP[i].style.margin = \"0px\";\n }\n\n // Remove the overview graph.\n const overviewGraphs = element.getElementsByClassName(\"overview_graph\");\n for (let i = 0; i < overviewGraphs.length; i++) {\n overviewGraphs[i].remove();\n }\n\n // Hack to execute the JS after the HTML is added to the DOM.\n const scripts = element.getElementsByTagName(\"script\");\n for (let i = 0; i < scripts.length; i++) {\n if (scripts[i].src.length === 0) {\n setTimeout(() => {\n try {\n eval(scripts[i].innerHTML.trim());\n } catch (ignored) {} // eslint-disable-line no-empty\n }, 0);\n }\n }\n\n return element;\n }\n\n protected updateDomElement(element: HTMLElement): void {\n element.innerHTML = this.props.html;\n\n // Remove the overview graph.\n const legendP = element.getElementsByTagName(\"p\");\n for (let i = 0; i < legendP.length; i++) {\n legendP[i].style.margin = \"0px\";\n }\n\n // Remove the overview graph.\n const overviewGraphs = element.getElementsByClassName(\"overview_graph\");\n for (let i = 0; i < overviewGraphs.length; i++) {\n overviewGraphs[i].remove();\n }\n\n // Hack to execute the JS after the HTML is added to the DOM.\n const aux = document.createElement(\"div\");\n aux.innerHTML = this.props.html;\n const scripts = aux.getElementsByTagName(\"script\");\n for (let i = 0; i < scripts.length; i++) {\n if (scripts[i].src.length === 0) {\n eval(scripts[i].innerHTML.trim());\n }\n }\n }\n}\n","import {\n WithModuleProps,\n LinkedVisualConsoleProps,\n UnknownObject\n} from \"../types\";\n\nimport {\n modulePropsDecoder,\n linkedVCPropsDecoder,\n notEmptyStringOr\n} from \"../lib\";\nimport Item, { ItemType, ItemProps, itemBasePropsDecoder } from \"../Item\";\n\nexport type StaticGraphProps = {\n type: ItemType.STATIC_GRAPH;\n imageSrc: string; // URL?\n showLastValueTooltip: \"default\" | \"enabled\" | \"disabled\";\n statusImageSrc: string | null; // URL?\n lastValue: string | null;\n} & ItemProps &\n (WithModuleProps | LinkedVisualConsoleProps);\n\n/**\n * Extract a valid enum value from a raw unknown value.\n * @param showLastValueTooltip Raw value.\n */\nconst parseShowLastValueTooltip = (\n showLastValueTooltip: any // eslint-disable-line @typescript-eslint/no-explicit-any\n): StaticGraphProps[\"showLastValueTooltip\"] => {\n switch (showLastValueTooltip) {\n case \"default\":\n case \"enabled\":\n case \"disabled\":\n return showLastValueTooltip;\n default:\n return \"default\";\n }\n};\n\n/**\n * Build a valid typed object from a raw object.\n * This will allow us to ensure the type safety.\n *\n * @param data Raw object.\n * @return An object representing the static graph props.\n * @throws Will throw a TypeError if some property\n * is missing from the raw object or have an invalid type.\n */\nexport function staticGraphPropsDecoder(\n data: UnknownObject\n): StaticGraphProps | never {\n if (typeof data.imageSrc !== \"string\" || data.imageSrc.length === 0) {\n throw new TypeError(\"invalid image src.\");\n }\n\n return {\n ...itemBasePropsDecoder(data), // Object spread. It will merge the properties of the two objects.\n type: ItemType.STATIC_GRAPH,\n imageSrc: data.imageSrc,\n showLastValueTooltip: parseShowLastValueTooltip(data.showLastValueTooltip),\n statusImageSrc: notEmptyStringOr(data.statusImageSrc, null),\n lastValue: notEmptyStringOr(data.lastValue, null),\n ...modulePropsDecoder(data), // Object spread. It will merge the properties of the two objects.\n ...linkedVCPropsDecoder(data) // Object spread. It will merge the properties of the two objects.\n };\n}\n\nexport default class StaticGraph extends Item {\n protected createDomElement(): HTMLElement {\n const imgSrc = this.props.statusImageSrc || this.props.imageSrc;\n const element = document.createElement(\"div\");\n element.className = \"static-graph\";\n element.style.background = `url(${imgSrc}) no-repeat`;\n element.style.backgroundSize = \"contain\";\n element.style.backgroundPosition = \"center\";\n\n // Show last value in a tooltip.\n if (\n this.props.lastValue !== null &&\n this.props.showLastValueTooltip !== \"disabled\"\n ) {\n element.className = \"static-graph image forced_title\";\n element.setAttribute(\"data-use_title_for_force_title\", \"1\");\n element.setAttribute(\"data-title\", this.props.lastValue);\n }\n\n return element;\n }\n}\n","import { LinkedVisualConsoleProps, UnknownObject } from \"../types\";\nimport { linkedVCPropsDecoder } from \"../lib\";\nimport Item, { ItemType, ItemProps, itemBasePropsDecoder } from \"../Item\";\n\nexport type IconProps = {\n type: ItemType.ICON;\n imageSrc: string; // URL?\n} & ItemProps &\n LinkedVisualConsoleProps;\n\n/**\n * Build a valid typed object from a raw object.\n * This will allow us to ensure the type safety.\n *\n * @param data Raw object.\n * @return An object representing the icon props.\n * @throws Will throw a TypeError if some property\n * is missing from the raw object or have an invalid type.\n */\nexport function iconPropsDecoder(data: UnknownObject): IconProps | never {\n if (typeof data.imageSrc !== \"string\" || data.imageSrc.length === 0) {\n throw new TypeError(\"invalid image src.\");\n }\n\n return {\n ...itemBasePropsDecoder(data), // Object spread. It will merge the properties of the two objects.\n type: ItemType.ICON,\n imageSrc: data.imageSrc,\n ...linkedVCPropsDecoder(data) // Object spread. It will merge the properties of the two objects.\n };\n}\n\nexport default class Icon extends Item {\n protected createDomElement(): HTMLElement {\n const element = document.createElement(\"div\");\n element.className = \"icon\";\n element.style.background = `url(${this.props.imageSrc}) no-repeat`;\n element.style.backgroundSize = \"contain\";\n element.style.backgroundPosition = \"center\";\n\n return element;\n }\n}\n","import {\n WithModuleProps,\n LinkedVisualConsoleProps,\n UnknownObject\n} from \"../types\";\nimport { modulePropsDecoder, linkedVCPropsDecoder } from \"../lib\";\nimport Item, { itemBasePropsDecoder, ItemType, ItemProps } from \"../Item\";\n\nexport type ColorCloudProps = {\n type: ItemType.COLOR_CLOUD;\n color: string;\n // TODO: Add the rest of the color cloud values?\n} & ItemProps &\n WithModuleProps &\n LinkedVisualConsoleProps;\n\n/**\n * Build a valid typed object from a raw object.\n * This will allow us to ensure the type safety.\n *\n * @param data Raw object.\n * @return An object representing the static graph props.\n * @throws Will throw a TypeError if some property\n * is missing from the raw object or have an invalid type.\n */\nexport function colorCloudPropsDecoder(\n data: UnknownObject\n): ColorCloudProps | never {\n // TODO: Validate the color.\n if (typeof data.color !== \"string\" || data.color.length === 0) {\n throw new TypeError(\"invalid color.\");\n }\n\n return {\n ...itemBasePropsDecoder(data), // Object spread. It will merge the properties of the two objects.\n type: ItemType.COLOR_CLOUD,\n color: data.color,\n ...modulePropsDecoder(data), // Object spread. It will merge the properties of the two objects.\n ...linkedVCPropsDecoder(data) // Object spread. It will merge the properties of the two objects.\n };\n}\n\nconst svgNS = \"http://www.w3.org/2000/svg\";\n\nexport default class ColorCloud extends Item {\n protected createDomElement(): HTMLElement {\n const container: HTMLDivElement = document.createElement(\"div\");\n container.className = \"color-cloud\";\n\n // Add the SVG.\n container.append(this.createSvgElement());\n\n return container;\n }\n\n public createSvgElement(): SVGSVGElement {\n const gradientId = `grad_${this.props.id}`;\n // SVG container.\n const svg = document.createElementNS(svgNS, \"svg\");\n // Auto resize SVG using the view box magic: https://css-tricks.com/scale-svg/\n svg.setAttribute(\"viewBox\", \"0 0 100 100\");\n\n // Defs.\n const defs = document.createElementNS(svgNS, \"defs\");\n // Radial gradient.\n const radialGradient = document.createElementNS(svgNS, \"radialGradient\");\n radialGradient.setAttribute(\"id\", gradientId);\n radialGradient.setAttribute(\"cx\", \"50%\");\n radialGradient.setAttribute(\"cy\", \"50%\");\n radialGradient.setAttribute(\"r\", \"50%\");\n radialGradient.setAttribute(\"fx\", \"50%\");\n radialGradient.setAttribute(\"fy\", \"50%\");\n // Stops.\n const stop0 = document.createElementNS(svgNS, \"stop\");\n stop0.setAttribute(\"offset\", \"0%\");\n stop0.setAttribute(\n \"style\",\n `stop-color:${this.props.color};stop-opacity:0.9`\n );\n const stop100 = document.createElementNS(svgNS, \"stop\");\n stop100.setAttribute(\"offset\", \"100%\");\n stop100.setAttribute(\n \"style\",\n `stop-color:${this.props.color};stop-opacity:0`\n );\n // Circle.\n const circle = document.createElementNS(svgNS, \"circle\");\n circle.setAttribute(\"fill\", `url(#${gradientId})`);\n circle.setAttribute(\"cx\", \"50%\");\n circle.setAttribute(\"cy\", \"50%\");\n circle.setAttribute(\"r\", \"50%\");\n\n // Append elements.\n radialGradient.append(stop0, stop100);\n defs.append(radialGradient);\n svg.append(defs, circle);\n\n return svg;\n }\n}\n","import { LinkedVisualConsoleProps, UnknownObject } from \"../types\";\nimport {\n linkedVCPropsDecoder,\n parseIntOr,\n notEmptyStringOr,\n stringIsEmpty,\n decodeBase64,\n parseBoolean\n} from \"../lib\";\nimport Item, { ItemProps, itemBasePropsDecoder, ItemType } from \"../Item\";\n\nexport type GroupProps = {\n type: ItemType.GROUP_ITEM;\n groupId: number;\n imageSrc: string | null; // URL?\n statusImageSrc: string | null;\n showStatistics: boolean;\n html?: string | null;\n} & ItemProps &\n LinkedVisualConsoleProps;\n\nfunction extractHtml(data: UnknownObject): string | null {\n if (!stringIsEmpty(data.html)) return data.html;\n if (!stringIsEmpty(data.encodedHtml)) return decodeBase64(data.encodedHtml);\n return null;\n}\n\n/**\n * Build a valid typed object from a raw object.\n * This will allow us to ensure the type safety.\n *\n * @param data Raw object.\n * @return An object representing the group props.\n * @throws Will throw a TypeError if some property\n * is missing from the raw object or have an invalid type.\n */\nexport function groupPropsDecoder(data: UnknownObject): GroupProps | never {\n if (\n (typeof data.imageSrc !== \"string\" || data.imageSrc.length === 0) &&\n data.encodedHtml === null\n ) {\n throw new TypeError(\"invalid image src.\");\n }\n if (parseIntOr(data.groupId, null) === null) {\n throw new TypeError(\"invalid group Id.\");\n }\n\n const showStatistics = parseBoolean(data.showStatistics);\n const html = showStatistics ? extractHtml(data) : null;\n\n return {\n ...itemBasePropsDecoder(data), // Object spread. It will merge the properties of the two objects.\n type: ItemType.GROUP_ITEM,\n groupId: parseInt(data.groupId),\n imageSrc: notEmptyStringOr(data.imageSrc, null),\n statusImageSrc: notEmptyStringOr(data.statusImageSrc, null),\n showStatistics,\n html,\n ...linkedVCPropsDecoder(data) // Object spread. It will merge the properties of the two objects.\n };\n}\n\nexport default class Group extends Item {\n protected createDomElement(): HTMLElement {\n const element = document.createElement(\"div\");\n element.className = \"group\";\n\n if (!this.props.showStatistics && this.props.statusImageSrc !== null) {\n // Icon with status.\n element.style.background = `url(${this.props.statusImageSrc}) no-repeat`;\n element.style.backgroundSize = \"contain\";\n element.style.backgroundPosition = \"center\";\n } else if (this.props.showStatistics && this.props.html != null) {\n // Stats table.\n element.innerHTML = this.props.html;\n }\n\n return element;\n }\n}\n","import \"./styles.css\";\n\nimport { LinkedVisualConsoleProps, UnknownObject, Size } from \"../../types\";\nimport {\n linkedVCPropsDecoder,\n parseIntOr,\n parseBoolean,\n prefixedCssRules,\n notEmptyStringOr,\n humanDate,\n humanTime\n} from \"../../lib\";\nimport Item, { ItemProps, itemBasePropsDecoder, ItemType } from \"../../Item\";\n\nexport type ClockProps = {\n type: ItemType.CLOCK;\n clockType: \"analogic\" | \"digital\";\n clockFormat: \"datetime\" | \"time\";\n clockTimezone: string;\n clockTimezoneOffset: number; // Offset of the timezone to UTC in seconds.\n showClockTimezone: boolean;\n color?: string | null;\n} & ItemProps &\n LinkedVisualConsoleProps;\n\n/**\n * Extract a valid enum value from a raw unknown value.\n * @param clockType Raw value.\n */\nconst parseClockType = (\n clockType: any // eslint-disable-line @typescript-eslint/no-explicit-any\n): ClockProps[\"clockType\"] => {\n switch (clockType) {\n case \"analogic\":\n case \"digital\":\n return clockType;\n default:\n return \"analogic\";\n }\n};\n\n/**\n * Extract a valid enum value from a raw unknown value.\n * @param clockFormat Raw value.\n */\nconst parseClockFormat = (\n clockFormat: any // eslint-disable-line @typescript-eslint/no-explicit-any\n): ClockProps[\"clockFormat\"] => {\n switch (clockFormat) {\n case \"datetime\":\n case \"date\":\n case \"time\":\n return clockFormat;\n default:\n return \"datetime\";\n }\n};\n\n/**\n * Build a valid typed object from a raw object.\n * This will allow us to ensure the type safety.\n *\n * @param data Raw object.\n * @return An object representing the clock props.\n * @throws Will throw a TypeError if some property\n * is missing from the raw object or have an invalid type.\n */\nexport function clockPropsDecoder(data: UnknownObject): ClockProps | never {\n if (\n typeof data.clockTimezone !== \"string\" ||\n data.clockTimezone.length === 0\n ) {\n throw new TypeError(\"invalid timezone.\");\n }\n\n return {\n ...itemBasePropsDecoder(data), // Object spread. It will merge the properties of the two objects.\n type: ItemType.CLOCK,\n clockType: parseClockType(data.clockType),\n clockFormat: parseClockFormat(data.clockFormat),\n clockTimezone: data.clockTimezone,\n clockTimezoneOffset: parseIntOr(data.clockTimezoneOffset, 0),\n showClockTimezone: parseBoolean(data.showClockTimezone),\n color: notEmptyStringOr(data.color, null),\n ...linkedVCPropsDecoder(data) // Object spread. It will merge the properties of the two objects.\n };\n}\n\nexport default class Clock extends Item {\n public static readonly TICK_INTERVAL = 1000; // In ms.\n private intervalRef: number | null = null;\n\n public constructor(props: ClockProps) {\n // Call the superclass constructor.\n super(props);\n\n /* The item is already loaded and inserted into the DOM.\n * The class properties are now initialized.\n * Now you can modify the item, add event handlers, timers, etc.\n */\n\n /* The use of the arrow function is important here. startTick will\n * use the function passed as an argument to call the global setInterval\n * function. The interval, timeout or event functions, among other, are\n * called into another execution loop and using a different context.\n * The arrow functions, unlike the classic functions, doesn't create\n * their own context (this), so their context at execution time will be\n * use the current context at the declaration time.\n * http://es6-features.org/#Lexicalthis\n */\n this.startTick(\n () => {\n // Replace the old element with the updated date.\n this.childElementRef.innerHTML = this.createClock().innerHTML;\n },\n /* The analogic clock doesn't need to tick,\n * but it will be refreshed every 20 seconds\n * to avoid a desync caused by page freezes.\n */\n this.props.clockType === \"analogic\" ? 20000 : Clock.TICK_INTERVAL\n );\n }\n\n /**\n * Wrap a window.clearInterval call.\n */\n private stopTick(): void {\n if (this.intervalRef !== null) {\n window.clearInterval(this.intervalRef);\n this.intervalRef = null;\n }\n }\n\n /**\n * Wrap a window.setInterval call.\n * @param handler Function to be called every time the interval\n * timer is reached.\n * @param interval Number in milliseconds for the interval timer.\n */\n private startTick(\n handler: TimerHandler,\n interval: number = Clock.TICK_INTERVAL\n ): void {\n this.stopTick();\n this.intervalRef = window.setInterval(handler, interval);\n }\n\n /**\n * Create a element which contains the DOM representation of the item.\n * @return DOM Element.\n * @override\n */\n protected createDomElement(): HTMLElement | never {\n return this.createClock();\n }\n\n /**\n * To remove the event listeners and the elements from the DOM.\n * @override\n */\n public remove(): void {\n // Clear the interval.\n this.stopTick();\n // Call to the parent clean function.\n super.remove();\n }\n\n /**\n * @override Item.resizeElement\n * Resize the DOM content container.\n * @param width\n * @param height\n */\n protected resizeElement(width: number, height: number): void {\n const { width: newWidth, height: newHeight } = this.getElementSize(\n width,\n height\n ); // Destructuring assigment: http://es6-features.org/#ObjectMatchingShorthandNotation\n super.resizeElement(newWidth, newHeight);\n // Re-render the item to force it calculate a new font size.\n if (this.props.clockType === \"digital\") {\n // Replace the old element with the updated date.\n this.childElementRef.innerHTML = this.createClock().innerHTML;\n }\n }\n\n /**\n * Create a element which contains a representation of a clock.\n * It choose between the clock types.\n * @return DOM Element.\n * @throws Error.\n */\n private createClock(): HTMLElement | never {\n switch (this.props.clockType) {\n case \"analogic\":\n return this.createAnalogicClock();\n case \"digital\":\n return this.createDigitalClock();\n default:\n throw new Error(\"invalid clock type.\");\n }\n }\n\n /**\n * Create a element which contains a representation of an analogic clock.\n * @return DOM Element.\n */\n private createAnalogicClock(): HTMLElement {\n const svgNS = \"http://www.w3.org/2000/svg\";\n const colors = {\n watchFace: \"#FFFFF0\",\n watchFaceBorder: \"#242124\",\n mark: \"#242124\",\n handDark: \"#242124\",\n handLight: \"#525252\",\n secondHand: \"#DC143C\"\n };\n\n const { width, height } = this.getElementSize(); // Destructuring assigment: http://es6-features.org/#ObjectMatchingShorthandNotation\n\n const div = document.createElement(\"div\");\n div.className = \"analogic-clock\";\n div.style.width = `${width}px`;\n div.style.height = `${height}px`;\n\n // SVG container.\n const svg = document.createElementNS(svgNS, \"svg\");\n // Auto resize SVG using the view box magic: https://css-tricks.com/scale-svg/\n svg.setAttribute(\"viewBox\", \"0 0 100 100\");\n\n // Clock face.\n const clockFace = document.createElementNS(svgNS, \"g\");\n clockFace.setAttribute(\"class\", \"clockface\");\n const clockFaceBackground = document.createElementNS(svgNS, \"circle\");\n clockFaceBackground.setAttribute(\"cx\", \"50\");\n clockFaceBackground.setAttribute(\"cy\", \"50\");\n clockFaceBackground.setAttribute(\"r\", \"48\");\n clockFaceBackground.setAttribute(\"fill\", colors.watchFace);\n clockFaceBackground.setAttribute(\"stroke\", colors.watchFaceBorder);\n clockFaceBackground.setAttribute(\"stroke-width\", \"2\");\n clockFaceBackground.setAttribute(\"stroke-linecap\", \"round\");\n // Insert the clockface background into the clockface group.\n clockFace.append(clockFaceBackground);\n\n // Timezone complication.\n const city = this.getHumanTimezone();\n if (city.length > 0) {\n const timezoneComplication = document.createElementNS(svgNS, \"text\");\n timezoneComplication.setAttribute(\"text-anchor\", \"middle\");\n timezoneComplication.setAttribute(\"font-size\", \"8\");\n timezoneComplication.setAttribute(\n \"transform\",\n \"translate(30 50) rotate(90)\" // Rotate to counter the clock rotation.\n );\n timezoneComplication.setAttribute(\"fill\", colors.mark);\n timezoneComplication.textContent = city;\n clockFace.append(timezoneComplication);\n }\n\n // Marks group.\n const marksGroup = document.createElementNS(svgNS, \"g\");\n marksGroup.setAttribute(\"class\", \"marks\");\n // Build the 12 hours mark.\n const mainMarkGroup = document.createElementNS(svgNS, \"g\");\n mainMarkGroup.setAttribute(\"class\", \"mark\");\n mainMarkGroup.setAttribute(\"transform\", \"translate(50 50)\");\n const mark1a = document.createElementNS(svgNS, \"line\");\n mark1a.setAttribute(\"x1\", \"36\");\n mark1a.setAttribute(\"y1\", \"0\");\n mark1a.setAttribute(\"x2\", \"46\");\n mark1a.setAttribute(\"y2\", \"0\");\n mark1a.setAttribute(\"stroke\", colors.mark);\n mark1a.setAttribute(\"stroke-width\", \"5\");\n const mark1b = document.createElementNS(svgNS, \"line\");\n mark1b.setAttribute(\"x1\", \"36\");\n mark1b.setAttribute(\"y1\", \"0\");\n mark1b.setAttribute(\"x2\", \"46\");\n mark1b.setAttribute(\"y2\", \"0\");\n mark1b.setAttribute(\"stroke\", colors.watchFace);\n mark1b.setAttribute(\"stroke-width\", \"1\");\n // Insert the 12 mark lines into their group.\n mainMarkGroup.append(mark1a, mark1b);\n // Insert the main mark into the marks group.\n marksGroup.append(mainMarkGroup);\n // Build the rest of the marks.\n for (let i = 1; i < 60; i++) {\n const mark = document.createElementNS(svgNS, \"line\");\n mark.setAttribute(\"y1\", \"0\");\n mark.setAttribute(\"y2\", \"0\");\n mark.setAttribute(\"stroke\", colors.mark);\n mark.setAttribute(\"transform\", `translate(50 50) rotate(${i * 6})`);\n\n if (i % 5 === 0) {\n mark.setAttribute(\"x1\", \"38\");\n mark.setAttribute(\"x2\", \"46\");\n mark.setAttribute(\"stroke-width\", i % 15 === 0 ? \"2\" : \"1\");\n } else {\n mark.setAttribute(\"x1\", \"42\");\n mark.setAttribute(\"x2\", \"46\");\n mark.setAttribute(\"stroke-width\", \"0.5\");\n }\n\n // Insert the mark into the marks group.\n marksGroup.append(mark);\n }\n\n /* Clock hands */\n\n // Hour hand.\n const hourHand = document.createElementNS(svgNS, \"g\");\n hourHand.setAttribute(\"class\", \"hour-hand\");\n hourHand.setAttribute(\"transform\", \"translate(50 50)\");\n // This will go back and will act like a border.\n const hourHandA = document.createElementNS(svgNS, \"line\");\n hourHandA.setAttribute(\"class\", \"hour-hand-a\");\n hourHandA.setAttribute(\"x1\", \"0\");\n hourHandA.setAttribute(\"y1\", \"0\");\n hourHandA.setAttribute(\"x2\", \"30\");\n hourHandA.setAttribute(\"y2\", \"0\");\n hourHandA.setAttribute(\"stroke\", colors.handLight);\n hourHandA.setAttribute(\"stroke-width\", \"4\");\n hourHandA.setAttribute(\"stroke-linecap\", \"round\");\n // This will go in front of the previous line.\n const hourHandB = document.createElementNS(svgNS, \"line\");\n hourHandB.setAttribute(\"class\", \"hour-hand-b\");\n hourHandB.setAttribute(\"x1\", \"0\");\n hourHandB.setAttribute(\"y1\", \"0\");\n hourHandB.setAttribute(\"x2\", \"29.9\");\n hourHandB.setAttribute(\"y2\", \"0\");\n hourHandB.setAttribute(\"stroke\", colors.handDark);\n hourHandB.setAttribute(\"stroke-width\", \"3.1\");\n hourHandB.setAttribute(\"stroke-linecap\", \"round\");\n // Append the elements to finish the hour hand.\n hourHand.append(hourHandA, hourHandB);\n\n // Minute hand.\n const minuteHand = document.createElementNS(svgNS, \"g\");\n minuteHand.setAttribute(\"class\", \"minute-hand\");\n minuteHand.setAttribute(\"transform\", \"translate(50 50)\");\n // This will go back and will act like a border.\n const minuteHandA = document.createElementNS(svgNS, \"line\");\n minuteHandA.setAttribute(\"class\", \"minute-hand-a\");\n minuteHandA.setAttribute(\"x1\", \"0\");\n minuteHandA.setAttribute(\"y1\", \"0\");\n minuteHandA.setAttribute(\"x2\", \"40\");\n minuteHandA.setAttribute(\"y2\", \"0\");\n minuteHandA.setAttribute(\"stroke\", colors.handLight);\n minuteHandA.setAttribute(\"stroke-width\", \"2\");\n minuteHandA.setAttribute(\"stroke-linecap\", \"round\");\n // This will go in front of the previous line.\n const minuteHandB = document.createElementNS(svgNS, \"line\");\n minuteHandB.setAttribute(\"class\", \"minute-hand-b\");\n minuteHandB.setAttribute(\"x1\", \"0\");\n minuteHandB.setAttribute(\"y1\", \"0\");\n minuteHandB.setAttribute(\"x2\", \"39.9\");\n minuteHandB.setAttribute(\"y2\", \"0\");\n minuteHandB.setAttribute(\"stroke\", colors.handDark);\n minuteHandB.setAttribute(\"stroke-width\", \"1.5\");\n minuteHandB.setAttribute(\"stroke-linecap\", \"round\");\n const minuteHandPin = document.createElementNS(svgNS, \"circle\");\n minuteHandPin.setAttribute(\"r\", \"3\");\n minuteHandPin.setAttribute(\"fill\", colors.handDark);\n // Append the elements to finish the minute hand.\n minuteHand.append(minuteHandA, minuteHandB, minuteHandPin);\n\n // Second hand.\n const secondHand = document.createElementNS(svgNS, \"g\");\n secondHand.setAttribute(\"class\", \"second-hand\");\n secondHand.setAttribute(\"transform\", \"translate(50 50)\");\n const secondHandBar = document.createElementNS(svgNS, \"line\");\n secondHandBar.setAttribute(\"x1\", \"0\");\n secondHandBar.setAttribute(\"y1\", \"0\");\n secondHandBar.setAttribute(\"x2\", \"46\");\n secondHandBar.setAttribute(\"y2\", \"0\");\n secondHandBar.setAttribute(\"stroke\", colors.secondHand);\n secondHandBar.setAttribute(\"stroke-width\", \"1\");\n secondHandBar.setAttribute(\"stroke-linecap\", \"round\");\n const secondHandPin = document.createElementNS(svgNS, \"circle\");\n secondHandPin.setAttribute(\"r\", \"2\");\n secondHandPin.setAttribute(\"fill\", colors.secondHand);\n // Append the elements to finish the second hand.\n secondHand.append(secondHandBar, secondHandPin);\n\n // Pin.\n const pin = document.createElementNS(svgNS, \"circle\");\n pin.setAttribute(\"cx\", \"50\");\n pin.setAttribute(\"cy\", \"50\");\n pin.setAttribute(\"r\", \"0.3\");\n pin.setAttribute(\"fill\", colors.handDark);\n\n // Get the hand angles.\n const date = this.getOriginDate();\n const seconds = date.getSeconds();\n const minutes = date.getMinutes();\n const hours = date.getHours();\n const secAngle = (360 / 60) * seconds;\n const minuteAngle = (360 / 60) * minutes + (360 / 60) * (seconds / 60);\n const hourAngle = (360 / 12) * hours + (360 / 12) * (minutes / 60);\n // Set the clock time by moving the hands.\n hourHand.setAttribute(\"transform\", `translate(50 50) rotate(${hourAngle})`);\n minuteHand.setAttribute(\n \"transform\",\n `translate(50 50) rotate(${minuteAngle})`\n );\n secondHand.setAttribute(\n \"transform\",\n `translate(50 50) rotate(${secAngle})`\n );\n\n // Build the clock\n svg.append(clockFace, marksGroup, hourHand, minuteHand, secondHand, pin);\n // Rotate the clock to its normal position.\n svg.setAttribute(\"transform\", \"rotate(-90)\");\n\n /* Add the animation declaration to the container.\n * Since the animation keyframes need to know the\n * start angle, this angle is dynamic (current time),\n * and we can't edit keyframes through javascript\n * safely and with backwards compatibility, we need\n * to inject it.\n */\n div.innerHTML = `\n \n `;\n // Add the clock to the container\n div.append(svg);\n\n return div;\n }\n\n /**\n * Create a element which contains a representation of a digital clock.\n * @return DOM Element.\n */\n private createDigitalClock(): HTMLElement {\n const element: HTMLDivElement = document.createElement(\"div\");\n element.className = \"digital-clock\";\n\n const { width } = this.getElementSize(); // Destructuring assigment: http://es6-features.org/#ObjectMatchingShorthandNotation\n\n // Calculate font size to adapt the font to the item size.\n const baseTimeFontSize = 20; // Per 100px of width.\n const dateFontSizeMultiplier = 0.5;\n const tzFontSizeMultiplier = 6 / this.props.clockTimezone.length;\n const timeFontSize = (baseTimeFontSize * width) / 100;\n const dateFontSize =\n (baseTimeFontSize * dateFontSizeMultiplier * width) / 100;\n const tzFontSize = Math.min(\n (baseTimeFontSize * tzFontSizeMultiplier * width) / 100,\n (width / 100) * 10\n );\n\n // Date calculated using the original timezone.\n const date = this.getOriginDate();\n\n // Date.\n if (this.props.clockFormat === \"datetime\") {\n const dateElem: HTMLSpanElement = document.createElement(\"span\");\n dateElem.className = \"date\";\n dateElem.textContent = humanDate(date, \"default\");\n dateElem.style.fontSize = `${dateFontSize}px`;\n if (this.props.color) dateElem.style.color = this.props.color;\n element.append(dateElem);\n }\n\n // Time.\n const timeElem: HTMLSpanElement = document.createElement(\"span\");\n timeElem.className = \"time\";\n timeElem.textContent = humanTime(date);\n timeElem.style.fontSize = `${timeFontSize}px`;\n if (this.props.color) timeElem.style.color = this.props.color;\n element.append(timeElem);\n\n // City name.\n const city = this.getHumanTimezone();\n if (city.length > 0) {\n const tzElem: HTMLSpanElement = document.createElement(\"span\");\n tzElem.className = \"timezone\";\n tzElem.textContent = city;\n tzElem.style.fontSize = `${tzFontSize}px`;\n if (this.props.color) tzElem.style.color = this.props.color;\n element.append(tzElem);\n }\n\n return element;\n }\n\n /**\n * Generate the current date using the timezone offset stored into the properties.\n * @return The current date.\n */\n private getOriginDate(initialDate: Date | null = null): Date {\n const d = initialDate ? initialDate : new Date();\n const targetTZOffset = this.props.clockTimezoneOffset * 1000; // In ms.\n const localTZOffset = d.getTimezoneOffset() * 60 * 1000; // In ms.\n const utimestamp = d.getTime() + targetTZOffset + localTZOffset;\n\n return new Date(utimestamp);\n }\n\n /**\n * Extract a human readable city name from the timezone text.\n * @param timezone Timezone text.\n */\n public getHumanTimezone(timezone: string = this.props.clockTimezone): string {\n const [, city = \"\"] = timezone.split(\"/\");\n return city.replace(\"_\", \" \");\n }\n\n /**\n * Generate a element size using the current size and the default values.\n * @return The size.\n */\n private getElementSize(\n width: number = this.props.width,\n height: number = this.props.height\n ): Size {\n switch (this.props.clockType) {\n case \"analogic\": {\n let diameter = 100; // Default value.\n\n if (width > 0 && height > 0) {\n diameter = Math.min(width, height);\n } else if (width > 0) {\n diameter = width;\n } else if (height > 0) {\n diameter = height;\n }\n\n return {\n width: diameter,\n height: diameter\n };\n }\n case \"digital\": {\n if (width > 0 && height > 0) {\n // The proportion of the clock should be (width = height / 2) aproximately.\n height = width / 2 < height ? width / 2 : height;\n } else if (width > 0) {\n height = width / 2;\n } else if (height > 0) {\n // The proportion of the clock should be (height * 2 = width) aproximately.\n width = height * 2;\n } else {\n width = 100; // Default value.\n height = 50; // Default value.\n }\n\n return {\n width,\n height\n };\n }\n default:\n throw new Error(\"invalid clock type.\");\n }\n }\n}\n","import { UnknownObject } from \"../types\";\nimport { parseIntOr, notEmptyStringOr } from \"../lib\";\nimport Item, { ItemType, ItemProps, itemBasePropsDecoder } from \"../Item\";\n\ninterface BoxProps extends ItemProps {\n // Overrided properties.\n readonly type: ItemType.BOX_ITEM;\n label: null;\n isLinkEnabled: false;\n parentId: null;\n aclGroupId: null;\n // Custom properties.\n borderWidth: number;\n borderColor: string | null;\n fillColor: string | null;\n}\n\n/**\n * Build a valid typed object from a raw object.\n * This will allow us to ensure the type safety.\n *\n * @param data Raw object.\n * @return An object representing the item props.\n * @throws Will throw a TypeError if some property\n * is missing from the raw object or have an invalid type.\n */\nexport function boxPropsDecoder(data: UnknownObject): BoxProps | never {\n return {\n ...itemBasePropsDecoder(data), // Object spread. It will merge the properties of the two objects.\n type: ItemType.BOX_ITEM,\n label: null,\n isLinkEnabled: false,\n parentId: null,\n aclGroupId: null,\n // Custom properties.\n borderWidth: parseIntOr(data.borderWidth, 0),\n borderColor: notEmptyStringOr(data.borderColor, null),\n fillColor: notEmptyStringOr(data.fillColor, null)\n };\n}\n\nexport default class Box extends Item {\n protected createDomElement(): HTMLElement {\n const box: HTMLDivElement = document.createElement(\"div\");\n box.className = \"box\";\n // To prevent this item to expand beyond its parent.\n box.style.boxSizing = \"border-box\";\n\n if (this.props.fillColor) {\n box.style.backgroundColor = this.props.fillColor;\n }\n\n // Border.\n if (this.props.borderWidth > 0) {\n box.style.borderStyle = \"solid\";\n // Control the max width to prevent this item to expand beyond its parent.\n const maxBorderWidth = Math.min(this.props.width, this.props.height) / 2;\n const borderWidth = Math.min(this.props.borderWidth, maxBorderWidth);\n box.style.borderWidth = `${borderWidth}px`;\n\n if (this.props.borderColor) {\n box.style.borderColor = this.props.borderColor;\n }\n }\n\n return box;\n }\n}\n","import { UnknownObject, Position, Size } from \"../types\";\nimport { parseIntOr, notEmptyStringOr } from \"../lib\";\nimport Item, { ItemType, ItemProps, itemBasePropsDecoder } from \"../Item\";\n\ninterface LineProps extends ItemProps {\n // Overrided properties.\n readonly type: ItemType.LINE_ITEM;\n label: null;\n isLinkEnabled: false;\n parentId: null;\n aclGroupId: null;\n // Custom properties.\n startPosition: Position;\n endPosition: Position;\n lineWidth: number;\n color: string | null;\n}\n\n/**\n * Build a valid typed object from a raw object.\n * This will allow us to ensure the type safety.\n *\n * @param data Raw object.\n * @return An object representing the item props.\n * @throws Will throw a TypeError if some property\n * is missing from the raw object or have an invalid type.\n */\nexport function linePropsDecoder(data: UnknownObject): LineProps | never {\n const props: LineProps = {\n ...itemBasePropsDecoder({ ...data, width: 1, height: 1 }), // Object spread. It will merge the properties of the two objects.\n type: ItemType.LINE_ITEM,\n label: null,\n isLinkEnabled: false,\n parentId: null,\n aclGroupId: null,\n // Initialize Position & Size.\n x: 0,\n y: 0,\n width: 0,\n height: 0,\n // Custom properties.\n startPosition: {\n x: parseIntOr(data.startX, 0),\n y: parseIntOr(data.startY, 0)\n },\n endPosition: {\n x: parseIntOr(data.endX, 0),\n y: parseIntOr(data.endY, 0)\n },\n lineWidth: parseIntOr(data.lineWidth || data.borderWidth, 1),\n color: notEmptyStringOr(data.borderColor || data.color, null)\n };\n\n /*\n * We need to enhance the props with the extracted size and position\n * of the box cause there are missing at the props update. A better\n * solution would be overriding the props setter to do it there, but\n * the language doesn't allow it while targetting ES5.\n * TODO: We need to figure out a more consistent solution.\n */\n\n return {\n ...props,\n // Enhance the props extracting the box size and position.\n // eslint-disable-next-line @typescript-eslint/no-use-before-define\n ...Line.extractBoxSizeAndPosition(props)\n };\n}\n\nexport default class Line extends Item {\n /**\n * @override\n */\n public constructor(props: LineProps) {\n /*\n * We need to override the constructor cause we need to obtain\n * the\n * box size and position from the start and finish points\n * of the line.\n */\n super({\n ...props,\n ...Line.extractBoxSizeAndPosition(props)\n });\n }\n\n /**\n * @override\n * To create the item's DOM representation.\n * @return Item.\n */\n protected createDomElement(): HTMLElement {\n const element: HTMLDivElement = document.createElement(\"div\");\n element.className = \"line\";\n\n const svgNS = \"http://www.w3.org/2000/svg\";\n // SVG container.\n const svg = document.createElementNS(svgNS, \"svg\");\n // Set SVG size.\n svg.setAttribute(\n \"width\",\n (this.props.width + this.props.lineWidth).toString()\n );\n svg.setAttribute(\n \"height\",\n (this.props.height + this.props.lineWidth).toString()\n );\n const line = document.createElementNS(svgNS, \"line\");\n line.setAttribute(\n \"x1\",\n `${this.props.startPosition.x - this.props.x + this.props.lineWidth / 2}`\n );\n line.setAttribute(\n \"y1\",\n `${this.props.startPosition.y - this.props.y + this.props.lineWidth / 2}`\n );\n line.setAttribute(\n \"x2\",\n `${this.props.endPosition.x - this.props.x + this.props.lineWidth / 2}`\n );\n line.setAttribute(\n \"y2\",\n `${this.props.endPosition.y - this.props.y + this.props.lineWidth / 2}`\n );\n line.setAttribute(\"stroke\", this.props.color || \"black\");\n line.setAttribute(\"stroke-width\", this.props.lineWidth.toString());\n\n svg.append(line);\n element.append(svg);\n\n return element;\n }\n\n /**\n * Extract the size and position of the box from\n * the start and the finish of the line.\n * @param props Item properties.\n */\n public static extractBoxSizeAndPosition(props: LineProps): Size & Position {\n return {\n width: Math.abs(props.startPosition.x - props.endPosition.x),\n height: Math.abs(props.startPosition.y - props.endPosition.y),\n x: Math.min(props.startPosition.x, props.endPosition.x),\n y: Math.min(props.startPosition.y, props.endPosition.y)\n };\n }\n}\n","import { LinkedVisualConsoleProps, UnknownObject } from \"../types\";\nimport { linkedVCPropsDecoder } from \"../lib\";\nimport Item, { ItemType, ItemProps, itemBasePropsDecoder } from \"../Item\";\n\nexport type LabelProps = {\n type: ItemType.LABEL;\n} & ItemProps &\n LinkedVisualConsoleProps;\n\n/**\n * Build a valid typed object from a raw object.\n * This will allow us to ensure the type safety.\n *\n * @param data Raw object.\n * @return An object representing the label props.\n * @throws Will throw a TypeError if some property\n * is missing from the raw object or have an invalid type.\n */\nexport function labelPropsDecoder(data: UnknownObject): LabelProps | never {\n return {\n ...itemBasePropsDecoder(data), // Object spread. It will merge the properties of the two objects.\n type: ItemType.LABEL,\n ...linkedVCPropsDecoder(data) // Object spread. It will merge the properties of the two objects.\n };\n}\n\nexport default class Label extends Item {\n protected createDomElement(): HTMLElement {\n const element = document.createElement(\"div\");\n element.className = \"label\";\n element.innerHTML = this.getLabelWithMacrosReplaced();\n\n return element;\n }\n\n /**\n * @override Item.createLabelDomElement\n * Create a new label for the visual console item.\n * @return Item label.\n */\n public createLabelDomElement(): HTMLElement {\n const element = document.createElement(\"div\");\n element.className = \"visual-console-item-label\";\n // Always return an empty label.\n return element;\n }\n}\n","import {\n LinkedVisualConsoleProps,\n UnknownObject,\n WithModuleProps\n} from \"../types\";\nimport {\n linkedVCPropsDecoder,\n parseIntOr,\n modulePropsDecoder,\n replaceMacros\n} from \"../lib\";\nimport Item, { ItemType, ItemProps, itemBasePropsDecoder } from \"../Item\";\n\nexport type SimpleValueProps = {\n type: ItemType.SIMPLE_VALUE;\n valueType: \"string\" | \"image\";\n value: string;\n} & (\n | {\n processValue: \"none\";\n }\n | {\n processValue: \"avg\" | \"max\" | \"min\";\n period: number;\n }) &\n ItemProps &\n WithModuleProps &\n LinkedVisualConsoleProps;\n\n/**\n * Extract a valid enum value from a raw value type.\n * @param valueType Raw value.\n */\nconst parseValueType = (\n valueType: any // eslint-disable-line @typescript-eslint/no-explicit-any\n): SimpleValueProps[\"valueType\"] => {\n switch (valueType) {\n case \"string\":\n case \"image\":\n return valueType;\n default:\n return \"string\";\n }\n};\n\n/**\n * Extract a valid enum value from a raw process value.\n * @param processValue Raw value.\n */\nconst parseProcessValue = (\n processValue: any // eslint-disable-line @typescript-eslint/no-explicit-any\n): SimpleValueProps[\"processValue\"] => {\n switch (processValue) {\n case \"none\":\n case \"avg\":\n case \"max\":\n case \"min\":\n return processValue;\n default:\n return \"none\";\n }\n};\n\n/**\n * Build a valid typed object from a raw object.\n * This will allow us to ensure the type safety.\n *\n * @param data Raw object.\n * @return An object representing the simple value props.\n * @throws Will throw a TypeError if some property\n * is missing from the raw object or have an invalid type.\n */\nexport function simpleValuePropsDecoder(\n data: UnknownObject\n): SimpleValueProps | never {\n if (typeof data.value !== \"string\" || data.value.length === 0) {\n throw new TypeError(\"invalid value\");\n }\n\n const processValue = parseProcessValue(data.processValue);\n\n return {\n ...itemBasePropsDecoder(data), // Object spread. It will merge the properties of the two objects.\n type: ItemType.SIMPLE_VALUE,\n valueType: parseValueType(data.valueType),\n value: data.value,\n ...(processValue === \"none\"\n ? { processValue }\n : { processValue, period: parseIntOr(data.period, 0) }), // Object spread. It will merge the properties of the two objects.\n ...modulePropsDecoder(data), // Object spread. It will merge the properties of the two objects.\n ...linkedVCPropsDecoder(data) // Object spread. It will merge the properties of the two objects.\n };\n}\n\nexport default class SimpleValue extends Item {\n protected createDomElement(): HTMLElement {\n const element = document.createElement(\"div\");\n element.className = \"simple-value\";\n\n if (this.props.valueType === \"image\") {\n const img = document.createElement(\"img\");\n img.src = this.props.value;\n element.append(img);\n } else {\n // Add the value to the label and show it.\n let text = this.props.value;\n let label = this.getLabelWithMacrosReplaced();\n if (label.length > 0) {\n text = replaceMacros([{ macro: /\\(?_VALUE_\\)?/i, value: text }], label);\n }\n\n element.innerHTML = text;\n }\n\n return element;\n }\n\n /**\n * @override Item.createLabelDomElement\n * Create a new label for the visual console item.\n * @return Item label.\n */\n protected createLabelDomElement(): HTMLElement {\n const element = document.createElement(\"div\");\n element.className = \"visual-console-item-label\";\n // Always return an empty label.\n return element;\n }\n}\n","var pi = Math.PI,\n tau = 2 * pi,\n epsilon = 1e-6,\n tauEpsilon = tau - epsilon;\n\nfunction Path() {\n this._x0 = this._y0 = // start of current subpath\n this._x1 = this._y1 = null; // end of current subpath\n this._ = \"\";\n}\n\nfunction path() {\n return new Path;\n}\n\nPath.prototype = path.prototype = {\n constructor: Path,\n moveTo: function(x, y) {\n this._ += \"M\" + (this._x0 = this._x1 = +x) + \",\" + (this._y0 = this._y1 = +y);\n },\n closePath: function() {\n if (this._x1 !== null) {\n this._x1 = this._x0, this._y1 = this._y0;\n this._ += \"Z\";\n }\n },\n lineTo: function(x, y) {\n this._ += \"L\" + (this._x1 = +x) + \",\" + (this._y1 = +y);\n },\n quadraticCurveTo: function(x1, y1, x, y) {\n this._ += \"Q\" + (+x1) + \",\" + (+y1) + \",\" + (this._x1 = +x) + \",\" + (this._y1 = +y);\n },\n bezierCurveTo: function(x1, y1, x2, y2, x, y) {\n this._ += \"C\" + (+x1) + \",\" + (+y1) + \",\" + (+x2) + \",\" + (+y2) + \",\" + (this._x1 = +x) + \",\" + (this._y1 = +y);\n },\n arcTo: function(x1, y1, x2, y2, r) {\n x1 = +x1, y1 = +y1, x2 = +x2, y2 = +y2, r = +r;\n var x0 = this._x1,\n y0 = this._y1,\n x21 = x2 - x1,\n y21 = y2 - y1,\n x01 = x0 - x1,\n y01 = y0 - y1,\n l01_2 = x01 * x01 + y01 * y01;\n\n // Is the radius negative? Error.\n if (r < 0) throw new Error(\"negative radius: \" + r);\n\n // Is this path empty? Move to (x1,y1).\n if (this._x1 === null) {\n this._ += \"M\" + (this._x1 = x1) + \",\" + (this._y1 = y1);\n }\n\n // Or, is (x1,y1) coincident with (x0,y0)? Do nothing.\n else if (!(l01_2 > epsilon));\n\n // Or, are (x0,y0), (x1,y1) and (x2,y2) collinear?\n // Equivalently, is (x1,y1) coincident with (x2,y2)?\n // Or, is the radius zero? Line to (x1,y1).\n else if (!(Math.abs(y01 * x21 - y21 * x01) > epsilon) || !r) {\n this._ += \"L\" + (this._x1 = x1) + \",\" + (this._y1 = y1);\n }\n\n // Otherwise, draw an arc!\n else {\n var x20 = x2 - x0,\n y20 = y2 - y0,\n l21_2 = x21 * x21 + y21 * y21,\n l20_2 = x20 * x20 + y20 * y20,\n l21 = Math.sqrt(l21_2),\n l01 = Math.sqrt(l01_2),\n l = r * Math.tan((pi - Math.acos((l21_2 + l01_2 - l20_2) / (2 * l21 * l01))) / 2),\n t01 = l / l01,\n t21 = l / l21;\n\n // If the start tangent is not coincident with (x0,y0), line to.\n if (Math.abs(t01 - 1) > epsilon) {\n this._ += \"L\" + (x1 + t01 * x01) + \",\" + (y1 + t01 * y01);\n }\n\n this._ += \"A\" + r + \",\" + r + \",0,0,\" + (+(y01 * x20 > x01 * y20)) + \",\" + (this._x1 = x1 + t21 * x21) + \",\" + (this._y1 = y1 + t21 * y21);\n }\n },\n arc: function(x, y, r, a0, a1, ccw) {\n x = +x, y = +y, r = +r;\n var dx = r * Math.cos(a0),\n dy = r * Math.sin(a0),\n x0 = x + dx,\n y0 = y + dy,\n cw = 1 ^ ccw,\n da = ccw ? a0 - a1 : a1 - a0;\n\n // Is the radius negative? Error.\n if (r < 0) throw new Error(\"negative radius: \" + r);\n\n // Is this path empty? Move to (x0,y0).\n if (this._x1 === null) {\n this._ += \"M\" + x0 + \",\" + y0;\n }\n\n // Or, is (x0,y0) not coincident with the previous point? Line to (x0,y0).\n else if (Math.abs(this._x1 - x0) > epsilon || Math.abs(this._y1 - y0) > epsilon) {\n this._ += \"L\" + x0 + \",\" + y0;\n }\n\n // Is this arc empty? We’re done.\n if (!r) return;\n\n // Does the angle go the wrong way? Flip the direction.\n if (da < 0) da = da % tau + tau;\n\n // Is this a complete circle? Draw two arcs to complete the circle.\n if (da > tauEpsilon) {\n this._ += \"A\" + r + \",\" + r + \",0,1,\" + cw + \",\" + (x - dx) + \",\" + (y - dy) + \"A\" + r + \",\" + r + \",0,1,\" + cw + \",\" + (this._x1 = x0) + \",\" + (this._y1 = y0);\n }\n\n // Is this arc non-empty? Draw an arc!\n else if (da > epsilon) {\n this._ += \"A\" + r + \",\" + r + \",0,\" + (+(da >= pi)) + \",\" + cw + \",\" + (this._x1 = x + r * Math.cos(a1)) + \",\" + (this._y1 = y + r * Math.sin(a1));\n }\n },\n rect: function(x, y, w, h) {\n this._ += \"M\" + (this._x0 = this._x1 = +x) + \",\" + (this._y0 = this._y1 = +y) + \"h\" + (+w) + \"v\" + (+h) + \"h\" + (-w) + \"Z\";\n },\n toString: function() {\n return this._;\n }\n};\n\nexport default path;\n","export default function(x) {\n return function constant() {\n return x;\n };\n}\n","export var abs = Math.abs;\nexport var atan2 = Math.atan2;\nexport var cos = Math.cos;\nexport var max = Math.max;\nexport var min = Math.min;\nexport var sin = Math.sin;\nexport var sqrt = Math.sqrt;\n\nexport var epsilon = 1e-12;\nexport var pi = Math.PI;\nexport var halfPi = pi / 2;\nexport var tau = 2 * pi;\n\nexport function acos(x) {\n return x > 1 ? 0 : x < -1 ? pi : Math.acos(x);\n}\n\nexport function asin(x) {\n return x >= 1 ? halfPi : x <= -1 ? -halfPi : Math.asin(x);\n}\n","import {path} from \"d3-path\";\nimport constant from \"./constant\";\nimport {abs, acos, asin, atan2, cos, epsilon, halfPi, max, min, pi, sin, sqrt, tau} from \"./math\";\n\nfunction arcInnerRadius(d) {\n return d.innerRadius;\n}\n\nfunction arcOuterRadius(d) {\n return d.outerRadius;\n}\n\nfunction arcStartAngle(d) {\n return d.startAngle;\n}\n\nfunction arcEndAngle(d) {\n return d.endAngle;\n}\n\nfunction arcPadAngle(d) {\n return d && d.padAngle; // Note: optional!\n}\n\nfunction intersect(x0, y0, x1, y1, x2, y2, x3, y3) {\n var x10 = x1 - x0, y10 = y1 - y0,\n x32 = x3 - x2, y32 = y3 - y2,\n t = y32 * x10 - x32 * y10;\n if (t * t < epsilon) return;\n t = (x32 * (y0 - y2) - y32 * (x0 - x2)) / t;\n return [x0 + t * x10, y0 + t * y10];\n}\n\n// Compute perpendicular offset line of length rc.\n// http://mathworld.wolfram.com/Circle-LineIntersection.html\nfunction cornerTangents(x0, y0, x1, y1, r1, rc, cw) {\n var x01 = x0 - x1,\n y01 = y0 - y1,\n lo = (cw ? rc : -rc) / sqrt(x01 * x01 + y01 * y01),\n ox = lo * y01,\n oy = -lo * x01,\n x11 = x0 + ox,\n y11 = y0 + oy,\n x10 = x1 + ox,\n y10 = y1 + oy,\n x00 = (x11 + x10) / 2,\n y00 = (y11 + y10) / 2,\n dx = x10 - x11,\n dy = y10 - y11,\n d2 = dx * dx + dy * dy,\n r = r1 - rc,\n D = x11 * y10 - x10 * y11,\n d = (dy < 0 ? -1 : 1) * sqrt(max(0, r * r * d2 - D * D)),\n cx0 = (D * dy - dx * d) / d2,\n cy0 = (-D * dx - dy * d) / d2,\n cx1 = (D * dy + dx * d) / d2,\n cy1 = (-D * dx + dy * d) / d2,\n dx0 = cx0 - x00,\n dy0 = cy0 - y00,\n dx1 = cx1 - x00,\n dy1 = cy1 - y00;\n\n // Pick the closer of the two intersection points.\n // TODO Is there a faster way to determine which intersection to use?\n if (dx0 * dx0 + dy0 * dy0 > dx1 * dx1 + dy1 * dy1) cx0 = cx1, cy0 = cy1;\n\n return {\n cx: cx0,\n cy: cy0,\n x01: -ox,\n y01: -oy,\n x11: cx0 * (r1 / r - 1),\n y11: cy0 * (r1 / r - 1)\n };\n}\n\nexport default function() {\n var innerRadius = arcInnerRadius,\n outerRadius = arcOuterRadius,\n cornerRadius = constant(0),\n padRadius = null,\n startAngle = arcStartAngle,\n endAngle = arcEndAngle,\n padAngle = arcPadAngle,\n context = null;\n\n function arc() {\n var buffer,\n r,\n r0 = +innerRadius.apply(this, arguments),\n r1 = +outerRadius.apply(this, arguments),\n a0 = startAngle.apply(this, arguments) - halfPi,\n a1 = endAngle.apply(this, arguments) - halfPi,\n da = abs(a1 - a0),\n cw = a1 > a0;\n\n if (!context) context = buffer = path();\n\n // Ensure that the outer radius is always larger than the inner radius.\n if (r1 < r0) r = r1, r1 = r0, r0 = r;\n\n // Is it a point?\n if (!(r1 > epsilon)) context.moveTo(0, 0);\n\n // Or is it a circle or annulus?\n else if (da > tau - epsilon) {\n context.moveTo(r1 * cos(a0), r1 * sin(a0));\n context.arc(0, 0, r1, a0, a1, !cw);\n if (r0 > epsilon) {\n context.moveTo(r0 * cos(a1), r0 * sin(a1));\n context.arc(0, 0, r0, a1, a0, cw);\n }\n }\n\n // Or is it a circular or annular sector?\n else {\n var a01 = a0,\n a11 = a1,\n a00 = a0,\n a10 = a1,\n da0 = da,\n da1 = da,\n ap = padAngle.apply(this, arguments) / 2,\n rp = (ap > epsilon) && (padRadius ? +padRadius.apply(this, arguments) : sqrt(r0 * r0 + r1 * r1)),\n rc = min(abs(r1 - r0) / 2, +cornerRadius.apply(this, arguments)),\n rc0 = rc,\n rc1 = rc,\n t0,\n t1;\n\n // Apply padding? Note that since r1 ≥ r0, da1 ≥ da0.\n if (rp > epsilon) {\n var p0 = asin(rp / r0 * sin(ap)),\n p1 = asin(rp / r1 * sin(ap));\n if ((da0 -= p0 * 2) > epsilon) p0 *= (cw ? 1 : -1), a00 += p0, a10 -= p0;\n else da0 = 0, a00 = a10 = (a0 + a1) / 2;\n if ((da1 -= p1 * 2) > epsilon) p1 *= (cw ? 1 : -1), a01 += p1, a11 -= p1;\n else da1 = 0, a01 = a11 = (a0 + a1) / 2;\n }\n\n var x01 = r1 * cos(a01),\n y01 = r1 * sin(a01),\n x10 = r0 * cos(a10),\n y10 = r0 * sin(a10);\n\n // Apply rounded corners?\n if (rc > epsilon) {\n var x11 = r1 * cos(a11),\n y11 = r1 * sin(a11),\n x00 = r0 * cos(a00),\n y00 = r0 * sin(a00),\n oc;\n\n // Restrict the corner radius according to the sector angle.\n if (da < pi && (oc = intersect(x01, y01, x00, y00, x11, y11, x10, y10))) {\n var ax = x01 - oc[0],\n ay = y01 - oc[1],\n bx = x11 - oc[0],\n by = y11 - oc[1],\n kc = 1 / sin(acos((ax * bx + ay * by) / (sqrt(ax * ax + ay * ay) * sqrt(bx * bx + by * by))) / 2),\n lc = sqrt(oc[0] * oc[0] + oc[1] * oc[1]);\n rc0 = min(rc, (r0 - lc) / (kc - 1));\n rc1 = min(rc, (r1 - lc) / (kc + 1));\n }\n }\n\n // Is the sector collapsed to a line?\n if (!(da1 > epsilon)) context.moveTo(x01, y01);\n\n // Does the sector’s outer ring have rounded corners?\n else if (rc1 > epsilon) {\n t0 = cornerTangents(x00, y00, x01, y01, r1, rc1, cw);\n t1 = cornerTangents(x11, y11, x10, y10, r1, rc1, cw);\n\n context.moveTo(t0.cx + t0.x01, t0.cy + t0.y01);\n\n // Have the corners merged?\n if (rc1 < rc) context.arc(t0.cx, t0.cy, rc1, atan2(t0.y01, t0.x01), atan2(t1.y01, t1.x01), !cw);\n\n // Otherwise, draw the two corners and the ring.\n else {\n context.arc(t0.cx, t0.cy, rc1, atan2(t0.y01, t0.x01), atan2(t0.y11, t0.x11), !cw);\n context.arc(0, 0, r1, atan2(t0.cy + t0.y11, t0.cx + t0.x11), atan2(t1.cy + t1.y11, t1.cx + t1.x11), !cw);\n context.arc(t1.cx, t1.cy, rc1, atan2(t1.y11, t1.x11), atan2(t1.y01, t1.x01), !cw);\n }\n }\n\n // Or is the outer ring just a circular arc?\n else context.moveTo(x01, y01), context.arc(0, 0, r1, a01, a11, !cw);\n\n // Is there no inner ring, and it’s a circular sector?\n // Or perhaps it’s an annular sector collapsed due to padding?\n if (!(r0 > epsilon) || !(da0 > epsilon)) context.lineTo(x10, y10);\n\n // Does the sector’s inner ring (or point) have rounded corners?\n else if (rc0 > epsilon) {\n t0 = cornerTangents(x10, y10, x11, y11, r0, -rc0, cw);\n t1 = cornerTangents(x01, y01, x00, y00, r0, -rc0, cw);\n\n context.lineTo(t0.cx + t0.x01, t0.cy + t0.y01);\n\n // Have the corners merged?\n if (rc0 < rc) context.arc(t0.cx, t0.cy, rc0, atan2(t0.y01, t0.x01), atan2(t1.y01, t1.x01), !cw);\n\n // Otherwise, draw the two corners and the ring.\n else {\n context.arc(t0.cx, t0.cy, rc0, atan2(t0.y01, t0.x01), atan2(t0.y11, t0.x11), !cw);\n context.arc(0, 0, r0, atan2(t0.cy + t0.y11, t0.cx + t0.x11), atan2(t1.cy + t1.y11, t1.cx + t1.x11), cw);\n context.arc(t1.cx, t1.cy, rc0, atan2(t1.y11, t1.x11), atan2(t1.y01, t1.x01), !cw);\n }\n }\n\n // Or is the inner ring just a circular arc?\n else context.arc(0, 0, r0, a10, a00, cw);\n }\n\n context.closePath();\n\n if (buffer) return context = null, buffer + \"\" || null;\n }\n\n arc.centroid = function() {\n var r = (+innerRadius.apply(this, arguments) + +outerRadius.apply(this, arguments)) / 2,\n a = (+startAngle.apply(this, arguments) + +endAngle.apply(this, arguments)) / 2 - pi / 2;\n return [cos(a) * r, sin(a) * r];\n };\n\n arc.innerRadius = function(_) {\n return arguments.length ? (innerRadius = typeof _ === \"function\" ? _ : constant(+_), arc) : innerRadius;\n };\n\n arc.outerRadius = function(_) {\n return arguments.length ? (outerRadius = typeof _ === \"function\" ? _ : constant(+_), arc) : outerRadius;\n };\n\n arc.cornerRadius = function(_) {\n return arguments.length ? (cornerRadius = typeof _ === \"function\" ? _ : constant(+_), arc) : cornerRadius;\n };\n\n arc.padRadius = function(_) {\n return arguments.length ? (padRadius = _ == null ? null : typeof _ === \"function\" ? _ : constant(+_), arc) : padRadius;\n };\n\n arc.startAngle = function(_) {\n return arguments.length ? (startAngle = typeof _ === \"function\" ? _ : constant(+_), arc) : startAngle;\n };\n\n arc.endAngle = function(_) {\n return arguments.length ? (endAngle = typeof _ === \"function\" ? _ : constant(+_), arc) : endAngle;\n };\n\n arc.padAngle = function(_) {\n return arguments.length ? (padAngle = typeof _ === \"function\" ? _ : constant(+_), arc) : padAngle;\n };\n\n arc.context = function(_) {\n return arguments.length ? ((context = _ == null ? null : _), arc) : context;\n };\n\n return arc;\n}\n","function Linear(context) {\n this._context = context;\n}\n\nLinear.prototype = {\n areaStart: function() {\n this._line = 0;\n },\n areaEnd: function() {\n this._line = NaN;\n },\n lineStart: function() {\n this._point = 0;\n },\n lineEnd: function() {\n if (this._line || (this._line !== 0 && this._point === 1)) this._context.closePath();\n this._line = 1 - this._line;\n },\n point: function(x, y) {\n x = +x, y = +y;\n switch (this._point) {\n case 0: this._point = 1; this._line ? this._context.lineTo(x, y) : this._context.moveTo(x, y); break;\n case 1: this._point = 2; // proceed\n default: this._context.lineTo(x, y); break;\n }\n }\n};\n\nexport default function(context) {\n return new Linear(context);\n}\n","import curveLinear from \"./linear\";\n\nexport var curveRadialLinear = curveRadial(curveLinear);\n\nfunction Radial(curve) {\n this._curve = curve;\n}\n\nRadial.prototype = {\n areaStart: function() {\n this._curve.areaStart();\n },\n areaEnd: function() {\n this._curve.areaEnd();\n },\n lineStart: function() {\n this._curve.lineStart();\n },\n lineEnd: function() {\n this._curve.lineEnd();\n },\n point: function(a, r) {\n this._curve.point(r * Math.sin(a), r * -Math.cos(a));\n }\n};\n\nexport default function curveRadial(curve) {\n\n function radial(context) {\n return new Radial(curve(context));\n }\n\n radial._curve = curve;\n\n return radial;\n}\n","export var slice = Array.prototype.slice;\n","var tan30 = Math.sqrt(1 / 3),\n tan30_2 = tan30 * 2;\n\nexport default {\n draw: function(context, size) {\n var y = Math.sqrt(size / tan30_2),\n x = y * tan30;\n context.moveTo(0, -y);\n context.lineTo(x, 0);\n context.lineTo(0, y);\n context.lineTo(-x, 0);\n context.closePath();\n }\n};\n","import {pi, tau} from \"../math\";\n\nexport default {\n draw: function(context, size) {\n var r = Math.sqrt(size / pi);\n context.moveTo(r, 0);\n context.arc(0, 0, r, 0, tau);\n }\n};\n","import {pi, tau} from \"../math\";\n\nvar ka = 0.89081309152928522810,\n kr = Math.sin(pi / 10) / Math.sin(7 * pi / 10),\n kx = Math.sin(tau / 10) * kr,\n ky = -Math.cos(tau / 10) * kr;\n\nexport default {\n draw: function(context, size) {\n var r = Math.sqrt(size * ka),\n x = kx * r,\n y = ky * r;\n context.moveTo(0, -r);\n context.lineTo(x, y);\n for (var i = 1; i < 5; ++i) {\n var a = tau * i / 5,\n c = Math.cos(a),\n s = Math.sin(a);\n context.lineTo(s * r, -c * r);\n context.lineTo(c * x - s * y, s * x + c * y);\n }\n context.closePath();\n }\n};\n","export default function() {}\n","var sqrt3 = Math.sqrt(3);\n\nexport default {\n draw: function(context, size) {\n var y = -Math.sqrt(size / (sqrt3 * 3));\n context.moveTo(0, y * 2);\n context.lineTo(-sqrt3 * y, -y);\n context.lineTo(sqrt3 * y, -y);\n context.closePath();\n }\n};\n","var c = -0.5,\n s = Math.sqrt(3) / 2,\n k = 1 / Math.sqrt(12),\n a = (k / 2 + 1) * 3;\n\nexport default {\n draw: function(context, size) {\n var r = Math.sqrt(size / a),\n x0 = r / 2,\n y0 = r * k,\n x1 = x0,\n y1 = r * k + r,\n x2 = -x1,\n y2 = y1;\n context.moveTo(x0, y0);\n context.lineTo(x1, y1);\n context.lineTo(x2, y2);\n context.lineTo(c * x0 - s * y0, s * x0 + c * y0);\n context.lineTo(c * x1 - s * y1, s * x1 + c * y1);\n context.lineTo(c * x2 - s * y2, s * x2 + c * y2);\n context.lineTo(c * x0 + s * y0, c * y0 - s * x0);\n context.lineTo(c * x1 + s * y1, c * y1 - s * x1);\n context.lineTo(c * x2 + s * y2, c * y2 - s * x2);\n context.closePath();\n }\n};\n","export function point(that, x, y) {\n that._context.bezierCurveTo(\n (2 * that._x0 + that._x1) / 3,\n (2 * that._y0 + that._y1) / 3,\n (that._x0 + 2 * that._x1) / 3,\n (that._y0 + 2 * that._y1) / 3,\n (that._x0 + 4 * that._x1 + x) / 6,\n (that._y0 + 4 * that._y1 + y) / 6\n );\n}\n\nexport function Basis(context) {\n this._context = context;\n}\n\nBasis.prototype = {\n areaStart: function() {\n this._line = 0;\n },\n areaEnd: function() {\n this._line = NaN;\n },\n lineStart: function() {\n this._x0 = this._x1 =\n this._y0 = this._y1 = NaN;\n this._point = 0;\n },\n lineEnd: function() {\n switch (this._point) {\n case 3: point(this, this._x1, this._y1); // proceed\n case 2: this._context.lineTo(this._x1, this._y1); break;\n }\n if (this._line || (this._line !== 0 && this._point === 1)) this._context.closePath();\n this._line = 1 - this._line;\n },\n point: function(x, y) {\n x = +x, y = +y;\n switch (this._point) {\n case 0: this._point = 1; this._line ? this._context.lineTo(x, y) : this._context.moveTo(x, y); break;\n case 1: this._point = 2; break;\n case 2: this._point = 3; this._context.lineTo((5 * this._x0 + this._x1) / 6, (5 * this._y0 + this._y1) / 6); // proceed\n default: point(this, x, y); break;\n }\n this._x0 = this._x1, this._x1 = x;\n this._y0 = this._y1, this._y1 = y;\n }\n};\n\nexport default function(context) {\n return new Basis(context);\n}\n","import noop from \"../noop\";\nimport {point} from \"./basis\";\n\nfunction BasisClosed(context) {\n this._context = context;\n}\n\nBasisClosed.prototype = {\n areaStart: noop,\n areaEnd: noop,\n lineStart: function() {\n this._x0 = this._x1 = this._x2 = this._x3 = this._x4 =\n this._y0 = this._y1 = this._y2 = this._y3 = this._y4 = NaN;\n this._point = 0;\n },\n lineEnd: function() {\n switch (this._point) {\n case 1: {\n this._context.moveTo(this._x2, this._y2);\n this._context.closePath();\n break;\n }\n case 2: {\n this._context.moveTo((this._x2 + 2 * this._x3) / 3, (this._y2 + 2 * this._y3) / 3);\n this._context.lineTo((this._x3 + 2 * this._x2) / 3, (this._y3 + 2 * this._y2) / 3);\n this._context.closePath();\n break;\n }\n case 3: {\n this.point(this._x2, this._y2);\n this.point(this._x3, this._y3);\n this.point(this._x4, this._y4);\n break;\n }\n }\n },\n point: function(x, y) {\n x = +x, y = +y;\n switch (this._point) {\n case 0: this._point = 1; this._x2 = x, this._y2 = y; break;\n case 1: this._point = 2; this._x3 = x, this._y3 = y; break;\n case 2: this._point = 3; this._x4 = x, this._y4 = y; this._context.moveTo((this._x0 + 4 * this._x1 + x) / 6, (this._y0 + 4 * this._y1 + y) / 6); break;\n default: point(this, x, y); break;\n }\n this._x0 = this._x1, this._x1 = x;\n this._y0 = this._y1, this._y1 = y;\n }\n};\n\nexport default function(context) {\n return new BasisClosed(context);\n}\n","import {point} from \"./basis\";\n\nfunction BasisOpen(context) {\n this._context = context;\n}\n\nBasisOpen.prototype = {\n areaStart: function() {\n this._line = 0;\n },\n areaEnd: function() {\n this._line = NaN;\n },\n lineStart: function() {\n this._x0 = this._x1 =\n this._y0 = this._y1 = NaN;\n this._point = 0;\n },\n lineEnd: function() {\n if (this._line || (this._line !== 0 && this._point === 3)) this._context.closePath();\n this._line = 1 - this._line;\n },\n point: function(x, y) {\n x = +x, y = +y;\n switch (this._point) {\n case 0: this._point = 1; break;\n case 1: this._point = 2; break;\n case 2: this._point = 3; var x0 = (this._x0 + 4 * this._x1 + x) / 6, y0 = (this._y0 + 4 * this._y1 + y) / 6; this._line ? this._context.lineTo(x0, y0) : this._context.moveTo(x0, y0); break;\n case 3: this._point = 4; // proceed\n default: point(this, x, y); break;\n }\n this._x0 = this._x1, this._x1 = x;\n this._y0 = this._y1, this._y1 = y;\n }\n};\n\nexport default function(context) {\n return new BasisOpen(context);\n}\n","import {Basis} from \"./basis\";\n\nfunction Bundle(context, beta) {\n this._basis = new Basis(context);\n this._beta = beta;\n}\n\nBundle.prototype = {\n lineStart: function() {\n this._x = [];\n this._y = [];\n this._basis.lineStart();\n },\n lineEnd: function() {\n var x = this._x,\n y = this._y,\n j = x.length - 1;\n\n if (j > 0) {\n var x0 = x[0],\n y0 = y[0],\n dx = x[j] - x0,\n dy = y[j] - y0,\n i = -1,\n t;\n\n while (++i <= j) {\n t = i / j;\n this._basis.point(\n this._beta * x[i] + (1 - this._beta) * (x0 + t * dx),\n this._beta * y[i] + (1 - this._beta) * (y0 + t * dy)\n );\n }\n }\n\n this._x = this._y = null;\n this._basis.lineEnd();\n },\n point: function(x, y) {\n this._x.push(+x);\n this._y.push(+y);\n }\n};\n\nexport default (function custom(beta) {\n\n function bundle(context) {\n return beta === 1 ? new Basis(context) : new Bundle(context, beta);\n }\n\n bundle.beta = function(beta) {\n return custom(+beta);\n };\n\n return bundle;\n})(0.85);\n","export function point(that, x, y) {\n that._context.bezierCurveTo(\n that._x1 + that._k * (that._x2 - that._x0),\n that._y1 + that._k * (that._y2 - that._y0),\n that._x2 + that._k * (that._x1 - x),\n that._y2 + that._k * (that._y1 - y),\n that._x2,\n that._y2\n );\n}\n\nexport function Cardinal(context, tension) {\n this._context = context;\n this._k = (1 - tension) / 6;\n}\n\nCardinal.prototype = {\n areaStart: function() {\n this._line = 0;\n },\n areaEnd: function() {\n this._line = NaN;\n },\n lineStart: function() {\n this._x0 = this._x1 = this._x2 =\n this._y0 = this._y1 = this._y2 = NaN;\n this._point = 0;\n },\n lineEnd: function() {\n switch (this._point) {\n case 2: this._context.lineTo(this._x2, this._y2); break;\n case 3: point(this, this._x1, this._y1); break;\n }\n if (this._line || (this._line !== 0 && this._point === 1)) this._context.closePath();\n this._line = 1 - this._line;\n },\n point: function(x, y) {\n x = +x, y = +y;\n switch (this._point) {\n case 0: this._point = 1; this._line ? this._context.lineTo(x, y) : this._context.moveTo(x, y); break;\n case 1: this._point = 2; this._x1 = x, this._y1 = y; break;\n case 2: this._point = 3; // proceed\n default: point(this, x, y); break;\n }\n this._x0 = this._x1, this._x1 = this._x2, this._x2 = x;\n this._y0 = this._y1, this._y1 = this._y2, this._y2 = y;\n }\n};\n\nexport default (function custom(tension) {\n\n function cardinal(context) {\n return new Cardinal(context, tension);\n }\n\n cardinal.tension = function(tension) {\n return custom(+tension);\n };\n\n return cardinal;\n})(0);\n","import noop from \"../noop\";\nimport {point} from \"./cardinal\";\n\nexport function CardinalClosed(context, tension) {\n this._context = context;\n this._k = (1 - tension) / 6;\n}\n\nCardinalClosed.prototype = {\n areaStart: noop,\n areaEnd: noop,\n lineStart: function() {\n this._x0 = this._x1 = this._x2 = this._x3 = this._x4 = this._x5 =\n this._y0 = this._y1 = this._y2 = this._y3 = this._y4 = this._y5 = NaN;\n this._point = 0;\n },\n lineEnd: function() {\n switch (this._point) {\n case 1: {\n this._context.moveTo(this._x3, this._y3);\n this._context.closePath();\n break;\n }\n case 2: {\n this._context.lineTo(this._x3, this._y3);\n this._context.closePath();\n break;\n }\n case 3: {\n this.point(this._x3, this._y3);\n this.point(this._x4, this._y4);\n this.point(this._x5, this._y5);\n break;\n }\n }\n },\n point: function(x, y) {\n x = +x, y = +y;\n switch (this._point) {\n case 0: this._point = 1; this._x3 = x, this._y3 = y; break;\n case 1: this._point = 2; this._context.moveTo(this._x4 = x, this._y4 = y); break;\n case 2: this._point = 3; this._x5 = x, this._y5 = y; break;\n default: point(this, x, y); break;\n }\n this._x0 = this._x1, this._x1 = this._x2, this._x2 = x;\n this._y0 = this._y1, this._y1 = this._y2, this._y2 = y;\n }\n};\n\nexport default (function custom(tension) {\n\n function cardinal(context) {\n return new CardinalClosed(context, tension);\n }\n\n cardinal.tension = function(tension) {\n return custom(+tension);\n };\n\n return cardinal;\n})(0);\n","import {point} from \"./cardinal\";\n\nexport function CardinalOpen(context, tension) {\n this._context = context;\n this._k = (1 - tension) / 6;\n}\n\nCardinalOpen.prototype = {\n areaStart: function() {\n this._line = 0;\n },\n areaEnd: function() {\n this._line = NaN;\n },\n lineStart: function() {\n this._x0 = this._x1 = this._x2 =\n this._y0 = this._y1 = this._y2 = NaN;\n this._point = 0;\n },\n lineEnd: function() {\n if (this._line || (this._line !== 0 && this._point === 3)) this._context.closePath();\n this._line = 1 - this._line;\n },\n point: function(x, y) {\n x = +x, y = +y;\n switch (this._point) {\n case 0: this._point = 1; break;\n case 1: this._point = 2; break;\n case 2: this._point = 3; this._line ? this._context.lineTo(this._x2, this._y2) : this._context.moveTo(this._x2, this._y2); break;\n case 3: this._point = 4; // proceed\n default: point(this, x, y); break;\n }\n this._x0 = this._x1, this._x1 = this._x2, this._x2 = x;\n this._y0 = this._y1, this._y1 = this._y2, this._y2 = y;\n }\n};\n\nexport default (function custom(tension) {\n\n function cardinal(context) {\n return new CardinalOpen(context, tension);\n }\n\n cardinal.tension = function(tension) {\n return custom(+tension);\n };\n\n return cardinal;\n})(0);\n","import {epsilon} from \"../math\";\nimport {Cardinal} from \"./cardinal\";\n\nexport function point(that, x, y) {\n var x1 = that._x1,\n y1 = that._y1,\n x2 = that._x2,\n y2 = that._y2;\n\n if (that._l01_a > epsilon) {\n var a = 2 * that._l01_2a + 3 * that._l01_a * that._l12_a + that._l12_2a,\n n = 3 * that._l01_a * (that._l01_a + that._l12_a);\n x1 = (x1 * a - that._x0 * that._l12_2a + that._x2 * that._l01_2a) / n;\n y1 = (y1 * a - that._y0 * that._l12_2a + that._y2 * that._l01_2a) / n;\n }\n\n if (that._l23_a > epsilon) {\n var b = 2 * that._l23_2a + 3 * that._l23_a * that._l12_a + that._l12_2a,\n m = 3 * that._l23_a * (that._l23_a + that._l12_a);\n x2 = (x2 * b + that._x1 * that._l23_2a - x * that._l12_2a) / m;\n y2 = (y2 * b + that._y1 * that._l23_2a - y * that._l12_2a) / m;\n }\n\n that._context.bezierCurveTo(x1, y1, x2, y2, that._x2, that._y2);\n}\n\nfunction CatmullRom(context, alpha) {\n this._context = context;\n this._alpha = alpha;\n}\n\nCatmullRom.prototype = {\n areaStart: function() {\n this._line = 0;\n },\n areaEnd: function() {\n this._line = NaN;\n },\n lineStart: function() {\n this._x0 = this._x1 = this._x2 =\n this._y0 = this._y1 = this._y2 = NaN;\n this._l01_a = this._l12_a = this._l23_a =\n this._l01_2a = this._l12_2a = this._l23_2a =\n this._point = 0;\n },\n lineEnd: function() {\n switch (this._point) {\n case 2: this._context.lineTo(this._x2, this._y2); break;\n case 3: this.point(this._x2, this._y2); break;\n }\n if (this._line || (this._line !== 0 && this._point === 1)) this._context.closePath();\n this._line = 1 - this._line;\n },\n point: function(x, y) {\n x = +x, y = +y;\n\n if (this._point) {\n var x23 = this._x2 - x,\n y23 = this._y2 - y;\n this._l23_a = Math.sqrt(this._l23_2a = Math.pow(x23 * x23 + y23 * y23, this._alpha));\n }\n\n switch (this._point) {\n case 0: this._point = 1; this._line ? this._context.lineTo(x, y) : this._context.moveTo(x, y); break;\n case 1: this._point = 2; break;\n case 2: this._point = 3; // proceed\n default: point(this, x, y); break;\n }\n\n this._l01_a = this._l12_a, this._l12_a = this._l23_a;\n this._l01_2a = this._l12_2a, this._l12_2a = this._l23_2a;\n this._x0 = this._x1, this._x1 = this._x2, this._x2 = x;\n this._y0 = this._y1, this._y1 = this._y2, this._y2 = y;\n }\n};\n\nexport default (function custom(alpha) {\n\n function catmullRom(context) {\n return alpha ? new CatmullRom(context, alpha) : new Cardinal(context, 0);\n }\n\n catmullRom.alpha = function(alpha) {\n return custom(+alpha);\n };\n\n return catmullRom;\n})(0.5);\n","import {CardinalClosed} from \"./cardinalClosed\";\nimport noop from \"../noop\";\nimport {point} from \"./catmullRom\";\n\nfunction CatmullRomClosed(context, alpha) {\n this._context = context;\n this._alpha = alpha;\n}\n\nCatmullRomClosed.prototype = {\n areaStart: noop,\n areaEnd: noop,\n lineStart: function() {\n this._x0 = this._x1 = this._x2 = this._x3 = this._x4 = this._x5 =\n this._y0 = this._y1 = this._y2 = this._y3 = this._y4 = this._y5 = NaN;\n this._l01_a = this._l12_a = this._l23_a =\n this._l01_2a = this._l12_2a = this._l23_2a =\n this._point = 0;\n },\n lineEnd: function() {\n switch (this._point) {\n case 1: {\n this._context.moveTo(this._x3, this._y3);\n this._context.closePath();\n break;\n }\n case 2: {\n this._context.lineTo(this._x3, this._y3);\n this._context.closePath();\n break;\n }\n case 3: {\n this.point(this._x3, this._y3);\n this.point(this._x4, this._y4);\n this.point(this._x5, this._y5);\n break;\n }\n }\n },\n point: function(x, y) {\n x = +x, y = +y;\n\n if (this._point) {\n var x23 = this._x2 - x,\n y23 = this._y2 - y;\n this._l23_a = Math.sqrt(this._l23_2a = Math.pow(x23 * x23 + y23 * y23, this._alpha));\n }\n\n switch (this._point) {\n case 0: this._point = 1; this._x3 = x, this._y3 = y; break;\n case 1: this._point = 2; this._context.moveTo(this._x4 = x, this._y4 = y); break;\n case 2: this._point = 3; this._x5 = x, this._y5 = y; break;\n default: point(this, x, y); break;\n }\n\n this._l01_a = this._l12_a, this._l12_a = this._l23_a;\n this._l01_2a = this._l12_2a, this._l12_2a = this._l23_2a;\n this._x0 = this._x1, this._x1 = this._x2, this._x2 = x;\n this._y0 = this._y1, this._y1 = this._y2, this._y2 = y;\n }\n};\n\nexport default (function custom(alpha) {\n\n function catmullRom(context) {\n return alpha ? new CatmullRomClosed(context, alpha) : new CardinalClosed(context, 0);\n }\n\n catmullRom.alpha = function(alpha) {\n return custom(+alpha);\n };\n\n return catmullRom;\n})(0.5);\n","import {CardinalOpen} from \"./cardinalOpen\";\nimport {point} from \"./catmullRom\";\n\nfunction CatmullRomOpen(context, alpha) {\n this._context = context;\n this._alpha = alpha;\n}\n\nCatmullRomOpen.prototype = {\n areaStart: function() {\n this._line = 0;\n },\n areaEnd: function() {\n this._line = NaN;\n },\n lineStart: function() {\n this._x0 = this._x1 = this._x2 =\n this._y0 = this._y1 = this._y2 = NaN;\n this._l01_a = this._l12_a = this._l23_a =\n this._l01_2a = this._l12_2a = this._l23_2a =\n this._point = 0;\n },\n lineEnd: function() {\n if (this._line || (this._line !== 0 && this._point === 3)) this._context.closePath();\n this._line = 1 - this._line;\n },\n point: function(x, y) {\n x = +x, y = +y;\n\n if (this._point) {\n var x23 = this._x2 - x,\n y23 = this._y2 - y;\n this._l23_a = Math.sqrt(this._l23_2a = Math.pow(x23 * x23 + y23 * y23, this._alpha));\n }\n\n switch (this._point) {\n case 0: this._point = 1; break;\n case 1: this._point = 2; break;\n case 2: this._point = 3; this._line ? this._context.lineTo(this._x2, this._y2) : this._context.moveTo(this._x2, this._y2); break;\n case 3: this._point = 4; // proceed\n default: point(this, x, y); break;\n }\n\n this._l01_a = this._l12_a, this._l12_a = this._l23_a;\n this._l01_2a = this._l12_2a, this._l12_2a = this._l23_2a;\n this._x0 = this._x1, this._x1 = this._x2, this._x2 = x;\n this._y0 = this._y1, this._y1 = this._y2, this._y2 = y;\n }\n};\n\nexport default (function custom(alpha) {\n\n function catmullRom(context) {\n return alpha ? new CatmullRomOpen(context, alpha) : new CardinalOpen(context, 0);\n }\n\n catmullRom.alpha = function(alpha) {\n return custom(+alpha);\n };\n\n return catmullRom;\n})(0.5);\n","import noop from \"../noop\";\n\nfunction LinearClosed(context) {\n this._context = context;\n}\n\nLinearClosed.prototype = {\n areaStart: noop,\n areaEnd: noop,\n lineStart: function() {\n this._point = 0;\n },\n lineEnd: function() {\n if (this._point) this._context.closePath();\n },\n point: function(x, y) {\n x = +x, y = +y;\n if (this._point) this._context.lineTo(x, y);\n else this._point = 1, this._context.moveTo(x, y);\n }\n};\n\nexport default function(context) {\n return new LinearClosed(context);\n}\n","function sign(x) {\n return x < 0 ? -1 : 1;\n}\n\n// Calculate the slopes of the tangents (Hermite-type interpolation) based on\n// the following paper: Steffen, M. 1990. A Simple Method for Monotonic\n// Interpolation in One Dimension. Astronomy and Astrophysics, Vol. 239, NO.\n// NOV(II), P. 443, 1990.\nfunction slope3(that, x2, y2) {\n var h0 = that._x1 - that._x0,\n h1 = x2 - that._x1,\n s0 = (that._y1 - that._y0) / (h0 || h1 < 0 && -0),\n s1 = (y2 - that._y1) / (h1 || h0 < 0 && -0),\n p = (s0 * h1 + s1 * h0) / (h0 + h1);\n return (sign(s0) + sign(s1)) * Math.min(Math.abs(s0), Math.abs(s1), 0.5 * Math.abs(p)) || 0;\n}\n\n// Calculate a one-sided slope.\nfunction slope2(that, t) {\n var h = that._x1 - that._x0;\n return h ? (3 * (that._y1 - that._y0) / h - t) / 2 : t;\n}\n\n// According to https://en.wikipedia.org/wiki/Cubic_Hermite_spline#Representations\n// \"you can express cubic Hermite interpolation in terms of cubic Bézier curves\n// with respect to the four values p0, p0 + m0 / 3, p1 - m1 / 3, p1\".\nfunction point(that, t0, t1) {\n var x0 = that._x0,\n y0 = that._y0,\n x1 = that._x1,\n y1 = that._y1,\n dx = (x1 - x0) / 3;\n that._context.bezierCurveTo(x0 + dx, y0 + dx * t0, x1 - dx, y1 - dx * t1, x1, y1);\n}\n\nfunction MonotoneX(context) {\n this._context = context;\n}\n\nMonotoneX.prototype = {\n areaStart: function() {\n this._line = 0;\n },\n areaEnd: function() {\n this._line = NaN;\n },\n lineStart: function() {\n this._x0 = this._x1 =\n this._y0 = this._y1 =\n this._t0 = NaN;\n this._point = 0;\n },\n lineEnd: function() {\n switch (this._point) {\n case 2: this._context.lineTo(this._x1, this._y1); break;\n case 3: point(this, this._t0, slope2(this, this._t0)); break;\n }\n if (this._line || (this._line !== 0 && this._point === 1)) this._context.closePath();\n this._line = 1 - this._line;\n },\n point: function(x, y) {\n var t1 = NaN;\n\n x = +x, y = +y;\n if (x === this._x1 && y === this._y1) return; // Ignore coincident points.\n switch (this._point) {\n case 0: this._point = 1; this._line ? this._context.lineTo(x, y) : this._context.moveTo(x, y); break;\n case 1: this._point = 2; break;\n case 2: this._point = 3; point(this, slope2(this, t1 = slope3(this, x, y)), t1); break;\n default: point(this, this._t0, t1 = slope3(this, x, y)); break;\n }\n\n this._x0 = this._x1, this._x1 = x;\n this._y0 = this._y1, this._y1 = y;\n this._t0 = t1;\n }\n}\n\nfunction MonotoneY(context) {\n this._context = new ReflectContext(context);\n}\n\n(MonotoneY.prototype = Object.create(MonotoneX.prototype)).point = function(x, y) {\n MonotoneX.prototype.point.call(this, y, x);\n};\n\nfunction ReflectContext(context) {\n this._context = context;\n}\n\nReflectContext.prototype = {\n moveTo: function(x, y) { this._context.moveTo(y, x); },\n closePath: function() { this._context.closePath(); },\n lineTo: function(x, y) { this._context.lineTo(y, x); },\n bezierCurveTo: function(x1, y1, x2, y2, x, y) { this._context.bezierCurveTo(y1, x1, y2, x2, y, x); }\n};\n\nexport function monotoneX(context) {\n return new MonotoneX(context);\n}\n\nexport function monotoneY(context) {\n return new MonotoneY(context);\n}\n","function Natural(context) {\n this._context = context;\n}\n\nNatural.prototype = {\n areaStart: function() {\n this._line = 0;\n },\n areaEnd: function() {\n this._line = NaN;\n },\n lineStart: function() {\n this._x = [];\n this._y = [];\n },\n lineEnd: function() {\n var x = this._x,\n y = this._y,\n n = x.length;\n\n if (n) {\n this._line ? this._context.lineTo(x[0], y[0]) : this._context.moveTo(x[0], y[0]);\n if (n === 2) {\n this._context.lineTo(x[1], y[1]);\n } else {\n var px = controlPoints(x),\n py = controlPoints(y);\n for (var i0 = 0, i1 = 1; i1 < n; ++i0, ++i1) {\n this._context.bezierCurveTo(px[0][i0], py[0][i0], px[1][i0], py[1][i0], x[i1], y[i1]);\n }\n }\n }\n\n if (this._line || (this._line !== 0 && n === 1)) this._context.closePath();\n this._line = 1 - this._line;\n this._x = this._y = null;\n },\n point: function(x, y) {\n this._x.push(+x);\n this._y.push(+y);\n }\n};\n\n// See https://www.particleincell.com/2012/bezier-splines/ for derivation.\nfunction controlPoints(x) {\n var i,\n n = x.length - 1,\n m,\n a = new Array(n),\n b = new Array(n),\n r = new Array(n);\n a[0] = 0, b[0] = 2, r[0] = x[0] + 2 * x[1];\n for (i = 1; i < n - 1; ++i) a[i] = 1, b[i] = 4, r[i] = 4 * x[i] + 2 * x[i + 1];\n a[n - 1] = 2, b[n - 1] = 7, r[n - 1] = 8 * x[n - 1] + x[n];\n for (i = 1; i < n; ++i) m = a[i] / b[i - 1], b[i] -= m, r[i] -= m * r[i - 1];\n a[n - 1] = r[n - 1] / b[n - 1];\n for (i = n - 2; i >= 0; --i) a[i] = (r[i] - a[i + 1]) / b[i];\n b[n - 1] = (x[n] + a[n - 1]) / 2;\n for (i = 0; i < n - 1; ++i) b[i] = 2 * x[i + 1] - a[i + 1];\n return [a, b];\n}\n\nexport default function(context) {\n return new Natural(context);\n}\n","function Step(context, t) {\n this._context = context;\n this._t = t;\n}\n\nStep.prototype = {\n areaStart: function() {\n this._line = 0;\n },\n areaEnd: function() {\n this._line = NaN;\n },\n lineStart: function() {\n this._x = this._y = NaN;\n this._point = 0;\n },\n lineEnd: function() {\n if (0 < this._t && this._t < 1 && this._point === 2) this._context.lineTo(this._x, this._y);\n if (this._line || (this._line !== 0 && this._point === 1)) this._context.closePath();\n if (this._line >= 0) this._t = 1 - this._t, this._line = 1 - this._line;\n },\n point: function(x, y) {\n x = +x, y = +y;\n switch (this._point) {\n case 0: this._point = 1; this._line ? this._context.lineTo(x, y) : this._context.moveTo(x, y); break;\n case 1: this._point = 2; // proceed\n default: {\n if (this._t <= 0) {\n this._context.lineTo(this._x, y);\n this._context.lineTo(x, y);\n } else {\n var x1 = this._x * (1 - this._t) + x * this._t;\n this._context.lineTo(x1, this._y);\n this._context.lineTo(x1, y);\n }\n break;\n }\n }\n this._x = x, this._y = y;\n }\n};\n\nexport default function(context) {\n return new Step(context, 0.5);\n}\n\nexport function stepBefore(context) {\n return new Step(context, 0);\n}\n\nexport function stepAfter(context) {\n return new Step(context, 1);\n}\n","import ascending from \"./ascending\";\n\nexport default function(series) {\n return ascending(series).reverse();\n}\n","import { arc as arcFactory } from \"d3-shape\";\n\nimport {\n LinkedVisualConsoleProps,\n UnknownObject,\n WithModuleProps\n} from \"../types\";\nimport {\n linkedVCPropsDecoder,\n modulePropsDecoder,\n notEmptyStringOr,\n parseIntOr,\n parseFloatOr\n} from \"../lib\";\nimport Item, { ItemType, ItemProps, itemBasePropsDecoder } from \"../Item\";\n\nexport type PercentileProps = {\n type: ItemType.PERCENTILE_BAR;\n percentileType:\n | \"progress-bar\"\n | \"bubble\"\n | \"circular-progress-bar\"\n | \"circular-progress-bar-alt\";\n valueType: \"percent\" | \"value\";\n minValue: number | null;\n maxValue: number | null;\n color: string | null;\n labelColor: string | null;\n value: number | null;\n unit: string | null;\n} & ItemProps &\n WithModuleProps &\n LinkedVisualConsoleProps;\n\n/**\n * Extract a valid enum value from a raw type value.\n * @param type Raw value.\n */\n// eslint-disable-next-line @typescript-eslint/no-explicit-any\nfunction extractPercentileType(type: any): PercentileProps[\"percentileType\"] {\n switch (type) {\n case \"progress-bar\":\n case \"bubble\":\n case \"circular-progress-bar\":\n case \"circular-progress-bar-alt\":\n return type;\n default:\n case ItemType.PERCENTILE_BAR:\n return \"progress-bar\";\n case ItemType.PERCENTILE_BUBBLE:\n return \"bubble\";\n case ItemType.CIRCULAR_PROGRESS_BAR:\n return \"circular-progress-bar\";\n case ItemType.CIRCULAR_INTERIOR_PROGRESS_BAR:\n return \"circular-progress-bar-alt\";\n }\n}\n\n/**\n * Extract a valid enum value from a raw value type value.\n * @param type Raw value.\n */\n// eslint-disable-next-line @typescript-eslint/no-explicit-any\nfunction extractValueType(valueType: any): PercentileProps[\"valueType\"] {\n switch (valueType) {\n case \"percent\":\n case \"value\":\n return valueType;\n default:\n return \"percent\";\n }\n}\n\n/**\n * Build a valid typed object from a raw object.\n * This will allow us to ensure the type safety.\n *\n * @param data Raw object.\n * @return An object representing the percentile props.\n * @throws Will throw a TypeError if some property\n * is missing from the raw object or have an invalid type.\n */\nexport function percentilePropsDecoder(\n data: UnknownObject\n): PercentileProps | never {\n return {\n ...itemBasePropsDecoder(data), // Object spread. It will merge the properties of the two objects.\n type: ItemType.PERCENTILE_BAR,\n percentileType: extractPercentileType(data.percentileType || data.type),\n valueType: extractValueType(data.valueType),\n minValue: parseIntOr(data.minValue, null),\n maxValue: parseIntOr(data.maxValue, null),\n color: notEmptyStringOr(data.color, null),\n labelColor: notEmptyStringOr(data.labelColor, null),\n value: parseFloatOr(data.value, null),\n unit: notEmptyStringOr(data.unit, null),\n ...modulePropsDecoder(data), // Object spread. It will merge the properties of the two objects.\n ...linkedVCPropsDecoder(data) // Object spread. It will merge the properties of the two objects.\n };\n}\n\nconst svgNS = \"http://www.w3.org/2000/svg\";\n\nexport default class Percentile extends Item {\n protected createDomElement(): HTMLElement {\n const colors = {\n background: \"#000000\",\n progress: this.props.color || \"#F0F0F0\",\n text: this.props.labelColor || \"#444444\"\n };\n // Progress.\n const progress = this.getProgress();\n // Main element.\n const element = document.createElement(\"div\");\n // SVG container.\n const svg = document.createElementNS(svgNS, \"svg\");\n\n switch (this.props.percentileType) {\n case \"progress-bar\":\n {\n const backgroundRect = document.createElementNS(svgNS, \"rect\");\n backgroundRect.setAttribute(\"fill\", colors.background);\n backgroundRect.setAttribute(\"fill-opacity\", \"0.5\");\n backgroundRect.setAttribute(\"width\", \"100\");\n backgroundRect.setAttribute(\"height\", \"20\");\n backgroundRect.setAttribute(\"rx\", \"5\");\n backgroundRect.setAttribute(\"ry\", \"5\");\n const progressRect = document.createElementNS(svgNS, \"rect\");\n progressRect.setAttribute(\"fill\", colors.progress);\n progressRect.setAttribute(\"fill-opacity\", \"1\");\n progressRect.setAttribute(\"width\", `${progress}`);\n progressRect.setAttribute(\"height\", \"20\");\n progressRect.setAttribute(\"rx\", \"5\");\n progressRect.setAttribute(\"ry\", \"5\");\n const text = document.createElementNS(svgNS, \"text\");\n text.setAttribute(\"text-anchor\", \"middle\");\n text.setAttribute(\"alignment-baseline\", \"middle\");\n text.setAttribute(\"font-size\", \"12\");\n text.setAttribute(\"font-family\", \"arial\");\n text.setAttribute(\"font-weight\", \"bold\");\n text.setAttribute(\"transform\", \"translate(50 11)\");\n text.setAttribute(\"fill\", colors.text);\n\n if (this.props.valueType === \"value\") {\n text.textContent = this.props.unit\n ? `${this.props.value} ${this.props.unit}`\n : `${this.props.value}`;\n } else {\n text.textContent = `${progress}%`;\n }\n\n // Auto resize SVG using the view box magic: https://css-tricks.com/scale-svg/\n svg.setAttribute(\"viewBox\", \"0 0 100 20\");\n svg.append(backgroundRect, progressRect, text);\n }\n break;\n case \"bubble\":\n case \"circular-progress-bar\":\n case \"circular-progress-bar-alt\":\n {\n // Auto resize SVG using the view box magic: https://css-tricks.com/scale-svg/\n svg.setAttribute(\"viewBox\", \"0 0 100 100\");\n\n if (this.props.percentileType === \"bubble\") {\n // Create and append the circles.\n const backgroundCircle = document.createElementNS(svgNS, \"circle\");\n backgroundCircle.setAttribute(\"transform\", \"translate(50 50)\");\n backgroundCircle.setAttribute(\"fill\", colors.background);\n backgroundCircle.setAttribute(\"fill-opacity\", \"0.5\");\n backgroundCircle.setAttribute(\"r\", \"50\");\n const progressCircle = document.createElementNS(svgNS, \"circle\");\n progressCircle.setAttribute(\"transform\", \"translate(50 50)\");\n progressCircle.setAttribute(\"fill\", colors.progress);\n progressCircle.setAttribute(\"fill-opacity\", \"1\");\n progressCircle.setAttribute(\"r\", `${progress / 2}`);\n\n svg.append(backgroundCircle, progressCircle);\n } else {\n // Create and append the circles.\n const arcProps = {\n innerRadius:\n this.props.percentileType === \"circular-progress-bar\" ? 30 : 0,\n outerRadius: 50,\n startAngle: 0,\n endAngle: Math.PI * 2\n };\n const arc = arcFactory();\n\n const backgroundCircle = document.createElementNS(svgNS, \"path\");\n backgroundCircle.setAttribute(\"transform\", \"translate(50 50)\");\n backgroundCircle.setAttribute(\"fill\", colors.background);\n backgroundCircle.setAttribute(\"fill-opacity\", \"0.5\");\n backgroundCircle.setAttribute(\"d\", `${arc(arcProps)}`);\n const progressCircle = document.createElementNS(svgNS, \"path\");\n progressCircle.setAttribute(\"transform\", \"translate(50 50)\");\n progressCircle.setAttribute(\"fill\", colors.progress);\n progressCircle.setAttribute(\"fill-opacity\", \"1\");\n progressCircle.setAttribute(\n \"d\",\n `${arc({\n ...arcProps,\n endAngle: arcProps.endAngle * (progress / 100)\n })}`\n );\n\n svg.append(backgroundCircle, progressCircle);\n }\n\n // Create and append the text.\n const text = document.createElementNS(svgNS, \"text\");\n text.setAttribute(\"text-anchor\", \"middle\");\n text.setAttribute(\"alignment-baseline\", \"middle\");\n text.setAttribute(\"font-size\", \"16\");\n text.setAttribute(\"font-family\", \"arial\");\n text.setAttribute(\"font-weight\", \"bold\");\n text.setAttribute(\"fill\", colors.text);\n\n if (this.props.valueType === \"value\") {\n // Show value and unit in 1 (no unit) or 2 lines.\n if (this.props.unit && this.props.unit.length > 0) {\n const value = document.createElementNS(svgNS, \"tspan\");\n value.setAttribute(\"x\", \"0\");\n value.setAttribute(\"dy\", \"1em\");\n value.textContent = `${this.props.value}`;\n const unit = document.createElementNS(svgNS, \"tspan\");\n unit.setAttribute(\"x\", \"0\");\n unit.setAttribute(\"dy\", \"1em\");\n unit.textContent = `${this.props.unit}`;\n text.append(value, unit);\n text.setAttribute(\"transform\", \"translate(50 33)\");\n } else {\n text.textContent = `${this.props.value}`;\n text.setAttribute(\"transform\", \"translate(50 50)\");\n }\n } else {\n // Percentage.\n text.textContent = `${progress}%`;\n text.setAttribute(\"transform\", \"translate(50 50)\");\n }\n\n svg.append(text);\n }\n break;\n }\n\n element.append(svg);\n\n return element;\n }\n\n private getProgress(): number {\n const minValue = this.props.minValue || 0;\n const maxValue = this.props.maxValue || 100;\n const value = this.props.value || 100;\n\n if (value <= minValue) return 0;\n else if (value >= maxValue) return 100;\n else return ((value - minValue) / (maxValue - minValue)) * 100;\n }\n}\n","import { UnknownObject } from \"../types\";\nimport {\n stringIsEmpty,\n notEmptyStringOr,\n decodeBase64,\n parseIntOr\n} from \"../lib\";\nimport Item, { ItemType, ItemProps, itemBasePropsDecoder } from \"../Item\";\n\nexport type ServiceProps = {\n type: ItemType.SERVICE;\n serviceId: number;\n imageSrc: string | null;\n statusImageSrc: string | null;\n encodedTitle: string | null;\n} & ItemProps;\n\n/**\n * Build a valid typed object from a raw object.\n * This will allow us to ensure the type safety.\n *\n * @param data Raw object.\n * @return An object representing the service props.\n * @throws Will throw a TypeError if some property\n * is missing from the raw object or have an invalid type.\n */\nexport function servicePropsDecoder(data: UnknownObject): ServiceProps | never {\n if (data.imageSrc !== null) {\n if (\n typeof data.statusImageSrc !== \"string\" ||\n data.imageSrc.statusImageSrc === 0\n ) {\n throw new TypeError(\"invalid status image src.\");\n }\n } else {\n if (stringIsEmpty(data.encodedTitle)) {\n throw new TypeError(\"missing encode tittle content.\");\n }\n }\n\n if (parseIntOr(data.serviceId, null) === null) {\n throw new TypeError(\"invalid service id.\");\n }\n\n return {\n ...itemBasePropsDecoder(data), // Object spread. It will merge the properties of the two objects.\n type: ItemType.SERVICE,\n serviceId: data.serviceId,\n imageSrc: notEmptyStringOr(data.imageSrc, null),\n statusImageSrc: notEmptyStringOr(data.statusImageSrc, null),\n encodedTitle: notEmptyStringOr(data.encodedTitle, null)\n };\n}\n\nexport default class Service extends Item {\n public createDomElement(): HTMLElement {\n const element = document.createElement(\"div\");\n element.className = \"service\";\n\n if (this.props.statusImageSrc !== null) {\n element.style.background = `url(${this.props.statusImageSrc}) no-repeat`;\n element.style.backgroundSize = \"contain\";\n element.style.backgroundPosition = \"center\";\n } else if (this.props.encodedTitle !== null) {\n element.innerHTML = decodeBase64(this.props.encodedTitle);\n }\n\n return element;\n }\n}\n","import { UnknownObject, Size } from \"./types\";\nimport {\n parseBoolean,\n sizePropsDecoder,\n parseIntOr,\n notEmptyStringOr\n} from \"./lib\";\nimport Item, {\n ItemType,\n ItemProps,\n ItemClickEvent,\n ItemRemoveEvent\n} from \"./Item\";\nimport StaticGraph, { staticGraphPropsDecoder } from \"./items/StaticGraph\";\nimport Icon, { iconPropsDecoder } from \"./items/Icon\";\nimport ColorCloud, { colorCloudPropsDecoder } from \"./items/ColorCloud\";\nimport Group, { groupPropsDecoder } from \"./items/Group\";\nimport Clock, { clockPropsDecoder } from \"./items/Clock\";\nimport Box, { boxPropsDecoder } from \"./items/Box\";\nimport Line, { linePropsDecoder } from \"./items/Line\";\nimport Label, { labelPropsDecoder } from \"./items/Label\";\nimport SimpleValue, { simpleValuePropsDecoder } from \"./items/SimpleValue\";\nimport EventsHistory, {\n eventsHistoryPropsDecoder\n} from \"./items/EventsHistory\";\nimport Percentile, { percentilePropsDecoder } from \"./items/Percentile\";\nimport TypedEvent, { Disposable, Listener } from \"./TypedEvent\";\nimport DonutGraph, { donutGraphPropsDecoder } from \"./items/DonutGraph\";\nimport BarsGraph, { barsGraphPropsDecoder } from \"./items/BarsGraph\";\nimport ModuleGraph, { moduleGraphPropsDecoder } from \"./items/ModuleGraph\";\nimport Service, { servicePropsDecoder } from \"./items/Service\";\n\n// TODO: Document.\n// eslint-disable-next-line @typescript-eslint/explicit-function-return-type\nfunction itemInstanceFrom(data: UnknownObject) {\n const type = parseIntOr(data.type, null);\n if (type == null) throw new TypeError(\"missing item type.\");\n\n switch (type as ItemType) {\n case ItemType.STATIC_GRAPH:\n return new StaticGraph(staticGraphPropsDecoder(data));\n case ItemType.MODULE_GRAPH:\n return new ModuleGraph(moduleGraphPropsDecoder(data));\n case ItemType.SIMPLE_VALUE:\n case ItemType.SIMPLE_VALUE_MAX:\n case ItemType.SIMPLE_VALUE_MIN:\n case ItemType.SIMPLE_VALUE_AVG:\n return new SimpleValue(simpleValuePropsDecoder(data));\n case ItemType.PERCENTILE_BAR:\n case ItemType.PERCENTILE_BUBBLE:\n case ItemType.CIRCULAR_PROGRESS_BAR:\n case ItemType.CIRCULAR_INTERIOR_PROGRESS_BAR:\n return new Percentile(percentilePropsDecoder(data));\n case ItemType.LABEL:\n return new Label(labelPropsDecoder(data));\n case ItemType.ICON:\n return new Icon(iconPropsDecoder(data));\n case ItemType.SERVICE:\n return new Service(servicePropsDecoder(data));\n case ItemType.GROUP_ITEM:\n return new Group(groupPropsDecoder(data));\n case ItemType.BOX_ITEM:\n return new Box(boxPropsDecoder(data));\n case ItemType.LINE_ITEM:\n return new Line(linePropsDecoder(data));\n case ItemType.AUTO_SLA_GRAPH:\n return new EventsHistory(eventsHistoryPropsDecoder(data));\n case ItemType.DONUT_GRAPH:\n return new DonutGraph(donutGraphPropsDecoder(data));\n case ItemType.BARS_GRAPH:\n return new BarsGraph(barsGraphPropsDecoder(data));\n case ItemType.CLOCK:\n return new Clock(clockPropsDecoder(data));\n case ItemType.COLOR_CLOUD:\n return new ColorCloud(colorCloudPropsDecoder(data));\n default:\n throw new TypeError(\"item not found\");\n }\n}\n\n// TODO: Document.\n// eslint-disable-next-line @typescript-eslint/explicit-function-return-type\nfunction decodeProps(data: UnknownObject) {\n const type = parseIntOr(data.type, null);\n if (type == null) throw new TypeError(\"missing item type.\");\n\n switch (type as ItemType) {\n case ItemType.STATIC_GRAPH:\n return staticGraphPropsDecoder(data);\n case ItemType.MODULE_GRAPH:\n return moduleGraphPropsDecoder(data);\n case ItemType.SIMPLE_VALUE:\n case ItemType.SIMPLE_VALUE_MAX:\n case ItemType.SIMPLE_VALUE_MIN:\n case ItemType.SIMPLE_VALUE_AVG:\n return simpleValuePropsDecoder(data);\n case ItemType.PERCENTILE_BAR:\n case ItemType.PERCENTILE_BUBBLE:\n case ItemType.CIRCULAR_PROGRESS_BAR:\n case ItemType.CIRCULAR_INTERIOR_PROGRESS_BAR:\n return percentilePropsDecoder(data);\n case ItemType.LABEL:\n return labelPropsDecoder(data);\n case ItemType.ICON:\n return iconPropsDecoder(data);\n case ItemType.SERVICE:\n return servicePropsDecoder(data);\n case ItemType.GROUP_ITEM:\n return groupPropsDecoder(data);\n case ItemType.BOX_ITEM:\n return boxPropsDecoder(data);\n case ItemType.LINE_ITEM:\n return linePropsDecoder(data);\n case ItemType.AUTO_SLA_GRAPH:\n return eventsHistoryPropsDecoder(data);\n case ItemType.DONUT_GRAPH:\n return donutGraphPropsDecoder(data);\n case ItemType.BARS_GRAPH:\n return barsGraphPropsDecoder(data);\n case ItemType.CLOCK:\n return clockPropsDecoder(data);\n case ItemType.COLOR_CLOUD:\n return colorCloudPropsDecoder(data);\n default:\n throw new TypeError(\"decoder not found\");\n }\n}\n\n// Base properties.\nexport interface VisualConsoleProps extends Size {\n readonly id: number;\n name: string;\n groupId: number;\n backgroundURL: string | null; // URL?\n backgroundColor: string | null;\n isFavorite: boolean;\n relationLineWidth: number;\n}\n\n/**\n * Build a valid typed object from a raw object.\n * This will allow us to ensure the type safety.\n *\n * @param data Raw object.\n * @return An object representing the Visual Console props.\n * @throws Will throw a TypeError if some property\n * is missing from the raw object or have an invalid type.\n */\nexport function visualConsolePropsDecoder(\n data: UnknownObject\n): VisualConsoleProps | never {\n // Object destructuring: http://es6-features.org/#ObjectMatchingShorthandNotation\n const {\n id,\n name,\n groupId,\n backgroundURL,\n backgroundColor,\n isFavorite,\n relationLineWidth\n } = data;\n\n if (id == null || isNaN(parseInt(id))) {\n throw new TypeError(\"invalid Id.\");\n }\n if (typeof name !== \"string\" || name.length === 0) {\n throw new TypeError(\"invalid name.\");\n }\n if (groupId == null || isNaN(parseInt(groupId))) {\n throw new TypeError(\"invalid group Id.\");\n }\n\n return {\n id: parseInt(id),\n name,\n groupId: parseInt(groupId),\n backgroundURL: notEmptyStringOr(backgroundURL, null),\n backgroundColor: notEmptyStringOr(backgroundColor, null),\n isFavorite: parseBoolean(isFavorite),\n relationLineWidth: parseIntOr(relationLineWidth, 0),\n ...sizePropsDecoder(data)\n };\n}\n\nexport default class VisualConsole {\n // Reference to the DOM element which will contain the items.\n private readonly containerRef: HTMLElement;\n // Properties.\n private _props: VisualConsoleProps;\n // Visual Console Item instances by their Id.\n private elementsById: {\n [key: number]: Item;\n } = {};\n // Visual Console Item Ids.\n private elementIds: ItemProps[\"id\"][] = [];\n // Dictionary which store the created lines.\n private relations: {\n [key: string]: Line;\n } = {};\n // Event manager for click events.\n private readonly clickEventManager = new TypedEvent<\n ItemClickEvent\n >();\n // List of references to clean the event listeners.\n private readonly disposables: Disposable[] = [];\n\n /**\n * React to a click on an element.\n * @param e Event object.\n */\n private handleElementClick: (e: ItemClickEvent) => void = e => {\n this.clickEventManager.emit(e);\n // console.log(`Clicked element #${e.data.id}`, e);\n };\n\n /**\n * Clear some element references.\n * @param e Event object.\n */\n private handleElementRemove: (e: ItemRemoveEvent) => void = e => {\n // Remove the element from the list and its relations.\n this.elementIds = this.elementIds.filter(id => id !== e.data.id);\n delete this.elementsById[e.data.id];\n this.clearRelations(e.data.id);\n };\n\n public constructor(\n container: HTMLElement,\n props: UnknownObject,\n items: UnknownObject[]\n ) {\n this.containerRef = container;\n this._props = visualConsolePropsDecoder(props);\n\n // Force the first render.\n this.render();\n\n // Sort by isOnTop, id ASC\n items = items.sort(function(a, b) {\n if (\n a.isOnTop == null ||\n b.isOnTop == null ||\n a.id == null ||\n b.id == null\n ) {\n return 0;\n }\n\n if (a.isOnTop && !b.isOnTop) return 1;\n else if (!a.isOnTop && b.isOnTop) return -1;\n else if (a.id < b.id) return 1;\n else return -1;\n });\n\n // Initialize the items.\n items.forEach(item => {\n try {\n const itemInstance = itemInstanceFrom(item);\n // Add the item to the list.\n this.elementsById[itemInstance.props.id] = itemInstance;\n this.elementIds.push(itemInstance.props.id);\n // Item event handlers.\n itemInstance.onClick(this.handleElementClick);\n itemInstance.onRemove(this.handleElementRemove);\n // Add the item to the DOM.\n this.containerRef.append(itemInstance.elementRef);\n } catch (error) {\n console.log(\"Error creating a new element:\", error.message);\n }\n });\n\n // Create lines.\n this.buildRelations();\n }\n\n /**\n * Public accessor of the `elements` property.\n * @return Properties.\n */\n public get elements(): Item[] {\n // Ensure the type cause Typescript doesn't know the filter removes null items.\n return this.elementIds\n .map(id => this.elementsById[id])\n .filter(_ => _ != null) as Item[];\n }\n\n /**\n * Public setter of the `elements` property.\n * @param items.\n */\n public updateElements(items: UnknownObject[]): void {\n const itemIds = items.map(item => item.id || null).filter(id => id != null);\n itemIds as number[]; // Tell the type system to rely on us.\n // Get the elements we should delete.\n const deletedIds: number[] = this.elementIds.filter(\n id => itemIds.indexOf(id) < 0\n );\n // Delete the elements.\n deletedIds.forEach(id => {\n if (this.elementsById[id] != null) {\n this.elementsById[id].remove();\n delete this.elementsById[id];\n }\n });\n // Replace the element ids.\n this.elementIds = itemIds;\n\n // Initialize the items.\n items.forEach(item => {\n if (item.id) {\n if (this.elementsById[item.id] == null) {\n // New item.\n try {\n const itemInstance = itemInstanceFrom(item);\n // Add the item to the list.\n this.elementsById[itemInstance.props.id] = itemInstance;\n // Item event handlers.\n itemInstance.onClick(this.handleElementClick);\n itemInstance.onRemove(this.handleElementRemove);\n // Add the item to the DOM.\n this.containerRef.append(itemInstance.elementRef);\n } catch (error) {\n console.log(\"Error creating a new element:\", error.message);\n }\n } else {\n // Update item.\n try {\n this.elementsById[item.id].props = decodeProps(item);\n } catch (error) {\n console.log(\"Error updating an element:\", error.message);\n }\n }\n }\n });\n\n // Re-build relations.\n this.buildRelations();\n }\n\n /**\n * Public accessor of the `props` property.\n * @return Properties.\n */\n public get props(): VisualConsoleProps {\n return { ...this._props }; // Return a copy.\n }\n\n /**\n * Public setter of the `props` property.\n * If the new props are different enough than the\n * stored props, a render would be fired.\n * @param newProps\n */\n public set props(newProps: VisualConsoleProps) {\n const prevProps = this.props;\n // Update the internal props.\n this._props = newProps;\n\n // From this point, things which rely on this.props can access to the changes.\n\n // Re-render.\n this.render(prevProps);\n }\n\n /**\n * Recreate or update the HTMLElement which represents the Visual Console into the DOM.\n * @param prevProps If exists it will be used to only DOM updates instead of a full replace.\n */\n public render(prevProps: VisualConsoleProps | null = null): void {\n if (prevProps) {\n if (prevProps.backgroundURL !== this.props.backgroundURL) {\n this.containerRef.style.backgroundImage =\n this.props.backgroundURL !== null\n ? `url(${this.props.backgroundURL})`\n : null;\n }\n if (prevProps.backgroundColor !== this.props.backgroundColor) {\n this.containerRef.style.backgroundColor = this.props.backgroundColor;\n }\n if (this.sizeChanged(prevProps, this.props)) {\n this.resizeElement(this.props.width, this.props.height);\n }\n } else {\n this.containerRef.style.backgroundImage =\n this.props.backgroundURL !== null\n ? `url(${this.props.backgroundURL})`\n : null;\n\n this.containerRef.style.backgroundColor = this.props.backgroundColor;\n this.resizeElement(this.props.width, this.props.height);\n }\n }\n\n /**\n * Compare the previous and the new size and return\n * a boolean value in case the size changed.\n * @param prevSize\n * @param newSize\n * @return Whether the size changed or not.\n */\n public sizeChanged(prevSize: Size, newSize: Size): boolean {\n return (\n prevSize.width !== newSize.width || prevSize.height !== newSize.height\n );\n }\n\n /**\n * Resize the DOM container.\n * @param width\n * @param height\n */\n public resizeElement(width: number, height: number): void {\n this.containerRef.style.width = `${width}px`;\n this.containerRef.style.height = `${height}px`;\n }\n\n /**\n * Update the size into the properties and resize the DOM container.\n * @param width\n * @param height\n */\n public resize(width: number, height: number): void {\n this.props = {\n ...this.props, // Object spread: http://es6-features.org/#SpreadOperator\n width,\n height\n };\n }\n\n /**\n * To remove the event listeners and the elements from the DOM.\n */\n public remove(): void {\n this.disposables.forEach(d => d.dispose()); // Arrow function.\n this.elements.forEach(e => e.remove()); // Arrow function.\n this.elementsById = {};\n this.elementIds = [];\n // Clear relations.\n this.clearRelations();\n // Clean container.\n this.containerRef.innerHTML = \"\";\n }\n\n /**\n * Create line elements which connect the elements with their parents.\n */\n private buildRelations(): void {\n // Clear relations.\n this.clearRelations();\n // Add relations.\n this.elements.forEach(item => {\n if (item.props.parentId !== null) {\n const parent = this.elementsById[item.props.parentId];\n const child = this.elementsById[item.props.id];\n if (parent && child) this.addRelationLine(parent, child);\n }\n });\n }\n\n /**\n * @param itemId Optional identifier of a parent or child item.\n * Remove the line elements which connect the elements with their parents.\n */\n private clearRelations(itemId?: number): void {\n if (itemId != null) {\n for (let key in this.relations) {\n const ids = key.split(\"|\");\n const parentId = Number.parseInt(ids[0]);\n const childId = Number.parseInt(ids[1]);\n\n if (itemId === parentId || itemId === childId) {\n this.relations[key].remove();\n delete this.relations[key];\n }\n }\n } else {\n for (let key in this.relations) {\n this.relations[key].remove();\n delete this.relations[key];\n }\n }\n }\n\n /**\n * Retrieve the line element which represent the relation between items.\n * @param parentId Identifier of the parent item.\n * @param childId Itentifier of the child item.\n * @return The line element or nothing.\n */\n private getRelationLine(parentId: number, childId: number): Line | null {\n const identifier = `${parentId}|${childId}`;\n return this.relations[identifier] || null;\n }\n\n /**\n * Add a new line item to represent a relation between the items.\n * @param parent Parent item.\n * @param child Child item.\n * @return Whether the line was added or not.\n */\n private addRelationLine(\n parent: Item,\n child: Item\n ): Line {\n const identifier = `${parent.props.id}|${child.props.id}`;\n if (this.relations[identifier] != null) {\n this.relations[identifier].remove();\n }\n\n // Get the items center.\n const startX = parent.props.x + parent.elementRef.clientWidth / 2;\n const startY =\n parent.props.y +\n (parent.elementRef.clientHeight - parent.labelElementRef.clientHeight) /\n 2;\n const endX = child.props.x + child.elementRef.clientWidth / 2;\n const endY =\n child.props.y +\n (child.elementRef.clientHeight - child.labelElementRef.clientHeight) / 2;\n\n const line = new Line(\n linePropsDecoder({\n id: 0,\n type: ItemType.LINE_ITEM,\n startX,\n startY,\n endX,\n endY,\n width: 0,\n height: 0,\n lineWidth: this.props.relationLineWidth,\n color: \"#CCCCCC\"\n })\n );\n // Save a reference to the line item.\n this.relations[identifier] = line;\n\n // Add the line to the DOM.\n line.elementRef.style.zIndex = \"0\";\n this.containerRef.append(line.elementRef);\n\n return line;\n }\n\n /**\n * Add an event handler to the click of the linked visual console elements.\n * @param listener Function which is going to be executed when a linked console is clicked.\n */\n public onClick(listener: Listener>): Disposable {\n /*\n * The '.on' function returns a function which will clean the event\n * listener when executed. We store all the 'dispose' functions to\n * call them when the item should be cleared.\n */\n const disposable = this.clickEventManager.on(listener);\n this.disposables.push(disposable);\n\n return disposable;\n }\n}\n","/*\n * Useful resources.\n * http://es6-features.org/\n * http://exploringjs.com/es6\n * https://www.typescriptlang.org/\n */\n\nimport \"./main.css\"; // CSS import.\nimport VisualConsole from \"./VisualConsole\";\n\n// Export the VisualConsole class to the global object.\n\n// eslint-disable-next-line\n(window as any).VisualConsole = VisualConsole;\n"],"sourceRoot":""} \ No newline at end of file diff --git a/pandora_console/operation/visual_console/legacy_public_view.php b/pandora_console/operation/visual_console/legacy_public_view.php new file mode 100644 index 0000000000..18d8e2ca55 --- /dev/null +++ b/pandora_console/operation/visual_console/legacy_public_view.php @@ -0,0 +1,265 @@ +'."\n"; +echo ''."\n"; +echo ''; + +global $vc_public_view; +$vc_public_view = true; +// This starts the page head. In the call back function, +// things from $page['head'] array will be processed into the head +ob_start('ui_process_page_head'); +// Enterprise main +enterprise_include('index.php'); + +require_once 'include/functions_visual_map.php'; + +$hash = get_parameter('hash'); +$id_layout = (int) get_parameter('id_layout'); +$graph_javascript = (bool) get_parameter('graph_javascript'); +$config['id_user'] = get_parameter('id_user'); + +$myhash = md5($config['dbpass'].$id_layout.$config['id_user']); + +// Check input hash +if ($myhash != $hash) { + exit; +} + +$refr = (int) get_parameter('refr', 0); +$layout = db_get_row('tlayout', 'id', $id_layout); + +if (! $layout) { + db_pandora_audit('ACL Violation', 'Trying to access visual console without id layout'); + include $config['homedir'].'/general/noaccess.php'; + exit; +} + +if (!isset($config['pure'])) { + $config['pure'] = 0; +} + +// ~ $xhr = (bool) get_parameter('xhr'); +if ($layout) { + $id_group = $layout['id_group']; + $layout_name = $layout['name']; + $background = $layout['background']; + $bwidth = $layout['width']; + $bheight = $layout['height']; + // ~ $width = (int) get_parameter('width'); + // ~ if ($width <= 0) $width = null; + // ~ $height = (int) get_parameter('height'); + // ~ if ($height <= 0) $height = null; + // ~ ob_start(); + // ~ // Render map + visual_map_print_visual_map( + $id_layout, + true, + true, + $width, + $height, + '../../', + true, + true, + true + ); + // ~ return; +} else { + echo '
'; +} + +// Floating menu - Start +echo '
'; + +echo ''; + +echo '
'; +// Floating menu - End +// QR code dialog +echo ''; + +ui_require_jquery_file('countdown'); +ui_require_javascript_file('wz_jsgraphics'); +ui_require_javascript_file('pandora_visual_console'); +$ignored_params['refr'] = ''; +?> + + + + diff --git a/pandora_console/operation/visual_console/legacy_view.php b/pandora_console/operation/visual_console/legacy_view.php new file mode 100644 index 0000000000..f5eec24096 --- /dev/null +++ b/pandora_console/operation/visual_console/legacy_view.php @@ -0,0 +1,386 @@ +'.html_print_image( + 'images/visual_console.png', + true, + ['title' => __('Visual consoles list')] +).''; + +if ($vconsole_write || $vconsole_manage) { + $url_base = 'index.php?sec=network&sec2=godmode/reporting/visual_console_builder&action='; + + $hash = md5($config['dbpass'].$id_layout.$config['id_user']); + + $options['public_link']['text'] = ''.html_print_image( + 'images/camera_mc.png', + true, + ['title' => __('Show link to public Visual Console')] + ).''; + $options['public_link']['active'] = false; + + $options['data']['text'] = ''.html_print_image( + 'images/op_reporting.png', + true, + ['title' => __('Main data')] + ).''; + $options['list_elements']['text'] = ''.html_print_image( + 'images/list.png', + true, + ['title' => __('List elements')] + ).''; + + if (enterprise_installed()) { + $options['wizard_services']['text'] = ''.html_print_image( + 'images/wand_services.png', + true, + ['title' => __('Services wizard')] + ).''; + } + + $options['wizard']['text'] = ''.html_print_image( + 'images/wand.png', + true, + ['title' => __('Wizard')] + ).''; + $options['editor']['text'] = ''.html_print_image( + 'images/builder.png', + true, + ['title' => __('Builder')] + ).''; +} + +$options['view']['text'] = ''.html_print_image('images/operation.png', true, ['title' => __('View')]).''; +$options['view']['active'] = true; + +if (!is_metaconsole()) { + if (!$config['pure']) { + $options['pure']['text'] = ''.html_print_image('images/full_screen.png', true, ['title' => __('Full screen mode')]).''; + ui_print_page_header($layout_name, 'images/visual_console.png', false, '', false, $options); + } + + // Set the hidden value for the javascript + html_print_input_hidden('metaconsole', 0); +} else { + // Set the hidden value for the javascript + html_print_input_hidden('metaconsole', 1); +} + +if ($config['pure']) { + // Container of the visual map (ajax loaded) + echo '
'.visual_map_print_visual_map( + $id_layout, + true, + true, + null, + null, + '', + false, + true + ).'
'; + + // Floating menu - Start + echo '
'; + + echo ''; + + echo '
'; + // Floating menu - End + ui_require_jquery_file('countdown'); + + ?> + + + + + + \ No newline at end of file diff --git a/pandora_console/operation/visual_console/public_console.php b/pandora_console/operation/visual_console/public_console.php index bc5b3d2a20..8a6ff462fc 100755 --- a/pandora_console/operation/visual_console/public_console.php +++ b/pandora_console/operation/visual_console/public_console.php @@ -15,245 +15,9 @@ // The session is configured and started inside the config process. require_once '../../include/config.php'; -// Set root on homedir, as defined in setup -chdir($config['homedir']); - -ob_start(); -// Enterprise support -if (file_exists(ENTERPRISE_DIR.'/load_enterprise.php')) { - include_once ENTERPRISE_DIR.'/load_enterprise.php'; -} - -if (file_exists(ENTERPRISE_DIR.'/include/functions_login.php')) { - include_once ENTERPRISE_DIR.'/include/functions_login.php'; -} - -echo ''."\n"; -echo ''."\n"; -echo ''; - -global $vc_public_view; -$vc_public_view = true; -// This starts the page head. In the call back function, -// things from $page['head'] array will be processed into the head -ob_start('ui_process_page_head'); -// Enterprise main -enterprise_include('index.php'); - -require_once 'include/functions_visual_map.php'; - -$hash = get_parameter('hash'); -$id_layout = (int) get_parameter('id_layout'); -$graph_javascript = (bool) get_parameter('graph_javascript'); -$config['id_user'] = get_parameter('id_user'); - -$myhash = md5($config['dbpass'].$id_layout.$config['id_user']); - -// Check input hash -if ($myhash != $hash) { - exit; -} - -$refr = (int) get_parameter('refr', 0); -$layout = db_get_row('tlayout', 'id', $id_layout); - -if (! $layout) { - db_pandora_audit('ACL Violation', 'Trying to access visual console without id layout'); - include $config['homedir'].'/general/noaccess.php'; - exit; -} - -if (!isset($config['pure'])) { - $config['pure'] = 0; -} - -// ~ $xhr = (bool) get_parameter('xhr'); -if ($layout) { - $id_group = $layout['id_group']; - $layout_name = $layout['name']; - $background = $layout['background']; - $bwidth = $layout['width']; - $bheight = $layout['height']; - // ~ $width = (int) get_parameter('width'); - // ~ if ($width <= 0) $width = null; - // ~ $height = (int) get_parameter('height'); - // ~ if ($height <= 0) $height = null; - // ~ ob_start(); - // ~ // Render map - visual_map_print_visual_map( - $id_layout, - true, - true, - $width, - $height, - '../../', - true, - true, - true - ); - // ~ return; +$legacy = (bool) get_parameter('legacy', $config['legacy_vc']); +if ($legacy === false) { + include_once $config['homedir'].'/operation/visual_console/public_view.php'; } else { - echo '
'; + include_once $config['homedir'].'/operation/visual_console/legacy_public_view.php'; } - -// Floating menu - Start -echo '
'; - -echo ''; - -echo '
'; -// Floating menu - End -// QR code dialog -echo ''; - -ui_require_jquery_file('countdown'); -ui_require_javascript_file('wz_jsgraphics'); -ui_require_javascript_file('pandora_visual_console'); -$ignored_params['refr'] = ''; -?> - - diff --git a/pandora_console/operation/visual_console/public_view.php b/pandora_console/operation/visual_console/public_view.php new file mode 100644 index 0000000000..dce6cb4bdb --- /dev/null +++ b/pandora_console/operation/visual_console/public_view.php @@ -0,0 +1,193 @@ +'."\n"; +echo ''."\n"; +echo ''; + +global $vc_public_view; +$vc_public_view = true; +$config['public_view'] = true; + +// This starts the page head. In the call back function, +// things from $page['head'] array will be processed into the head. +ob_start('ui_process_page_head'); +// Enterprise main. +enterprise_include('index.php'); + +require_once 'include/functions_visual_map.php'; + +$hash = (string) get_parameter('hash'); +$visualConsoleId = (int) get_parameter('id_layout'); +$config['id_user'] = (string) get_parameter('id_user'); +$refr = (int) get_parameter('refr', $config['refr']); + +if (!isset($config['pure'])) { + $config['pure'] = 0; +} + +$myhash = md5($config['dbpass'].$visualConsoleId.$config['id_user']); + +// Check input hash. +if ($myhash != $hash) { + exit; +} + +// Load Visual Console. +use Models\VisualConsole\Container as VisualConsole; +$visualConsole = null; +try { + $visualConsole = VisualConsole::fromDB(['id' => $visualConsoleId]); +} catch (Throwable $e) { + db_pandora_audit( + 'ACL Violation', + 'Trying to access visual console without Id' + ); + include $config['homedir'].'/general/noaccess.php'; + exit; +} + +$visualConsoleData = $visualConsole->toArray(); +$visualConsoleName = $visualConsoleData['name']; + +echo '
'; + +// Floating menu - Start. +echo '
'; + +echo ''; + +echo '
'; + +// QR code dialog. +echo ''; + +// Check groups can access user. +$aclUserGroups = []; +if (!users_can_manage_group_all('AR')) { + $aclUserGroups = array_keys(users_get_groups(false, 'AR')); +} + +ui_require_javascript_file('pandora_visual_console'); +include_javascript_d3(); +visual_map_load_client_resources(); + +// Load Visual Console Items. +$visualConsoleItems = VisualConsole::getItemsFromDB( + $visualConsoleId, + $aclUserGroups +); + +?> + + + + diff --git a/pandora_console/operation/visual_console/render_view.php b/pandora_console/operation/visual_console/render_view.php index 4799932e2c..00cdc2d113 100755 --- a/pandora_console/operation/visual_console/render_view.php +++ b/pandora_console/operation/visual_console/render_view.php @@ -13,368 +13,9 @@ // GNU General Public License for more details. global $config; -// Login check -require_once $config['homedir'].'/include/functions_visual_map.php'; - -check_login(); - -if (!defined('METACONSOLE')) { - $id_layout = (int) get_parameter('id'); +$legacy = (bool) get_parameter('legacy', $config['legacy_vc']); +if ($legacy === false) { + include_once $config['homedir'].'/operation/visual_console/view.php'; } else { - $id_layout = (int) get_parameter('id_visualmap'); + include_once $config['homedir'].'/operation/visual_console/legacy_view.php'; } - -if ($id_layout) { - $default_action = 'edit'; -} else { - $default_action = 'new'; -} - -if (!defined('METACONSOLE')) { - $action = get_parameterBetweenListValues( - 'action', - [ - 'new', - 'save', - 'edit', - 'update', - 'delete', - ], - $default_action - ); -} else { - $action = get_parameterBetweenListValues( - 'action2', - [ - 'new', - 'save', - 'edit', - 'update', - 'delete', - ], - $default_action - ); -} - -$refr = (int) get_parameter('refr', $config['vc_refr']); -$graph_javascript = (bool) get_parameter('graph_javascript', true); -$vc_refr = false; - -if (isset($config['vc_refr']) and $config['vc_refr'] != 0) { - $view_refresh = $config['vc_refr']; -} else { - $view_refresh = '300'; -} - -// Get input parameter for layout id -if (! $id_layout) { - db_pandora_audit( - 'ACL Violation', - 'Trying to access visual console without id layout' - ); - include 'general/noaccess.php'; - exit; -} - -$layout = db_get_row('tlayout', 'id', $id_layout); - -if (! $layout) { - db_pandora_audit( - 'ACL Violation', - 'Trying to access visual console without id layout' - ); - include 'general/noaccess.php'; - exit; -} - -$id_group = $layout['id_group']; -$layout_name = $layout['name']; -$background = $layout['background']; -$bwidth = $layout['width']; -$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'); - -if (! $vconsole_read && !$vconsole_write && !$vconsole_manage) { - db_pandora_audit( - 'ACL Violation', - 'Trying to access visual console without group access' - ); - include 'general/noaccess.php'; - exit; -} - -// Render map -$options = []; - -$options['consoles_list']['text'] = ''.html_print_image( - 'images/visual_console.png', - true, - ['title' => __('Visual consoles list')] -).''; - -if ($vconsole_write || $vconsole_manage) { - $url_base = 'index.php?sec=network&sec2=godmode/reporting/visual_console_builder&action='; - - $hash = md5($config['dbpass'].$id_layout.$config['id_user']); - - $options['public_link']['text'] = ''.html_print_image( - 'images/camera_mc.png', - true, - ['title' => __('Show link to public Visual Console')] - ).''; - $options['public_link']['active'] = false; - - $options['data']['text'] = ''.html_print_image( - 'images/op_reporting.png', - true, - ['title' => __('Main data')] - ).''; - $options['list_elements']['text'] = ''.html_print_image( - 'images/list.png', - true, - ['title' => __('List elements')] - ).''; - - if (enterprise_installed()) { - $options['wizard_services']['text'] = ''.html_print_image( - 'images/wand_services.png', - true, - ['title' => __('Services wizard')] - ).''; - } - - $options['wizard']['text'] = ''.html_print_image( - 'images/wand.png', - true, - ['title' => __('Wizard')] - ).''; - $options['editor']['text'] = ''.html_print_image( - 'images/builder.png', - true, - ['title' => __('Builder')] - ).''; -} - -$options['view']['text'] = ''.html_print_image('images/operation.png', true, ['title' => __('View')]).''; -$options['view']['active'] = true; - -if (!is_metaconsole()) { - if (!$config['pure']) { - $options['pure']['text'] = ''.html_print_image('images/full_screen.png', true, ['title' => __('Full screen mode')]).''; - ui_print_page_header($layout_name, 'images/visual_console.png', false, '', false, $options); - } - - // Set the hidden value for the javascript - html_print_input_hidden('metaconsole', 0); -} else { - // Set the hidden value for the javascript - html_print_input_hidden('metaconsole', 1); -} - -if ($config['pure']) { - // Container of the visual map (ajax loaded) - echo '
'.visual_map_print_visual_map( - $id_layout, - true, - true, - null, - null, - '', - false, - true - ).'
'; - - // Floating menu - Start - echo '
'; - - echo ''; - - echo '
'; - // Floating menu - End - ui_require_jquery_file('countdown'); - - ?> - - - - \ No newline at end of file diff --git a/pandora_console/operation/visual_console/view.php b/pandora_console/operation/visual_console/view.php new file mode 100644 index 0000000000..4baa52e948 --- /dev/null +++ b/pandora_console/operation/visual_console/view.php @@ -0,0 +1,301 @@ + $visualConsoleId]); +} catch (Throwable $e) { + db_pandora_audit( + 'ACL Violation', + 'Trying to access visual console without Id' + ); + include 'general/noaccess.php'; + exit; +} + +$visualConsoleData = $visualConsole->toArray(); +$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'); + +if (!$aclRead && !$aclWrite && !$aclManage) { + db_pandora_audit( + 'ACL Violation', + 'Trying to access visual console without group access' + ); + include 'general/noaccess.php'; + exit; +} + +// Render map. +$options = []; + +$options['consoles_list']['text'] = ''.html_print_image( + 'images/visual_console.png', + true, + ['title' => __('Visual consoles list')] +).''; + +if ($aclWrite || $aclManage) { + $action = get_parameterBetweenListValues( + is_metaconsole() ? 'action2' : 'action', + [ + 'new', + 'save', + 'edit', + 'update', + 'delete', + ], + 'edit' + ); + + $baseUrl = 'index.php?sec=network&sec2=godmode/reporting/visual_console_builder&action='.$action; + + $hash = md5($config['dbpass'].$visualConsoleId.$config['id_user']); + + $options['public_link']['text'] = ''.html_print_image( + 'images/camera_mc.png', + true, + ['title' => __('Show link to public Visual Console')] + ).''; + $options['public_link']['active'] = false; + + $options['data']['text'] = ''.html_print_image( + 'images/op_reporting.png', + true, + ['title' => __('Main data')] + ).''; + $options['list_elements']['text'] = ''.html_print_image( + 'images/list.png', + true, + ['title' => __('List elements')] + ).''; + + if (enterprise_installed()) { + $options['wizard_services']['text'] = ''.html_print_image( + 'images/wand_services.png', + true, + ['title' => __('Services wizard')] + ).''; + } + + $options['wizard']['text'] = ''.html_print_image( + 'images/wand.png', + true, + ['title' => __('Wizard')] + ).''; + $options['editor']['text'] = ''.html_print_image( + 'images/builder.png', + true, + ['title' => __('Builder')] + ).''; +} + +$options['view']['text'] = ''.html_print_image( + 'images/operation.png', + true, + ['title' => __('View')] +).''; +$options['view']['active'] = true; + +if (!is_metaconsole()) { + if (!$config['pure']) { + $options['pure']['text'] = ''.html_print_image( + 'images/full_screen.png', + true, + ['title' => __('Full screen mode')] + ).''; + ui_print_page_header( + $visualConsoleName, + 'images/visual_console.png', + false, + '', + false, + $options + ); + } + + // Set the hidden value for the javascript. + html_print_input_hidden('metaconsole', 0); +} else { + // Set the hidden value for the javascript. + html_print_input_hidden('metaconsole', 1); +} + +echo '
'; + +if ($pure === true) { + // Floating menu - Start. + echo '
'; + + echo ''; + + echo '
'; + // Floating menu - End. + ?> + + + + diff --git a/pandora_console/tests/Functional/Models/VisualConsole/ContainerTest.php b/pandora_console/tests/Functional/Models/VisualConsole/ContainerTest.php new file mode 100644 index 0000000000..b7a1a7c6de --- /dev/null +++ b/pandora_console/tests/Functional/Models/VisualConsole/ContainerTest.php @@ -0,0 +1,268 @@ +assertInstanceOf( + VisualConsole::class, + VisualConsole::fromArray( + [ + 'id' => 1, + 'name' => 'foo', + 'groupId' => 1, + 'backgroundURL' => 'aaa', + 'backgroundColor' => 'bbb', + 'width' => 800, + 'height' => 800, + ] + ) + ); + + $this->assertInstanceOf( + VisualConsole::class, + VisualConsole::fromArray( + [ + 'id' => 69, + 'name' => 'New visual console', + 'groupId' => 0, + 'background' => 'globalmap.jpg', + 'background_color' => 'white', + 'is_favourite' => 1, + 'width' => 100, + 'height' => 200, + ] + ) + ); + + $this->assertInstanceOf( + VisualConsole::class, + VisualConsole::fromArray( + [ + 'id' => 1030, + 'name' => 'console2', + 'groupId' => 12, + 'width' => 1024, + 'height' => 768, + ] + ) + ); + } + + + /** + * Test if the instance is not created when using a invalid id. + * + * @return void + */ + public function testCannotBeCreatedWithInvalidId(): void + { + $this->expectException(InvalidArgumentException::class); + // Invalid id. + VisualConsole::fromArray( + [ + 'id' => 'bar', + 'name' => 'foo', + 'groupId' => 0, + 'is_favourite' => 1, + 'width' => 1024, + 'height' => 768, + ] + ); + // Missing id. + VisualConsole::fromArray( + [ + 'name' => 'foo', + 'groupId' => 0, + 'width' => 1024, + 'height' => 768, + ] + ); + } + + + /** + * Test if the instance is not created when using a invalid name. + * + * @return void + */ + public function testCannotBeCreatedWithInvalidName(): void + { + $this->expectException(InvalidArgumentException::class); + // Empty name. + VisualConsole::fromArray( + [ + 'id' => 1, + 'name' => '', + 'groupId' => 0, + 'width' => 1024, + 'height' => 768, + ] + ); + // Missing name. + VisualConsole::fromArray( + [ + 'id' => 1, + 'groupId' => 8, + 'width' => 1024, + 'height' => 768, + ] + ); + } + + + /** + * Test if the instance is not created when using a invalid group id. + * + * @return void + */ + public function testCannotBeCreatedWithInvalidGroupId(): void + { + $this->expectException(InvalidArgumentException::class); + // Invalid group id. + VisualConsole::fromArray( + [ + 'id' => 1, + 'name' => 'test', + 'groupId' => 'Hi', + 'width' => 1024, + 'height' => 768, + ] + ); + + // Missing group id. + VisualConsole::fromArray( + [ + 'id' => 1, + 'name' => 'test', + 'width' => 1024, + 'height' => 768, + ] + ); + } + + + /** + * Test if the instance is not created when using a invalid width. + * + * @return void + */ + public function testCannotBeCreatedWithInvalidWidth(): void + { + $this->expectException(InvalidArgumentException::class); + // Invalid width. + VisualConsole::fromArray( + [ + 'id' => 1, + 'name' => 'test', + 'groupId' => 10, + 'width' => 0, + 'height' => 768, + ] + ); + + // Missing width. + VisualConsole::fromArray( + [ + 'id' => 1, + 'name' => 'test', + 'groupId' => 10, + 'height' => 768, + ] + ); + } + + + /** + * Test if the instance is not created when using a invalid height. + * + * @return void + */ + public function testCannotBeCreatedWithInvalidHeigth(): void + { + $this->expectException(InvalidArgumentException::class); + // Invalid height. + VisualConsole::fromArray( + [ + 'id' => 1, + 'name' => 'test', + 'groupId' => 10, + 'width' => 1024, + 'height' => -1, + ] + ); + + // Missing height. + VisualConsole::fromArray( + [ + 'id' => 1, + 'name' => 'test', + 'groupId' => 10, + 'width' => 1024, + ] + ); + } + + + /** + * Test if the model has a valid JSON representation. + * + * @return void + */ + public function testContainerIsRepresentedAsJson(): void + { + $this->assertEquals( + '{"backgroundColor":null,"backgroundURL":null,"groupId":0,"height":768,"id":1,"isFavorite":false,"name":"foo","width":1024}', + (string) VisualConsole::fromArray( + [ + 'id' => 1, + 'name' => 'foo', + 'groupId' => 0, + 'width' => 1024, + 'height' => 768, + ] + ) + ); + } + + + /** + * Test if the item's instance is returned properly. + * + * @return void + */ + public function testItemClassIsReturned(): void + { + $this->assertEquals( + VisualConsole::getItemClass(STATIC_GRAPH), + Models\VisualConsole\Items\StaticGraph::class + ); + + $this->assertEquals( + VisualConsole::getItemClass(COLOR_CLOUD), + Models\VisualConsole\Items\ColorCloud::class + ); + + $this->assertEquals( + VisualConsole::getItemClass(LABEL), + Models\VisualConsole\Items\Label::class + ); + } + + +} diff --git a/pandora_console/tests/Functional/Models/VisualConsole/ItemTest.php b/pandora_console/tests/Functional/Models/VisualConsole/ItemTest.php new file mode 100644 index 0000000000..b4b5dea59b --- /dev/null +++ b/pandora_console/tests/Functional/Models/VisualConsole/ItemTest.php @@ -0,0 +1,298 @@ +assertInstanceOf( + ItemConsole::class, + ItemConsole::fromArray( + [ + 'id' => 1, + 'type' => 5, + 'label' => 'test', + 'labelPosition' => 'down', + 'isLinkEnabled' => false, + 'isOnTop' => true, + 'parentId' => 0, + 'aclGroupId' => 12, + 'width' => 800, + 'height' => 600, + 'x' => 0, + 'y' => 0, + ] + ) + ); + + $this->assertInstanceOf( + ItemConsole::class, + ItemConsole::fromArray( + [ + 'id' => 1, + 'type' => 5, + 'width' => 0, + 'height' => 0, + ] + ) + ); + } + + + /** + * Test if the instance is not created when using a invalid id. + * + * @return void + */ + public function testCannotBeCreatedWithInvalidId(): void + { + $this->expectException(InvalidArgumentException::class); + // Invalid id. + ItemConsole::fromArray( + [ + 'id' => 'foo', + 'type' => 5, + 'label' => 'test', + 'labelPosition' => 'down', + 'isLinkEnabled' => false, + 'isOnTop' => true, + 'parentId' => 0, + 'aclGroupId' => 12, + 'width' => 800, + 'height' => 600, + 'x' => 0, + 'y' => 0, + ] + ); + // Missing id. + ItemConsole::fromArray( + [ + 'type' => 5, + 'label' => 'test', + 'labelPosition' => 'down', + 'isLinkEnabled' => false, + 'isOnTop' => true, + 'parentId' => 0, + 'aclGroupId' => 12, + 'width' => 800, + 'height' => 600, + 'x' => 0, + 'y' => 0, + ] + ); + } + + + /** + * Test if the instance is not created when using a invalid type. + * + * @return void + */ + public function testCannotBeCreatedWithInvalidType(): void + { + $this->expectException(InvalidArgumentException::class); + // Invalid id. + ItemConsole::fromArray( + [ + 'id' => 15, + 'type' => 'clock', + 'label' => 'test', + 'labelPosition' => 'down', + 'isLinkEnabled' => false, + 'isOnTop' => true, + 'parentId' => 0, + 'aclGroupId' => 12, + 'width' => 800, + 'height' => 600, + 'x' => 0, + 'y' => 0, + ] + ); + // Missing id. + ItemConsole::fromArray( + [ + 'id' => 6, + 'label' => 'test', + 'labelPosition' => 'down', + 'isLinkEnabled' => false, + 'isOnTop' => true, + 'parentId' => 0, + 'aclGroupId' => 12, + 'width' => 800, + 'height' => 600, + 'x' => 0, + 'y' => 0, + ] + ); + } + + + /** + * Test if the instance is not created when using a invalid width. + * + * @return void + */ + public function testCannotBeCreatedWithInvalidWidth(): void + { + $this->expectException(InvalidArgumentException::class); + // Invalid id. + ItemConsole::fromArray( + [ + 'id' => 15, + 'type' => 3, + 'label' => 'test', + 'labelPosition' => 'down', + 'isLinkEnabled' => false, + 'isOnTop' => true, + 'parentId' => 0, + 'aclGroupId' => 12, + 'width' => -1, + 'height' => 600, + 'x' => 0, + 'y' => 0, + ] + ); + // Missing id. + ItemConsole::fromArray( + [ + 'id' => 15, + 'type' => 3, + 'label' => 'test', + 'labelPosition' => 'down', + 'isLinkEnabled' => false, + 'isOnTop' => true, + 'parentId' => 0, + 'aclGroupId' => 12, + 'height' => 600, + 'x' => 0, + 'y' => 0, + ] + ); + } + + + /** + * Test if the instance is not created when using a invalid height. + * + * @return void + */ + public function testCannotBeCreatedWithInvalidHeight(): void + { + $this->expectException(InvalidArgumentException::class); + // Invalid id. + ItemConsole::fromArray( + [ + 'id' => 15, + 'type' => 3, + 'label' => 'test', + 'labelPosition' => 'down', + 'isLinkEnabled' => false, + 'isOnTop' => true, + 'parentId' => 0, + 'aclGroupId' => 12, + 'width' => 800, + 'height' => -1, + 'x' => 0, + 'y' => 0, + ] + ); + // Missing id. + ItemConsole::fromArray( + [ + 'id' => 15, + 'type' => 3, + 'label' => 'test', + 'labelPosition' => 'down', + 'isLinkEnabled' => false, + 'isOnTop' => true, + 'parentId' => 0, + 'aclGroupId' => 12, + 'width' => 600, + 'x' => 0, + 'y' => 0, + ] + ); + } + + + /** + * Test if the model has a valid JSON representation. + * + * @return void + */ + public function testItemIsRepresentedAsJson(): void + { + $this->assertEquals( + '{"aclGroupId":12,"height":600,"id":15,"isLinkEnabled":false,"isOnTop":true,"label":"test","labelPosition":"down","parentId":0,"type":3,"width":800,"x":0,"y":0}', + (string) ItemConsole::fromArray( + [ + 'id' => 15, + 'type' => 3, + 'label' => 'test', + 'labelPosition' => 'down', + 'isLinkEnabled' => false, + 'isOnTop' => true, + 'parentId' => 0, + 'aclGroupId' => 12, + 'width' => 800, + 'height' => 600, + 'x' => 0, + 'y' => 0, + ] + ) + ); + + $this->assertEquals( + '{"aclGroupId":12,"height":600,"id":15,"isLinkEnabled":false,"isOnTop":false,"label":null,"labelPosition":"down","parentId":0,"type":3,"width":800,"x":0,"y":0}', + (string) ItemConsole::fromArray( + [ + 'id' => 15, + 'type' => 3, + 'label' => '', + 'labelPosition' => 'test', + 'parentId' => 0, + 'aclGroupId' => 12, + 'width' => 800, + 'height' => 600, + 'x' => 0, + 'y' => 0, + ] + ) + ); + + $this->assertEquals( + '{"aclGroupId":null,"height":0,"id":69,"isLinkEnabled":true,"isOnTop":false,"label":null,"labelPosition":"up","parentId":null,"type":20,"width":0,"x":-666,"y":76}', + (string) ItemConsole::fromArray( + [ + 'id' => 69, + 'type' => 20, + 'label' => null, + 'labelPosition' => 'up', + 'isLinkEnabled' => true, + 'isOnTop' => false, + 'parentId' => null, + 'width' => '0', + 'height' => '0', + 'x' => -666, + 'y' => 76, + ] + ) + ); + } + + +} diff --git a/pandora_console/tests/Functional/Models/VisualConsole/Items/BarsGraphTest.php b/pandora_console/tests/Functional/Models/VisualConsole/Items/BarsGraphTest.php new file mode 100644 index 0000000000..16866a4fc6 --- /dev/null +++ b/pandora_console/tests/Functional/Models/VisualConsole/Items/BarsGraphTest.php @@ -0,0 +1,111 @@ +assertInstanceOf( + BarsGraph::class, + BarsGraph::fromArray( + [ + 'id' => 7, + 'type' => BARS_GRAPH, + 'width' => '600', + 'height' => '500', + 'typeGraph' => 'horizontal', + 'backgroundColor' => 'white', + 'gridColor' => '#33CCFF', + 'encodedHtml' => '

Foo

', + ] + ) + ); + + $this->assertInstanceOf( + BarsGraph::class, + BarsGraph::fromArray( + [ + 'id' => 23, + 'type' => BARS_GRAPH, + 'width' => '800', + 'height' => '600', + 'type_graph' => 'vertical', + 'image' => 'transparent', + 'border_color' => '#33CCFF', + 'html' => '

Foo

', + ] + ) + ); + } + + + /** + * Test if the model has a valid JSON representation. + * + * @return void + */ + public function testContainerIsRepresentedAsJson(): void + { + $this->assertEquals( + '{"aclGroupId":null,"agentId":null,"agentName":null,"backgroundColor":"transparent","encodedHtml":"PGgxPkZvbzwvaDE+","gridColor":"#33CCFF","height":0,"id":7,"isLinkEnabled":true,"isOnTop":false,"label":null,"labelPosition":"up","moduleId":null,"moduleName":null,"parentId":null,"type":18,"typeGraph":"vertical","width":0,"x":-666,"y":76}', + (string) BarsGraph::fromArray( + [ + 'id' => 7, + 'type' => DONUT_GRAPH, + 'label' => null, + 'labelPosition' => 'up', + 'isLinkEnabled' => true, + 'isOnTop' => false, + 'parentId' => null, + 'width' => '0', + 'height' => '0', + 'x' => -666, + 'y' => 76, + 'type_graph' => 'vertical', + 'image' => 'transparent', + 'border_color' => '#33CCFF', + 'html' => '

Foo

', + ] + ) + ); + + $this->assertEquals( + '{"aclGroupId":null,"agentId":null,"agentName":null,"backgroundColor":"white","encodedHtml":"PGgxPkZvbzwvaDE+","gridColor":"#33CCFF","height":300,"id":7,"isLinkEnabled":true,"isOnTop":false,"label":"test","labelPosition":"left","moduleId":null,"moduleName":null,"parentId":null,"type":18,"typeGraph":"horizontal","width":300,"x":-666,"y":76}', + (string) BarsGraph::fromArray( + [ + 'id' => 7, + 'type' => DONUT_GRAPH, + 'label' => 'test', + 'labelPosition' => 'left', + 'isLinkEnabled' => true, + 'isOnTop' => false, + 'parentId' => null, + 'width' => '300', + 'height' => '300', + 'x' => -666, + 'y' => 76, + 'typeGraph' => 'horizontal', + 'backgroundColor' => 'white', + 'gridColor' => '#33CCFF', + 'encodedHtml' => 'PGgxPkZvbzwvaDE+', + ] + ) + ); + } + + +} diff --git a/pandora_console/tests/Functional/Models/VisualConsole/Items/BoxTest.php b/pandora_console/tests/Functional/Models/VisualConsole/Items/BoxTest.php new file mode 100644 index 0000000000..9d2e0b2f4d --- /dev/null +++ b/pandora_console/tests/Functional/Models/VisualConsole/Items/BoxTest.php @@ -0,0 +1,84 @@ +assertInstanceOf( + Box::class, + Box::fromArray( + [ + 'id' => 69, + 'type' => 12, + 'label' => null, + 'labelPosition' => 'up', + 'isLinkEnabled' => true, + 'isOnTop' => false, + 'parentId' => null, + 'width' => '0', + 'height' => '0', + 'x' => -666, + 'y' => 76, + ] + ) + ); + + $this->assertInstanceOf( + Box::class, + Box::fromArray( + [ + 'id' => 1000, + 'type' => 8, + 'name' => 'test', + 'width' => 100, + 'height' => 900, + ] + ) + ); + } + + + /** + * Test if the model has a valid JSON representation. + * + * @return void + */ + public function testContainerIsRepresentedAsJson(): void + { + $this->assertEquals( + '{"aclGroupId":null,"borderColor":null,"borderWidth":0,"fillColor":null,"height":0,"id":7,"isLinkEnabled":true,"isOnTop":false,"label":null,"labelPosition":"up","parentId":null,"type":12,"width":0,"x":-666,"y":76}', + (string) Box::fromArray( + [ + 'id' => 7, + 'type' => 10, + 'label' => null, + 'labelPosition' => 'up', + 'isLinkEnabled' => true, + 'isOnTop' => false, + 'parentId' => null, + 'width' => '0', + 'height' => '0', + 'x' => -666, + 'y' => 76, + ] + ) + ); + } + + +} diff --git a/pandora_console/tests/Functional/Models/VisualConsole/Items/ClockTest.php b/pandora_console/tests/Functional/Models/VisualConsole/Items/ClockTest.php new file mode 100644 index 0000000000..269bb7bef5 --- /dev/null +++ b/pandora_console/tests/Functional/Models/VisualConsole/Items/ClockTest.php @@ -0,0 +1,173 @@ +assertInstanceOf( + Clock::class, + Clock::fromArray( + [ + 'id' => 69, + 'type' => CLOCK, + 'label' => null, + 'labelPosition' => 'up', + 'isLinkEnabled' => true, + 'isOnTop' => false, + 'parentId' => null, + 'width' => '0', + 'height' => '0', + 'x' => -666, + 'y' => 76, + 'clockType' => 'digital', + 'clockFormat' => 'time', + 'clockTimezone' => 'Europe/Madrid', + 'showClockTimezone' => false, + 'color' => 'white', + ] + ) + ); + + $this->assertInstanceOf( + Clock::class, + Clock::fromArray( + [ + 'id' => 1000, + 'type' => CLOCK, + 'width' => 100, + 'height' => 900, + 'clockType' => 'analogic', + 'clockFormat' => 'datetime', + 'clockTimezone' => 'Asia/Tokyo', + 'showClockTimezone' => true, + 'color' => 'red', + ] + ) + ); + } + + + /** + * Test if the instance is not created when using a invalid clockTimezone. + * + * @return void + */ + public function testCannotBeCreatedWithInvalidClockTimezone(): void + { + $this->expectException(Exception::class); + // Invalid clockTimezone. + Clock::fromArray( + [ + 'id' => 69, + 'type' => CLOCK, + 'label' => null, + 'labelPosition' => 'up', + 'isLinkEnabled' => true, + 'isOnTop' => false, + 'parentId' => null, + 'width' => '0', + 'height' => '0', + 'x' => -666, + 'y' => 76, + 'clockType' => 'digital', + 'clockFormat' => 'time', + 'clockTimezone' => 'Europe/Tokyo', + 'showClockTimezone' => false, + 'color' => 'white', + ] + ); + + // Invalid clockTimezone. + Clock::fromArray( + [ + 'id' => 69, + 'type' => CLOCK, + 'label' => null, + 'labelPosition' => 'up', + 'isLinkEnabled' => true, + 'isOnTop' => false, + 'parentId' => null, + 'width' => '0', + 'height' => '0', + 'x' => -666, + 'y' => 76, + 'clockType' => 'digital', + 'clockFormat' => 'time', + 'clockTimezone' => 'Europe/Tokyo', + 'showClockTimezone' => false, + 'color' => 'white', + ] + ); + + // Missing clockTimezone. + Clock::fromArray( + [ + 'id' => 69, + 'type' => CLOCK, + 'label' => null, + 'labelPosition' => 'up', + 'isLinkEnabled' => true, + 'isOnTop' => false, + 'parentId' => null, + 'width' => '0', + 'height' => '0', + 'x' => -666, + 'y' => 76, + 'clockType' => 'digital', + 'clockFormat' => 'time', + 'showClockTimezone' => false, + 'color' => 'white', + ] + ); + } + + + /** + * Test if the model has a valid JSON representation. + * + * @return void + */ + public function testContainerIsRepresentedAsJson(): void + { + $this->assertEquals( + '{"aclGroupId":null,"clockFormat":"time","clockTimezone":"Europe\/Madrid","clockTimezoneOffset":7200,"clockType":"digital","color":"white","height":0,"id":69,"isLinkEnabled":true,"isOnTop":false,"label":null,"labelPosition":"up","linkedLayoutAgentId":null,"linkedLayoutId":null,"linkedLayoutStatusType":"default","parentId":null,"showClockTimezone":false,"type":19,"width":0,"x":-666,"y":76}', + (string) Clock::fromArray( + [ + 'id' => 69, + 'type' => CLOCK, + 'label' => null, + 'labelPosition' => 'up', + 'isLinkEnabled' => true, + 'isOnTop' => false, + 'parentId' => null, + 'width' => '0', + 'height' => '0', + 'x' => -666, + 'y' => 76, + 'clockType' => 'digital', + 'clockFormat' => 'time', + 'clockTimezone' => 'Europe/Madrid', + 'showClockTimezone' => false, + 'color' => 'white', + ] + ) + ); + } + + +} diff --git a/pandora_console/tests/Functional/Models/VisualConsole/Items/ColorCloudTest.php b/pandora_console/tests/Functional/Models/VisualConsole/Items/ColorCloudTest.php new file mode 100644 index 0000000000..3dbb8abe9a --- /dev/null +++ b/pandora_console/tests/Functional/Models/VisualConsole/Items/ColorCloudTest.php @@ -0,0 +1,253 @@ +assertInstanceOf( + ColorCloud::class, + ColorCloud::fromArray( + [ + 'id' => 345, + 'type' => COLOR_CLOUD, + 'label' => null, + 'isLinkEnabled' => true, + 'isOnTop' => false, + 'parentId' => null, + 'width' => '0', + 'height' => '0', + 'x' => -666, + 'y' => 76, + 'defaultColor' => '#FFF', + 'colorRanges' => [], + ] + ) + ); + + $this->assertInstanceOf( + ColorCloud::class, + ColorCloud::fromArray( + [ + 'id' => 1000, + 'type' => COLOR_CLOUD, + 'width' => 100, + 'height' => 900, + 'label' => 'eyJkZWZhdWx0X2NvbG9yIjoiI0ZGRiIsImNvbG9yX3JhbmdlcyI6W3siY29sb3IiOiIjMDAwIiwiZnJvbV92YWx1ZSI6MTAuMDUsInRvX3ZhbHVlIjoxMDAuMH1dfQ==', + 'colorRanges' => [ + [ + 'color' => '#000', + 'fromValue' => 10.05, + 'toValue' => 100.0, + ], + ], + 'color' => '#000', + ] + ) + ); + + $this->assertInstanceOf( + ColorCloud::class, + ColorCloud::fromArray( + [ + 'id' => 1000, + 'type' => COLOR_CLOUD, + 'width' => 100, + 'height' => 900, + 'label' => 'eyJkZWZhdWx0X2NvbG9yIjoiI0ZGRiIsImNvbG9yX3JhbmdlcyI6W3siY29sb3IiOiIjMDAwIiwiZnJvbV92YWx1ZSI6MTAuMDUsInRvX3ZhbHVlIjoxMDAuMH1dfQ==', + ] + ) + ); + } + + + /** + * Test if the model has a valid JSON representation. + * + * @return void + */ + public function testContainerIsRepresentedAsJson(): void + { + $this->assertEquals( + '{"aclGroupId":null,"agentId":null,"agentName":null,"color":"#000","colorRanges":[{"color":"#000","fromValue":10.05,"toValue":100}],"defaultColor":"#FFF","height":0,"id":7,"isLinkEnabled":true,"isOnTop":false,"label":null,"labelPosition":"up","linkedLayoutAgentId":null,"linkedLayoutId":null,"linkedLayoutStatusType":"default","moduleId":null,"moduleName":null,"parentId":null,"type":20,"width":0,"x":-666,"y":76}', + (string) ColorCloud::fromArray( + [ + 'id' => 7, + 'type' => COLOR_CLOUD, + 'label' => null, + 'labelPosition' => 'up', + 'isLinkEnabled' => true, + 'isOnTop' => false, + 'parentId' => null, + 'width' => '0', + 'height' => '0', + 'x' => -666, + 'y' => 76, + 'defaultColor' => '#FFF', + 'colorRanges' => [ + [ + 'color' => '#000', + 'fromValue' => 10.05, + 'toValue' => 100.0, + ], + ], + 'color' => '#000', + ] + ) + ); + + $this->assertEquals( + '{"aclGroupId":null,"agentId":null,"agentName":null,"color":null,"colorRanges":[],"defaultColor":"#FFF","height":0,"id":7,"isLinkEnabled":true,"isOnTop":false,"label":null,"labelPosition":"up","linkedLayoutAgentId":null,"linkedLayoutId":null,"linkedLayoutStatusType":"default","moduleId":null,"moduleName":null,"parentId":null,"type":20,"width":0,"x":-666,"y":76}', + (string) ColorCloud::fromArray( + [ + 'id' => 7, + 'type' => COLOR_CLOUD, + 'label' => null, + 'labelPosition' => 'up', + 'isLinkEnabled' => true, + 'isOnTop' => false, + 'parentId' => null, + 'width' => '0', + 'height' => '0', + 'x' => -666, + 'y' => 76, + 'defaultColor' => '#FFF', + 'colorRanges' => [], + ] + ) + ); + + $this->assertEquals( + '{"aclGroupId":null,"agentId":null,"agentName":null,"color":"#000","colorRanges":[{"color":"#000","fromValue":10.05,"toValue":100}],"defaultColor":"#FFF","height":0,"id":7,"isLinkEnabled":true,"isOnTop":false,"label":null,"labelPosition":"up","linkedLayoutAgentId":3,"linkedLayoutId":2,"linkedLayoutStatusType":"default","metaconsoleId":5,"moduleId":null,"moduleName":null,"parentId":null,"type":20,"width":0,"x":-666,"y":76}', + (string) ColorCloud::fromArray( + [ + 'id' => 7, + 'type' => COLOR_CLOUD, + 'label' => 'eyJkZWZhdWx0X2NvbG9yIjoiI0ZGRiIsImNvbG9yX3JhbmdlcyI6W3siY29sb3IiOiIjMDAwIiwiZnJvbV92YWx1ZSI6MTAuMDUsInRvX3ZhbHVlIjoxMDAuMH1dfQ==', + 'labelPosition' => 'up', + 'isLinkEnabled' => true, + 'isOnTop' => false, + 'parentId' => null, + 'width' => '0', + 'height' => '0', + 'x' => -666, + 'y' => 76, + 'color' => '#000', + 'id_metaconsole' => 5, + 'linked_layout_node_id' => 3, + 'linkedLayoutId' => 2, + ] + ) + ); + + $this->assertEquals( + '{"aclGroupId":null,"agentId":null,"agentName":null,"color":"#000","colorRanges":[{"color":"#000","fromValue":10.05,"toValue":100}],"defaultColor":"#FFF","height":0,"id":7,"isLinkEnabled":true,"isOnTop":false,"label":null,"labelPosition":"up","linkedLayoutAgentId":null,"linkedLayoutId":1,"linkedLayoutStatusType":"default","moduleId":null,"moduleName":null,"parentId":null,"type":20,"width":0,"x":-666,"y":76}', + (string) ColorCloud::fromArray( + [ + 'id' => 7, + 'type' => COLOR_CLOUD, + 'label' => 'eyJkZWZhdWx0X2NvbG9yIjoiI0ZGRiIsImNvbG9yX3JhbmdlcyI6W3siY29sb3IiOiIjMDAwIiwiZnJvbV92YWx1ZSI6MTAuMDUsInRvX3ZhbHVlIjoxMDAuMH1dfQ==', + 'labelPosition' => 'up', + 'isLinkEnabled' => true, + 'isOnTop' => false, + 'parentId' => null, + 'width' => '0', + 'height' => '0', + 'x' => -666, + 'y' => 76, + 'defaultColor' => '#FFF', + 'color' => '#000', + 'id_layout_linked' => 1, + ] + ) + ); + + $this->assertEquals( + '{"aclGroupId":null,"agentId":null,"agentName":null,"color":"#000","colorRanges":[{"color":"#000","fromValue":10.05,"toValue":100}],"defaultColor":"#FFF","height":0,"id":7,"isLinkEnabled":true,"isOnTop":false,"label":null,"labelPosition":"up","linkedLayoutAgentId":null,"linkedLayoutId":2,"linkedLayoutStatusType":"service","linkedLayoutStatusTypeCriticalThreshold":80,"linkedLayoutStatusTypeWarningThreshold":50,"moduleId":null,"moduleName":null,"parentId":null,"type":20,"width":0,"x":-666,"y":76}', + (string) ColorCloud::fromArray( + [ + 'id' => 7, + 'type' => COLOR_CLOUD, + 'label' => 'eyJkZWZhdWx0X2NvbG9yIjoiI0ZGRiIsImNvbG9yX3JhbmdlcyI6W3siY29sb3IiOiIjMDAwIiwiZnJvbV92YWx1ZSI6MTAuMDUsInRvX3ZhbHVlIjoxMDAuMH1dfQ==', + 'labelPosition' => 'up', + 'isLinkEnabled' => true, + 'isOnTop' => false, + 'parentId' => null, + 'width' => '0', + 'height' => '0', + 'x' => -666, + 'y' => 76, + 'colorRanges' => [ + [ + 'color' => '#000', + 'fromValue' => 10.05, + 'toValue' => 100.0, + ], + ], + 'color' => '#000', + 'linkedLayoutId' => 2, + 'linked_layout_status_type' => 'service', + 'linkedLayoutStatusTypeWarningThreshold' => 50, + 'linked_layout_status_as_service_critical' => 80, + ] + ) + ); + } + + + /** + * Test if the instance is not created when using a invalid dynamic data. + * + * @return void + */ + public function testCannotBeCreatedWithInvalidDynamicData(): void + { + $this->expectException(InvalidArgumentException::class); + // Invalid dynamic data. + ColorCloud::fromArray( + [ + 'id' => 3, + 'type' => COLOR_CLOUD, + 'label' => null, + 'isLinkEnabled' => true, + 'isOnTop' => false, + 'parentId' => null, + 'width' => '330', + 'height' => '0', + 'x' => 511, + 'y' => 76, + ] + ); + // Missing dynamic data. + ColorCloud::fromArray( + [ + 'id' => 3, + 'type' => COLOR_CLOUD, + 'label' => null, + 'isLinkEnabled' => true, + 'isOnTop' => false, + 'parentId' => null, + 'width' => '330', + 'height' => '0', + 'x' => 511, + 'y' => 76, + ] + ); + } + + +} diff --git a/pandora_console/tests/Functional/Models/VisualConsole/Items/DonutGraphTest.php b/pandora_console/tests/Functional/Models/VisualConsole/Items/DonutGraphTest.php new file mode 100644 index 0000000000..e9c0367151 --- /dev/null +++ b/pandora_console/tests/Functional/Models/VisualConsole/Items/DonutGraphTest.php @@ -0,0 +1,103 @@ +assertInstanceOf( + DonutGraph::class, + DonutGraph::fromArray( + [ + 'id' => 3, + 'type' => DONUT_GRAPH, + 'width' => '600', + 'height' => '500', + 'legendBackgroundColor' => '#33CCFF', + 'html' => '

Foo

', + ] + ) + ); + + $this->assertInstanceOf( + DonutGraph::class, + DonutGraph::fromArray( + [ + 'id' => 14, + 'type' => DONUT_GRAPH, + 'width' => '600', + 'height' => '500', + 'border_color' => '#000000', + 'encodedHtml' => 'PGgxPkZvbzwvaDE+', + ] + ) + ); + } + + + /** + * Test if the model has a valid JSON representation. + * + * @return void + */ + public function testContainerIsRepresentedAsJson(): void + { + $this->assertEquals( + '{"aclGroupId":null,"agentId":null,"agentName":null,"encodedHtml":"PGgxPkZvbzwvaDE+","height":0,"id":7,"isLinkEnabled":true,"isOnTop":false,"label":null,"labelPosition":"up","legendBackgroundColor":"#33CCFF","linkedLayoutAgentId":null,"linkedLayoutId":null,"linkedLayoutStatusType":"default","moduleId":null,"moduleName":null,"parentId":null,"type":17,"width":0,"x":-666,"y":76}', + (string) DonutGraph::fromArray( + [ + 'id' => 7, + 'type' => DONUT_GRAPH, + 'label' => null, + 'labelPosition' => 'up', + 'isLinkEnabled' => true, + 'isOnTop' => false, + 'parentId' => null, + 'width' => '0', + 'height' => '0', + 'x' => -666, + 'y' => 76, + 'border_color' => '#33CCFF', + 'html' => '

Foo

', + ] + ) + ); + + $this->assertEquals( + '{"aclGroupId":null,"agentId":null,"agentName":null,"encodedHtml":"PGgxPkZvbzwvaDE+","height":0,"id":7,"isLinkEnabled":true,"isOnTop":false,"label":null,"labelPosition":"left","legendBackgroundColor":"#000000","linkedLayoutAgentId":null,"linkedLayoutId":null,"linkedLayoutStatusType":"default","moduleId":null,"moduleName":null,"parentId":null,"type":17,"width":0,"x":-666,"y":76}', + (string) DonutGraph::fromArray( + [ + 'id' => 7, + 'type' => DONUT_GRAPH, + 'label' => null, + 'labelPosition' => 'left', + 'isLinkEnabled' => true, + 'isOnTop' => false, + 'parentId' => null, + 'width' => '0', + 'height' => '0', + 'x' => -666, + 'y' => 76, + 'legendBackgroundColor' => '#000000', + 'encodedHtml' => 'PGgxPkZvbzwvaDE+', + ] + ) + ); + } + + +} diff --git a/pandora_console/tests/Functional/Models/VisualConsole/Items/EventsHistoryTest.php b/pandora_console/tests/Functional/Models/VisualConsole/Items/EventsHistoryTest.php new file mode 100644 index 0000000000..3bd8e65949 --- /dev/null +++ b/pandora_console/tests/Functional/Models/VisualConsole/Items/EventsHistoryTest.php @@ -0,0 +1,176 @@ +assertInstanceOf( + EventsHistory::class, + EventsHistory::fromArray( + [ + 'id' => 3, + 'type' => AUTO_SLA_GRAPH, + 'width' => '600', + 'height' => '500', + 'maxTime' => null, + 'html' => '

Foo

', + ] + ) + ); + + $this->assertInstanceOf( + EventsHistory::class, + EventsHistory::fromArray( + [ + 'id' => 14, + 'type' => AUTO_SLA_GRAPH, + 'width' => '600', + 'height' => '500', + 'maxTime' => 12800, + 'encodedHtml' => 'PGgxPkZvbzwvaDE+', + ] + ) + ); + } + + + /** + * Test if the model has a valid JSON representation. + * + * @return void + */ + public function testContainerIsRepresentedAsJson(): void + { + $this->assertEquals( + '{"aclGroupId":null,"agentId":null,"agentName":null,"encodedHtml":"PGgxPkZvbzwvaDE+","height":0,"id":7,"isLinkEnabled":true,"isOnTop":false,"label":null,"labelPosition":"up","linkedLayoutAgentId":null,"linkedLayoutId":null,"linkedLayoutStatusType":"default","maxTime":null,"moduleId":null,"moduleName":null,"parentId":null,"type":14,"width":0,"x":-666,"y":76}', + (string) EventsHistory::fromArray( + [ + 'id' => 7, + 'type' => AUTO_SLA_GRAPH, + 'label' => null, + 'labelPosition' => 'up', + 'isLinkEnabled' => true, + 'isOnTop' => false, + 'parentId' => null, + 'width' => '0', + 'height' => '0', + 'x' => -666, + 'y' => 76, + 'maxTime' => null, + 'html' => '

Foo

', + ] + ) + ); + + $this->assertEquals( + '{"aclGroupId":null,"agentId":null,"agentName":null,"encodedHtml":"PGgxPkZvbzwvaDE+","height":0,"id":7,"isLinkEnabled":true,"isOnTop":false,"label":null,"labelPosition":"up","linkedLayoutAgentId":null,"linkedLayoutId":null,"linkedLayoutStatusType":"default","maxTime":12800,"moduleId":null,"moduleName":null,"parentId":null,"type":14,"width":0,"x":-666,"y":76}', + (string) EventsHistory::fromArray( + [ + 'id' => 7, + 'type' => AUTO_SLA_GRAPH, + 'label' => null, + 'labelPosition' => 'up', + 'isLinkEnabled' => true, + 'isOnTop' => false, + 'parentId' => null, + 'width' => '0', + 'height' => '0', + 'x' => -666, + 'y' => 76, + 'maxTime' => 12800, + 'encodedHtml' => 'PGgxPkZvbzwvaDE+', + ] + ) + ); + + $this->assertEquals( + '{"aclGroupId":null,"agentId":null,"agentName":null,"encodedHtml":"PGgxPkZvbzwvaDE+","height":0,"id":7,"isLinkEnabled":true,"isOnTop":false,"label":null,"labelPosition":"up","linkedLayoutAgentId":null,"linkedLayoutId":1,"linkedLayoutStatusType":"default","maxTime":null,"moduleId":null,"moduleName":null,"parentId":null,"type":14,"width":0,"x":-666,"y":76}', + (string) EventsHistory::fromArray( + [ + 'id' => 7, + 'type' => AUTO_SLA_GRAPH, + 'label' => null, + 'labelPosition' => 'up', + 'isLinkEnabled' => true, + 'isOnTop' => false, + 'parentId' => null, + 'width' => '0', + 'height' => '0', + 'x' => -666, + 'y' => 76, + 'maxTime' => null, + 'encodedHtml' => 'PGgxPkZvbzwvaDE+', + 'id_layout_linked' => 1, + ] + ) + ); + + $this->assertEquals( + '{"aclGroupId":null,"agentId":null,"agentName":null,"encodedHtml":"PGgxPkZvbzwvaDE+","height":0,"id":7,"isLinkEnabled":true,"isOnTop":false,"label":null,"labelPosition":"up","linkedLayoutAgentId":3,"linkedLayoutId":2,"linkedLayoutStatusType":"default","maxTime":12800,"metaconsoleId":5,"moduleId":null,"moduleName":null,"parentId":null,"type":14,"width":0,"x":-666,"y":76}', + (string) EventsHistory::fromArray( + [ + 'id' => 7, + 'type' => AUTO_SLA_GRAPH, + 'label' => null, + 'labelPosition' => 'up', + 'isLinkEnabled' => true, + 'isOnTop' => false, + 'parentId' => null, + 'width' => '0', + 'height' => '0', + 'x' => -666, + 'y' => 76, + 'maxTime' => 12800, + 'encodedHtml' => 'PGgxPkZvbzwvaDE+', + 'id_metaconsole' => 5, + 'linked_layout_node_id' => 3, + 'linkedLayoutId' => 2, + ] + ) + ); + + $this->assertEquals( + '{"aclGroupId":null,"agentId":21,"agentName":null,"encodedHtml":"PGgxPkZvbzwvaDE+","height":0,"id":7,"isLinkEnabled":true,"isOnTop":false,"label":null,"labelPosition":"up","linkedLayoutAgentId":15,"linkedLayoutId":3,"linkedLayoutStatusType":"default","maxTime":12800,"metaconsoleId":2,"moduleId":385,"moduleName":"module_test","parentId":null,"type":14,"width":0,"x":-666,"y":76}', + (string) EventsHistory::fromArray( + [ + 'id' => 7, + 'type' => AUTO_SLA_GRAPH, + 'label' => null, + 'labelPosition' => 'up', + 'isLinkEnabled' => true, + 'isOnTop' => false, + 'parentId' => null, + 'width' => '0', + 'height' => '0', + 'x' => -666, + 'y' => 76, + 'maxTime' => 12800, + 'encodedHtml' => 'PGgxPkZvbzwvaDE+', + 'id_metaconsole' => 2, + 'linked_layout_node_id' => 15, + 'linkedLayoutId' => 3, + 'agentId' => 21, + 'moduleId' => 385, + 'moduleName' => 'module_test', + ] + ) + ); + } + + +} diff --git a/pandora_console/tests/Functional/Models/VisualConsole/Items/GroupTest.php b/pandora_console/tests/Functional/Models/VisualConsole/Items/GroupTest.php new file mode 100644 index 0000000000..00e3a90620 --- /dev/null +++ b/pandora_console/tests/Functional/Models/VisualConsole/Items/GroupTest.php @@ -0,0 +1,279 @@ +assertInstanceOf( + Group::class, + Group::fromArray( + [ + 'id' => 13, + 'type' => GROUP_ITEM, + 'width' => '600', + 'height' => '500', + 'imageSrc' => 'image.jpg', + 'groupId' => 12, + 'statusImageSrc' => 'image.bad.jpg', + ] + ) + ); + + $this->assertInstanceOf( + Group::class, + Group::fromArray( + [ + 'id' => 1004, + 'type' => GROUP_ITEM, + 'width' => '600', + 'height' => '500', + 'image' => 'test_image.png', + 'id_group' => 0, + 'statusImageSrc' => 'test_image.bad.jpg', + ] + ) + ); + } + + + /** + * Test if the instance is not created when using a invalid image src. + * + * @return void + */ + public function testCannotBeCreatedWithInvalidImageSrc(): void + { + $this->expectException(InvalidArgumentException::class); + // Invalid imageSrc. + Group::fromArray( + [ + 'id' => 7, + 'type' => GROUP_ITEM, + 'label' => null, + 'labelPosition' => 'up', + 'isLinkEnabled' => true, + 'isOnTop' => false, + 'parentId' => null, + 'width' => '0', + 'height' => '0', + 'x' => -666, + 'y' => 76, + 'imageSrc' => '', + 'groupId' => 0, + ] + ); + // Missing imageSrc. + Group::fromArray( + [ + 'id' => 7, + 'type' => GROUP_ITEM, + 'label' => null, + 'labelPosition' => 'up', + 'isLinkEnabled' => true, + 'isOnTop' => false, + 'parentId' => null, + 'width' => '0', + 'height' => '0', + 'x' => -666, + 'y' => 76, + 'id_group' => 11, + ] + ); + } + + + /** + * Test if the instance is not created when using a invalid group Id. + * + * @return void + */ + public function testCannotBeCreatedWithInvalidGroupId(): void + { + $this->expectException(InvalidArgumentException::class); + // Invalid groupId. + Group::fromArray( + [ + 'id' => 7, + 'type' => GROUP_ITEM, + 'label' => null, + 'labelPosition' => 'up', + 'isLinkEnabled' => true, + 'isOnTop' => false, + 'parentId' => null, + 'width' => '0', + 'height' => '0', + 'x' => -666, + 'y' => 76, + 'imageSrc' => 'test.jpg', + 'groupId' => 'bar', + 'statusImageSrc' => 'image.bad.jpg', + ] + ); + // Missing groupId. + Group::fromArray( + [ + 'id' => 7, + 'type' => GROUP_ITEM, + 'label' => null, + 'labelPosition' => 'up', + 'isLinkEnabled' => true, + 'isOnTop' => false, + 'parentId' => null, + 'width' => '0', + 'height' => '0', + 'x' => -666, + 'y' => 76, + 'imageSrc' => 'test.jpg', + 'statusImageSrc' => 'image.bad.jpg', + ] + ); + } + + + /** + * Test if the model has a valid JSON representation. + * + * @return void + */ + public function testContainerIsRepresentedAsJson(): void + { + $this->assertEquals( + '{"aclGroupId":null,"groupId":12,"height":0,"id":7,"imageSrc":"image.jpg","isLinkEnabled":true,"isOnTop":false,"label":null,"labelPosition":"up","linkedLayoutAgentId":null,"linkedLayoutId":null,"linkedLayoutStatusType":"default","parentId":null,"statusImageSrc":"image.bad.jpg","type":11,"width":0,"x":-666,"y":76}', + (string) Group::fromArray( + [ + 'id' => 7, + 'type' => GROUP_ITEM, + 'label' => null, + 'labelPosition' => 'up', + 'isLinkEnabled' => true, + 'isOnTop' => false, + 'parentId' => null, + 'width' => '0', + 'height' => '0', + 'x' => -666, + 'y' => 76, + 'imageSrc' => 'image.jpg', + 'groupId' => 12, + 'statusImageSrc' => 'image.bad.jpg', + ] + ) + ); + + // With a linked layout. + $this->assertEquals( + '{"aclGroupId":null,"groupId":12,"height":0,"id":7,"imageSrc":"image.jpg","isLinkEnabled":true,"isOnTop":false,"label":null,"labelPosition":"up","linkedLayoutAgentId":null,"linkedLayoutId":1,"linkedLayoutStatusType":"default","parentId":null,"statusImageSrc":"image.bad.jpg","type":11,"width":0,"x":-666,"y":76}', + (string) Group::fromArray( + [ + 'id' => 7, + 'type' => GROUP_ITEM, + 'label' => null, + 'labelPosition' => 'up', + 'isLinkEnabled' => true, + 'isOnTop' => false, + 'parentId' => null, + 'width' => '0', + 'height' => '0', + 'x' => -666, + 'y' => 76, + 'imageSrc' => 'image.jpg', + 'groupId' => 12, + 'statusImageSrc' => 'image.bad.jpg', + 'id_layout_linked' => 1, + ] + ) + ); + + $this->assertEquals( + '{"aclGroupId":null,"groupId":12,"height":0,"id":7,"imageSrc":"image.jpg","isLinkEnabled":true,"isOnTop":false,"label":null,"labelPosition":"up","linkedLayoutAgentId":3,"linkedLayoutId":2,"linkedLayoutStatusType":"default","metaconsoleId":5,"parentId":null,"statusImageSrc":"image.bad.jpg","type":11,"width":0,"x":-666,"y":76}', + (string) Group::fromArray( + [ + 'id' => 7, + 'type' => GROUP_ITEM, + 'label' => null, + 'labelPosition' => 'up', + 'isLinkEnabled' => true, + 'isOnTop' => false, + 'parentId' => null, + 'width' => '0', + 'height' => '0', + 'x' => -666, + 'y' => 76, + 'imageSrc' => 'image.jpg', + 'groupId' => 12, + 'statusImageSrc' => 'image.bad.jpg', + 'id_metaconsole' => 5, + 'linked_layout_node_id' => 3, + 'linkedLayoutId' => 2, + ] + ) + ); + $this->assertEquals( + '{"aclGroupId":null,"groupId":12,"height":0,"id":7,"imageSrc":"image.jpg","isLinkEnabled":true,"isOnTop":false,"label":null,"labelPosition":"up","linkedLayoutAgentId":3,"linkedLayoutId":2,"linkedLayoutStatusType":"weight","linkedLayoutStatusTypeWeight":80,"metaconsoleId":5,"parentId":null,"statusImageSrc":"image.bad.jpg","type":11,"width":0,"x":-666,"y":76}', + (string) Group::fromArray( + [ + 'id' => 7, + 'type' => GROUP_ITEM, + 'label' => null, + 'labelPosition' => 'up', + 'isLinkEnabled' => true, + 'isOnTop' => false, + 'parentId' => null, + 'width' => '0', + 'height' => '0', + 'x' => -666, + 'y' => 76, + 'imageSrc' => 'image.jpg', + 'groupId' => 12, + 'statusImageSrc' => 'image.bad.jpg', + 'id_metaconsole' => 5, + 'linked_layout_node_id' => 3, + 'linkedLayoutId' => 2, + 'linkedLayoutStatusType' => 'weight', + 'linkedLayoutStatusTypeWeight' => 80, + ] + ) + ); + $this->assertEquals( + '{"aclGroupId":null,"groupId":12,"height":0,"id":7,"imageSrc":"image.jpg","isLinkEnabled":true,"isOnTop":false,"label":null,"labelPosition":"up","linkedLayoutAgentId":null,"linkedLayoutId":2,"linkedLayoutStatusType":"service","linkedLayoutStatusTypeCriticalThreshold":80,"linkedLayoutStatusTypeWarningThreshold":50,"parentId":null,"statusImageSrc":"image.bad.jpg","type":11,"width":0,"x":-666,"y":76}', + (string) Group::fromArray( + [ + 'id' => 7, + 'type' => GROUP_ITEM, + 'label' => null, + 'labelPosition' => 'up', + 'isLinkEnabled' => true, + 'isOnTop' => false, + 'parentId' => null, + 'width' => '0', + 'height' => '0', + 'x' => -666, + 'y' => 76, + 'imageSrc' => 'image.jpg', + 'groupId' => 12, + 'statusImageSrc' => 'image.bad.jpg', + 'linkedLayoutId' => 2, + 'linked_layout_status_type' => 'service', + 'linkedLayoutStatusTypeWarningThreshold' => 50, + 'linked_layout_status_as_service_critical' => 80, + ] + ) + ); + } + + +} diff --git a/pandora_console/tests/Functional/Models/VisualConsole/Items/IconTest.php b/pandora_console/tests/Functional/Models/VisualConsole/Items/IconTest.php new file mode 100644 index 0000000000..44fa077959 --- /dev/null +++ b/pandora_console/tests/Functional/Models/VisualConsole/Items/IconTest.php @@ -0,0 +1,207 @@ +assertInstanceOf( + Icon::class, + Icon::fromArray( + [ + 'id' => 69, + 'type' => ICON, + 'width' => '0', + 'height' => '0', + 'imageSrc' => 'image.jpg', + ] + ) + ); + } + + + /** + * Test if the instance is not created when using a invalid image src. + * + * @return void + */ + public function testCannotBeCreatedWithInvalidImageSrc(): void + { + $this->expectException(InvalidArgumentException::class); + // Invalid imageSrc. + Icon::fromArray( + [ + 'id' => 7, + 'type' => ICON, + 'label' => null, + 'labelPosition' => 'up', + 'isLinkEnabled' => true, + 'isOnTop' => false, + 'parentId' => null, + 'width' => '0', + 'height' => '0', + 'x' => -666, + 'y' => 76, + 'imageSrc' => '', + ] + ); + // Missing imageSrc. + Icon::fromArray( + [ + 'id' => 7, + 'type' => ICON, + 'label' => null, + 'labelPosition' => 'up', + 'isLinkEnabled' => true, + 'isOnTop' => false, + 'parentId' => null, + 'width' => '0', + 'height' => '0', + 'x' => -666, + 'y' => 76, + ] + ); + } + + + /** + * Test if the model has a valid JSON representation. + * + * @return void + */ + public function testContainerIsRepresentedAsJson(): void + { + $this->assertEquals( + '{"aclGroupId":null,"height":0,"id":7,"imageSrc":"image.jpg","isLinkEnabled":true,"isOnTop":false,"label":null,"labelPosition":"up","linkedLayoutAgentId":null,"linkedLayoutId":null,"linkedLayoutStatusType":"default","parentId":null,"type":5,"width":0,"x":-666,"y":76}', + (string) Icon::fromArray( + [ + 'id' => 7, + 'type' => ICON, + 'label' => null, + 'labelPosition' => 'up', + 'isLinkEnabled' => true, + 'isOnTop' => false, + 'parentId' => null, + 'width' => '0', + 'height' => '0', + 'x' => -666, + 'y' => 76, + 'imageSrc' => 'image.jpg', + ] + ) + ); + + // With a linked layout. + $this->assertEquals( + '{"aclGroupId":null,"height":0,"id":7,"imageSrc":"image.jpg","isLinkEnabled":true,"isOnTop":false,"label":null,"labelPosition":"up","linkedLayoutAgentId":null,"linkedLayoutId":1,"linkedLayoutStatusType":"default","parentId":null,"type":5,"width":0,"x":-666,"y":76}', + (string) Icon::fromArray( + [ + 'id' => 7, + 'type' => ICON, + 'label' => null, + 'labelPosition' => 'up', + 'isLinkEnabled' => true, + 'isOnTop' => false, + 'parentId' => null, + 'width' => '0', + 'height' => '0', + 'x' => -666, + 'y' => 76, + 'imageSrc' => 'image.jpg', + 'id_layout_linked' => 1, + ] + ) + ); + + $this->assertEquals( + '{"aclGroupId":null,"height":0,"id":7,"imageSrc":"image.jpg","isLinkEnabled":true,"isOnTop":false,"label":null,"labelPosition":"up","linkedLayoutAgentId":3,"linkedLayoutId":2,"linkedLayoutStatusType":"default","metaconsoleId":5,"parentId":null,"type":5,"width":0,"x":-666,"y":76}', + (string) Icon::fromArray( + [ + 'id' => 7, + 'type' => ICON, + 'label' => null, + 'labelPosition' => 'up', + 'isLinkEnabled' => true, + 'isOnTop' => false, + 'parentId' => null, + 'width' => '0', + 'height' => '0', + 'x' => -666, + 'y' => 76, + 'imageSrc' => 'image.jpg', + 'id_metaconsole' => 5, + 'linked_layout_node_id' => 3, + 'linkedLayoutId' => 2, + ] + ) + ); + + $this->assertEquals( + '{"aclGroupId":null,"height":0,"id":7,"imageSrc":"image.jpg","isLinkEnabled":true,"isOnTop":false,"label":null,"labelPosition":"up","linkedLayoutAgentId":3,"linkedLayoutId":2,"linkedLayoutStatusType":"weight","linkedLayoutStatusTypeWeight":80,"metaconsoleId":5,"parentId":null,"type":5,"width":0,"x":-666,"y":76}', + (string) Icon::fromArray( + [ + 'id' => 7, + 'type' => ICON, + 'label' => null, + 'labelPosition' => 'up', + 'isLinkEnabled' => true, + 'isOnTop' => false, + 'parentId' => null, + 'width' => '0', + 'height' => '0', + 'x' => -666, + 'y' => 76, + 'imageSrc' => 'image.jpg', + 'id_metaconsole' => 5, + 'linked_layout_node_id' => 3, + 'linkedLayoutId' => 2, + 'linkedLayoutStatusType' => 'weight', + 'linkedLayoutStatusTypeWeight' => 80, + ] + ) + ); + + $this->assertEquals( + '{"aclGroupId":null,"height":0,"id":7,"imageSrc":"image.jpg","isLinkEnabled":true,"isOnTop":false,"label":null,"labelPosition":"up","linkedLayoutAgentId":3,"linkedLayoutId":2,"linkedLayoutStatusType":"service","linkedLayoutStatusTypeCriticalThreshold":80,"linkedLayoutStatusTypeWarningThreshold":50,"metaconsoleId":5,"parentId":null,"type":5,"width":0,"x":-666,"y":76}', + (string) Icon::fromArray( + [ + 'id' => 7, + 'type' => ICON, + 'label' => null, + 'labelPosition' => 'up', + 'isLinkEnabled' => true, + 'isOnTop' => false, + 'parentId' => null, + 'width' => '0', + 'height' => '0', + 'x' => -666, + 'y' => 76, + 'imageSrc' => 'image.jpg', + 'id_metaconsole' => 5, + 'linked_layout_node_id' => 3, + 'linkedLayoutId' => 2, + 'linked_layout_status_type' => 'service', + 'linkedLayoutStatusTypeWarningThreshold' => 50, + 'linked_layout_status_as_service_critical' => 80, + ] + ) + ); + + } + + +} diff --git a/pandora_console/tests/Functional/Models/VisualConsole/Items/LabelTest.php b/pandora_console/tests/Functional/Models/VisualConsole/Items/LabelTest.php new file mode 100644 index 0000000000..2c1fd963cd --- /dev/null +++ b/pandora_console/tests/Functional/Models/VisualConsole/Items/LabelTest.php @@ -0,0 +1,129 @@ +assertInstanceOf( + Label::class, + Label::fromArray( + [ + 'id' => 69, + 'type' => LABEL, + 'width' => '0', + 'height' => '0', + 'label' => 'Label', + ] + ) + ); + } + + + /** + * Test if the instance is not created when using a invalid label. + * + * @return void + */ + public function testCannotBeCreatedWithInvalidLabel(): void + { + $this->expectException(InvalidArgumentException::class); + // Missing label. + Label::fromArray( + [ + 'id' => 7, + 'type' => LABEL, + 'labelPosition' => 'up', + 'isLinkEnabled' => true, + 'isOnTop' => false, + 'parentId' => null, + 'width' => '0', + 'height' => '0', + 'x' => -666, + 'y' => 76, + ] + ); + // Empty label. + Label::fromArray( + [ + 'id' => 7, + 'type' => LABEL, + 'label' => '', + 'labelPosition' => 'up', + 'isLinkEnabled' => true, + 'isOnTop' => false, + 'parentId' => null, + 'width' => '0', + 'height' => '0', + 'x' => -666, + 'y' => 76, + ] + ); + } + + + /** + * Test if the model has a valid JSON representation. + * + * @return void + */ + public function testContainerIsRepresentedAsJson(): void + { + $this->assertEquals( + '{"aclGroupId":null,"height":0,"id":7,"isLinkEnabled":true,"isOnTop":false,"label":"Label","labelPosition":"up","linkedLayoutAgentId":null,"linkedLayoutId":null,"linkedLayoutStatusType":"default","parentId":null,"type":4,"width":0,"x":-666,"y":76}', + (string) Label::fromArray( + [ + 'id' => 7, + 'type' => LABEL, + 'label' => 'Label', + 'labelPosition' => 'up', + 'isLinkEnabled' => true, + 'isOnTop' => false, + 'parentId' => null, + 'width' => '0', + 'height' => '0', + 'x' => -666, + 'y' => 76, + ] + ) + ); + + // With a linked layout. + $this->assertEquals( + '{"aclGroupId":null,"height":0,"id":7,"isLinkEnabled":true,"isOnTop":false,"label":"Label","labelPosition":"up","linkedLayoutAgentId":null,"linkedLayoutId":1,"linkedLayoutStatusType":"default","parentId":null,"type":4,"width":0,"x":-666,"y":76}', + (string) Label::fromArray( + [ + 'id' => 7, + 'type' => LABEL, + 'label' => 'Label', + 'labelPosition' => 'up', + 'isLinkEnabled' => true, + 'isOnTop' => false, + 'parentId' => null, + 'width' => '0', + 'height' => '0', + 'x' => -666, + 'y' => 76, + 'id_layout_linked' => 1, + ] + ) + ); + + } + + +} diff --git a/pandora_console/tests/Functional/Models/VisualConsole/Items/LineTest.php b/pandora_console/tests/Functional/Models/VisualConsole/Items/LineTest.php new file mode 100644 index 0000000000..b70b1699a6 --- /dev/null +++ b/pandora_console/tests/Functional/Models/VisualConsole/Items/LineTest.php @@ -0,0 +1,156 @@ +assertInstanceOf( + Line::class, + Line::fromArray( + [ + 'id' => 10, + 'type' => LINE_ITEM, + 'startX' => 50, + 'startY' => 100, + 'endX' => 0, + 'endY' => 10, + 'isOnTop' => false, + 'borderWidth' => 0, + 'borderColor' => 'white', + ] + ) + ); + + $this->assertInstanceOf( + Line::class, + Line::fromArray( + [ + 'id' => 10, + 'type' => LINE_ITEM, + 'startX' => 50, + 'endY' => 10, + 'borderColor' => 'black', + ] + ) + ); + } + + + /** + * Test if the instance is not created when using a invalid Id. + * + * @return void + */ + public function testCannotBeCreatedWithInvalidId(): void + { + $this->expectException(InvalidArgumentException::class); + // Invalid id. + Line::fromArray( + [ + 'id' => 'foo', + 'type' => LINE_ITEM, + 'startX' => 50, + 'startY' => 100, + 'endX' => 0, + 'endY' => 10, + 'isOnTop' => false, + 'borderWidth' => 0, + 'borderColor' => 'white', + ] + ); + // Missing id. + Line::fromArray( + [ + 'type' => LINE_ITEM, + 'startX' => 50, + 'startY' => 100, + 'endX' => 0, + 'endY' => 10, + 'isOnTop' => false, + 'borderWidth' => 0, + 'borderColor' => 'white', + ] + ); + } + + + /** + * Test if the instance is not created when using a invalid type. + * + * @return void + */ + public function testCannotBeCreatedWithInvalidtype(): void + { + $this->expectException(InvalidArgumentException::class); + // Invalid type. + Line::fromArray( + [ + 'id' => 13, + 'type' => 'test', + 'startX' => 50, + 'startY' => 100, + 'endX' => 0, + 'endY' => 10, + 'isOnTop' => false, + 'borderWidth' => 0, + 'borderColor' => 'white', + ] + ); + // Missing type. + Line::fromArray( + [ + 'id' => 13, + 'startX' => 50, + 'startY' => 100, + 'endX' => 0, + 'endY' => 10, + 'isOnTop' => true, + 'borderWidth' => 0, + 'borderColor' => 'white', + ] + ); + } + + + /** + * Test if the model has a valid JSON representation. + * + * @return void + */ + public function testContainerIsRepresentedAsJson(): void + { + $this->assertEquals( + '{"borderColor":"white","borderWidth":0,"endX":0,"endY":10,"id":1,"isOnTop":false,"startX":50,"startY":100,"type":13}', + (string) Line::fromArray( + [ + 'id' => 1, + 'type' => LINE_ITEM, + 'startX' => 50, + 'startY' => 100, + 'endX' => 0, + 'endY' => 10, + 'isOnTop' => false, + 'borderWidth' => 0, + 'borderColor' => 'white', + ] + ) + ); + } + + +} diff --git a/pandora_console/tests/Functional/Models/VisualConsole/Items/PercentileTest.php b/pandora_console/tests/Functional/Models/VisualConsole/Items/PercentileTest.php new file mode 100644 index 0000000000..ebe80f6b8c --- /dev/null +++ b/pandora_console/tests/Functional/Models/VisualConsole/Items/PercentileTest.php @@ -0,0 +1,149 @@ +assertInstanceOf( + Percentile::class, + Percentile::fromArray( + [ + 'id' => 3, + 'type' => PERCENTILE_BAR, + 'width' => '600', + 'height' => '500', + 'maxTime' => null, + 'valueType' => 'value', + 'value' => '123ms', + ] + ) + ); + + $this->assertInstanceOf( + Percentile::class, + Percentile::fromArray( + [ + 'id' => 14, + 'type' => PERCENTILE_BUBBLE, + 'width' => '600', + 'height' => '500', + 'maxTime' => 12800, + 'valueType' => 'image', + 'value' => 'data:image;asdasoih==', + ] + ) + ); + } + + + /** + * Test if the model has a valid JSON representation. + * + * @return void + */ + public function testContainerIsRepresentedAsJson(): void + { + $this->assertEquals( + '{"aclGroupId":null,"agentId":null,"agentName":null,"color":null,"height":0,"id":7,"isLinkEnabled":true,"isOnTop":false,"label":null,"labelColor":null,"labelPosition":"up","linkedLayoutAgentId":null,"linkedLayoutId":null,"linkedLayoutStatusType":"default","maxValue":0,"minValue":null,"moduleId":null,"moduleName":null,"parentId":null,"percentileType":"progress-bar","type":3,"unit":null,"value":null,"valueType":"percent","width":0,"x":-666,"y":76}', + (string) Percentile::fromArray( + [ + 'id' => 7, + 'type' => PERCENTILE_BAR, + 'label' => null, + 'labelPosition' => 'up', + 'isLinkEnabled' => true, + 'isOnTop' => false, + 'parentId' => null, + 'width' => '0', + 'height' => '0', + 'x' => -666, + 'y' => 76, + 'maxTime' => null, + ] + ) + ); + + $this->assertEquals( + '{"aclGroupId":null,"agentId":null,"agentName":null,"color":null,"height":0,"id":7,"isLinkEnabled":true,"isOnTop":false,"label":null,"labelColor":null,"labelPosition":"up","linkedLayoutAgentId":null,"linkedLayoutId":null,"linkedLayoutStatusType":"default","maxValue":0,"minValue":null,"moduleId":null,"moduleName":null,"parentId":null,"percentileType":"bubble","type":3,"unit":null,"value":null,"valueType":"percent","width":0,"x":-666,"y":76}', + (string) Percentile::fromArray( + [ + 'id' => 7, + 'type' => PERCENTILE_BUBBLE, + 'label' => null, + 'labelPosition' => 'up', + 'isLinkEnabled' => true, + 'isOnTop' => false, + 'parentId' => null, + 'width' => '0', + 'height' => '0', + 'x' => -666, + 'y' => 76, + 'maxTime' => 12800, + ] + ) + ); + + $this->assertEquals( + '{"aclGroupId":null,"agentId":null,"agentName":null,"color":null,"height":0,"id":7,"isLinkEnabled":true,"isOnTop":false,"label":null,"labelColor":null,"labelPosition":"up","linkedLayoutAgentId":null,"linkedLayoutId":null,"linkedLayoutStatusType":"default","maxValue":0,"minValue":null,"moduleId":null,"moduleName":null,"parentId":null,"percentileType":"circular-progress-bar","type":3,"unit":null,"value":1,"valueType":"value","width":0,"x":-666,"y":76}', + (string) Percentile::fromArray( + [ + 'id' => 7, + 'type' => CIRCULAR_PROGRESS_BAR, + 'label' => null, + 'labelPosition' => 'up', + 'isLinkEnabled' => true, + 'isOnTop' => false, + 'parentId' => null, + 'width' => '0', + 'height' => '0', + 'x' => -666, + 'y' => 76, + 'maxTime' => 12800, + 'valueType' => 'value', + 'value' => '1', + ] + ) + ); + + $this->assertEquals( + '{"aclGroupId":null,"agentId":null,"agentName":null,"color":"#FFF","height":0,"id":7,"isLinkEnabled":true,"isOnTop":false,"label":null,"labelColor":"#000","labelPosition":"up","linkedLayoutAgentId":null,"linkedLayoutId":null,"linkedLayoutStatusType":"default","maxValue":0,"minValue":null,"moduleId":null,"moduleName":null,"parentId":null,"percentileType":"circular-progress-bar","type":3,"unit":null,"value":80,"valueType":"percent","width":0,"x":-666,"y":76}', + (string) Percentile::fromArray( + [ + 'id' => 7, + 'type' => CIRCULAR_PROGRESS_BAR, + 'label' => null, + 'labelPosition' => 'up', + 'isLinkEnabled' => true, + 'isOnTop' => false, + 'parentId' => null, + 'width' => '0', + 'height' => '0', + 'x' => -666, + 'y' => 76, + 'maxTime' => 12800, + 'valueType' => 'percent', + 'value' => '80', + 'color' => '#FFF', + 'labelColor' => '#000', + ] + ) + ); + } + + +} diff --git a/pandora_console/tests/Functional/Models/VisualConsole/Items/SimpleValueTest.php b/pandora_console/tests/Functional/Models/VisualConsole/Items/SimpleValueTest.php new file mode 100644 index 0000000000..a417699fe0 --- /dev/null +++ b/pandora_console/tests/Functional/Models/VisualConsole/Items/SimpleValueTest.php @@ -0,0 +1,148 @@ +assertInstanceOf( + SimpleValue::class, + SimpleValue::fromArray( + [ + 'id' => 3, + 'type' => SIMPLE_VALUE, + 'width' => '600', + 'height' => '500', + 'valueType' => 'string', + 'value' => 57, + 'processValue' => 'avg', + 'period' => 12800, + ] + ) + ); + + $this->assertInstanceOf( + SimpleValue::class, + SimpleValue::fromArray( + [ + 'id' => 14, + 'type' => SIMPLE_VALUE, + 'width' => '600', + 'height' => '500', + 'valueType' => 'image', + 'value' => 3598, + 'processValue' => 'max', + 'period' => 9000, + ] + ) + ); + } + + + /** + * Test if the model has a valid JSON representation. + * + * @return void + */ + public function testContainerIsRepresentedAsJson(): void + { + $this->assertEquals( + '{"aclGroupId":null,"agentId":null,"agentName":null,"height":500,"id":3,"isLinkEnabled":false,"isOnTop":false,"label":null,"labelPosition":"down","linkedLayoutAgentId":null,"linkedLayoutId":null,"linkedLayoutStatusType":"default","moduleId":null,"moduleName":null,"parentId":null,"period":12800,"processValue":"avg","type":2,"value":57,"valueType":"string","width":600,"x":0,"y":0}', + (string) SimpleValue::fromArray( + [ + 'id' => 3, + 'type' => SIMPLE_VALUE, + 'width' => '600', + 'height' => '500', + 'valueType' => 'string', + 'value' => 57, + 'processValue' => 'avg', + 'period' => 12800, + ] + ) + ); + + $this->assertEquals( + '{"aclGroupId":null,"agentId":null,"agentName":null,"height":500,"id":3,"isLinkEnabled":false,"isOnTop":false,"label":null,"labelPosition":"down","linkedLayoutAgentId":null,"linkedLayoutId":null,"linkedLayoutStatusType":"default","moduleId":null,"moduleName":null,"parentId":null,"processValue":"none","type":2,"value":57,"valueType":"string","width":600,"x":0,"y":0}', + (string) SimpleValue::fromArray( + [ + 'id' => 3, + 'type' => SIMPLE_VALUE, + 'width' => '600', + 'height' => '500', + 'valueType' => 'string', + 'value' => 57, + ] + ) + ); + + $this->assertEquals( + '{"aclGroupId":null,"agentId":null,"agentName":null,"height":500,"id":3,"isLinkEnabled":false,"isOnTop":false,"label":null,"labelPosition":"down","linkedLayoutAgentId":null,"linkedLayoutId":1,"linkedLayoutStatusType":"default","moduleId":null,"moduleName":null,"parentId":null,"processValue":"none","type":2,"value":57,"valueType":"string","width":600,"x":0,"y":0}', + (string) SimpleValue::fromArray( + [ + 'id' => 3, + 'type' => SIMPLE_VALUE, + 'width' => '600', + 'height' => '500', + 'valueType' => 'string', + 'value' => 57, + 'id_layout_linked' => 1, + ] + ) + ); + + $this->assertEquals( + '{"aclGroupId":null,"agentId":null,"agentName":null,"height":500,"id":3,"isLinkEnabled":false,"isOnTop":false,"label":null,"labelPosition":"down","linkedLayoutAgentId":3,"linkedLayoutId":2,"linkedLayoutStatusType":"default","metaconsoleId":5,"moduleId":null,"moduleName":null,"parentId":null,"processValue":"none","type":2,"value":57,"valueType":"string","width":600,"x":0,"y":0}', + (string) SimpleValue::fromArray( + [ + 'id' => 3, + 'type' => SIMPLE_VALUE, + 'width' => '600', + 'height' => '500', + 'valueType' => 'string', + 'value' => 57, + 'id_metaconsole' => 5, + 'linked_layout_node_id' => 3, + 'linkedLayoutId' => 2, + ] + ) + ); + + $this->assertEquals( + '{"aclGroupId":null,"agentId":21,"agentName":null,"height":500,"id":3,"isLinkEnabled":false,"isOnTop":false,"label":null,"labelPosition":"down","linkedLayoutAgentId":15,"linkedLayoutId":3,"linkedLayoutStatusType":"default","metaconsoleId":2,"moduleId":385,"moduleName":"module_test","parentId":null,"processValue":"none","type":2,"value":57,"valueType":"string","width":600,"x":0,"y":0}', + (string) SimpleValue::fromArray( + [ + 'id' => 3, + 'type' => SIMPLE_VALUE, + 'width' => '600', + 'height' => '500', + 'valueType' => 'string', + 'value' => 57, + 'id_metaconsole' => 2, + 'linked_layout_node_id' => 15, + 'linkedLayoutId' => 3, + 'agentId' => 21, + 'moduleId' => 385, + 'moduleName' => 'module_test', + ] + ) + ); + + } + + +} diff --git a/pandora_console/tests/Functional/Models/VisualConsole/Items/StaticGraphTest.php b/pandora_console/tests/Functional/Models/VisualConsole/Items/StaticGraphTest.php new file mode 100644 index 0000000000..9b77fc7540 --- /dev/null +++ b/pandora_console/tests/Functional/Models/VisualConsole/Items/StaticGraphTest.php @@ -0,0 +1,224 @@ +assertInstanceOf( + StaticGraph::class, + StaticGraph::fromArray( + [ + 'id' => 345, + 'type' => STATIC_GRAPH, + 'label' => null, + 'labelPosition' => 'up', + 'isLinkEnabled' => true, + 'isOnTop' => false, + 'parentId' => null, + 'width' => '0', + 'height' => '0', + 'x' => -666, + 'y' => 76, + 'imageSrc' => 'aaaaa', + 'showLastValueTooltip' => 'enabled', + ] + ) + ); + + $this->assertInstanceOf( + StaticGraph::class, + StaticGraph::fromArray( + [ + 'id' => 1000, + 'type' => STATIC_GRAPH, + 'width' => 100, + 'height' => 900, + 'image' => 'test.jpg', + 'show_last_value' => 2, + ] + ) + ); + } + + + /** + * Test if the model has a valid JSON representation. + * + * @return void + */ + public function testContainerIsRepresentedAsJson(): void + { + $this->assertEquals( + '{"aclGroupId":null,"agentId":null,"agentName":null,"height":0,"id":7,"imageSrc":"image.jpg","isLinkEnabled":true,"isOnTop":false,"label":null,"labelPosition":"up","linkedLayoutAgentId":null,"linkedLayoutId":null,"linkedLayoutStatusType":"default","moduleId":null,"moduleName":null,"parentId":null,"showLastValueTooltip":"default","statusImageSrc":null,"type":0,"width":0,"x":-666,"y":76}', + (string) StaticGraph::fromArray( + [ + 'id' => 7, + 'type' => STATIC_GRAPH, + 'label' => null, + 'labelPosition' => 'up', + 'isLinkEnabled' => true, + 'isOnTop' => false, + 'parentId' => null, + 'width' => '0', + 'height' => '0', + 'x' => -666, + 'y' => 76, + 'imageSrc' => 'image.jpg', + ] + ) + ); + + $this->assertEquals( + '{"aclGroupId":null,"agentId":null,"agentName":null,"height":0,"id":7,"imageSrc":"image.jpg","isLinkEnabled":true,"isOnTop":false,"label":null,"labelPosition":"up","linkedLayoutAgentId":null,"linkedLayoutId":null,"linkedLayoutStatusType":"default","moduleId":null,"moduleName":null,"parentId":null,"showLastValueTooltip":"disabled","statusImageSrc":null,"type":0,"width":0,"x":-666,"y":76}', + (string) StaticGraph::fromArray( + [ + 'id' => 7, + 'type' => STATIC_GRAPH, + 'label' => null, + 'labelPosition' => 'up', + 'isLinkEnabled' => true, + 'isOnTop' => false, + 'parentId' => null, + 'width' => '0', + 'height' => '0', + 'x' => -666, + 'y' => 76, + 'image' => 'image.jpg', + 'showLastValueTooltip' => 'disabled', + ] + ) + ); + + $this->assertEquals( + '{"aclGroupId":null,"agentId":null,"agentName":null,"height":0,"id":7,"imageSrc":"image.jpg","isLinkEnabled":true,"isOnTop":false,"label":null,"labelPosition":"up","linkedLayoutAgentId":3,"linkedLayoutId":2,"linkedLayoutStatusType":"default","metaconsoleId":5,"moduleId":null,"moduleName":null,"parentId":null,"showLastValueTooltip":"default","statusImageSrc":"image.bad.jpg","type":0,"width":0,"x":-666,"y":76}', + (string) StaticGraph::fromArray( + [ + 'id' => 7, + 'type' => STATIC_GRAPH, + 'label' => null, + 'labelPosition' => 'up', + 'isLinkEnabled' => true, + 'isOnTop' => false, + 'parentId' => null, + 'width' => '0', + 'height' => '0', + 'x' => -666, + 'y' => 76, + 'imageSrc' => 'image.jpg', + 'id_metaconsole' => 5, + 'linked_layout_node_id' => 3, + 'linkedLayoutId' => 2, + 'statusImageSrc' => 'image.bad.jpg', + ] + ) + ); + + $this->assertEquals( + '{"aclGroupId":null,"agentId":null,"agentName":null,"height":0,"id":7,"imageSrc":"image.jpg","isLinkEnabled":true,"isOnTop":false,"label":null,"labelPosition":"up","linkedLayoutAgentId":null,"linkedLayoutId":1,"linkedLayoutStatusType":"default","moduleId":null,"moduleName":null,"parentId":null,"showLastValueTooltip":"default","statusImageSrc":null,"type":0,"width":0,"x":-666,"y":76}', + (string) StaticGraph::fromArray( + [ + 'id' => 7, + 'type' => STATIC_GRAPH, + 'label' => null, + 'labelPosition' => 'up', + 'isLinkEnabled' => true, + 'isOnTop' => false, + 'parentId' => null, + 'width' => '0', + 'height' => '0', + 'x' => -666, + 'y' => 76, + 'image' => 'image.jpg', + 'id_layout_linked' => 1, + ] + ) + ); + + $this->assertEquals( + '{"aclGroupId":null,"agentId":null,"agentName":null,"height":0,"id":7,"imageSrc":"image.jpg","isLinkEnabled":true,"isOnTop":false,"label":null,"labelPosition":"up","linkedLayoutAgentId":null,"linkedLayoutId":2,"linkedLayoutStatusType":"service","linkedLayoutStatusTypeCriticalThreshold":80,"linkedLayoutStatusTypeWarningThreshold":50,"moduleId":null,"moduleName":null,"parentId":null,"showLastValueTooltip":"default","statusImageSrc":"image.bad.jpg","type":0,"width":0,"x":-666,"y":76}', + (string) StaticGraph::fromArray( + [ + 'id' => 7, + 'type' => STATIC_GRAPH, + 'label' => null, + 'labelPosition' => 'up', + 'isLinkEnabled' => true, + 'isOnTop' => false, + 'parentId' => null, + 'width' => '0', + 'height' => '0', + 'x' => -666, + 'y' => 76, + 'image' => 'image.jpg', + 'linkedLayoutId' => 2, + 'linked_layout_status_type' => 'service', + 'linkedLayoutStatusTypeWarningThreshold' => 50, + 'linked_layout_status_as_service_critical' => 80, + 'statusImageSrc' => 'image.bad.jpg', + ] + ) + ); + } + + + /** + * Test if the instance is not created when using a invalid image src. + * + * @return void + */ + public function testCannotBeCreatedWithInvalidImageSrc(): void + { + $this->expectException(InvalidArgumentException::class); + // Invalid imageSrc. + StaticGraph::fromArray( + [ + 'id' => 3, + 'type' => STATIC_GRAPH, + 'label' => null, + 'isLinkEnabled' => true, + 'isOnTop' => false, + 'parentId' => null, + 'width' => '330', + 'height' => '0', + 'x' => 511, + 'y' => 76, + 'imageSrc' => 45, + 'showLastValueTooltip' => 'disabled', + ] + ); + + // Missing imageSrc. + StaticGraph::fromArray( + [ + 'id' => 3, + 'type' => STATIC_GRAPH, + 'label' => null, + 'isLinkEnabled' => true, + 'isOnTop' => false, + 'parentId' => null, + 'width' => '330', + 'height' => '0', + 'x' => 511, + 'y' => 76, + 'showLastValueTooltip' => 'enabled', + ] + ); + } + + +} diff --git a/pandora_console/tests/bootstrap.php b/pandora_console/tests/bootstrap.php new file mode 100644 index 0000000000..c2d6c31bf2 --- /dev/null +++ b/pandora_console/tests/bootstrap.php @@ -0,0 +1,4 @@ +apcuPrefix = function_exists('apcu_fetch') && ini_get('apc.enabled') ? $apcuPrefix : null; + $this->apcuPrefix = function_exists('apcu_fetch') && filter_var(ini_get('apc.enabled'), FILTER_VALIDATE_BOOLEAN) ? $apcuPrefix : null; } /** diff --git a/pandora_console/vendor/composer/autoload_psr4.php b/pandora_console/vendor/composer/autoload_psr4.php index cd3de7c147..57404185e1 100644 --- a/pandora_console/vendor/composer/autoload_psr4.php +++ b/pandora_console/vendor/composer/autoload_psr4.php @@ -6,8 +6,11 @@ $vendorDir = dirname(dirname(__FILE__)); $baseDir = dirname($vendorDir); return array( + 'Tests\\' => array($baseDir . '/tests'), 'Psr\\Log\\' => array($vendorDir . '/psr/log/Psr/Log'), 'Mpdf\\' => array($vendorDir . '/mpdf/mpdf/src'), + 'Models\\' => array($baseDir . '/include/rest-api/models'), + 'Enterprise\\Models\\' => array($baseDir . '/enterprise/include/rest-api/models'), 'Egulias\\EmailValidator\\' => array($vendorDir . '/egulias/email-validator/EmailValidator'), 'DeepCopy\\' => array($vendorDir . '/myclabs/deep-copy/src/DeepCopy'), ); diff --git a/pandora_console/vendor/composer/autoload_static.php b/pandora_console/vendor/composer/autoload_static.php index 2e8ac5a72e..f6d8bc0456 100644 --- a/pandora_console/vendor/composer/autoload_static.php +++ b/pandora_console/vendor/composer/autoload_static.php @@ -12,6 +12,10 @@ class ComposerStaticInitfdecadadce22e6dde51e9535fe4ad7aa ); public static $prefixLengthsPsr4 = array ( + 'T' => + array ( + 'Tests\\' => 6, + ), 'P' => array ( 'Psr\\Log\\' => 8, @@ -19,9 +23,11 @@ class ComposerStaticInitfdecadadce22e6dde51e9535fe4ad7aa 'M' => array ( 'Mpdf\\' => 5, + 'Models\\' => 7, ), 'E' => array ( + 'Enterprise\\Models\\' => 18, 'Egulias\\EmailValidator\\' => 23, ), 'D' => @@ -31,6 +37,10 @@ class ComposerStaticInitfdecadadce22e6dde51e9535fe4ad7aa ); public static $prefixDirsPsr4 = array ( + 'Tests\\' => + array ( + 0 => __DIR__ . '/../..' . '/tests', + ), 'Psr\\Log\\' => array ( 0 => __DIR__ . '/..' . '/psr/log/Psr/Log', @@ -39,6 +49,14 @@ class ComposerStaticInitfdecadadce22e6dde51e9535fe4ad7aa array ( 0 => __DIR__ . '/..' . '/mpdf/mpdf/src', ), + 'Models\\' => + array ( + 0 => __DIR__ . '/../..' . '/include/rest-api/models', + ), + 'Enterprise\\Models\\' => + array ( + 0 => __DIR__ . '/../..' . '/enterprise/include/rest-api/models', + ), 'Egulias\\EmailValidator\\' => array ( 0 => __DIR__ . '/..' . '/egulias/email-validator/EmailValidator', diff --git a/visual_console_client/.eslintrc b/visual_console_client/.eslintrc new file mode 100644 index 0000000000..af67551834 --- /dev/null +++ b/visual_console_client/.eslintrc @@ -0,0 +1,19 @@ +{ + "env": { + "browser": true, + "node": true, + "jest": true + }, + "parser": "@typescript-eslint/parser", + "plugins": ["@typescript-eslint", "prettier"], + "extends": [ + "eslint:recommended", + "plugin:prettier/recommended", + "plugin:@typescript-eslint/recommended" + ], + "rules": { + "no-console": "off", + "@typescript-eslint/indent": "off", + "@typescript-eslint/explicit-function-return-type": "off" + } +} diff --git a/visual_console_client/.gitignore b/visual_console_client/.gitignore new file mode 100644 index 0000000000..46091fbe36 --- /dev/null +++ b/visual_console_client/.gitignore @@ -0,0 +1,27 @@ +# See https://help.github.com/articles/ignoring-files/ for more about ignoring files. + +# dependencies +/node_modules +/.pnp +.pnp.js + +# testing +/coverage + +# production +/build +/dist + +# editor +# .vscode + +# misc +.DS_Store +.env.local +.env.development.local +.env.test.local +.env.production.local + +npm-debug.log* +yarn-debug.log* +yarn-error.log* diff --git a/visual_console_client/.vscode/launch.json b/visual_console_client/.vscode/launch.json new file mode 100644 index 0000000000..115b2f51b6 --- /dev/null +++ b/visual_console_client/.vscode/launch.json @@ -0,0 +1,15 @@ +{ + // Use IntelliSense para saber los atributos posibles. + // Mantenga el puntero para ver las descripciones de los existentes atributos + // Para más información, visite: https://go.microsoft.com/fwlink/?linkid=830387 + "version": "0.2.0", + "configurations": [ + { + "type": "chrome", + "request": "launch", + "name": "Debug", + "url": "http://localhost:8080", + "webRoot": "${workspaceFolder}" + } + ] +} diff --git a/visual_console_client/.vscode/settings.json b/visual_console_client/.vscode/settings.json new file mode 100644 index 0000000000..024b36878e --- /dev/null +++ b/visual_console_client/.vscode/settings.json @@ -0,0 +1,10 @@ +{ + "typescript.tsdk": "node_modules/typescript/lib", + "editor.formatOnSave": true, + "eslint.validate": [ + "javascript", + "javascriptreact", + "typescript", + "typescriptreact" + ] +} diff --git a/visual_console_client/README.md b/visual_console_client/README.md new file mode 100644 index 0000000000..b480fac939 --- /dev/null +++ b/visual_console_client/README.md @@ -0,0 +1 @@ +# Visual Console Client diff --git a/visual_console_client/__mocks__/fileMock.js b/visual_console_client/__mocks__/fileMock.js new file mode 100644 index 0000000000..0a445d0600 --- /dev/null +++ b/visual_console_client/__mocks__/fileMock.js @@ -0,0 +1 @@ +module.exports = "test-file-stub"; diff --git a/visual_console_client/__mocks__/styleMock.js b/visual_console_client/__mocks__/styleMock.js new file mode 100644 index 0000000000..f053ebf797 --- /dev/null +++ b/visual_console_client/__mocks__/styleMock.js @@ -0,0 +1 @@ +module.exports = {}; diff --git a/visual_console_client/jest.config.js b/visual_console_client/jest.config.js new file mode 100644 index 0000000000..ec2b954ec2 --- /dev/null +++ b/visual_console_client/jest.config.js @@ -0,0 +1,14 @@ +module.exports = { + roots: ["/src"], + transform: { + "^.+\\.tsx?$": "ts-jest" + }, + moduleFileExtensions: ["ts", "tsx", "js", "jsx", "json", "node"], + // This configuration is used to mock the css and file imports used by Webpack. + // https://jestjs.io/docs/en/webpack.html + moduleNameMapper: { + "\\.(jpg|jpeg|png|gif|eot|otf|webp|svg|ttf|woff|woff2|mp4|webm|wav|mp3|m4a|aac|oga)$": + "/__mocks__/fileMock.js", + "\\.(css|less)$": "/__mocks__/styleMock.js" + } +}; diff --git a/visual_console_client/package-lock.json b/visual_console_client/package-lock.json new file mode 100644 index 0000000000..c5a4da6f7a --- /dev/null +++ b/visual_console_client/package-lock.json @@ -0,0 +1,7957 @@ +{ + "name": "pandora-fms-visual-console", + "version": "1.0.0", + "lockfileVersion": 1, + "requires": true, + "dependencies": { + "@babel/code-frame": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.0.0.tgz", + "integrity": "sha512-OfC2uemaknXr87bdLUkWog7nYuliM9Ij5HUcajsVcMCpQrcLmtxRbVFTIqmcSkSeYRBFBRxs2FiUqFJDLdiebA==", + "requires": { + "@babel/highlight": "^7.0.0" + } + }, + "@babel/core": { + "version": "7.4.3", + "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.4.3.tgz", + "integrity": "sha512-oDpASqKFlbspQfzAE7yaeTmdljSH2ADIvBlb0RwbStltTuWa0+7CCI1fYVINNv9saHPa1W7oaKeuNuKj+RQCvA==", + "requires": { + "@babel/code-frame": "^7.0.0", + "@babel/generator": "^7.4.0", + "@babel/helpers": "^7.4.3", + "@babel/parser": "^7.4.3", + "@babel/template": "^7.4.0", + "@babel/traverse": "^7.4.3", + "@babel/types": "^7.4.0", + "convert-source-map": "^1.1.0", + "debug": "^4.1.0", + "json5": "^2.1.0", + "lodash": "^4.17.11", + "resolve": "^1.3.2", + "semver": "^5.4.1", + "source-map": "^0.5.0" + }, + "dependencies": { + "debug": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.1.1.tgz", + "integrity": "sha512-pYAIzeRo8J6KPEaJ0VWOh5Pzkbw/RetuzehGM7QRRX5he4fPHx2rdKMB256ehJCkX+XRQm16eZLqLNS8RSZXZw==", + "requires": { + "ms": "^2.1.1" + } + }, + "json5": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/json5/-/json5-2.1.0.tgz", + "integrity": "sha512-8Mh9h6xViijj36g7Dxi+Y4S6hNGV96vcJZr/SrlHh1LR/pEn/8j/+qIBbs44YKl69Lrfctp4QD+AdWLTMqEZAQ==", + "requires": { + "minimist": "^1.2.0" + } + }, + "ms": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.1.tgz", + "integrity": "sha512-tgp+dl5cGk28utYktBsrFqA7HKgrhgPsg6Z/EfhWI4gl1Hwq8B/GmY/0oXZ6nF8hDVesS/FpnYaD/kOWhYQvyg==" + } + } + }, + "@babel/generator": { + "version": "7.4.0", + "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.4.0.tgz", + "integrity": "sha512-/v5I+a1jhGSKLgZDcmAUZ4K/VePi43eRkUs3yePW1HB1iANOD5tqJXwGSG4BZhSksP8J9ejSlwGeTiiOFZOrXQ==", + "requires": { + "@babel/types": "^7.4.0", + "jsesc": "^2.5.1", + "lodash": "^4.17.11", + "source-map": "^0.5.0", + "trim-right": "^1.0.1" + } + }, + "@babel/helper-function-name": { + "version": "7.1.0", + "resolved": "https://registry.npmjs.org/@babel/helper-function-name/-/helper-function-name-7.1.0.tgz", + "integrity": "sha512-A95XEoCpb3TO+KZzJ4S/5uW5fNe26DjBGqf1o9ucyLyCmi1dXq/B3c8iaWTfBk3VvetUxl16e8tIrd5teOCfGw==", + "requires": { + "@babel/helper-get-function-arity": "^7.0.0", + "@babel/template": "^7.1.0", + "@babel/types": "^7.0.0" + } + }, + "@babel/helper-get-function-arity": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/@babel/helper-get-function-arity/-/helper-get-function-arity-7.0.0.tgz", + "integrity": "sha512-r2DbJeg4svYvt3HOS74U4eWKsUAMRH01Z1ds1zx8KNTPtpTL5JAsdFv8BNyOpVqdFhHkkRDIg5B4AsxmkjAlmQ==", + "requires": { + "@babel/types": "^7.0.0" + } + }, + "@babel/helper-plugin-utils": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.0.0.tgz", + "integrity": "sha512-CYAOUCARwExnEixLdB6sDm2dIJ/YgEAKDM1MOeMeZu9Ld/bDgVo8aiWrXwcY7OBh+1Ea2uUcVRcxKk0GJvW7QA==" + }, + "@babel/helper-split-export-declaration": { + "version": "7.4.0", + "resolved": "https://registry.npmjs.org/@babel/helper-split-export-declaration/-/helper-split-export-declaration-7.4.0.tgz", + "integrity": "sha512-7Cuc6JZiYShaZnybDmfwhY4UYHzI6rlqhWjaIqbsJGsIqPimEYy5uh3akSRLMg65LSdSEnJ8a8/bWQN6u2oMGw==", + "requires": { + "@babel/types": "^7.4.0" + } + }, + "@babel/helpers": { + "version": "7.4.3", + "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.4.3.tgz", + "integrity": "sha512-BMh7X0oZqb36CfyhvtbSmcWc3GXocfxv3yNsAEuM0l+fAqSO22rQrUpijr3oE/10jCTrB6/0b9kzmG4VetCj8Q==", + "requires": { + "@babel/template": "^7.4.0", + "@babel/traverse": "^7.4.3", + "@babel/types": "^7.4.0" + } + }, + "@babel/highlight": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.0.0.tgz", + "integrity": "sha512-UFMC4ZeFC48Tpvj7C8UgLvtkaUuovQX+5xNWrsIoMG8o2z+XFKjKaN9iVmS84dPwVN00W4wPmqvYoZF3EGAsfw==", + "requires": { + "chalk": "^2.0.0", + "esutils": "^2.0.2", + "js-tokens": "^4.0.0" + } + }, + "@babel/parser": { + "version": "7.4.3", + "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.4.3.tgz", + "integrity": "sha512-gxpEUhTS1sGA63EGQGuA+WESPR/6tz6ng7tSHFCmaTJK/cGK8y37cBTspX+U2xCAue2IQVvF6Z0oigmjwD8YGQ==" + }, + "@babel/plugin-syntax-object-rest-spread": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-object-rest-spread/-/plugin-syntax-object-rest-spread-7.2.0.tgz", + "integrity": "sha512-t0JKGgqk2We+9may3t0xDdmneaXmyxq0xieYcKHxIsrJO64n1OiMWNUtc5gQK1PA0NpdCRrtZp4z+IUaKugrSA==", + "requires": { + "@babel/helper-plugin-utils": "^7.0.0" + } + }, + "@babel/template": { + "version": "7.4.0", + "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.4.0.tgz", + "integrity": "sha512-SOWwxxClTTh5NdbbYZ0BmaBVzxzTh2tO/TeLTbF6MO6EzVhHTnff8CdBXx3mEtazFBoysmEM6GU/wF+SuSx4Fw==", + "requires": { + "@babel/code-frame": "^7.0.0", + "@babel/parser": "^7.4.0", + "@babel/types": "^7.4.0" + } + }, + "@babel/traverse": { + "version": "7.4.3", + "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.4.3.tgz", + "integrity": "sha512-HmA01qrtaCwwJWpSKpA948cBvU5BrmviAief/b3AVw936DtcdsTexlbyzNuDnthwhOQ37xshn7hvQaEQk7ISYQ==", + "requires": { + "@babel/code-frame": "^7.0.0", + "@babel/generator": "^7.4.0", + "@babel/helper-function-name": "^7.1.0", + "@babel/helper-split-export-declaration": "^7.4.0", + "@babel/parser": "^7.4.3", + "@babel/types": "^7.4.0", + "debug": "^4.1.0", + "globals": "^11.1.0", + "lodash": "^4.17.11" + }, + "dependencies": { + "debug": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.1.1.tgz", + "integrity": "sha512-pYAIzeRo8J6KPEaJ0VWOh5Pzkbw/RetuzehGM7QRRX5he4fPHx2rdKMB256ehJCkX+XRQm16eZLqLNS8RSZXZw==", + "requires": { + "ms": "^2.1.1" + } + }, + "ms": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.1.tgz", + "integrity": "sha512-tgp+dl5cGk28utYktBsrFqA7HKgrhgPsg6Z/EfhWI4gl1Hwq8B/GmY/0oXZ6nF8hDVesS/FpnYaD/kOWhYQvyg==" + } + } + }, + "@babel/types": { + "version": "7.4.0", + "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.4.0.tgz", + "integrity": "sha512-aPvkXyU2SPOnztlgo8n9cEiXW755mgyvueUPcpStqdzoSPm0fjO0vQBjLkt3JKJW7ufikfcnMTTPsN1xaTsBPA==", + "requires": { + "esutils": "^2.0.2", + "lodash": "^4.17.11", + "to-fast-properties": "^2.0.0" + } + }, + "@cnakazawa/watch": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/@cnakazawa/watch/-/watch-1.0.3.tgz", + "integrity": "sha512-r5160ogAvGyHsal38Kux7YYtodEKOj89RGb28ht1jh3SJb08VwRwAKKJL0bGb04Zd/3r9FL3BFIc3bBidYffCA==", + "requires": { + "exec-sh": "^0.3.2", + "minimist": "^1.2.0" + } + }, + "@jest/console": { + "version": "24.7.1", + "resolved": "https://registry.npmjs.org/@jest/console/-/console-24.7.1.tgz", + "integrity": "sha512-iNhtIy2M8bXlAOULWVTUxmnelTLFneTNEkHCgPmgd+zNwy9zVddJ6oS5rZ9iwoscNdT5mMwUd0C51v/fSlzItg==", + "requires": { + "@jest/source-map": "^24.3.0", + "chalk": "^2.0.1", + "slash": "^2.0.0" + } + }, + "@jest/core": { + "version": "24.7.1", + "resolved": "https://registry.npmjs.org/@jest/core/-/core-24.7.1.tgz", + "integrity": "sha512-ivlZ8HX/FOASfHcb5DJpSPFps8ydfUYzLZfgFFqjkLijYysnIEOieg72YRhO4ZUB32xu40hsSMmaw+IGYeKONA==", + "requires": { + "@jest/console": "^24.7.1", + "@jest/reporters": "^24.7.1", + "@jest/test-result": "^24.7.1", + "@jest/transform": "^24.7.1", + "@jest/types": "^24.7.0", + "ansi-escapes": "^3.0.0", + "chalk": "^2.0.1", + "exit": "^0.1.2", + "graceful-fs": "^4.1.15", + "jest-changed-files": "^24.7.0", + "jest-config": "^24.7.1", + "jest-haste-map": "^24.7.1", + "jest-message-util": "^24.7.1", + "jest-regex-util": "^24.3.0", + "jest-resolve-dependencies": "^24.7.1", + "jest-runner": "^24.7.1", + "jest-runtime": "^24.7.1", + "jest-snapshot": "^24.7.1", + "jest-util": "^24.7.1", + "jest-validate": "^24.7.0", + "jest-watcher": "^24.7.1", + "micromatch": "^3.1.10", + "p-each-series": "^1.0.0", + "pirates": "^4.0.1", + "realpath-native": "^1.1.0", + "rimraf": "^2.5.4", + "strip-ansi": "^5.0.0" + }, + "dependencies": { + "ansi-regex": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-4.1.0.tgz", + "integrity": "sha512-1apePfXM1UOSqw0o9IiFAovVz9M5S1Dg+4TrDwfMewQ6p/rmMueb7tWZjQ1rx4Loy1ArBggoqGpfqqdI4rondg==" + }, + "strip-ansi": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-5.2.0.tgz", + "integrity": "sha512-DuRs1gKbBqsMKIZlrffwlug8MHkcnpjs5VPmL1PAh+mA30U0DTotfDZ0d2UUsXpPmPmMMJ6W773MaA3J+lbiWA==", + "requires": { + "ansi-regex": "^4.1.0" + } + } + } + }, + "@jest/environment": { + "version": "24.7.1", + "resolved": "https://registry.npmjs.org/@jest/environment/-/environment-24.7.1.tgz", + "integrity": "sha512-wmcTTYc4/KqA+U5h1zQd5FXXynfa7VGP2NfF+c6QeGJ7c+2nStgh65RQWNX62SC716dTtqheTRrZl0j+54oGHw==", + "requires": { + "@jest/fake-timers": "^24.7.1", + "@jest/transform": "^24.7.1", + "@jest/types": "^24.7.0", + "jest-mock": "^24.7.0" + } + }, + "@jest/fake-timers": { + "version": "24.7.1", + "resolved": "https://registry.npmjs.org/@jest/fake-timers/-/fake-timers-24.7.1.tgz", + "integrity": "sha512-4vSQJDKfR2jScOe12L9282uiwuwQv9Lk7mgrCSZHA9evB9efB/qx8i0KJxsAKtp8fgJYBJdYY7ZU6u3F4/pyjA==", + "requires": { + "@jest/types": "^24.7.0", + "jest-message-util": "^24.7.1", + "jest-mock": "^24.7.0" + } + }, + "@jest/reporters": { + "version": "24.7.1", + "resolved": "https://registry.npmjs.org/@jest/reporters/-/reporters-24.7.1.tgz", + "integrity": "sha512-bO+WYNwHLNhrjB9EbPL4kX/mCCG4ZhhfWmO3m4FSpbgr7N83MFejayz30kKjgqr7smLyeaRFCBQMbXpUgnhAJw==", + "requires": { + "@jest/environment": "^24.7.1", + "@jest/test-result": "^24.7.1", + "@jest/transform": "^24.7.1", + "@jest/types": "^24.7.0", + "chalk": "^2.0.1", + "exit": "^0.1.2", + "glob": "^7.1.2", + "istanbul-api": "^2.1.1", + "istanbul-lib-coverage": "^2.0.2", + "istanbul-lib-instrument": "^3.0.1", + "istanbul-lib-source-maps": "^3.0.1", + "jest-haste-map": "^24.7.1", + "jest-resolve": "^24.7.1", + "jest-runtime": "^24.7.1", + "jest-util": "^24.7.1", + "jest-worker": "^24.6.0", + "node-notifier": "^5.2.1", + "slash": "^2.0.0", + "source-map": "^0.6.0", + "string-length": "^2.0.0" + }, + "dependencies": { + "source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==" + } + } + }, + "@jest/source-map": { + "version": "24.3.0", + "resolved": "https://registry.npmjs.org/@jest/source-map/-/source-map-24.3.0.tgz", + "integrity": "sha512-zALZt1t2ou8le/crCeeiRYzvdnTzaIlpOWaet45lNSqNJUnXbppUUFR4ZUAlzgDmKee4Q5P/tKXypI1RiHwgag==", + "requires": { + "callsites": "^3.0.0", + "graceful-fs": "^4.1.15", + "source-map": "^0.6.0" + }, + "dependencies": { + "source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==" + } + } + }, + "@jest/test-result": { + "version": "24.7.1", + "resolved": "https://registry.npmjs.org/@jest/test-result/-/test-result-24.7.1.tgz", + "integrity": "sha512-3U7wITxstdEc2HMfBX7Yx3JZgiNBubwDqQMh+BXmZXHa3G13YWF3p6cK+5g0hGkN3iufg/vGPl3hLxQXD74Npg==", + "requires": { + "@jest/console": "^24.7.1", + "@jest/types": "^24.7.0", + "@types/istanbul-lib-coverage": "^2.0.0" + } + }, + "@jest/test-sequencer": { + "version": "24.7.1", + "resolved": "https://registry.npmjs.org/@jest/test-sequencer/-/test-sequencer-24.7.1.tgz", + "integrity": "sha512-84HQkCpVZI/G1zq53gHJvSmhUer4aMYp9tTaffW28Ih5OxfCg8hGr3nTSbL1OhVDRrFZwvF+/R9gY6JRkDUpUA==", + "requires": { + "@jest/test-result": "^24.7.1", + "jest-haste-map": "^24.7.1", + "jest-runner": "^24.7.1", + "jest-runtime": "^24.7.1" + } + }, + "@jest/transform": { + "version": "24.7.1", + "resolved": "https://registry.npmjs.org/@jest/transform/-/transform-24.7.1.tgz", + "integrity": "sha512-EsOUqP9ULuJ66IkZQhI5LufCHlTbi7hrcllRMUEV/tOgqBVQi93+9qEvkX0n8mYpVXQ8VjwmICeRgg58mrtIEw==", + "requires": { + "@babel/core": "^7.1.0", + "@jest/types": "^24.7.0", + "babel-plugin-istanbul": "^5.1.0", + "chalk": "^2.0.1", + "convert-source-map": "^1.4.0", + "fast-json-stable-stringify": "^2.0.0", + "graceful-fs": "^4.1.15", + "jest-haste-map": "^24.7.1", + "jest-regex-util": "^24.3.0", + "jest-util": "^24.7.1", + "micromatch": "^3.1.10", + "realpath-native": "^1.1.0", + "slash": "^2.0.0", + "source-map": "^0.6.1", + "write-file-atomic": "2.4.1" + }, + "dependencies": { + "source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==" + } + } + }, + "@jest/types": { + "version": "24.7.0", + "resolved": "https://registry.npmjs.org/@jest/types/-/types-24.7.0.tgz", + "integrity": "sha512-ipJUa2rFWiKoBqMKP63Myb6h9+iT3FHRTF2M8OR6irxWzItisa8i4dcSg14IbvmXUnBlHBlUQPYUHWyX3UPpYA==", + "requires": { + "@types/istanbul-lib-coverage": "^2.0.0", + "@types/yargs": "^12.0.9" + } + }, + "@types/babel__core": { + "version": "7.1.1", + "resolved": "https://registry.npmjs.org/@types/babel__core/-/babel__core-7.1.1.tgz", + "integrity": "sha512-+hjBtgcFPYyCTo0A15+nxrCVJL7aC6Acg87TXd5OW3QhHswdrOLoles+ldL2Uk8q++7yIfl4tURtztccdeeyOw==", + "requires": { + "@babel/parser": "^7.1.0", + "@babel/types": "^7.0.0", + "@types/babel__generator": "*", + "@types/babel__template": "*", + "@types/babel__traverse": "*" + } + }, + "@types/babel__generator": { + "version": "7.0.2", + "resolved": "https://registry.npmjs.org/@types/babel__generator/-/babel__generator-7.0.2.tgz", + "integrity": "sha512-NHcOfab3Zw4q5sEE2COkpfXjoE7o+PmqD9DQW4koUT3roNxwziUdXGnRndMat/LJNUtePwn1TlP4do3uoe3KZQ==", + "requires": { + "@babel/types": "^7.0.0" + } + }, + "@types/babel__template": { + "version": "7.0.2", + "resolved": "https://registry.npmjs.org/@types/babel__template/-/babel__template-7.0.2.tgz", + "integrity": "sha512-/K6zCpeW7Imzgab2bLkLEbz0+1JlFSrUMdw7KoIIu+IUdu51GWaBZpd3y1VXGVXzynvGa4DaIaxNZHiON3GXUg==", + "requires": { + "@babel/parser": "^7.1.0", + "@babel/types": "^7.0.0" + } + }, + "@types/babel__traverse": { + "version": "7.0.6", + "resolved": "https://registry.npmjs.org/@types/babel__traverse/-/babel__traverse-7.0.6.tgz", + "integrity": "sha512-XYVgHF2sQ0YblLRMLNPB3CkFMewzFmlDsH/TneZFHUXDlABQgh88uOxuez7ZcXxayLFrqLwtDH1t+FmlFwNZxw==", + "requires": { + "@babel/types": "^7.3.0" + } + }, + "@types/d3-path": { + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/@types/d3-path/-/d3-path-1.0.8.tgz", + "integrity": "sha512-AZGHWslq/oApTAHu9+yH/Bnk63y9oFOMROtqPAtxl5uB6qm1x2lueWdVEjsjjV3Qc2+QfuzKIwIR5MvVBakfzA==" + }, + "@types/d3-shape": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/@types/d3-shape/-/d3-shape-1.3.1.tgz", + "integrity": "sha512-usqdvUvPJ7AJNwpd2drOzRKs1ELie53p2m2GnPKr076/ADM579jVTJ5dPsoZ5E/CMNWk8lvPWYQSvilpp6jjwg==", + "requires": { + "@types/d3-path": "*" + } + }, + "@types/istanbul-lib-coverage": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/@types/istanbul-lib-coverage/-/istanbul-lib-coverage-2.0.0.tgz", + "integrity": "sha512-eAtOAFZefEnfJiRFQBGw1eYqa5GTLCZ1y86N0XSI/D6EB+E8z6VPV/UL7Gi5UEclFqoQk+6NRqEDsfmDLXn8sg==" + }, + "@types/jest": { + "version": "24.0.11", + "resolved": "https://registry.npmjs.org/@types/jest/-/jest-24.0.11.tgz", + "integrity": "sha512-2kLuPC5FDnWIDvaJBzsGTBQaBbnDweznicvK7UGYzlIJP4RJR2a4A/ByLUXEyEgag6jz8eHdlWExGDtH3EYUXQ==", + "requires": { + "@types/jest-diff": "*" + } + }, + "@types/jest-diff": { + "version": "20.0.1", + "resolved": "https://registry.npmjs.org/@types/jest-diff/-/jest-diff-20.0.1.tgz", + "integrity": "sha512-yALhelO3i0hqZwhjtcr6dYyaLoCHbAMshwtj6cGxTvHZAKXHsYGdff6E8EPw3xLKY0ELUTQ69Q1rQiJENnccMA==" + }, + "@types/stack-utils": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/@types/stack-utils/-/stack-utils-1.0.1.tgz", + "integrity": "sha512-l42BggppR6zLmpfU6fq9HEa2oGPEI8yrSPL3GITjfRInppYFahObbIQOQK3UGxEnyQpltZLaPe75046NOZQikw==" + }, + "@types/yargs": { + "version": "12.0.12", + "resolved": "https://registry.npmjs.org/@types/yargs/-/yargs-12.0.12.tgz", + "integrity": "sha512-SOhuU4wNBxhhTHxYaiG5NY4HBhDIDnJF60GU+2LqHAdKKer86//e4yg69aENCtQ04n0ovz+tq2YPME5t5yp4pw==" + }, + "@typescript-eslint/eslint-plugin": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-1.6.0.tgz", + "integrity": "sha512-U224c29E2lo861TQZs6GSmyC0OYeRNg6bE9UVIiFBxN2MlA0nq2dCrgIVyyRbC05UOcrgf2Wk/CF2gGOPQKUSQ==", + "requires": { + "@typescript-eslint/parser": "1.6.0", + "@typescript-eslint/typescript-estree": "1.6.0", + "requireindex": "^1.2.0", + "tsutils": "^3.7.0" + } + }, + "@typescript-eslint/parser": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-1.6.0.tgz", + "integrity": "sha512-VB9xmSbfafI+/kI4gUK3PfrkGmrJQfh0N4EScT1gZXSZyUxpsBirPL99EWZg9MmPG0pzq/gMtgkk7/rAHj4aQw==", + "requires": { + "@typescript-eslint/typescript-estree": "1.6.0", + "eslint-scope": "^4.0.0", + "eslint-visitor-keys": "^1.0.0" + } + }, + "@typescript-eslint/typescript-estree": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-1.6.0.tgz", + "integrity": "sha512-A4CanUwfaG4oXobD5y7EXbsOHjCwn8tj1RDd820etpPAjH+Icjc2K9e/DQM1Hac5zH2BSy+u6bjvvF2wwREvYA==", + "requires": { + "lodash.unescape": "4.0.1", + "semver": "5.5.0" + } + }, + "@webassemblyjs/ast": { + "version": "1.8.5", + "resolved": "https://registry.npmjs.org/@webassemblyjs/ast/-/ast-1.8.5.tgz", + "integrity": "sha512-aJMfngIZ65+t71C3y2nBBg5FFG0Okt9m0XEgWZ7Ywgn1oMAT8cNwx00Uv1cQyHtidq0Xn94R4TAywO+LCQ+ZAQ==", + "requires": { + "@webassemblyjs/helper-module-context": "1.8.5", + "@webassemblyjs/helper-wasm-bytecode": "1.8.5", + "@webassemblyjs/wast-parser": "1.8.5" + } + }, + "@webassemblyjs/floating-point-hex-parser": { + "version": "1.8.5", + "resolved": "https://registry.npmjs.org/@webassemblyjs/floating-point-hex-parser/-/floating-point-hex-parser-1.8.5.tgz", + "integrity": "sha512-9p+79WHru1oqBh9ewP9zW95E3XAo+90oth7S5Re3eQnECGq59ly1Ri5tsIipKGpiStHsUYmY3zMLqtk3gTcOtQ==" + }, + "@webassemblyjs/helper-api-error": { + "version": "1.8.5", + "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-api-error/-/helper-api-error-1.8.5.tgz", + "integrity": "sha512-Za/tnzsvnqdaSPOUXHyKJ2XI7PDX64kWtURyGiJJZKVEdFOsdKUCPTNEVFZq3zJ2R0G5wc2PZ5gvdTRFgm81zA==" + }, + "@webassemblyjs/helper-buffer": { + "version": "1.8.5", + "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-buffer/-/helper-buffer-1.8.5.tgz", + "integrity": "sha512-Ri2R8nOS0U6G49Q86goFIPNgjyl6+oE1abW1pS84BuhP1Qcr5JqMwRFT3Ah3ADDDYGEgGs1iyb1DGX+kAi/c/Q==" + }, + "@webassemblyjs/helper-code-frame": { + "version": "1.8.5", + "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-code-frame/-/helper-code-frame-1.8.5.tgz", + "integrity": "sha512-VQAadSubZIhNpH46IR3yWO4kZZjMxN1opDrzePLdVKAZ+DFjkGD/rf4v1jap744uPVU6yjL/smZbRIIJTOUnKQ==", + "requires": { + "@webassemblyjs/wast-printer": "1.8.5" + } + }, + "@webassemblyjs/helper-fsm": { + "version": "1.8.5", + "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-fsm/-/helper-fsm-1.8.5.tgz", + "integrity": "sha512-kRuX/saORcg8se/ft6Q2UbRpZwP4y7YrWsLXPbbmtepKr22i8Z4O3V5QE9DbZK908dh5Xya4Un57SDIKwB9eow==" + }, + "@webassemblyjs/helper-module-context": { + "version": "1.8.5", + "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-module-context/-/helper-module-context-1.8.5.tgz", + "integrity": "sha512-/O1B236mN7UNEU4t9X7Pj38i4VoU8CcMHyy3l2cV/kIF4U5KoHXDVqcDuOs1ltkac90IM4vZdHc52t1x8Yfs3g==", + "requires": { + "@webassemblyjs/ast": "1.8.5", + "mamacro": "^0.0.3" + } + }, + "@webassemblyjs/helper-wasm-bytecode": { + "version": "1.8.5", + "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-wasm-bytecode/-/helper-wasm-bytecode-1.8.5.tgz", + "integrity": "sha512-Cu4YMYG3Ddl72CbmpjU/wbP6SACcOPVbHN1dI4VJNJVgFwaKf1ppeFJrwydOG3NDHxVGuCfPlLZNyEdIYlQ6QQ==" + }, + "@webassemblyjs/helper-wasm-section": { + "version": "1.8.5", + "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-wasm-section/-/helper-wasm-section-1.8.5.tgz", + "integrity": "sha512-VV083zwR+VTrIWWtgIUpqfvVdK4ff38loRmrdDBgBT8ADXYsEZ5mPQ4Nde90N3UYatHdYoDIFb7oHzMncI02tA==", + "requires": { + "@webassemblyjs/ast": "1.8.5", + "@webassemblyjs/helper-buffer": "1.8.5", + "@webassemblyjs/helper-wasm-bytecode": "1.8.5", + "@webassemblyjs/wasm-gen": "1.8.5" + } + }, + "@webassemblyjs/ieee754": { + "version": "1.8.5", + "resolved": "https://registry.npmjs.org/@webassemblyjs/ieee754/-/ieee754-1.8.5.tgz", + "integrity": "sha512-aaCvQYrvKbY/n6wKHb/ylAJr27GglahUO89CcGXMItrOBqRarUMxWLJgxm9PJNuKULwN5n1csT9bYoMeZOGF3g==", + "requires": { + "@xtuc/ieee754": "^1.2.0" + } + }, + "@webassemblyjs/leb128": { + "version": "1.8.5", + "resolved": "https://registry.npmjs.org/@webassemblyjs/leb128/-/leb128-1.8.5.tgz", + "integrity": "sha512-plYUuUwleLIziknvlP8VpTgO4kqNaH57Y3JnNa6DLpu/sGcP6hbVdfdX5aHAV716pQBKrfuU26BJK29qY37J7A==", + "requires": { + "@xtuc/long": "4.2.2" + } + }, + "@webassemblyjs/utf8": { + "version": "1.8.5", + "resolved": "https://registry.npmjs.org/@webassemblyjs/utf8/-/utf8-1.8.5.tgz", + "integrity": "sha512-U7zgftmQriw37tfD934UNInokz6yTmn29inT2cAetAsaU9YeVCveWEwhKL1Mg4yS7q//NGdzy79nlXh3bT8Kjw==" + }, + "@webassemblyjs/wasm-edit": { + "version": "1.8.5", + "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-edit/-/wasm-edit-1.8.5.tgz", + "integrity": "sha512-A41EMy8MWw5yvqj7MQzkDjU29K7UJq1VrX2vWLzfpRHt3ISftOXqrtojn7nlPsZ9Ijhp5NwuODuycSvfAO/26Q==", + "requires": { + "@webassemblyjs/ast": "1.8.5", + "@webassemblyjs/helper-buffer": "1.8.5", + "@webassemblyjs/helper-wasm-bytecode": "1.8.5", + "@webassemblyjs/helper-wasm-section": "1.8.5", + "@webassemblyjs/wasm-gen": "1.8.5", + "@webassemblyjs/wasm-opt": "1.8.5", + "@webassemblyjs/wasm-parser": "1.8.5", + "@webassemblyjs/wast-printer": "1.8.5" + } + }, + "@webassemblyjs/wasm-gen": { + "version": "1.8.5", + "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-gen/-/wasm-gen-1.8.5.tgz", + "integrity": "sha512-BCZBT0LURC0CXDzj5FXSc2FPTsxwp3nWcqXQdOZE4U7h7i8FqtFK5Egia6f9raQLpEKT1VL7zr4r3+QX6zArWg==", + "requires": { + "@webassemblyjs/ast": "1.8.5", + "@webassemblyjs/helper-wasm-bytecode": "1.8.5", + "@webassemblyjs/ieee754": "1.8.5", + "@webassemblyjs/leb128": "1.8.5", + "@webassemblyjs/utf8": "1.8.5" + } + }, + "@webassemblyjs/wasm-opt": { + "version": "1.8.5", + "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-opt/-/wasm-opt-1.8.5.tgz", + "integrity": "sha512-HKo2mO/Uh9A6ojzu7cjslGaHaUU14LdLbGEKqTR7PBKwT6LdPtLLh9fPY33rmr5wcOMrsWDbbdCHq4hQUdd37Q==", + "requires": { + "@webassemblyjs/ast": "1.8.5", + "@webassemblyjs/helper-buffer": "1.8.5", + "@webassemblyjs/wasm-gen": "1.8.5", + "@webassemblyjs/wasm-parser": "1.8.5" + } + }, + "@webassemblyjs/wasm-parser": { + "version": "1.8.5", + "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-parser/-/wasm-parser-1.8.5.tgz", + "integrity": "sha512-pi0SYE9T6tfcMkthwcgCpL0cM9nRYr6/6fjgDtL6q/ZqKHdMWvxitRi5JcZ7RI4SNJJYnYNaWy5UUrHQy998lw==", + "requires": { + "@webassemblyjs/ast": "1.8.5", + "@webassemblyjs/helper-api-error": "1.8.5", + "@webassemblyjs/helper-wasm-bytecode": "1.8.5", + "@webassemblyjs/ieee754": "1.8.5", + "@webassemblyjs/leb128": "1.8.5", + "@webassemblyjs/utf8": "1.8.5" + } + }, + "@webassemblyjs/wast-parser": { + "version": "1.8.5", + "resolved": "https://registry.npmjs.org/@webassemblyjs/wast-parser/-/wast-parser-1.8.5.tgz", + "integrity": "sha512-daXC1FyKWHF1i11obK086QRlsMsY4+tIOKgBqI1lxAnkp9xe9YMcgOxm9kLe+ttjs5aWV2KKE1TWJCN57/Btsg==", + "requires": { + "@webassemblyjs/ast": "1.8.5", + "@webassemblyjs/floating-point-hex-parser": "1.8.5", + "@webassemblyjs/helper-api-error": "1.8.5", + "@webassemblyjs/helper-code-frame": "1.8.5", + "@webassemblyjs/helper-fsm": "1.8.5", + "@xtuc/long": "4.2.2" + } + }, + "@webassemblyjs/wast-printer": { + "version": "1.8.5", + "resolved": "https://registry.npmjs.org/@webassemblyjs/wast-printer/-/wast-printer-1.8.5.tgz", + "integrity": "sha512-w0U0pD4EhlnvRyeJzBqaVSJAo9w/ce7/WPogeXLzGkO6hzhr4GnQIZ4W4uUt5b9ooAaXPtnXlj0gzsXEOUNYMg==", + "requires": { + "@webassemblyjs/ast": "1.8.5", + "@webassemblyjs/wast-parser": "1.8.5", + "@xtuc/long": "4.2.2" + } + }, + "@xtuc/ieee754": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/@xtuc/ieee754/-/ieee754-1.2.0.tgz", + "integrity": "sha512-DX8nKgqcGwsc0eJSqYt5lwP4DH5FlHnmuWWBRy7X0NcaGR0ZtuyeESgMwTYVEtxmsNGY+qit4QYT/MIYTOTPeA==" + }, + "@xtuc/long": { + "version": "4.2.2", + "resolved": "https://registry.npmjs.org/@xtuc/long/-/long-4.2.2.tgz", + "integrity": "sha512-NuHqBY1PB/D8xU6s/thBgOAiAP7HOYDQ32+BFZILJ8ivkUkAHQnWfn6WhL79Owj1qmUnoN/YPhktdIoucipkAQ==" + }, + "abab": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/abab/-/abab-2.0.0.tgz", + "integrity": "sha512-sY5AXXVZv4Y1VACTtR11UJCPHHudgY5i26Qj5TypE6DKlIApbwb5uqhXcJ5UUGbvZNRh7EeIoW+LrJumBsKp7w==" + }, + "accepts": { + "version": "1.3.5", + "resolved": "https://registry.npmjs.org/accepts/-/accepts-1.3.5.tgz", + "integrity": "sha1-63d99gEXI6OxTopywIBcjoZ0a9I=", + "requires": { + "mime-types": "~2.1.18", + "negotiator": "0.6.1" + } + }, + "acorn": { + "version": "6.1.1", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-6.1.1.tgz", + "integrity": "sha512-jPTiwtOxaHNaAPg/dmrJ/beuzLRnXtB0kQPQ8JpotKJgTB6rX6c8mlf315941pyjBSaPg8NHXS9fhP4u17DpGA==" + }, + "acorn-dynamic-import": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/acorn-dynamic-import/-/acorn-dynamic-import-4.0.0.tgz", + "integrity": "sha512-d3OEjQV4ROpoflsnUA8HozoIR504TFxNivYEUi6uwz0IYhBkTDXGuWlNdMtybRt3nqVx/L6XqMt0FxkXuWKZhw==" + }, + "acorn-globals": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/acorn-globals/-/acorn-globals-4.3.0.tgz", + "integrity": "sha512-hMtHj3s5RnuhvHPowpBYvJVj3rAar82JiDQHvGs1zO0l10ocX/xEdBShNHTJaboucJUsScghp74pH3s7EnHHQw==", + "requires": { + "acorn": "^6.0.1", + "acorn-walk": "^6.0.1" + } + }, + "acorn-jsx": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/acorn-jsx/-/acorn-jsx-5.0.1.tgz", + "integrity": "sha512-HJ7CfNHrfJLlNTzIEUTj43LNWGkqpRLxm3YjAlcD0ACydk9XynzYsCBHxut+iqt+1aBXkx9UP/w/ZqMr13XIzg==" + }, + "acorn-walk": { + "version": "6.1.1", + "resolved": "https://registry.npmjs.org/acorn-walk/-/acorn-walk-6.1.1.tgz", + "integrity": "sha512-OtUw6JUTgxA2QoqqmrmQ7F2NYqiBPi/L2jqHyFtllhOUvXYQXf0Z1CYUinIfyT4bTCGmrA7gX9FvHA81uzCoVw==" + }, + "ajv": { + "version": "6.9.1", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.9.1.tgz", + "integrity": "sha512-XDN92U311aINL77ieWHmqCcNlwjoP5cHXDxIxbf2MaPYuCXOHS7gHH8jktxeK5omgd52XbSTX6a4Piwd1pQmzA==", + "requires": { + "fast-deep-equal": "^2.0.1", + "fast-json-stable-stringify": "^2.0.0", + "json-schema-traverse": "^0.4.1", + "uri-js": "^4.2.2" + } + }, + "ajv-errors": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/ajv-errors/-/ajv-errors-1.0.1.tgz", + "integrity": "sha512-DCRfO/4nQ+89p/RK43i8Ezd41EqdGIU4ld7nGF8OQ14oc/we5rEntLCUa7+jrn3nn83BosfwZA0wb4pon2o8iQ==" + }, + "ajv-keywords": { + "version": "3.4.0", + "resolved": "https://registry.npmjs.org/ajv-keywords/-/ajv-keywords-3.4.0.tgz", + "integrity": "sha512-aUjdRFISbuFOl0EIZc+9e4FfZp0bDZgAdOOf30bJmw8VM9v84SHyVyxDfbWxpGYbdZD/9XoKxfHVNmxPkhwyGw==" + }, + "ansi-colors": { + "version": "3.2.4", + "resolved": "https://registry.npmjs.org/ansi-colors/-/ansi-colors-3.2.4.tgz", + "integrity": "sha512-hHUXGagefjN2iRrID63xckIvotOXOojhQKWIPUZ4mNUZ9nLZW+7FMNoE1lOkEhNWYsx/7ysGIuJYCiMAA9FnrA==" + }, + "ansi-escapes": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/ansi-escapes/-/ansi-escapes-3.2.0.tgz", + "integrity": "sha512-cBhpre4ma+U0T1oM5fXg7Dy1Jw7zzwv7lt/GoCpr+hDQJoYnKVPLL4dCvSEFMmQurOQvSrwT7SL/DAlhBI97RQ==" + }, + "ansi-html": { + "version": "0.0.7", + "resolved": "https://registry.npmjs.org/ansi-html/-/ansi-html-0.0.7.tgz", + "integrity": "sha1-gTWEAhliqenm/QOflA0S9WynhZ4=" + }, + "ansi-regex": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-3.0.0.tgz", + "integrity": "sha1-7QMXwyIGT3lGbAKWa922Bas32Zg=" + }, + "ansi-styles": { + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", + "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", + "requires": { + "color-convert": "^1.9.0" + } + }, + "anymatch": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-2.0.0.tgz", + "integrity": "sha512-5teOsQWABXHHBFP9y3skS5P3d/WfWXpv3FUpy+LorMrNYaT9pI4oLMQX7jzQ2KklNpGpWHzdCXTDT2Y3XGlZBw==", + "requires": { + "micromatch": "^3.1.4", + "normalize-path": "^2.1.1" + }, + "dependencies": { + "normalize-path": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-2.1.1.tgz", + "integrity": "sha1-GrKLVW4Zg2Oowab35vogE3/mrtk=", + "requires": { + "remove-trailing-separator": "^1.0.1" + } + } + } + }, + "append-transform": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/append-transform/-/append-transform-1.0.0.tgz", + "integrity": "sha512-P009oYkeHyU742iSZJzZZywj4QRJdnTWffaKuJQLablCZ1uz6/cW4yaRgcDaoQ+uwOxxnt0gRUcwfsNP2ri0gw==", + "requires": { + "default-require-extensions": "^2.0.0" + } + }, + "aproba": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/aproba/-/aproba-1.2.0.tgz", + "integrity": "sha512-Y9J6ZjXtoYh8RnXVCMOU/ttDmk1aBjunq9vO0ta5x85WDQiQfUF9sIPBITdbiiIVcBo03Hi3jMxigBtsddlXRw==" + }, + "argparse": { + "version": "1.0.10", + "resolved": "https://registry.npmjs.org/argparse/-/argparse-1.0.10.tgz", + "integrity": "sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg==", + "requires": { + "sprintf-js": "~1.0.2" + } + }, + "arr-diff": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/arr-diff/-/arr-diff-4.0.0.tgz", + "integrity": "sha1-1kYQdP6/7HHn4VI1dhoyml3HxSA=" + }, + "arr-flatten": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/arr-flatten/-/arr-flatten-1.1.0.tgz", + "integrity": "sha512-L3hKV5R/p5o81R7O02IGnwpDmkp6E982XhtbuwSe3O4qOtMMMtodicASA1Cny2U+aCXcNpml+m4dPsvsJ3jatg==" + }, + "arr-union": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/arr-union/-/arr-union-3.1.0.tgz", + "integrity": "sha1-45sJrqne+Gao8gbiiK9jkZuuOcQ=" + }, + "array-equal": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/array-equal/-/array-equal-1.0.0.tgz", + "integrity": "sha1-jCpe8kcv2ep0KwTHenUJO6J1fJM=" + }, + "array-flatten": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/array-flatten/-/array-flatten-2.1.2.tgz", + "integrity": "sha512-hNfzcOV8W4NdualtqBFPyVO+54DSJuZGY9qT4pRroB6S9e3iiido2ISIC5h9R2sPJ8H3FHCIiEnsv1lPXO3KtQ==" + }, + "array-union": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/array-union/-/array-union-1.0.2.tgz", + "integrity": "sha1-mjRBDk9OPaI96jdb5b5w8kd47Dk=", + "requires": { + "array-uniq": "^1.0.1" + } + }, + "array-uniq": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/array-uniq/-/array-uniq-1.0.3.tgz", + "integrity": "sha1-r2rId6Jcx/dOBYiUdThY39sk/bY=" + }, + "array-unique": { + "version": "0.3.2", + "resolved": "https://registry.npmjs.org/array-unique/-/array-unique-0.3.2.tgz", + "integrity": "sha1-qJS3XUvE9s1nnvMkSp/Y9Gri1Cg=" + }, + "arrify": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/arrify/-/arrify-1.0.1.tgz", + "integrity": "sha1-iYUI2iIm84DfkEcoRWhJwVAaSw0=" + }, + "asn1": { + "version": "0.2.4", + "resolved": "https://registry.npmjs.org/asn1/-/asn1-0.2.4.tgz", + "integrity": "sha512-jxwzQpLQjSmWXgwaCZE9Nz+glAG01yF1QnWgbhGwHI5A6FRIEY6IVqtHhIepHqI7/kyEyQEagBC5mBEFlIYvdg==", + "requires": { + "safer-buffer": "~2.1.0" + } + }, + "asn1.js": { + "version": "4.10.1", + "resolved": "https://registry.npmjs.org/asn1.js/-/asn1.js-4.10.1.tgz", + "integrity": "sha512-p32cOF5q0Zqs9uBiONKYLm6BClCoBCM5O9JfeUSlnQLBTxYdTK+pW+nXflm8UkKd2UYlEbYz5qEi0JuZR9ckSw==", + "requires": { + "bn.js": "^4.0.0", + "inherits": "^2.0.1", + "minimalistic-assert": "^1.0.0" + } + }, + "assert": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/assert/-/assert-1.4.1.tgz", + "integrity": "sha1-mZEtWRg2tab1s0XA8H7vwI/GXZE=", + "requires": { + "util": "0.10.3" + }, + "dependencies": { + "inherits": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.1.tgz", + "integrity": "sha1-sX0I0ya0Qj5Wjv9xn5GwscvfafE=" + }, + "util": { + "version": "0.10.3", + "resolved": "https://registry.npmjs.org/util/-/util-0.10.3.tgz", + "integrity": "sha1-evsa/lCAUkZInj23/g7TeTNqwPk=", + "requires": { + "inherits": "2.0.1" + } + } + } + }, + "assert-plus": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/assert-plus/-/assert-plus-1.0.0.tgz", + "integrity": "sha1-8S4PPF13sLHN2RRpQuTpbB5N1SU=" + }, + "assign-symbols": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/assign-symbols/-/assign-symbols-1.0.0.tgz", + "integrity": "sha1-WWZ/QfrdTyDMvCu5a41Pf3jsA2c=" + }, + "astral-regex": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/astral-regex/-/astral-regex-1.0.0.tgz", + "integrity": "sha512-+Ryf6g3BKoRc7jfp7ad8tM4TtMiaWvbF/1/sQcZPkkS7ag3D5nMBCe2UfOTONtAkaG0tO0ij3C5Lwmf1EiyjHg==" + }, + "async": { + "version": "2.6.2", + "resolved": "https://registry.npmjs.org/async/-/async-2.6.2.tgz", + "integrity": "sha512-H1qVYh1MYhEEFLsP97cVKqCGo7KfCyTt6uEWqsTBr9SO84oK9Uwbyd/yCW+6rKJLHksBNUVWZDAjfS+Ccx0Bbg==", + "requires": { + "lodash": "^4.17.11" + } + }, + "async-each": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/async-each/-/async-each-1.0.1.tgz", + "integrity": "sha1-GdOGodntxufByF04iu28xW0zYC0=" + }, + "async-limiter": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/async-limiter/-/async-limiter-1.0.0.tgz", + "integrity": "sha512-jp/uFnooOiO+L211eZOoSyzpOITMXx1rBITauYykG3BRYPu8h0UcxsPNB04RR5vo4Tyz3+ay17tR6JVf9qzYWg==" + }, + "asynckit": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz", + "integrity": "sha1-x57Zf380y48robyXkLzDZkdLS3k=" + }, + "atob": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/atob/-/atob-2.1.2.tgz", + "integrity": "sha512-Wm6ukoaOGJi/73p/cl2GvLjTI5JM1k/O14isD73YML8StrH/7/lRFgmg8nICZgD3bZZvjwCGxtMOD3wWNAu8cg==" + }, + "awesome-typescript-loader": { + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/awesome-typescript-loader/-/awesome-typescript-loader-5.2.1.tgz", + "integrity": "sha512-slv66OAJB8orL+UUaTI3pKlLorwIvS4ARZzYR9iJJyGsEgOqueMfOMdKySWzZ73vIkEe3fcwFgsKMg4d8zyb1g==", + "requires": { + "chalk": "^2.4.1", + "enhanced-resolve": "^4.0.0", + "loader-utils": "^1.1.0", + "lodash": "^4.17.5", + "micromatch": "^3.1.9", + "mkdirp": "^0.5.1", + "source-map-support": "^0.5.3", + "webpack-log": "^1.2.0" + } + }, + "aws-sign2": { + "version": "0.7.0", + "resolved": "https://registry.npmjs.org/aws-sign2/-/aws-sign2-0.7.0.tgz", + "integrity": "sha1-tG6JCTSpWR8tL2+G1+ap8bP+dqg=" + }, + "aws4": { + "version": "1.8.0", + "resolved": "https://registry.npmjs.org/aws4/-/aws4-1.8.0.tgz", + "integrity": "sha512-ReZxvNHIOv88FlT7rxcXIIC0fPt4KZqZbOlivyWtXLt8ESx84zd3kMC6iK5jVeS2qt+g7ftS7ye4fi06X5rtRQ==" + }, + "babel-jest": { + "version": "24.7.1", + "resolved": "https://registry.npmjs.org/babel-jest/-/babel-jest-24.7.1.tgz", + "integrity": "sha512-GPnLqfk8Mtt0i4OemjWkChi73A3ALs4w2/QbG64uAj8b5mmwzxc7jbJVRZt8NJkxi6FopVHog9S3xX6UJKb2qg==", + "requires": { + "@jest/transform": "^24.7.1", + "@jest/types": "^24.7.0", + "@types/babel__core": "^7.1.0", + "babel-plugin-istanbul": "^5.1.0", + "babel-preset-jest": "^24.6.0", + "chalk": "^2.4.2", + "slash": "^2.0.0" + } + }, + "babel-plugin-istanbul": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/babel-plugin-istanbul/-/babel-plugin-istanbul-5.1.1.tgz", + "integrity": "sha512-RNNVv2lsHAXJQsEJ5jonQwrJVWK8AcZpG1oxhnjCUaAjL7xahYLANhPUZbzEQHjKy1NMYUwn+0NPKQc8iSY4xQ==", + "requires": { + "find-up": "^3.0.0", + "istanbul-lib-instrument": "^3.0.0", + "test-exclude": "^5.0.0" + } + }, + "babel-plugin-jest-hoist": { + "version": "24.6.0", + "resolved": "https://registry.npmjs.org/babel-plugin-jest-hoist/-/babel-plugin-jest-hoist-24.6.0.tgz", + "integrity": "sha512-3pKNH6hMt9SbOv0F3WVmy5CWQ4uogS3k0GY5XLyQHJ9EGpAT9XWkFd2ZiXXtkwFHdAHa5j7w7kfxSP5lAIwu7w==", + "requires": { + "@types/babel__traverse": "^7.0.6" + } + }, + "babel-preset-jest": { + "version": "24.6.0", + "resolved": "https://registry.npmjs.org/babel-preset-jest/-/babel-preset-jest-24.6.0.tgz", + "integrity": "sha512-pdZqLEdmy1ZK5kyRUfvBb2IfTPb2BUvIJczlPspS8fWmBQslNNDBqVfh7BW5leOVJMDZKzjD8XEyABTk6gQ5yw==", + "requires": { + "@babel/plugin-syntax-object-rest-spread": "^7.0.0", + "babel-plugin-jest-hoist": "^24.6.0" + } + }, + "balanced-match": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.0.tgz", + "integrity": "sha1-ibTRmasr7kneFk6gK4nORi1xt2c=" + }, + "base": { + "version": "0.11.2", + "resolved": "https://registry.npmjs.org/base/-/base-0.11.2.tgz", + "integrity": "sha512-5T6P4xPgpp0YDFvSWwEZ4NoE3aM4QBQXDzmVbraCkFj8zHM+mba8SyqB5DbZWyR7mYHo6Y7BdQo3MoA4m0TeQg==", + "requires": { + "cache-base": "^1.0.1", + "class-utils": "^0.3.5", + "component-emitter": "^1.2.1", + "define-property": "^1.0.0", + "isobject": "^3.0.1", + "mixin-deep": "^1.2.0", + "pascalcase": "^0.1.1" + }, + "dependencies": { + "define-property": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/define-property/-/define-property-1.0.0.tgz", + "integrity": "sha1-dp66rz9KY6rTr56NMEybvnm/sOY=", + "requires": { + "is-descriptor": "^1.0.0" + } + }, + "is-accessor-descriptor": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-1.0.0.tgz", + "integrity": "sha512-m5hnHTkcVsPfqx3AKlyttIPb7J+XykHvJP2B9bZDjlhLIoEq4XoK64Vg7boZlVWYK6LUY94dYPEE7Lh0ZkZKcQ==", + "requires": { + "kind-of": "^6.0.0" + } + }, + "is-data-descriptor": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-1.0.0.tgz", + "integrity": "sha512-jbRXy1FmtAoCjQkVmIVYwuuqDFUbaOeDjmed1tOGPrsMhtJA4rD9tkgA0F1qJ3gRFRXcHYVkdeaP50Q5rE/jLQ==", + "requires": { + "kind-of": "^6.0.0" + } + }, + "is-descriptor": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-1.0.2.tgz", + "integrity": "sha512-2eis5WqQGV7peooDyLmNEPUrps9+SXX5c9pL3xEB+4e9HnGuDa7mB7kHxHw4CbqS9k1T2hOH3miL8n8WtiYVtg==", + "requires": { + "is-accessor-descriptor": "^1.0.0", + "is-data-descriptor": "^1.0.0", + "kind-of": "^6.0.2" + } + } + } + }, + "base64-js": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/base64-js/-/base64-js-1.3.0.tgz", + "integrity": "sha512-ccav/yGvoa80BQDljCxsmmQ3Xvx60/UpBIij5QN21W3wBi/hhIC9OoO+KLpu9IJTS9j4DRVJ3aDDF9cMSoa2lw==" + }, + "batch": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/batch/-/batch-0.6.1.tgz", + "integrity": "sha1-3DQxT05nkxgJP8dgJyUl+UvyXBY=" + }, + "bcrypt-pbkdf": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/bcrypt-pbkdf/-/bcrypt-pbkdf-1.0.2.tgz", + "integrity": "sha1-pDAdOJtqQ/m2f/PKEaP2Y342Dp4=", + "requires": { + "tweetnacl": "^0.14.3" + } + }, + "big.js": { + "version": "5.2.2", + "resolved": "https://registry.npmjs.org/big.js/-/big.js-5.2.2.tgz", + "integrity": "sha512-vyL2OymJxmarO8gxMr0mhChsO9QGwhynfuu4+MHTAW6czfq9humCB7rKpUjDd9YUiDPU4mzpyupFSvOClAwbmQ==" + }, + "binary-extensions": { + "version": "1.13.0", + "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-1.13.0.tgz", + "integrity": "sha512-EgmjVLMn22z7eGGv3kcnHwSnJXmFHjISTY9E/S5lIcTD3Oxw05QTcBLNkJFzcb3cNueUdF/IN4U+d78V0zO8Hw==" + }, + "bluebird": { + "version": "3.5.3", + "resolved": "https://registry.npmjs.org/bluebird/-/bluebird-3.5.3.tgz", + "integrity": "sha512-/qKPUQlaW1OyR51WeCPBvRnAlnZFUJkCSG5HzGnuIqhgyJtF+T94lFnn33eiazjRm2LAHVy2guNnaq48X9SJuw==" + }, + "bn.js": { + "version": "4.11.8", + "resolved": "https://registry.npmjs.org/bn.js/-/bn.js-4.11.8.tgz", + "integrity": "sha512-ItfYfPLkWHUjckQCk8xC+LwxgK8NYcXywGigJgSwOP8Y2iyWT4f2vsZnoOXTTbo+o5yXmIUJ4gn5538SO5S3gA==" + }, + "body-parser": { + "version": "1.18.3", + "resolved": "https://registry.npmjs.org/body-parser/-/body-parser-1.18.3.tgz", + "integrity": "sha1-WykhmP/dVTs6DyDe0FkrlWlVyLQ=", + "requires": { + "bytes": "3.0.0", + "content-type": "~1.0.4", + "debug": "2.6.9", + "depd": "~1.1.2", + "http-errors": "~1.6.3", + "iconv-lite": "0.4.23", + "on-finished": "~2.3.0", + "qs": "6.5.2", + "raw-body": "2.3.3", + "type-is": "~1.6.16" + }, + "dependencies": { + "iconv-lite": { + "version": "0.4.23", + "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.23.tgz", + "integrity": "sha512-neyTUVFtahjf0mB3dZT77u+8O0QB89jFdnBkd5P1JgYPbPaia3gXXOVL2fq8VyU2gMMD7SaN7QukTB/pmXYvDA==", + "requires": { + "safer-buffer": ">= 2.1.2 < 3" + } + } + } + }, + "bonjour": { + "version": "3.5.0", + "resolved": "https://registry.npmjs.org/bonjour/-/bonjour-3.5.0.tgz", + "integrity": "sha1-jokKGD2O6aI5OzhExpGkK897yfU=", + "requires": { + "array-flatten": "^2.1.0", + "deep-equal": "^1.0.1", + "dns-equal": "^1.0.0", + "dns-txt": "^2.0.2", + "multicast-dns": "^6.0.1", + "multicast-dns-service-types": "^1.1.0" + } + }, + "brace-expansion": { + "version": "1.1.11", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", + "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", + "requires": { + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" + } + }, + "braces": { + "version": "2.3.2", + "resolved": "https://registry.npmjs.org/braces/-/braces-2.3.2.tgz", + "integrity": "sha512-aNdbnj9P8PjdXU4ybaWLK2IF3jc/EoDYbC7AazW6to3TRsfXxscC9UXOB5iDiEQrkyIbWp2SLQda4+QAa7nc3w==", + "requires": { + "arr-flatten": "^1.1.0", + "array-unique": "^0.3.2", + "extend-shallow": "^2.0.1", + "fill-range": "^4.0.0", + "isobject": "^3.0.1", + "repeat-element": "^1.1.2", + "snapdragon": "^0.8.1", + "snapdragon-node": "^2.0.1", + "split-string": "^3.0.2", + "to-regex": "^3.0.1" + }, + "dependencies": { + "extend-shallow": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", + "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", + "requires": { + "is-extendable": "^0.1.0" + } + } + } + }, + "brorand": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/brorand/-/brorand-1.1.0.tgz", + "integrity": "sha1-EsJe/kCkXjwyPrhnWgoM5XsiNx8=" + }, + "browser-process-hrtime": { + "version": "0.1.3", + "resolved": "https://registry.npmjs.org/browser-process-hrtime/-/browser-process-hrtime-0.1.3.tgz", + "integrity": "sha512-bRFnI4NnjO6cnyLmOV/7PVoDEMJChlcfN0z4s1YMBY989/SvlfMI1lgCnkFUs53e9gQF+w7qu7XdllSTiSl8Aw==" + }, + "browser-resolve": { + "version": "1.11.3", + "resolved": "https://registry.npmjs.org/browser-resolve/-/browser-resolve-1.11.3.tgz", + "integrity": "sha512-exDi1BYWB/6raKHmDTCicQfTkqwN5fioMFV4j8BsfMU4R2DK/QfZfK7kOVkmWCNANf0snkBzqGqAJBao9gZMdQ==", + "requires": { + "resolve": "1.1.7" + }, + "dependencies": { + "resolve": { + "version": "1.1.7", + "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.1.7.tgz", + "integrity": "sha1-IDEU2CrSxe2ejgQRs5ModeiJ6Xs=" + } + } + }, + "browserify-aes": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/browserify-aes/-/browserify-aes-1.2.0.tgz", + "integrity": "sha512-+7CHXqGuspUn/Sl5aO7Ea0xWGAtETPXNSAjHo48JfLdPWcMng33Xe4znFvQweqc/uzk5zSOI3H52CYnjCfb5hA==", + "requires": { + "buffer-xor": "^1.0.3", + "cipher-base": "^1.0.0", + "create-hash": "^1.1.0", + "evp_bytestokey": "^1.0.3", + "inherits": "^2.0.1", + "safe-buffer": "^5.0.1" + } + }, + "browserify-cipher": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/browserify-cipher/-/browserify-cipher-1.0.1.tgz", + "integrity": "sha512-sPhkz0ARKbf4rRQt2hTpAHqn47X3llLkUGn+xEJzLjwY8LRs2p0v7ljvI5EyoRO/mexrNunNECisZs+gw2zz1w==", + "requires": { + "browserify-aes": "^1.0.4", + "browserify-des": "^1.0.0", + "evp_bytestokey": "^1.0.0" + } + }, + "browserify-des": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/browserify-des/-/browserify-des-1.0.2.tgz", + "integrity": "sha512-BioO1xf3hFwz4kc6iBhI3ieDFompMhrMlnDFC4/0/vd5MokpuAc3R+LYbwTA9A5Yc9pq9UYPqffKpW2ObuwX5A==", + "requires": { + "cipher-base": "^1.0.1", + "des.js": "^1.0.0", + "inherits": "^2.0.1", + "safe-buffer": "^5.1.2" + } + }, + "browserify-rsa": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/browserify-rsa/-/browserify-rsa-4.0.1.tgz", + "integrity": "sha1-IeCr+vbyApzy+vsTNWenAdQTVSQ=", + "requires": { + "bn.js": "^4.1.0", + "randombytes": "^2.0.1" + } + }, + "browserify-sign": { + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/browserify-sign/-/browserify-sign-4.0.4.tgz", + "integrity": "sha1-qk62jl17ZYuqa/alfmMMvXqT0pg=", + "requires": { + "bn.js": "^4.1.1", + "browserify-rsa": "^4.0.0", + "create-hash": "^1.1.0", + "create-hmac": "^1.1.2", + "elliptic": "^6.0.0", + "inherits": "^2.0.1", + "parse-asn1": "^5.0.0" + } + }, + "browserify-zlib": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/browserify-zlib/-/browserify-zlib-0.2.0.tgz", + "integrity": "sha512-Z942RysHXmJrhqk88FmKBVq/v5tqmSkDz7p54G/MGyjMnCFFnC79XWNbg+Vta8W6Wb2qtSZTSxIGkJrRpCFEiA==", + "requires": { + "pako": "~1.0.5" + } + }, + "bs-logger": { + "version": "0.2.6", + "resolved": "https://registry.npmjs.org/bs-logger/-/bs-logger-0.2.6.tgz", + "integrity": "sha512-pd8DCoxmbgc7hyPKOvxtqNcjYoOsABPQdcCUjGp3d42VR2CX1ORhk2A87oqqu5R1kk+76nsxZupkmyd+MVtCog==", + "requires": { + "fast-json-stable-stringify": "2.x" + } + }, + "bser": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/bser/-/bser-2.0.0.tgz", + "integrity": "sha1-mseNPtXZFYBP2HrLFYvHlxR6Fxk=", + "requires": { + "node-int64": "^0.4.0" + } + }, + "buffer": { + "version": "4.9.1", + "resolved": "https://registry.npmjs.org/buffer/-/buffer-4.9.1.tgz", + "integrity": "sha1-bRu2AbB6TvztlwlBMgkwJ8lbwpg=", + "requires": { + "base64-js": "^1.0.2", + "ieee754": "^1.1.4", + "isarray": "^1.0.0" + } + }, + "buffer-from": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.1.tgz", + "integrity": "sha512-MQcXEUbCKtEo7bhqEs6560Hyd4XaovZlO/k9V3hjVUF/zwW7KBVdSK4gIt/bzwS9MbR5qob+F5jusZsb0YQK2A==" + }, + "buffer-indexof": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/buffer-indexof/-/buffer-indexof-1.1.1.tgz", + "integrity": "sha512-4/rOEg86jivtPTeOUUT61jJO1Ya1TrR/OkqCSZDyq84WJh3LuuiphBYJN+fm5xufIk4XAFcEwte/8WzC8If/1g==" + }, + "buffer-xor": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/buffer-xor/-/buffer-xor-1.0.3.tgz", + "integrity": "sha1-JuYe0UIvtw3ULm42cp7VHYVf6Nk=" + }, + "builtin-status-codes": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/builtin-status-codes/-/builtin-status-codes-3.0.0.tgz", + "integrity": "sha1-hZgoeOIbmOHGZCXgPQF0eI9Wnug=" + }, + "bytes": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.0.0.tgz", + "integrity": "sha1-0ygVQE1olpn4Wk6k+odV3ROpYEg=" + }, + "cacache": { + "version": "11.3.2", + "resolved": "https://registry.npmjs.org/cacache/-/cacache-11.3.2.tgz", + "integrity": "sha512-E0zP4EPGDOaT2chM08Als91eYnf8Z+eH1awwwVsngUmgppfM5jjJ8l3z5vO5p5w/I3LsiXawb1sW0VY65pQABg==", + "requires": { + "bluebird": "^3.5.3", + "chownr": "^1.1.1", + "figgy-pudding": "^3.5.1", + "glob": "^7.1.3", + "graceful-fs": "^4.1.15", + "lru-cache": "^5.1.1", + "mississippi": "^3.0.0", + "mkdirp": "^0.5.1", + "move-concurrently": "^1.0.1", + "promise-inflight": "^1.0.1", + "rimraf": "^2.6.2", + "ssri": "^6.0.1", + "unique-filename": "^1.1.1", + "y18n": "^4.0.0" + } + }, + "cache-base": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/cache-base/-/cache-base-1.0.1.tgz", + "integrity": "sha512-AKcdTnFSWATd5/GCPRxr2ChwIJ85CeyrEyjRHlKxQ56d4XJMGym0uAiKn0xbLOGOl3+yRpOTi484dVCEc5AUzQ==", + "requires": { + "collection-visit": "^1.0.0", + "component-emitter": "^1.2.1", + "get-value": "^2.0.6", + "has-value": "^1.0.0", + "isobject": "^3.0.1", + "set-value": "^2.0.0", + "to-object-path": "^0.3.0", + "union-value": "^1.0.0", + "unset-value": "^1.0.0" + } + }, + "callsites": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/callsites/-/callsites-3.0.0.tgz", + "integrity": "sha512-tWnkwu9YEq2uzlBDI4RcLn8jrFvF9AOi8PxDNU3hZZjJcjkcRAq3vCI+vZcg1SuxISDYe86k9VZFwAxDiJGoAw==" + }, + "camelcase": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-5.2.0.tgz", + "integrity": "sha512-IXFsBS2pC+X0j0N/GE7Dm7j3bsEBp+oTpb7F50dwEVX7rf3IgwO9XatnegTsDtniKCUtEJH4fSU6Asw7uoVLfQ==" + }, + "capture-exit": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/capture-exit/-/capture-exit-2.0.0.tgz", + "integrity": "sha512-PiT/hQmTonHhl/HFGN+Lx3JJUznrVYJ3+AQsnthneZbvW7x+f08Tk7yLJTLEOUvBTbduLeeBkxEaYXUOUrRq6g==", + "requires": { + "rsvp": "^4.8.4" + } + }, + "caseless": { + "version": "0.12.0", + "resolved": "https://registry.npmjs.org/caseless/-/caseless-0.12.0.tgz", + "integrity": "sha1-G2gcIf+EAzyCZUMJBolCDRhxUdw=" + }, + "chalk": { + "version": "2.4.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz", + "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==", + "requires": { + "ansi-styles": "^3.2.1", + "escape-string-regexp": "^1.0.5", + "supports-color": "^5.3.0" + } + }, + "chardet": { + "version": "0.7.0", + "resolved": "https://registry.npmjs.org/chardet/-/chardet-0.7.0.tgz", + "integrity": "sha512-mT8iDcrh03qDGRRmoA2hmBJnxpllMR+0/0qlzjqZES6NdiWDcZkCNAk4rPFZ9Q85r27unkiNNg8ZOiwZXBHwcA==" + }, + "chokidar": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-2.1.2.tgz", + "integrity": "sha512-IwXUx0FXc5ibYmPC2XeEj5mpXoV66sR+t3jqu2NS2GYwCktt3KF1/Qqjws/NkegajBA4RbZ5+DDwlOiJsxDHEg==", + "requires": { + "anymatch": "^2.0.0", + "async-each": "^1.0.1", + "braces": "^2.3.2", + "fsevents": "^1.2.7", + "glob-parent": "^3.1.0", + "inherits": "^2.0.3", + "is-binary-path": "^1.0.0", + "is-glob": "^4.0.0", + "normalize-path": "^3.0.0", + "path-is-absolute": "^1.0.0", + "readdirp": "^2.2.1", + "upath": "^1.1.0" + } + }, + "chownr": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/chownr/-/chownr-1.1.1.tgz", + "integrity": "sha512-j38EvO5+LHX84jlo6h4UzmOwi0UgW61WRyPtJz4qaadK5eY3BTS5TY/S1Stc3Uk2lIM6TPevAlULiEJwie860g==" + }, + "chrome-trace-event": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/chrome-trace-event/-/chrome-trace-event-1.0.0.tgz", + "integrity": "sha512-xDbVgyfDTT2piup/h8dK/y4QZfJRSa73bw1WZ8b4XM1o7fsFubUVGYcE+1ANtOzJJELGpYoG2961z0Z6OAld9A==", + "requires": { + "tslib": "^1.9.0" + } + }, + "ci-info": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ci-info/-/ci-info-2.0.0.tgz", + "integrity": "sha512-5tK7EtrZ0N+OLFMthtqOj4fI2Jeb88C4CAZPu25LDVUgXJ0A3Js4PMGqrn0JU1W0Mh1/Z8wZzYPxqUrXeBboCQ==" + }, + "cipher-base": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/cipher-base/-/cipher-base-1.0.4.tgz", + "integrity": "sha512-Kkht5ye6ZGmwv40uUDZztayT2ThLQGfnj/T71N/XzeZeo3nf8foyW7zGTsPYkEya3m5f3cAypH+qe7YOrM1U2Q==", + "requires": { + "inherits": "^2.0.1", + "safe-buffer": "^5.0.1" + } + }, + "class-utils": { + "version": "0.3.6", + "resolved": "https://registry.npmjs.org/class-utils/-/class-utils-0.3.6.tgz", + "integrity": "sha512-qOhPa/Fj7s6TY8H8esGu5QNpMMQxz79h+urzrNYN6mn+9BnxlDGf5QZ+XeCDsxSjPqsSR56XOZOJmpeurnLMeg==", + "requires": { + "arr-union": "^3.1.0", + "define-property": "^0.2.5", + "isobject": "^3.0.0", + "static-extend": "^0.1.1" + }, + "dependencies": { + "define-property": { + "version": "0.2.5", + "resolved": "https://registry.npmjs.org/define-property/-/define-property-0.2.5.tgz", + "integrity": "sha1-w1se+RjsPJkPmlvFe+BKrOxcgRY=", + "requires": { + "is-descriptor": "^0.1.0" + } + } + } + }, + "clean-webpack-plugin": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/clean-webpack-plugin/-/clean-webpack-plugin-2.0.1.tgz", + "integrity": "sha512-vway5pXGVd91bicwjaf8j188Al6VMf9R9Ekl6q0qeiaWStRsOOXuh4qtjX1UrUvmz5XevQVCdjBuzr4Tzsnpog==", + "requires": { + "del": "^4.0.0" + }, + "dependencies": { + "del": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/del/-/del-4.0.0.tgz", + "integrity": "sha512-/BnSJ+SuZyLu7xMn48kZY0nMXDi+5KNmR4g8n21Wivsl8+B9njV6/5kcTNE9juSprp0zRWBU28JuHUq0FqK1Nw==", + "requires": { + "globby": "^6.1.0", + "is-path-cwd": "^2.0.0", + "is-path-in-cwd": "^2.0.0", + "p-map": "^2.0.0", + "pify": "^4.0.1", + "rimraf": "^2.6.2" + } + }, + "is-path-cwd": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/is-path-cwd/-/is-path-cwd-2.0.0.tgz", + "integrity": "sha512-m5dHHzpOXEiv18JEORttBO64UgTEypx99vCxQLjbBvGhOJxnTNglYoFXxwo6AbsQb79sqqycQEHv2hWkHZAijA==" + }, + "is-path-in-cwd": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/is-path-in-cwd/-/is-path-in-cwd-2.0.0.tgz", + "integrity": "sha512-6Vz5Gc9s/sDA3JBVu0FzWufm8xaBsqy1zn8Q6gmvGP6nSDMw78aS4poBNeatWjaRpTpxxLn1WOndAiOlk+qY8A==", + "requires": { + "is-path-inside": "^1.0.0" + } + }, + "p-map": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/p-map/-/p-map-2.0.0.tgz", + "integrity": "sha512-GO107XdrSUmtHxVoi60qc9tUl/KkNKm+X2CF4P9amalpGxv5YqVPJNfSb0wcA+syCopkZvYYIzW8OVTQW59x/w==" + }, + "pify": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/pify/-/pify-4.0.1.tgz", + "integrity": "sha512-uB80kBFb/tfd68bVleG9T5GGsGPjJrLAUpR5PZIrhBnIaRTQRjqdJSsIKkOP6OAIFbj7GOrcudc5pNjZ+geV2g==" + } + } + }, + "cli-cursor": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/cli-cursor/-/cli-cursor-2.1.0.tgz", + "integrity": "sha1-s12sN2R5+sw+lHR9QdDQ9SOP/LU=", + "requires": { + "restore-cursor": "^2.0.0" + } + }, + "cli-width": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/cli-width/-/cli-width-2.2.0.tgz", + "integrity": "sha1-/xnt6Kml5XkyQUewwR8PvLq+1jk=" + }, + "cliui": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/cliui/-/cliui-4.1.0.tgz", + "integrity": "sha512-4FG+RSG9DL7uEwRUZXZn3SS34DiDPfzP0VOiEwtUWlE+AR2EIg+hSyvrIgUUfhdgR/UkAeW2QHgeP+hWrXs7jQ==", + "requires": { + "string-width": "^2.1.1", + "strip-ansi": "^4.0.0", + "wrap-ansi": "^2.0.0" + } + }, + "co": { + "version": "4.6.0", + "resolved": "https://registry.npmjs.org/co/-/co-4.6.0.tgz", + "integrity": "sha1-bqa989hTrlTMuOR7+gvz+QMfsYQ=" + }, + "code-point-at": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/code-point-at/-/code-point-at-1.1.0.tgz", + "integrity": "sha1-DQcLTQQ6W+ozovGkDi7bPZpMz3c=" + }, + "collection-visit": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/collection-visit/-/collection-visit-1.0.0.tgz", + "integrity": "sha1-S8A3PBZLwykbTTaMgpzxqApZ3KA=", + "requires": { + "map-visit": "^1.0.0", + "object-visit": "^1.0.0" + } + }, + "color-convert": { + "version": "1.9.3", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz", + "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==", + "requires": { + "color-name": "1.1.3" + } + }, + "color-name": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", + "integrity": "sha1-p9BVi9icQveV3UIyj3QIMcpTvCU=" + }, + "combined-stream": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.7.tgz", + "integrity": "sha512-brWl9y6vOB1xYPZcpZde3N9zDByXTosAeMDo4p1wzo6UMOX4vumB+TP1RZ76sfE6Md68Q0NJSrE/gbezd4Ul+w==", + "requires": { + "delayed-stream": "~1.0.0" + } + }, + "commander": { + "version": "2.19.0", + "resolved": "https://registry.npmjs.org/commander/-/commander-2.19.0.tgz", + "integrity": "sha512-6tvAOO+D6OENvRAh524Dh9jcfKTYDQAqvqezbCW82xj5X0pSrcpxtvRKHLG0yBY6SD7PSDrJaj+0AiOcKVd1Xg==" + }, + "commondir": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/commondir/-/commondir-1.0.1.tgz", + "integrity": "sha1-3dgA2gxmEnOTzKWVDqloo6rxJTs=" + }, + "compare-versions": { + "version": "3.4.0", + "resolved": "https://registry.npmjs.org/compare-versions/-/compare-versions-3.4.0.tgz", + "integrity": "sha512-tK69D7oNXXqUW3ZNo/z7NXTEz22TCF0pTE+YF9cxvaAM9XnkLo1fV621xCLrRR6aevJlKxExkss0vWqUCUpqdg==" + }, + "component-emitter": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/component-emitter/-/component-emitter-1.2.1.tgz", + "integrity": "sha1-E3kY1teCg/ffemt8WmPhQOaUJeY=" + }, + "compressible": { + "version": "2.0.16", + "resolved": "https://registry.npmjs.org/compressible/-/compressible-2.0.16.tgz", + "integrity": "sha512-JQfEOdnI7dASwCuSPWIeVYwc/zMsu/+tRhoUvEfXz2gxOA2DNjmG5vhtFdBlhWPPGo+RdT9S3tgc/uH5qgDiiA==", + "requires": { + "mime-db": ">= 1.38.0 < 2" + } + }, + "compression": { + "version": "1.7.4", + "resolved": "https://registry.npmjs.org/compression/-/compression-1.7.4.tgz", + "integrity": "sha512-jaSIDzP9pZVS4ZfQ+TzvtiWhdpFhE2RDHz8QJkpX9SIpLq88VueF5jJw6t+6CUQcAoA6t+x89MLrWAqpfDE8iQ==", + "requires": { + "accepts": "~1.3.5", + "bytes": "3.0.0", + "compressible": "~2.0.16", + "debug": "2.6.9", + "on-headers": "~1.0.2", + "safe-buffer": "5.1.2", + "vary": "~1.1.2" + } + }, + "concat-map": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", + "integrity": "sha1-2Klr13/Wjfd5OnMDajug1UBdR3s=" + }, + "concat-stream": { + "version": "1.6.2", + "resolved": "https://registry.npmjs.org/concat-stream/-/concat-stream-1.6.2.tgz", + "integrity": "sha512-27HBghJxjiZtIk3Ycvn/4kbJk/1uZuJFfuPEns6LaEvpvG1f0hTea8lilrouyo9mVc2GWdcEZ8OLoGmSADlrCw==", + "requires": { + "buffer-from": "^1.0.0", + "inherits": "^2.0.3", + "readable-stream": "^2.2.2", + "typedarray": "^0.0.6" + } + }, + "connect-history-api-fallback": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/connect-history-api-fallback/-/connect-history-api-fallback-1.6.0.tgz", + "integrity": "sha512-e54B99q/OUoH64zYYRf3HBP5z24G38h5D3qXu23JGRoigpX5Ss4r9ZnDk3g0Z8uQC2x2lPaJ+UlWBc1ZWBWdLg==" + }, + "console-browserify": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/console-browserify/-/console-browserify-1.1.0.tgz", + "integrity": "sha1-8CQcRXMKn8YyOyBtvzjtx0HQuxA=", + "requires": { + "date-now": "^0.1.4" + } + }, + "constants-browserify": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/constants-browserify/-/constants-browserify-1.0.0.tgz", + "integrity": "sha1-wguW2MYXdIqvHBYCF2DNJ/y4y3U=" + }, + "content-disposition": { + "version": "0.5.2", + "resolved": "https://registry.npmjs.org/content-disposition/-/content-disposition-0.5.2.tgz", + "integrity": "sha1-DPaLud318r55YcOoUXjLhdunjLQ=" + }, + "content-type": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/content-type/-/content-type-1.0.4.tgz", + "integrity": "sha512-hIP3EEPs8tB9AT1L+NUqtwOAps4mk2Zob89MWXMHjHWg9milF/j4osnnQLXBCBFBk/tvIG/tUc9mOUJiPBhPXA==" + }, + "convert-source-map": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-1.6.0.tgz", + "integrity": "sha512-eFu7XigvxdZ1ETfbgPBohgyQ/Z++C0eEhTor0qRwBw9unw+L0/6V8wkSuGgzdThkiS5lSpdptOQPD8Ak40a+7A==", + "requires": { + "safe-buffer": "~5.1.1" + } + }, + "cookie": { + "version": "0.3.1", + "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.3.1.tgz", + "integrity": "sha1-5+Ch+e9DtMi6klxcWpboBtFoc7s=" + }, + "cookie-signature": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/cookie-signature/-/cookie-signature-1.0.6.tgz", + "integrity": "sha1-4wOogrNCzD7oylE6eZmXNNqzriw=" + }, + "copy-concurrently": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/copy-concurrently/-/copy-concurrently-1.0.5.tgz", + "integrity": "sha512-f2domd9fsVDFtaFcbaRZuYXwtdmnzqbADSwhSWYxYB/Q8zsdUUFMXVRwXGDMWmbEzAn1kdRrtI1T/KTFOL4X2A==", + "requires": { + "aproba": "^1.1.1", + "fs-write-stream-atomic": "^1.0.8", + "iferr": "^0.1.5", + "mkdirp": "^0.5.1", + "rimraf": "^2.5.4", + "run-queue": "^1.0.0" + } + }, + "copy-descriptor": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/copy-descriptor/-/copy-descriptor-0.1.1.tgz", + "integrity": "sha1-Z29us8OZl8LuGsOpJP1hJHSPV40=" + }, + "core-util-is": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.2.tgz", + "integrity": "sha1-tf1UIgqivFq1eqtxQMlAdUUDwac=" + }, + "cosmiconfig": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/cosmiconfig/-/cosmiconfig-4.0.0.tgz", + "integrity": "sha512-6e5vDdrXZD+t5v0L8CrurPeybg4Fmf+FCSYxXKYVAqLUtyCSbuyqE059d0kDthTNRzKVjL7QMgNpEUlsoYH3iQ==", + "requires": { + "is-directory": "^0.3.1", + "js-yaml": "^3.9.0", + "parse-json": "^4.0.0", + "require-from-string": "^2.0.1" + } + }, + "create-ecdh": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/create-ecdh/-/create-ecdh-4.0.3.tgz", + "integrity": "sha512-GbEHQPMOswGpKXM9kCWVrremUcBmjteUaQ01T9rkKCPDXfUHX0IoP9LpHYo2NPFampa4e+/pFDc3jQdxrxQLaw==", + "requires": { + "bn.js": "^4.1.0", + "elliptic": "^6.0.0" + } + }, + "create-hash": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/create-hash/-/create-hash-1.2.0.tgz", + "integrity": "sha512-z00bCGNHDG8mHAkP7CtT1qVu+bFQUPjYq/4Iv3C3kWjTFV10zIjfSoeqXo9Asws8gwSHDGj/hl2u4OGIjapeCg==", + "requires": { + "cipher-base": "^1.0.1", + "inherits": "^2.0.1", + "md5.js": "^1.3.4", + "ripemd160": "^2.0.1", + "sha.js": "^2.4.0" + } + }, + "create-hmac": { + "version": "1.1.7", + "resolved": "https://registry.npmjs.org/create-hmac/-/create-hmac-1.1.7.tgz", + "integrity": "sha512-MJG9liiZ+ogc4TzUwuvbER1JRdgvUFSB5+VR/g5h82fGaIRWMWddtKBHi7/sVhfjQZ6SehlyhvQYrcYkaUIpLg==", + "requires": { + "cipher-base": "^1.0.3", + "create-hash": "^1.1.0", + "inherits": "^2.0.1", + "ripemd160": "^2.0.0", + "safe-buffer": "^5.0.1", + "sha.js": "^2.4.8" + } + }, + "cross-spawn": { + "version": "6.0.5", + "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-6.0.5.tgz", + "integrity": "sha512-eTVLrBSt7fjbDygz805pMnstIs2VTBNkRm0qxZd+M7A5XDdxVRWO5MxGBXZhjY4cqLYLdtrGqRf8mBPmzwSpWQ==", + "requires": { + "nice-try": "^1.0.4", + "path-key": "^2.0.1", + "semver": "^5.5.0", + "shebang-command": "^1.2.0", + "which": "^1.2.9" + } + }, + "crypto-browserify": { + "version": "3.12.0", + "resolved": "https://registry.npmjs.org/crypto-browserify/-/crypto-browserify-3.12.0.tgz", + "integrity": "sha512-fz4spIh+znjO2VjL+IdhEpRJ3YN6sMzITSBijk6FK2UvTqruSQW+/cCZTSNsMiZNvUeq0CqurF+dAbyiGOY6Wg==", + "requires": { + "browserify-cipher": "^1.0.0", + "browserify-sign": "^4.0.0", + "create-ecdh": "^4.0.0", + "create-hash": "^1.1.0", + "create-hmac": "^1.1.0", + "diffie-hellman": "^5.0.0", + "inherits": "^2.0.1", + "pbkdf2": "^3.0.3", + "public-encrypt": "^4.0.0", + "randombytes": "^2.0.0", + "randomfill": "^1.0.3" + } + }, + "css-loader": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/css-loader/-/css-loader-2.1.1.tgz", + "integrity": "sha512-OcKJU/lt232vl1P9EEDamhoO9iKY3tIjY5GU+XDLblAykTdgs6Ux9P1hTHve8nFKy5KPpOXOsVI/hIwi3841+w==", + "requires": { + "camelcase": "^5.2.0", + "icss-utils": "^4.1.0", + "loader-utils": "^1.2.3", + "normalize-path": "^3.0.0", + "postcss": "^7.0.14", + "postcss-modules-extract-imports": "^2.0.0", + "postcss-modules-local-by-default": "^2.0.6", + "postcss-modules-scope": "^2.1.0", + "postcss-modules-values": "^2.0.0", + "postcss-value-parser": "^3.3.0", + "schema-utils": "^1.0.0" + } + }, + "cssesc": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/cssesc/-/cssesc-3.0.0.tgz", + "integrity": "sha512-/Tb/JcjK111nNScGob5MNtsntNM1aCNUDipB/TkwZFhyDrrE47SOx/18wF2bbjgc3ZzCSKW1T5nt5EbFoAz/Vg==" + }, + "cssom": { + "version": "0.3.6", + "resolved": "https://registry.npmjs.org/cssom/-/cssom-0.3.6.tgz", + "integrity": "sha512-DtUeseGk9/GBW0hl0vVPpU22iHL6YB5BUX7ml1hB+GMpo0NX5G4voX3kdWiMSEguFtcW3Vh3djqNF4aIe6ne0A==" + }, + "cssstyle": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/cssstyle/-/cssstyle-1.2.2.tgz", + "integrity": "sha512-43wY3kl1CVQSvL7wUY1qXkxVGkStjpkDmVjiIKX8R97uhajy8Bybay78uOtqvh7Q5GK75dNPfW0geWjE6qQQow==", + "requires": { + "cssom": "0.3.x" + } + }, + "cyclist": { + "version": "0.2.2", + "resolved": "https://registry.npmjs.org/cyclist/-/cyclist-0.2.2.tgz", + "integrity": "sha1-GzN5LhHpFKL9bW7WRHRkRE5fpkA=" + }, + "d": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/d/-/d-1.0.0.tgz", + "integrity": "sha1-dUu1v+VUUdpppYuU1F9MWwRi1Y8=", + "requires": { + "es5-ext": "^0.10.9" + } + }, + "d3-path": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/d3-path/-/d3-path-1.0.7.tgz", + "integrity": "sha512-q0cW1RpvA5c5ma2rch62mX8AYaiLX0+bdaSM2wxSU9tXjU4DNvkx9qiUvjkuWCj3p22UO/hlPivujqMiR9PDzA==" + }, + "d3-shape": { + "version": "1.3.5", + "resolved": "https://registry.npmjs.org/d3-shape/-/d3-shape-1.3.5.tgz", + "integrity": "sha512-VKazVR3phgD+MUCldapHD7P9kcrvPcexeX/PkMJmkUov4JM8IxsSg1DvbYoYich9AtdTsa5nNk2++ImPiDiSxg==", + "requires": { + "d3-path": "1" + } + }, + "dashdash": { + "version": "1.14.1", + "resolved": "https://registry.npmjs.org/dashdash/-/dashdash-1.14.1.tgz", + "integrity": "sha1-hTz6D3y+L+1d4gMmuN1YEDX24vA=", + "requires": { + "assert-plus": "^1.0.0" + } + }, + "data-urls": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/data-urls/-/data-urls-1.1.0.tgz", + "integrity": "sha512-YTWYI9se1P55u58gL5GkQHW4P6VJBJ5iBT+B5a7i2Tjadhv52paJG0qHX4A0OR6/t52odI64KP2YvFpkDOi3eQ==", + "requires": { + "abab": "^2.0.0", + "whatwg-mimetype": "^2.2.0", + "whatwg-url": "^7.0.0" + }, + "dependencies": { + "whatwg-url": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-7.0.0.tgz", + "integrity": "sha512-37GeVSIJ3kn1JgKyjiYNmSLP1yzbpb29jdmwBSgkD9h40/hyrR/OifpVUndji3tmwGgD8qpw7iQu3RSbCrBpsQ==", + "requires": { + "lodash.sortby": "^4.7.0", + "tr46": "^1.0.1", + "webidl-conversions": "^4.0.2" + } + } + } + }, + "date-now": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/date-now/-/date-now-0.1.4.tgz", + "integrity": "sha1-6vQ5/U1ISK105cx9vvIAZyueNFs=" + }, + "debug": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "requires": { + "ms": "2.0.0" + } + }, + "decamelize": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/decamelize/-/decamelize-1.2.0.tgz", + "integrity": "sha1-9lNNFRSCabIDUue+4m9QH5oZEpA=" + }, + "decode-uri-component": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/decode-uri-component/-/decode-uri-component-0.2.0.tgz", + "integrity": "sha1-6zkTMzRYd1y4TNGh+uBiEGu4dUU=" + }, + "deep-equal": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/deep-equal/-/deep-equal-1.0.1.tgz", + "integrity": "sha1-9dJgKStmDghO/0zbyfCK0yR0SLU=" + }, + "deep-is": { + "version": "0.1.3", + "resolved": "https://registry.npmjs.org/deep-is/-/deep-is-0.1.3.tgz", + "integrity": "sha1-s2nW+128E+7PUk+RsHD+7cNXzzQ=" + }, + "default-gateway": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/default-gateway/-/default-gateway-4.2.0.tgz", + "integrity": "sha512-h6sMrVB1VMWVrW13mSc6ia/DwYYw5MN6+exNu1OaJeFac5aSAvwM7lZ0NVfTABuSkQelr4h5oebg3KB1XPdjgA==", + "requires": { + "execa": "^1.0.0", + "ip-regex": "^2.1.0" + } + }, + "default-require-extensions": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/default-require-extensions/-/default-require-extensions-2.0.0.tgz", + "integrity": "sha1-9fj7sYp9bVCyH2QfZJ67Uiz+JPc=", + "requires": { + "strip-bom": "^3.0.0" + } + }, + "define-properties": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/define-properties/-/define-properties-1.1.3.tgz", + "integrity": "sha512-3MqfYKj2lLzdMSf8ZIZE/V+Zuy+BgD6f164e8K2w7dgnpKArBDerGYpM46IYYcjnkdPNMjPk9A6VFB8+3SKlXQ==", + "requires": { + "object-keys": "^1.0.12" + } + }, + "define-property": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/define-property/-/define-property-2.0.2.tgz", + "integrity": "sha512-jwK2UV4cnPpbcG7+VRARKTZPUWowwXA8bzH5NP6ud0oeAxyYPuGZUAC7hMugpCdz4BeSZl2Dl9k66CHJ/46ZYQ==", + "requires": { + "is-descriptor": "^1.0.2", + "isobject": "^3.0.1" + }, + "dependencies": { + "is-accessor-descriptor": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-1.0.0.tgz", + "integrity": "sha512-m5hnHTkcVsPfqx3AKlyttIPb7J+XykHvJP2B9bZDjlhLIoEq4XoK64Vg7boZlVWYK6LUY94dYPEE7Lh0ZkZKcQ==", + "requires": { + "kind-of": "^6.0.0" + } + }, + "is-data-descriptor": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-1.0.0.tgz", + "integrity": "sha512-jbRXy1FmtAoCjQkVmIVYwuuqDFUbaOeDjmed1tOGPrsMhtJA4rD9tkgA0F1qJ3gRFRXcHYVkdeaP50Q5rE/jLQ==", + "requires": { + "kind-of": "^6.0.0" + } + }, + "is-descriptor": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-1.0.2.tgz", + "integrity": "sha512-2eis5WqQGV7peooDyLmNEPUrps9+SXX5c9pL3xEB+4e9HnGuDa7mB7kHxHw4CbqS9k1T2hOH3miL8n8WtiYVtg==", + "requires": { + "is-accessor-descriptor": "^1.0.0", + "is-data-descriptor": "^1.0.0", + "kind-of": "^6.0.2" + } + } + } + }, + "del": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/del/-/del-4.1.0.tgz", + "integrity": "sha512-C4kvKNlYrwXhKxz97BuohF8YoGgQ23Xm9lvoHmgT7JaPGprSEjk3+XFled74Yt/x0ZABUHg2D67covzAPUKx5Q==", + "requires": { + "globby": "^6.1.0", + "is-path-cwd": "^2.0.0", + "is-path-in-cwd": "^2.0.0", + "p-map": "^2.0.0", + "pify": "^4.0.1", + "rimraf": "^2.6.3" + }, + "dependencies": { + "pify": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/pify/-/pify-4.0.1.tgz", + "integrity": "sha512-uB80kBFb/tfd68bVleG9T5GGsGPjJrLAUpR5PZIrhBnIaRTQRjqdJSsIKkOP6OAIFbj7GOrcudc5pNjZ+geV2g==" + } + } + }, + "delayed-stream": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz", + "integrity": "sha1-3zrhmayt+31ECqrgsp4icrJOxhk=" + }, + "depd": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/depd/-/depd-1.1.2.tgz", + "integrity": "sha1-m81S4UwJd2PnSbJ0xDRu0uVgtak=" + }, + "des.js": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/des.js/-/des.js-1.0.0.tgz", + "integrity": "sha1-wHTS4qpqipoH29YfmhXCzYPsjsw=", + "requires": { + "inherits": "^2.0.1", + "minimalistic-assert": "^1.0.0" + } + }, + "destroy": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/destroy/-/destroy-1.0.4.tgz", + "integrity": "sha1-l4hXRCxEdJ5CBmE+N5RiBYJqvYA=" + }, + "detect-file": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/detect-file/-/detect-file-1.0.0.tgz", + "integrity": "sha1-8NZtA2cqglyxtzvbP+YjEMjlUrc=" + }, + "detect-newline": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/detect-newline/-/detect-newline-2.1.0.tgz", + "integrity": "sha1-9B8cEL5LAOh7XxPaaAdZ8sW/0+I=" + }, + "detect-node": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/detect-node/-/detect-node-2.0.4.tgz", + "integrity": "sha512-ZIzRpLJrOj7jjP2miAtgqIfmzbxa4ZOr5jJc601zklsfEx9oTzmmj2nVpIPRpNlRTIh8lc1kyViIY7BWSGNmKw==" + }, + "diff-sequences": { + "version": "24.3.0", + "resolved": "https://registry.npmjs.org/diff-sequences/-/diff-sequences-24.3.0.tgz", + "integrity": "sha512-xLqpez+Zj9GKSnPWS0WZw1igGocZ+uua8+y+5dDNTT934N3QuY1sp2LkHzwiaYQGz60hMq0pjAshdeXm5VUOEw==" + }, + "diffie-hellman": { + "version": "5.0.3", + "resolved": "https://registry.npmjs.org/diffie-hellman/-/diffie-hellman-5.0.3.tgz", + "integrity": "sha512-kqag/Nl+f3GwyK25fhUMYj81BUOrZ9IuJsjIcDE5icNM9FJHAVm3VcUDxdLPoQtTuUylWm6ZIknYJwwaPxsUzg==", + "requires": { + "bn.js": "^4.1.0", + "miller-rabin": "^4.0.0", + "randombytes": "^2.0.0" + } + }, + "dns-equal": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/dns-equal/-/dns-equal-1.0.0.tgz", + "integrity": "sha1-s55/HabrCnW6nBcySzR1PEfgZU0=" + }, + "dns-packet": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/dns-packet/-/dns-packet-1.3.1.tgz", + "integrity": "sha512-0UxfQkMhYAUaZI+xrNZOz/as5KgDU0M/fQ9b6SpkyLbk3GEswDi6PADJVaYJradtRVsRIlF1zLyOodbcTCDzUg==", + "requires": { + "ip": "^1.1.0", + "safe-buffer": "^5.0.1" + } + }, + "dns-txt": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/dns-txt/-/dns-txt-2.0.2.tgz", + "integrity": "sha1-uR2Ab10nGI5Ks+fRB9iBocxGQrY=", + "requires": { + "buffer-indexof": "^1.0.0" + } + }, + "doctrine": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/doctrine/-/doctrine-3.0.0.tgz", + "integrity": "sha512-yS+Q5i3hBf7GBkd4KG8a7eBNNWNGLTaEwwYWUijIYM7zrlYDM0BFXHjjPWlWZ1Rg7UaddZeIDmi9jF3HmqiQ2w==", + "requires": { + "esutils": "^2.0.2" + } + }, + "domain-browser": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/domain-browser/-/domain-browser-1.2.0.tgz", + "integrity": "sha512-jnjyiM6eRyZl2H+W8Q/zLMA481hzi0eszAaBUzIVnmYVDBbnLxVNnfu1HgEBvCbL+71FrxMl3E6lpKH7Ge3OXA==" + }, + "domexception": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/domexception/-/domexception-1.0.1.tgz", + "integrity": "sha512-raigMkn7CJNNo6Ihro1fzG7wr3fHuYVytzquZKX5n0yizGsTcYgzdIUwj1X9pK0VvjeihV+XiclP+DjwbsSKug==", + "requires": { + "webidl-conversions": "^4.0.2" + } + }, + "duplexify": { + "version": "3.7.1", + "resolved": "https://registry.npmjs.org/duplexify/-/duplexify-3.7.1.tgz", + "integrity": "sha512-07z8uv2wMyS51kKhD1KsdXJg5WQ6t93RneqRxUHnskXVtlYYkLqM0gqStQZ3pj073g687jPCHrqNfCzawLYh5g==", + "requires": { + "end-of-stream": "^1.0.0", + "inherits": "^2.0.1", + "readable-stream": "^2.0.0", + "stream-shift": "^1.0.0" + } + }, + "ecc-jsbn": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/ecc-jsbn/-/ecc-jsbn-0.1.2.tgz", + "integrity": "sha1-OoOpBOVDUyh4dMVkt1SThoSamMk=", + "requires": { + "jsbn": "~0.1.0", + "safer-buffer": "^2.1.0" + } + }, + "ee-first": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/ee-first/-/ee-first-1.1.1.tgz", + "integrity": "sha1-WQxhFWsK4vTwJVcyoViyZrxWsh0=" + }, + "elliptic": { + "version": "6.4.1", + "resolved": "https://registry.npmjs.org/elliptic/-/elliptic-6.4.1.tgz", + "integrity": "sha512-BsXLz5sqX8OHcsh7CqBMztyXARmGQ3LWPtGjJi6DiJHq5C/qvi9P3OqgswKSDftbu8+IoI/QDTAm2fFnQ9SZSQ==", + "requires": { + "bn.js": "^4.4.0", + "brorand": "^1.0.1", + "hash.js": "^1.0.0", + "hmac-drbg": "^1.0.0", + "inherits": "^2.0.1", + "minimalistic-assert": "^1.0.0", + "minimalistic-crypto-utils": "^1.0.0" + } + }, + "emoji-regex": { + "version": "7.0.3", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-7.0.3.tgz", + "integrity": "sha512-CwBLREIQ7LvYFB0WyRvwhq5N5qPhc6PMjD6bYggFlI5YyDgl+0vxq5VHbMOFqLg7hfWzmu8T5Z1QofhmTIhItA==" + }, + "emojis-list": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/emojis-list/-/emojis-list-2.1.0.tgz", + "integrity": "sha1-TapNnbAPmBmIDHn6RXrlsJof04k=" + }, + "encodeurl": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-1.0.2.tgz", + "integrity": "sha1-rT/0yG7C0CkyL1oCw6mmBslbP1k=" + }, + "end-of-stream": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/end-of-stream/-/end-of-stream-1.4.1.tgz", + "integrity": "sha512-1MkrZNvWTKCaigbn+W15elq2BB/L22nqrSY5DKlo3X6+vclJm8Bb5djXJBmEX6fS3+zCh/F4VBK5Z2KxJt4s2Q==", + "requires": { + "once": "^1.4.0" + } + }, + "enhanced-resolve": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/enhanced-resolve/-/enhanced-resolve-4.1.0.tgz", + "integrity": "sha512-F/7vkyTtyc/llOIn8oWclcB25KdRaiPBpZYDgJHgh/UHtpgT2p2eldQgtQnLtUvfMKPKxbRaQM/hHkvLHt1Vng==", + "requires": { + "graceful-fs": "^4.1.2", + "memory-fs": "^0.4.0", + "tapable": "^1.0.0" + } + }, + "errno": { + "version": "0.1.7", + "resolved": "https://registry.npmjs.org/errno/-/errno-0.1.7.tgz", + "integrity": "sha512-MfrRBDWzIWifgq6tJj60gkAwtLNb6sQPlcFrSOflcP1aFmmruKQ2wRnze/8V6kgyz7H3FF8Npzv78mZ7XLLflg==", + "requires": { + "prr": "~1.0.1" + } + }, + "error-ex": { + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/error-ex/-/error-ex-1.3.2.tgz", + "integrity": "sha512-7dFHNmqeFSEt2ZBsCriorKnn3Z2pj+fd9kmI6QoWw4//DL+icEBfc0U7qJCisqrTsKTjw4fNFy2pW9OqStD84g==", + "requires": { + "is-arrayish": "^0.2.1" + } + }, + "es-abstract": { + "version": "1.13.0", + "resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.13.0.tgz", + "integrity": "sha512-vDZfg/ykNxQVwup/8E1BZhVzFfBxs9NqMzGcvIJrqg5k2/5Za2bWo40dK2J1pgLngZ7c+Shh8lwYtLGyrwPutg==", + "requires": { + "es-to-primitive": "^1.2.0", + "function-bind": "^1.1.1", + "has": "^1.0.3", + "is-callable": "^1.1.4", + "is-regex": "^1.0.4", + "object-keys": "^1.0.12" + } + }, + "es-to-primitive": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/es-to-primitive/-/es-to-primitive-1.2.0.tgz", + "integrity": "sha512-qZryBOJjV//LaxLTV6UC//WewneB3LcXOL9NP++ozKVXsIIIpm/2c13UDiD9Jp2eThsecw9m3jPqDwTyobcdbg==", + "requires": { + "is-callable": "^1.1.4", + "is-date-object": "^1.0.1", + "is-symbol": "^1.0.2" + } + }, + "es5-ext": { + "version": "0.10.47", + "resolved": "https://registry.npmjs.org/es5-ext/-/es5-ext-0.10.47.tgz", + "integrity": "sha512-/1TItLfj+TTfWoeRcDn/0FbGV6SNo4R+On2GGVucPU/j3BWnXE2Co8h8CTo4Tu34gFJtnmwS9xiScKs4EjZhdw==", + "requires": { + "es6-iterator": "~2.0.3", + "es6-symbol": "~3.1.1", + "next-tick": "1" + } + }, + "es6-iterator": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/es6-iterator/-/es6-iterator-2.0.3.tgz", + "integrity": "sha1-p96IkUGgWpSwhUQDstCg+/qY87c=", + "requires": { + "d": "1", + "es5-ext": "^0.10.35", + "es6-symbol": "^3.1.1" + } + }, + "es6-symbol": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/es6-symbol/-/es6-symbol-3.1.1.tgz", + "integrity": "sha1-vwDvT9q2uhtG7Le2KbTH7VcVzHc=", + "requires": { + "d": "1", + "es5-ext": "~0.10.14" + } + }, + "escape-html": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/escape-html/-/escape-html-1.0.3.tgz", + "integrity": "sha1-Aljq5NPQwJdN4cFpGI7wBR0dGYg=" + }, + "escape-string-regexp": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", + "integrity": "sha1-G2HAViGQqN/2rjuyzwIAyhMLhtQ=" + }, + "escodegen": { + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/escodegen/-/escodegen-1.11.1.tgz", + "integrity": "sha512-JwiqFD9KdGVVpeuRa68yU3zZnBEOcPs0nKW7wZzXky8Z7tffdYUHbe11bPCV5jYlK6DVdKLWLm0f5I/QlL0Kmw==", + "requires": { + "esprima": "^3.1.3", + "estraverse": "^4.2.0", + "esutils": "^2.0.2", + "optionator": "^0.8.1", + "source-map": "~0.6.1" + }, + "dependencies": { + "esprima": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/esprima/-/esprima-3.1.3.tgz", + "integrity": "sha1-/cpRzuYTOJXjyI1TXOSdv/YqRjM=" + }, + "source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "optional": true + } + } + }, + "eslint": { + "version": "5.16.0", + "resolved": "https://registry.npmjs.org/eslint/-/eslint-5.16.0.tgz", + "integrity": "sha512-S3Rz11i7c8AA5JPv7xAH+dOyq/Cu/VXHiHXBPOU1k/JAM5dXqQPt3qcrhpHSorXmrpu2g0gkIBVXAqCpzfoZIg==", + "requires": { + "@babel/code-frame": "^7.0.0", + "ajv": "^6.9.1", + "chalk": "^2.1.0", + "cross-spawn": "^6.0.5", + "debug": "^4.0.1", + "doctrine": "^3.0.0", + "eslint-scope": "^4.0.3", + "eslint-utils": "^1.3.1", + "eslint-visitor-keys": "^1.0.0", + "espree": "^5.0.1", + "esquery": "^1.0.1", + "esutils": "^2.0.2", + "file-entry-cache": "^5.0.1", + "functional-red-black-tree": "^1.0.1", + "glob": "^7.1.2", + "globals": "^11.7.0", + "ignore": "^4.0.6", + "import-fresh": "^3.0.0", + "imurmurhash": "^0.1.4", + "inquirer": "^6.2.2", + "js-yaml": "^3.13.0", + "json-stable-stringify-without-jsonify": "^1.0.1", + "levn": "^0.3.0", + "lodash": "^4.17.11", + "minimatch": "^3.0.4", + "mkdirp": "^0.5.1", + "natural-compare": "^1.4.0", + "optionator": "^0.8.2", + "path-is-inside": "^1.0.2", + "progress": "^2.0.0", + "regexpp": "^2.0.1", + "semver": "^5.5.1", + "strip-ansi": "^4.0.0", + "strip-json-comments": "^2.0.1", + "table": "^5.2.3", + "text-table": "^0.2.0" + }, + "dependencies": { + "debug": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.1.1.tgz", + "integrity": "sha512-pYAIzeRo8J6KPEaJ0VWOh5Pzkbw/RetuzehGM7QRRX5he4fPHx2rdKMB256ehJCkX+XRQm16eZLqLNS8RSZXZw==", + "requires": { + "ms": "^2.1.1" + } + }, + "js-yaml": { + "version": "3.13.1", + "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.13.1.tgz", + "integrity": "sha512-YfbcO7jXDdyj0DGxYVSlSeQNHbD7XPWvrVWeVUujrQEoZzWJIRrCPoyk6kL6IAjAG2IolMK4T0hNUe0HOUs5Jw==", + "requires": { + "argparse": "^1.0.7", + "esprima": "^4.0.0" + } + }, + "ms": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.1.tgz", + "integrity": "sha512-tgp+dl5cGk28utYktBsrFqA7HKgrhgPsg6Z/EfhWI4gl1Hwq8B/GmY/0oXZ6nF8hDVesS/FpnYaD/kOWhYQvyg==" + }, + "semver": { + "version": "5.7.0", + "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.0.tgz", + "integrity": "sha512-Ya52jSX2u7QKghxeoFGpLwCtGlt7j0oY9DYb5apt9nPlJ42ID+ulTXESnt/qAQcoSERyZ5sl3LDIOw0nAn/5DA==" + } + } + }, + "eslint-config-prettier": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/eslint-config-prettier/-/eslint-config-prettier-4.1.0.tgz", + "integrity": "sha512-zILwX9/Ocz4SV2vX7ox85AsrAgXV3f2o2gpIicdMIOra48WYqgUnWNH/cR/iHtmD2Vb3dLSC3LiEJnS05Gkw7w==", + "requires": { + "get-stdin": "^6.0.0" + } + }, + "eslint-plugin-prettier": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/eslint-plugin-prettier/-/eslint-plugin-prettier-3.0.1.tgz", + "integrity": "sha512-/PMttrarPAY78PLvV3xfWibMOdMDl57hmlQ2XqFeA37wd+CJ7WSxV7txqjVPHi/AAFKd2lX0ZqfsOc/i5yFCSQ==", + "requires": { + "prettier-linter-helpers": "^1.0.0" + } + }, + "eslint-scope": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-4.0.3.tgz", + "integrity": "sha512-p7VutNr1O/QrxysMo3E45FjYDTeXBy0iTltPFNSqKAIfjDSXC+4dj+qfyuD8bfAXrW/y6lW3O76VaYNPKfpKrg==", + "requires": { + "esrecurse": "^4.1.0", + "estraverse": "^4.1.1" + } + }, + "eslint-utils": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/eslint-utils/-/eslint-utils-1.3.1.tgz", + "integrity": "sha512-Z7YjnIldX+2XMcjr7ZkgEsOj/bREONV60qYeB/bjMAqqqZ4zxKyWX+BOUkdmRmA9riiIPVvo5x86m5elviOk0Q==" + }, + "eslint-visitor-keys": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-1.0.0.tgz", + "integrity": "sha512-qzm/XxIbxm/FHyH341ZrbnMUpe+5Bocte9xkmFMzPMjRaZMcXww+MpBptFvtU+79L362nqiLhekCxCxDPaUMBQ==" + }, + "espree": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/espree/-/espree-5.0.1.tgz", + "integrity": "sha512-qWAZcWh4XE/RwzLJejfcofscgMc9CamR6Tn1+XRXNzrvUSSbiAjGOI/fggztjIi7y9VLPqnICMIPiGyr8JaZ0A==", + "requires": { + "acorn": "^6.0.7", + "acorn-jsx": "^5.0.0", + "eslint-visitor-keys": "^1.0.0" + } + }, + "esprima": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/esprima/-/esprima-4.0.1.tgz", + "integrity": "sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A==" + }, + "esquery": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/esquery/-/esquery-1.0.1.tgz", + "integrity": "sha512-SmiyZ5zIWH9VM+SRUReLS5Q8a7GxtRdxEBVZpm98rJM7Sb+A9DVCndXfkeFUd3byderg+EbDkfnevfCwynWaNA==", + "requires": { + "estraverse": "^4.0.0" + } + }, + "esrecurse": { + "version": "4.2.1", + "resolved": "https://registry.npmjs.org/esrecurse/-/esrecurse-4.2.1.tgz", + "integrity": "sha512-64RBB++fIOAXPw3P9cy89qfMlvZEXZkqqJkjqqXIvzP5ezRZjW+lPWjw35UX/3EhUPFYbg5ER4JYgDw4007/DQ==", + "requires": { + "estraverse": "^4.1.0" + } + }, + "estraverse": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-4.2.0.tgz", + "integrity": "sha1-De4/7TH81GlhjOc0IJn8GvoL2xM=" + }, + "esutils": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/esutils/-/esutils-2.0.2.tgz", + "integrity": "sha1-Cr9PHKpbyx96nYrMbepPqqBLrJs=" + }, + "etag": { + "version": "1.8.1", + "resolved": "https://registry.npmjs.org/etag/-/etag-1.8.1.tgz", + "integrity": "sha1-Qa4u62XvpiJorr/qg6x9eSmbCIc=" + }, + "eventemitter3": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/eventemitter3/-/eventemitter3-3.1.0.tgz", + "integrity": "sha512-ivIvhpq/Y0uSjcHDcOIccjmYjGLcP09MFGE7ysAwkAvkXfpZlC985pH2/ui64DKazbTW/4kN3yqozUxlXzI6cA==" + }, + "events": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/events/-/events-3.0.0.tgz", + "integrity": "sha512-Dc381HFWJzEOhQ+d8pkNon++bk9h6cdAoAj4iE6Q4y6xgTzySWXlKn05/TVNpjnfRqi/X0EpJEJohPjNI3zpVA==" + }, + "eventsource": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/eventsource/-/eventsource-1.0.7.tgz", + "integrity": "sha512-4Ln17+vVT0k8aWq+t/bF5arcS3EpT9gYtW66EPacdj/mAFevznsnyoHLPy2BA8gbIQeIHoPsvwmfBftfcG//BQ==", + "requires": { + "original": "^1.0.0" + } + }, + "evp_bytestokey": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/evp_bytestokey/-/evp_bytestokey-1.0.3.tgz", + "integrity": "sha512-/f2Go4TognH/KvCISP7OUsHn85hT9nUkxxA9BEWxFn+Oj9o8ZNLm/40hdlgSLyuOimsrTKLUMEorQexp/aPQeA==", + "requires": { + "md5.js": "^1.3.4", + "safe-buffer": "^5.1.1" + } + }, + "exec-sh": { + "version": "0.3.2", + "resolved": "https://registry.npmjs.org/exec-sh/-/exec-sh-0.3.2.tgz", + "integrity": "sha512-9sLAvzhI5nc8TpuQUh4ahMdCrWT00wPWz7j47/emR5+2qEfoZP5zzUXvx+vdx+H6ohhnsYC31iX04QLYJK8zTg==" + }, + "execa": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/execa/-/execa-1.0.0.tgz", + "integrity": "sha512-adbxcyWV46qiHyvSp50TKt05tB4tK3HcmF7/nxfAdhnox83seTDbwnaqKO4sXRy7roHAIFqJP/Rw/AuEbX61LA==", + "requires": { + "cross-spawn": "^6.0.0", + "get-stream": "^4.0.0", + "is-stream": "^1.1.0", + "npm-run-path": "^2.0.0", + "p-finally": "^1.0.0", + "signal-exit": "^3.0.0", + "strip-eof": "^1.0.0" + } + }, + "exit": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/exit/-/exit-0.1.2.tgz", + "integrity": "sha1-BjJjj42HfMghB9MKD/8aF8uhzQw=" + }, + "expand-brackets": { + "version": "2.1.4", + "resolved": "https://registry.npmjs.org/expand-brackets/-/expand-brackets-2.1.4.tgz", + "integrity": "sha1-t3c14xXOMPa27/D4OwQVGiJEliI=", + "requires": { + "debug": "^2.3.3", + "define-property": "^0.2.5", + "extend-shallow": "^2.0.1", + "posix-character-classes": "^0.1.0", + "regex-not": "^1.0.0", + "snapdragon": "^0.8.1", + "to-regex": "^3.0.1" + }, + "dependencies": { + "define-property": { + "version": "0.2.5", + "resolved": "https://registry.npmjs.org/define-property/-/define-property-0.2.5.tgz", + "integrity": "sha1-w1se+RjsPJkPmlvFe+BKrOxcgRY=", + "requires": { + "is-descriptor": "^0.1.0" + } + }, + "extend-shallow": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", + "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", + "requires": { + "is-extendable": "^0.1.0" + } + } + } + }, + "expand-tilde": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/expand-tilde/-/expand-tilde-2.0.2.tgz", + "integrity": "sha1-l+gBqgUt8CRU3kawK/YhZCzchQI=", + "requires": { + "homedir-polyfill": "^1.0.1" + } + }, + "expect": { + "version": "24.7.1", + "resolved": "https://registry.npmjs.org/expect/-/expect-24.7.1.tgz", + "integrity": "sha512-mGfvMTPduksV3xoI0xur56pQsg2vJjNf5+a+bXOjqCkiCBbmCayrBbHS/75y9K430cfqyocPr2ZjiNiRx4SRKw==", + "requires": { + "@jest/types": "^24.7.0", + "ansi-styles": "^3.2.0", + "jest-get-type": "^24.3.0", + "jest-matcher-utils": "^24.7.0", + "jest-message-util": "^24.7.1", + "jest-regex-util": "^24.3.0" + } + }, + "express": { + "version": "4.16.4", + "resolved": "https://registry.npmjs.org/express/-/express-4.16.4.tgz", + "integrity": "sha512-j12Uuyb4FMrd/qQAm6uCHAkPtO8FDTRJZBDd5D2KOL2eLaz1yUNdUB/NOIyq0iU4q4cFarsUCrnFDPBcnksuOg==", + "requires": { + "accepts": "~1.3.5", + "array-flatten": "1.1.1", + "body-parser": "1.18.3", + "content-disposition": "0.5.2", + "content-type": "~1.0.4", + "cookie": "0.3.1", + "cookie-signature": "1.0.6", + "debug": "2.6.9", + "depd": "~1.1.2", + "encodeurl": "~1.0.2", + "escape-html": "~1.0.3", + "etag": "~1.8.1", + "finalhandler": "1.1.1", + "fresh": "0.5.2", + "merge-descriptors": "1.0.1", + "methods": "~1.1.2", + "on-finished": "~2.3.0", + "parseurl": "~1.3.2", + "path-to-regexp": "0.1.7", + "proxy-addr": "~2.0.4", + "qs": "6.5.2", + "range-parser": "~1.2.0", + "safe-buffer": "5.1.2", + "send": "0.16.2", + "serve-static": "1.13.2", + "setprototypeof": "1.1.0", + "statuses": "~1.4.0", + "type-is": "~1.6.16", + "utils-merge": "1.0.1", + "vary": "~1.1.2" + }, + "dependencies": { + "array-flatten": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/array-flatten/-/array-flatten-1.1.1.tgz", + "integrity": "sha1-ml9pkFGx5wczKPKgCJaLZOopVdI=" + } + } + }, + "extend": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/extend/-/extend-3.0.2.tgz", + "integrity": "sha512-fjquC59cD7CyW6urNXK0FBufkZcoiGG80wTuPujX590cB5Ttln20E2UB4S/WARVqhXffZl2LNgS+gQdPIIim/g==" + }, + "extend-shallow": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-3.0.2.tgz", + "integrity": "sha1-Jqcarwc7OfshJxcnRhMcJwQCjbg=", + "requires": { + "assign-symbols": "^1.0.0", + "is-extendable": "^1.0.1" + }, + "dependencies": { + "is-extendable": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/is-extendable/-/is-extendable-1.0.1.tgz", + "integrity": "sha512-arnXMxT1hhoKo9k1LZdmlNyJdDDfy2v0fXjFlmok4+i8ul/6WlbVge9bhM74OpNPQPMGUToDtz+KXa1PneJxOA==", + "requires": { + "is-plain-object": "^2.0.4" + } + } + } + }, + "external-editor": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/external-editor/-/external-editor-3.0.3.tgz", + "integrity": "sha512-bn71H9+qWoOQKyZDo25mOMVpSmXROAsTJVVVYzrrtol3d4y+AsKjf4Iwl2Q+IuT0kFSQ1qo166UuIwqYq7mGnA==", + "requires": { + "chardet": "^0.7.0", + "iconv-lite": "^0.4.24", + "tmp": "^0.0.33" + } + }, + "extglob": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/extglob/-/extglob-2.0.4.tgz", + "integrity": "sha512-Nmb6QXkELsuBr24CJSkilo6UHHgbekK5UiZgfE6UHD3Eb27YC6oD+bhcT+tJ6cl8dmsgdQxnWlcry8ksBIBLpw==", + "requires": { + "array-unique": "^0.3.2", + "define-property": "^1.0.0", + "expand-brackets": "^2.1.4", + "extend-shallow": "^2.0.1", + "fragment-cache": "^0.2.1", + "regex-not": "^1.0.0", + "snapdragon": "^0.8.1", + "to-regex": "^3.0.1" + }, + "dependencies": { + "define-property": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/define-property/-/define-property-1.0.0.tgz", + "integrity": "sha1-dp66rz9KY6rTr56NMEybvnm/sOY=", + "requires": { + "is-descriptor": "^1.0.0" + } + }, + "extend-shallow": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", + "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", + "requires": { + "is-extendable": "^0.1.0" + } + }, + "is-accessor-descriptor": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-1.0.0.tgz", + "integrity": "sha512-m5hnHTkcVsPfqx3AKlyttIPb7J+XykHvJP2B9bZDjlhLIoEq4XoK64Vg7boZlVWYK6LUY94dYPEE7Lh0ZkZKcQ==", + "requires": { + "kind-of": "^6.0.0" + } + }, + "is-data-descriptor": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-1.0.0.tgz", + "integrity": "sha512-jbRXy1FmtAoCjQkVmIVYwuuqDFUbaOeDjmed1tOGPrsMhtJA4rD9tkgA0F1qJ3gRFRXcHYVkdeaP50Q5rE/jLQ==", + "requires": { + "kind-of": "^6.0.0" + } + }, + "is-descriptor": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-1.0.2.tgz", + "integrity": "sha512-2eis5WqQGV7peooDyLmNEPUrps9+SXX5c9pL3xEB+4e9HnGuDa7mB7kHxHw4CbqS9k1T2hOH3miL8n8WtiYVtg==", + "requires": { + "is-accessor-descriptor": "^1.0.0", + "is-data-descriptor": "^1.0.0", + "kind-of": "^6.0.2" + } + } + } + }, + "extsprintf": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/extsprintf/-/extsprintf-1.3.0.tgz", + "integrity": "sha1-lpGEQOMEGnpBT4xS48V06zw+HgU=" + }, + "fast-deep-equal": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-2.0.1.tgz", + "integrity": "sha1-ewUhjd+WZ79/Nwv3/bLLFf3Qqkk=" + }, + "fast-diff": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/fast-diff/-/fast-diff-1.2.0.tgz", + "integrity": "sha512-xJuoT5+L99XlZ8twedaRf6Ax2TgQVxvgZOYoPKqZufmJib0tL2tegPBOZb1pVNgIhlqDlA0eO0c3wBvQcmzx4w==" + }, + "fast-json-stable-stringify": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.0.0.tgz", + "integrity": "sha1-1RQsDK7msRifh9OnYREGT4bIu/I=" + }, + "fast-levenshtein": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz", + "integrity": "sha1-PYpcZog6FqMMqGQ+hR8Zuqd5eRc=" + }, + "faye-websocket": { + "version": "0.10.0", + "resolved": "https://registry.npmjs.org/faye-websocket/-/faye-websocket-0.10.0.tgz", + "integrity": "sha1-TkkvjQTftviQA1B/btvy1QHnxvQ=", + "requires": { + "websocket-driver": ">=0.5.1" + } + }, + "fb-watchman": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/fb-watchman/-/fb-watchman-2.0.0.tgz", + "integrity": "sha1-VOmr99+i8mzZsWNsWIwa/AXeXVg=", + "requires": { + "bser": "^2.0.0" + } + }, + "figgy-pudding": { + "version": "3.5.1", + "resolved": "https://registry.npmjs.org/figgy-pudding/-/figgy-pudding-3.5.1.tgz", + "integrity": "sha512-vNKxJHTEKNThjfrdJwHc7brvM6eVevuO5nTj6ez8ZQ1qbXTvGthucRF7S4vf2cr71QVnT70V34v0S1DyQsti0w==" + }, + "figures": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/figures/-/figures-2.0.0.tgz", + "integrity": "sha1-OrGi0qYsi/tDGgyUy3l6L84nyWI=", + "requires": { + "escape-string-regexp": "^1.0.5" + } + }, + "file-entry-cache": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/file-entry-cache/-/file-entry-cache-5.0.1.tgz", + "integrity": "sha512-bCg29ictuBaKUwwArK4ouCaqDgLZcysCFLmM/Yn/FDoqndh/9vNuQfXRDvTuXKLxfD/JtZQGKFT8MGcJBK644g==", + "requires": { + "flat-cache": "^2.0.1" + } + }, + "file-loader": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/file-loader/-/file-loader-3.0.1.tgz", + "integrity": "sha512-4sNIOXgtH/9WZq4NvlfU3Opn5ynUsqBwSLyM+I7UOwdGigTBYfVVQEwe/msZNX/j4pCJTIM14Fsw66Svo1oVrw==", + "requires": { + "loader-utils": "^1.0.2", + "schema-utils": "^1.0.0" + } + }, + "fileset": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/fileset/-/fileset-2.0.3.tgz", + "integrity": "sha1-jnVIqW08wjJ+5eZ0FocjozO7oqA=", + "requires": { + "glob": "^7.0.3", + "minimatch": "^3.0.3" + } + }, + "fill-range": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-4.0.0.tgz", + "integrity": "sha1-1USBHUKPmOsGpj3EAtJAPDKMOPc=", + "requires": { + "extend-shallow": "^2.0.1", + "is-number": "^3.0.0", + "repeat-string": "^1.6.1", + "to-regex-range": "^2.1.0" + }, + "dependencies": { + "extend-shallow": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", + "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", + "requires": { + "is-extendable": "^0.1.0" + } + } + } + }, + "finalhandler": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/finalhandler/-/finalhandler-1.1.1.tgz", + "integrity": "sha512-Y1GUDo39ez4aHAw7MysnUD5JzYX+WaIj8I57kO3aEPT1fFRL4sr7mjei97FgnwhAyyzRYmQZaTHb2+9uZ1dPtg==", + "requires": { + "debug": "2.6.9", + "encodeurl": "~1.0.2", + "escape-html": "~1.0.3", + "on-finished": "~2.3.0", + "parseurl": "~1.3.2", + "statuses": "~1.4.0", + "unpipe": "~1.0.0" + } + }, + "find-cache-dir": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/find-cache-dir/-/find-cache-dir-2.1.0.tgz", + "integrity": "sha512-Tq6PixE0w/VMFfCgbONnkiQIVol/JJL7nRMi20fqzA4NRs9AfeqMGeRdPi3wIhYkxjeBaWh2rxwapn5Tu3IqOQ==", + "requires": { + "commondir": "^1.0.1", + "make-dir": "^2.0.0", + "pkg-dir": "^3.0.0" + }, + "dependencies": { + "make-dir": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/make-dir/-/make-dir-2.1.0.tgz", + "integrity": "sha512-LS9X+dc8KLxXCb8dni79fLIIUA5VyZoyjSMCwTluaXA0o27cCK0bhXkpgw+sTXVpPy/lSO57ilRixqk0vDmtRA==", + "requires": { + "pify": "^4.0.1", + "semver": "^5.6.0" + } + }, + "pify": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/pify/-/pify-4.0.1.tgz", + "integrity": "sha512-uB80kBFb/tfd68bVleG9T5GGsGPjJrLAUpR5PZIrhBnIaRTQRjqdJSsIKkOP6OAIFbj7GOrcudc5pNjZ+geV2g==" + }, + "semver": { + "version": "5.7.0", + "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.0.tgz", + "integrity": "sha512-Ya52jSX2u7QKghxeoFGpLwCtGlt7j0oY9DYb5apt9nPlJ42ID+ulTXESnt/qAQcoSERyZ5sl3LDIOw0nAn/5DA==" + } + } + }, + "find-up": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-3.0.0.tgz", + "integrity": "sha512-1yD6RmLI1XBfxugvORwlck6f75tYL+iR0jqwsOrOxMZyGYqUuDhJ0l4AXdO1iX/FTs9cBAMEk1gWSEx1kSbylg==", + "requires": { + "locate-path": "^3.0.0" + } + }, + "findup-sync": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/findup-sync/-/findup-sync-2.0.0.tgz", + "integrity": "sha1-kyaxSIwi0aYIhlCoaQGy2akKLLw=", + "requires": { + "detect-file": "^1.0.0", + "is-glob": "^3.1.0", + "micromatch": "^3.0.4", + "resolve-dir": "^1.0.1" + }, + "dependencies": { + "is-glob": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-3.1.0.tgz", + "integrity": "sha1-e6WuJCF4BKxwcHuWkiVnSGzD6Eo=", + "requires": { + "is-extglob": "^2.1.0" + } + } + } + }, + "flat-cache": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/flat-cache/-/flat-cache-2.0.1.tgz", + "integrity": "sha512-LoQe6yDuUMDzQAEH8sgmh4Md6oZnc/7PjtwjNFSzveXqSHt6ka9fPBuso7IGf9Rz4uqnSnWiFH2B/zj24a5ReA==", + "requires": { + "flatted": "^2.0.0", + "rimraf": "2.6.3", + "write": "1.0.3" + } + }, + "flatted": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/flatted/-/flatted-2.0.0.tgz", + "integrity": "sha512-R+H8IZclI8AAkSBRQJLVOsxwAoHd6WC40b4QTNWIjzAa6BXOBfQcM587MXDTVPeYaopFNWHUFLx7eNmHDSxMWg==" + }, + "flush-write-stream": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/flush-write-stream/-/flush-write-stream-1.1.1.tgz", + "integrity": "sha512-3Z4XhFZ3992uIq0XOqb9AreonueSYphE6oYbpt5+3u06JWklbsPkNv3ZKkP9Bz/r+1MWCaMoSQ28P85+1Yc77w==", + "requires": { + "inherits": "^2.0.3", + "readable-stream": "^2.3.6" + } + }, + "follow-redirects": { + "version": "1.7.0", + "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.7.0.tgz", + "integrity": "sha512-m/pZQy4Gj287eNy94nivy5wchN3Kp+Q5WgUPNy5lJSZ3sgkVKSYV/ZChMAQVIgx1SqfZ2zBZtPA2YlXIWxxJOQ==", + "requires": { + "debug": "^3.2.6" + }, + "dependencies": { + "debug": { + "version": "3.2.6", + "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.6.tgz", + "integrity": "sha512-mel+jf7nrtEl5Pn1Qx46zARXKDpBbvzezse7p7LqINmdoIk8PYP5SySaxEmYv6TZ0JyEKA1hsCId6DIhgITtWQ==", + "requires": { + "ms": "^2.1.1" + } + }, + "ms": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.1.tgz", + "integrity": "sha512-tgp+dl5cGk28utYktBsrFqA7HKgrhgPsg6Z/EfhWI4gl1Hwq8B/GmY/0oXZ6nF8hDVesS/FpnYaD/kOWhYQvyg==" + } + } + }, + "for-in": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/for-in/-/for-in-1.0.2.tgz", + "integrity": "sha1-gQaNKVqBQuwKxybG4iAMMPttXoA=" + }, + "forever-agent": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/forever-agent/-/forever-agent-0.6.1.tgz", + "integrity": "sha1-+8cfDEGt6zf5bFd60e1C2P2sypE=" + }, + "form-data": { + "version": "2.3.3", + "resolved": "https://registry.npmjs.org/form-data/-/form-data-2.3.3.tgz", + "integrity": "sha512-1lLKB2Mu3aGP1Q/2eCOx0fNbRMe7XdwktwOruhfqqd0rIJWwN4Dh+E3hrPSlDCXnSR7UtZ1N38rVXm+6+MEhJQ==", + "requires": { + "asynckit": "^0.4.0", + "combined-stream": "^1.0.6", + "mime-types": "^2.1.12" + } + }, + "forwarded": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/forwarded/-/forwarded-0.1.2.tgz", + "integrity": "sha1-mMI9qxF1ZXuMBXPozszZGw/xjIQ=" + }, + "fragment-cache": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/fragment-cache/-/fragment-cache-0.2.1.tgz", + "integrity": "sha1-QpD60n8T6Jvn8zeZxrxaCr//DRk=", + "requires": { + "map-cache": "^0.2.2" + } + }, + "fresh": { + "version": "0.5.2", + "resolved": "https://registry.npmjs.org/fresh/-/fresh-0.5.2.tgz", + "integrity": "sha1-PYyt2Q2XZWn6g1qx+OSyOhBWBac=" + }, + "from2": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/from2/-/from2-2.3.0.tgz", + "integrity": "sha1-i/tVAr3kpNNs/e6gB/zKIdfjgq8=", + "requires": { + "inherits": "^2.0.1", + "readable-stream": "^2.0.0" + } + }, + "fs-write-stream-atomic": { + "version": "1.0.10", + "resolved": "https://registry.npmjs.org/fs-write-stream-atomic/-/fs-write-stream-atomic-1.0.10.tgz", + "integrity": "sha1-tH31NJPvkR33VzHnCp3tAYnbQMk=", + "requires": { + "graceful-fs": "^4.1.2", + "iferr": "^0.1.5", + "imurmurhash": "^0.1.4", + "readable-stream": "1 || 2" + } + }, + "fs.realpath": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", + "integrity": "sha1-FQStJSMVjKpA20onh8sBQRmU6k8=" + }, + "fsevents": { + "version": "1.2.7", + "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-1.2.7.tgz", + "integrity": "sha512-Pxm6sI2MeBD7RdD12RYsqaP0nMiwx8eZBXCa6z2L+mRHm2DYrOYwihmhjpkdjUHwQhslWQjRpEgNq4XvBmaAuw==", + "optional": true, + "requires": { + "nan": "^2.9.2", + "node-pre-gyp": "^0.10.0" + }, + "dependencies": { + "abbrev": { + "version": "1.1.1", + "bundled": true, + "optional": true + }, + "ansi-regex": { + "version": "2.1.1", + "bundled": true, + "optional": true + }, + "aproba": { + "version": "1.2.0", + "bundled": true, + "optional": true + }, + "are-we-there-yet": { + "version": "1.1.5", + "bundled": true, + "optional": true, + "requires": { + "delegates": "^1.0.0", + "readable-stream": "^2.0.6" + } + }, + "balanced-match": { + "version": "1.0.0", + "bundled": true, + "optional": true + }, + "brace-expansion": { + "version": "1.1.11", + "bundled": true, + "optional": true, + "requires": { + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" + } + }, + "chownr": { + "version": "1.1.1", + "bundled": true, + "optional": true + }, + "code-point-at": { + "version": "1.1.0", + "bundled": true, + "optional": true + }, + "concat-map": { + "version": "0.0.1", + "bundled": true, + "optional": true + }, + "console-control-strings": { + "version": "1.1.0", + "bundled": true, + "optional": true + }, + "core-util-is": { + "version": "1.0.2", + "bundled": true, + "optional": true + }, + "debug": { + "version": "2.6.9", + "bundled": true, + "optional": true, + "requires": { + "ms": "2.0.0" + } + }, + "deep-extend": { + "version": "0.6.0", + "bundled": true, + "optional": true + }, + "delegates": { + "version": "1.0.0", + "bundled": true, + "optional": true + }, + "detect-libc": { + "version": "1.0.3", + "bundled": true, + "optional": true + }, + "fs-minipass": { + "version": "1.2.5", + "bundled": true, + "optional": true, + "requires": { + "minipass": "^2.2.1" + } + }, + "fs.realpath": { + "version": "1.0.0", + "bundled": true, + "optional": true + }, + "gauge": { + "version": "2.7.4", + "bundled": true, + "optional": true, + "requires": { + "aproba": "^1.0.3", + "console-control-strings": "^1.0.0", + "has-unicode": "^2.0.0", + "object-assign": "^4.1.0", + "signal-exit": "^3.0.0", + "string-width": "^1.0.1", + "strip-ansi": "^3.0.1", + "wide-align": "^1.1.0" + } + }, + "glob": { + "version": "7.1.3", + "bundled": true, + "optional": true, + "requires": { + "fs.realpath": "^1.0.0", + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "^3.0.4", + "once": "^1.3.0", + "path-is-absolute": "^1.0.0" + } + }, + "has-unicode": { + "version": "2.0.1", + "bundled": true, + "optional": true + }, + "iconv-lite": { + "version": "0.4.24", + "bundled": true, + "optional": true, + "requires": { + "safer-buffer": ">= 2.1.2 < 3" + } + }, + "ignore-walk": { + "version": "3.0.1", + "bundled": true, + "optional": true, + "requires": { + "minimatch": "^3.0.4" + } + }, + "inflight": { + "version": "1.0.6", + "bundled": true, + "optional": true, + "requires": { + "once": "^1.3.0", + "wrappy": "1" + } + }, + "inherits": { + "version": "2.0.3", + "bundled": true, + "optional": true + }, + "ini": { + "version": "1.3.5", + "bundled": true, + "optional": true + }, + "is-fullwidth-code-point": { + "version": "1.0.0", + "bundled": true, + "optional": true, + "requires": { + "number-is-nan": "^1.0.0" + } + }, + "isarray": { + "version": "1.0.0", + "bundled": true, + "optional": true + }, + "minimatch": { + "version": "3.0.4", + "bundled": true, + "optional": true, + "requires": { + "brace-expansion": "^1.1.7" + } + }, + "minimist": { + "version": "0.0.8", + "bundled": true, + "optional": true + }, + "minipass": { + "version": "2.3.5", + "bundled": true, + "optional": true, + "requires": { + "safe-buffer": "^5.1.2", + "yallist": "^3.0.0" + } + }, + "minizlib": { + "version": "1.2.1", + "bundled": true, + "optional": true, + "requires": { + "minipass": "^2.2.1" + } + }, + "mkdirp": { + "version": "0.5.1", + "bundled": true, + "optional": true, + "requires": { + "minimist": "0.0.8" + } + }, + "ms": { + "version": "2.0.0", + "bundled": true, + "optional": true + }, + "needle": { + "version": "2.2.4", + "bundled": true, + "optional": true, + "requires": { + "debug": "^2.1.2", + "iconv-lite": "^0.4.4", + "sax": "^1.2.4" + } + }, + "node-pre-gyp": { + "version": "0.10.3", + "bundled": true, + "optional": true, + "requires": { + "detect-libc": "^1.0.2", + "mkdirp": "^0.5.1", + "needle": "^2.2.1", + "nopt": "^4.0.1", + "npm-packlist": "^1.1.6", + "npmlog": "^4.0.2", + "rc": "^1.2.7", + "rimraf": "^2.6.1", + "semver": "^5.3.0", + "tar": "^4" + } + }, + "nopt": { + "version": "4.0.1", + "bundled": true, + "optional": true, + "requires": { + "abbrev": "1", + "osenv": "^0.1.4" + } + }, + "npm-bundled": { + "version": "1.0.5", + "bundled": true, + "optional": true + }, + "npm-packlist": { + "version": "1.2.0", + "bundled": true, + "optional": true, + "requires": { + "ignore-walk": "^3.0.1", + "npm-bundled": "^1.0.1" + } + }, + "npmlog": { + "version": "4.1.2", + "bundled": true, + "optional": true, + "requires": { + "are-we-there-yet": "~1.1.2", + "console-control-strings": "~1.1.0", + "gauge": "~2.7.3", + "set-blocking": "~2.0.0" + } + }, + "number-is-nan": { + "version": "1.0.1", + "bundled": true, + "optional": true + }, + "object-assign": { + "version": "4.1.1", + "bundled": true, + "optional": true + }, + "once": { + "version": "1.4.0", + "bundled": true, + "optional": true, + "requires": { + "wrappy": "1" + } + }, + "os-homedir": { + "version": "1.0.2", + "bundled": true, + "optional": true + }, + "os-tmpdir": { + "version": "1.0.2", + "bundled": true, + "optional": true + }, + "osenv": { + "version": "0.1.5", + "bundled": true, + "optional": true, + "requires": { + "os-homedir": "^1.0.0", + "os-tmpdir": "^1.0.0" + } + }, + "path-is-absolute": { + "version": "1.0.1", + "bundled": true, + "optional": true + }, + "process-nextick-args": { + "version": "2.0.0", + "bundled": true, + "optional": true + }, + "rc": { + "version": "1.2.8", + "bundled": true, + "optional": true, + "requires": { + "deep-extend": "^0.6.0", + "ini": "~1.3.0", + "minimist": "^1.2.0", + "strip-json-comments": "~2.0.1" + }, + "dependencies": { + "minimist": { + "version": "1.2.0", + "bundled": true, + "optional": true + } + } + }, + "readable-stream": { + "version": "2.3.6", + "bundled": true, + "optional": true, + "requires": { + "core-util-is": "~1.0.0", + "inherits": "~2.0.3", + "isarray": "~1.0.0", + "process-nextick-args": "~2.0.0", + "safe-buffer": "~5.1.1", + "string_decoder": "~1.1.1", + "util-deprecate": "~1.0.1" + } + }, + "rimraf": { + "version": "2.6.3", + "bundled": true, + "optional": true, + "requires": { + "glob": "^7.1.3" + } + }, + "safe-buffer": { + "version": "5.1.2", + "bundled": true, + "optional": true + }, + "safer-buffer": { + "version": "2.1.2", + "bundled": true, + "optional": true + }, + "sax": { + "version": "1.2.4", + "bundled": true, + "optional": true + }, + "semver": { + "version": "5.6.0", + "bundled": true, + "optional": true + }, + "set-blocking": { + "version": "2.0.0", + "bundled": true, + "optional": true + }, + "signal-exit": { + "version": "3.0.2", + "bundled": true, + "optional": true + }, + "string-width": { + "version": "1.0.2", + "bundled": true, + "optional": true, + "requires": { + "code-point-at": "^1.0.0", + "is-fullwidth-code-point": "^1.0.0", + "strip-ansi": "^3.0.0" + } + }, + "string_decoder": { + "version": "1.1.1", + "bundled": true, + "optional": true, + "requires": { + "safe-buffer": "~5.1.0" + } + }, + "strip-ansi": { + "version": "3.0.1", + "bundled": true, + "optional": true, + "requires": { + "ansi-regex": "^2.0.0" + } + }, + "strip-json-comments": { + "version": "2.0.1", + "bundled": true, + "optional": true + }, + "tar": { + "version": "4.4.8", + "bundled": true, + "optional": true, + "requires": { + "chownr": "^1.1.1", + "fs-minipass": "^1.2.5", + "minipass": "^2.3.4", + "minizlib": "^1.1.1", + "mkdirp": "^0.5.0", + "safe-buffer": "^5.1.2", + "yallist": "^3.0.2" + } + }, + "util-deprecate": { + "version": "1.0.2", + "bundled": true, + "optional": true + }, + "wide-align": { + "version": "1.1.3", + "bundled": true, + "optional": true, + "requires": { + "string-width": "^1.0.2 || 2" + } + }, + "wrappy": { + "version": "1.0.2", + "bundled": true, + "optional": true + }, + "yallist": { + "version": "3.0.3", + "bundled": true, + "optional": true + } + } + }, + "function-bind": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.1.tgz", + "integrity": "sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A==" + }, + "functional-red-black-tree": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/functional-red-black-tree/-/functional-red-black-tree-1.0.1.tgz", + "integrity": "sha1-GwqzvVU7Kg1jmdKcDj6gslIHgyc=" + }, + "get-caller-file": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-1.0.3.tgz", + "integrity": "sha512-3t6rVToeoZfYSGd8YoLFR2DJkiQrIiUrGcjvFX2mDw3bn6k2OtwHN0TNCLbBO+w8qTvimhDkv+LSscbJY1vE6w==" + }, + "get-stdin": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/get-stdin/-/get-stdin-6.0.0.tgz", + "integrity": "sha512-jp4tHawyV7+fkkSKyvjuLZswblUtz+SQKzSWnBbii16BuZksJlU1wuBYXY75r+duh/llF1ur6oNwi+2ZzjKZ7g==" + }, + "get-stream": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-4.1.0.tgz", + "integrity": "sha512-GMat4EJ5161kIy2HevLlr4luNjBgvmj413KaQA7jt4V8B4RDsfpHk7WQ9GVqfYyyx8OS/L66Kox+rJRNklLK7w==", + "requires": { + "pump": "^3.0.0" + } + }, + "get-value": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/get-value/-/get-value-2.0.6.tgz", + "integrity": "sha1-3BXKHGcjh8p2vTesCjlbogQqLCg=" + }, + "getpass": { + "version": "0.1.7", + "resolved": "https://registry.npmjs.org/getpass/-/getpass-0.1.7.tgz", + "integrity": "sha1-Xv+OPmhNVprkyysSgmBOi6YhSfo=", + "requires": { + "assert-plus": "^1.0.0" + } + }, + "glob": { + "version": "7.1.3", + "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.3.tgz", + "integrity": "sha512-vcfuiIxogLV4DlGBHIUOwI0IbrJ8HWPc4MU7HzviGeNho/UJDfi6B5p3sHeWIQ0KGIU0Jpxi5ZHxemQfLkkAwQ==", + "requires": { + "fs.realpath": "^1.0.0", + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "^3.0.4", + "once": "^1.3.0", + "path-is-absolute": "^1.0.0" + } + }, + "glob-parent": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-3.1.0.tgz", + "integrity": "sha1-nmr2KZ2NO9K9QEMIMr0RPfkGxa4=", + "requires": { + "is-glob": "^3.1.0", + "path-dirname": "^1.0.0" + }, + "dependencies": { + "is-glob": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-3.1.0.tgz", + "integrity": "sha1-e6WuJCF4BKxwcHuWkiVnSGzD6Eo=", + "requires": { + "is-extglob": "^2.1.0" + } + } + } + }, + "global-modules": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/global-modules/-/global-modules-1.0.0.tgz", + "integrity": "sha512-sKzpEkf11GpOFuw0Zzjzmt4B4UZwjOcG757PPvrfhxcLFbq0wpsgpOqxpxtxFiCG4DtG93M6XRVbF2oGdev7bg==", + "requires": { + "global-prefix": "^1.0.1", + "is-windows": "^1.0.1", + "resolve-dir": "^1.0.0" + } + }, + "global-prefix": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/global-prefix/-/global-prefix-1.0.2.tgz", + "integrity": "sha1-2/dDxsFJklk8ZVVoy2btMsASLr4=", + "requires": { + "expand-tilde": "^2.0.2", + "homedir-polyfill": "^1.0.1", + "ini": "^1.3.4", + "is-windows": "^1.0.1", + "which": "^1.2.14" + } + }, + "globals": { + "version": "11.11.0", + "resolved": "https://registry.npmjs.org/globals/-/globals-11.11.0.tgz", + "integrity": "sha512-WHq43gS+6ufNOEqlrDBxVEbb8ntfXrfAUU2ZOpCxrBdGKW3gyv8mCxAfIBD0DroPKGrJ2eSsXsLtY9MPntsyTw==" + }, + "globby": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/globby/-/globby-6.1.0.tgz", + "integrity": "sha1-9abXDoOV4hyFj7BInWTfAkJNUGw=", + "requires": { + "array-union": "^1.0.1", + "glob": "^7.0.3", + "object-assign": "^4.0.1", + "pify": "^2.0.0", + "pinkie-promise": "^2.0.0" + }, + "dependencies": { + "pify": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/pify/-/pify-2.3.0.tgz", + "integrity": "sha1-7RQaasBDqEnqWISY59yosVMw6Qw=" + } + } + }, + "graceful-fs": { + "version": "4.1.15", + "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.1.15.tgz", + "integrity": "sha512-6uHUhOPEBgQ24HM+r6b/QwWfZq+yiFcipKFrOFiBEnWdy5sdzYoi+pJeQaPI5qOLRFqWmAXUPQNsielzdLoecA==" + }, + "growly": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/growly/-/growly-1.3.0.tgz", + "integrity": "sha1-8QdIy+dq+WS3yWyTxrzCivEgwIE=" + }, + "handle-thing": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/handle-thing/-/handle-thing-2.0.0.tgz", + "integrity": "sha512-d4sze1JNC454Wdo2fkuyzCr6aHcbL6PGGuFAz0Li/NcOm1tCHGnWDRmJP85dh9IhQErTc2svWFEX5xHIOo//kQ==" + }, + "handlebars": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/handlebars/-/handlebars-4.1.2.tgz", + "integrity": "sha512-nvfrjqvt9xQ8Z/w0ijewdD/vvWDTOweBUm96NTr66Wfvo1mJenBLwcYmPs3TIBP5ruzYGD7Hx/DaM9RmhroGPw==", + "requires": { + "neo-async": "^2.6.0", + "optimist": "^0.6.1", + "source-map": "^0.6.1", + "uglify-js": "^3.1.4" + }, + "dependencies": { + "source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==" + } + } + }, + "har-schema": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/har-schema/-/har-schema-2.0.0.tgz", + "integrity": "sha1-qUwiJOvKwEeCoNkDVSHyRzW37JI=" + }, + "har-validator": { + "version": "5.1.3", + "resolved": "https://registry.npmjs.org/har-validator/-/har-validator-5.1.3.tgz", + "integrity": "sha512-sNvOCzEQNr/qrvJgc3UG/kD4QtlHycrzwS+6mfTrrSq97BvaYcPZZI1ZSqGSPR73Cxn4LKTD4PttRwfU7jWq5g==", + "requires": { + "ajv": "^6.5.5", + "har-schema": "^2.0.0" + } + }, + "has": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/has/-/has-1.0.3.tgz", + "integrity": "sha512-f2dvO0VU6Oej7RkWJGrehjbzMAjFp5/VKPp5tTpWIV4JHHZK1/BxbFRtf/siA2SWTe09caDmVtYYzWEIbBS4zw==", + "requires": { + "function-bind": "^1.1.1" + } + }, + "has-flag": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", + "integrity": "sha1-tdRU3CGZriJWmfNGfloH87lVuv0=" + }, + "has-symbols": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.0.0.tgz", + "integrity": "sha1-uhqPGvKg/DllD1yFA2dwQSIGO0Q=" + }, + "has-value": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/has-value/-/has-value-1.0.0.tgz", + "integrity": "sha1-GLKB2lhbHFxR3vJMkw7SmgvmsXc=", + "requires": { + "get-value": "^2.0.6", + "has-values": "^1.0.0", + "isobject": "^3.0.0" + } + }, + "has-values": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/has-values/-/has-values-1.0.0.tgz", + "integrity": "sha1-lbC2P+whRmGab+V/51Yo1aOe/k8=", + "requires": { + "is-number": "^3.0.0", + "kind-of": "^4.0.0" + }, + "dependencies": { + "kind-of": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-4.0.0.tgz", + "integrity": "sha1-IIE989cSkosgc3hpGkUGb65y3Vc=", + "requires": { + "is-buffer": "^1.1.5" + } + } + } + }, + "hash-base": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/hash-base/-/hash-base-3.0.4.tgz", + "integrity": "sha1-X8hoaEfs1zSZQDMZprCj8/auSRg=", + "requires": { + "inherits": "^2.0.1", + "safe-buffer": "^5.0.1" + } + }, + "hash.js": { + "version": "1.1.7", + "resolved": "https://registry.npmjs.org/hash.js/-/hash.js-1.1.7.tgz", + "integrity": "sha512-taOaskGt4z4SOANNseOviYDvjEJinIkRgmp7LbKP2YTTmVxWBl87s/uzK9r+44BclBSp2X7K1hqeNfz9JbBeXA==", + "requires": { + "inherits": "^2.0.3", + "minimalistic-assert": "^1.0.1" + } + }, + "hmac-drbg": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/hmac-drbg/-/hmac-drbg-1.0.1.tgz", + "integrity": "sha1-0nRXAQJabHdabFRXk+1QL8DGSaE=", + "requires": { + "hash.js": "^1.0.3", + "minimalistic-assert": "^1.0.0", + "minimalistic-crypto-utils": "^1.0.1" + } + }, + "homedir-polyfill": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/homedir-polyfill/-/homedir-polyfill-1.0.3.tgz", + "integrity": "sha512-eSmmWE5bZTK2Nou4g0AI3zZ9rswp7GRKoKXS1BLUkvPviOqs4YTN1djQIqrXy9k5gEtdLPy86JjRwsNM9tnDcA==", + "requires": { + "parse-passwd": "^1.0.0" + } + }, + "hosted-git-info": { + "version": "2.7.1", + "resolved": "https://registry.npmjs.org/hosted-git-info/-/hosted-git-info-2.7.1.tgz", + "integrity": "sha512-7T/BxH19zbcCTa8XkMlbK5lTo1WtgkFi3GvdWEyNuc4Vex7/9Dqbnpsf4JMydcfj9HCg4zUWFTL3Za6lapg5/w==" + }, + "hpack.js": { + "version": "2.1.6", + "resolved": "https://registry.npmjs.org/hpack.js/-/hpack.js-2.1.6.tgz", + "integrity": "sha1-h3dMCUnlE/QuhFdbPEVoH63ioLI=", + "requires": { + "inherits": "^2.0.1", + "obuf": "^1.0.0", + "readable-stream": "^2.0.1", + "wbuf": "^1.1.0" + } + }, + "html-encoding-sniffer": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/html-encoding-sniffer/-/html-encoding-sniffer-1.0.2.tgz", + "integrity": "sha512-71lZziiDnsuabfdYiUeWdCVyKuqwWi23L8YeIgV9jSSZHCtb6wB1BKWooH7L3tn4/FuZJMVWyNaIDr4RGmaSYw==", + "requires": { + "whatwg-encoding": "^1.0.1" + } + }, + "html-entities": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/html-entities/-/html-entities-1.2.1.tgz", + "integrity": "sha1-DfKTUfByEWNRXfueVUPl9u7VFi8=" + }, + "http-deceiver": { + "version": "1.2.7", + "resolved": "https://registry.npmjs.org/http-deceiver/-/http-deceiver-1.2.7.tgz", + "integrity": "sha1-+nFolEq5pRnTN8sL7HKE3D5yPYc=" + }, + "http-errors": { + "version": "1.6.3", + "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-1.6.3.tgz", + "integrity": "sha1-i1VoC7S+KDoLW/TqLjhYC+HZMg0=", + "requires": { + "depd": "~1.1.2", + "inherits": "2.0.3", + "setprototypeof": "1.1.0", + "statuses": ">= 1.4.0 < 2" + } + }, + "http-parser-js": { + "version": "0.5.0", + "resolved": "https://registry.npmjs.org/http-parser-js/-/http-parser-js-0.5.0.tgz", + "integrity": "sha512-cZdEF7r4gfRIq7ezX9J0T+kQmJNOub71dWbgAXVHDct80TKP4MCETtZQ31xyv38UwgzkWPYF/Xc0ge55dW9Z9w==" + }, + "http-proxy": { + "version": "1.17.0", + "resolved": "https://registry.npmjs.org/http-proxy/-/http-proxy-1.17.0.tgz", + "integrity": "sha512-Taqn+3nNvYRfJ3bGvKfBSRwy1v6eePlm3oc/aWVxZp57DQr5Eq3xhKJi7Z4hZpS8PC3H4qI+Yly5EmFacGuA/g==", + "requires": { + "eventemitter3": "^3.0.0", + "follow-redirects": "^1.0.0", + "requires-port": "^1.0.0" + } + }, + "http-proxy-middleware": { + "version": "0.19.1", + "resolved": "https://registry.npmjs.org/http-proxy-middleware/-/http-proxy-middleware-0.19.1.tgz", + "integrity": "sha512-yHYTgWMQO8VvwNS22eLLloAkvungsKdKTLO8AJlftYIKNfJr3GK3zK0ZCfzDDGUBttdGc8xFy1mCitvNKQtC3Q==", + "requires": { + "http-proxy": "^1.17.0", + "is-glob": "^4.0.0", + "lodash": "^4.17.11", + "micromatch": "^3.1.10" + } + }, + "http-signature": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/http-signature/-/http-signature-1.2.0.tgz", + "integrity": "sha1-muzZJRFHcvPZW2WmCruPfBj7rOE=", + "requires": { + "assert-plus": "^1.0.0", + "jsprim": "^1.2.2", + "sshpk": "^1.7.0" + } + }, + "https-browserify": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/https-browserify/-/https-browserify-1.0.0.tgz", + "integrity": "sha1-7AbBDgo0wPL68Zn3/X/Hj//QPHM=" + }, + "iconv-lite": { + "version": "0.4.24", + "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.24.tgz", + "integrity": "sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==", + "requires": { + "safer-buffer": ">= 2.1.2 < 3" + } + }, + "icss-replace-symbols": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/icss-replace-symbols/-/icss-replace-symbols-1.1.0.tgz", + "integrity": "sha1-Bupvg2ead0njhs/h/oEq5dsiPe0=" + }, + "icss-utils": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/icss-utils/-/icss-utils-4.1.0.tgz", + "integrity": "sha512-3DEun4VOeMvSczifM3F2cKQrDQ5Pj6WKhkOq6HD4QTnDUAq8MQRxy5TX6Sy1iY6WPBe4gQ3p5vTECjbIkglkkQ==", + "requires": { + "postcss": "^7.0.14" + } + }, + "ieee754": { + "version": "1.1.13", + "resolved": "https://registry.npmjs.org/ieee754/-/ieee754-1.1.13.tgz", + "integrity": "sha512-4vf7I2LYV/HaWerSo3XmlMkp5eZ83i+/CDluXi/IGTs/O1sejBNhTtnxzmRZfvOUqj7lZjqHkeTvpgSFDlWZTg==" + }, + "iferr": { + "version": "0.1.5", + "resolved": "https://registry.npmjs.org/iferr/-/iferr-0.1.5.tgz", + "integrity": "sha1-xg7taebY/bazEEofy8ocGS3FtQE=" + }, + "ignore": { + "version": "4.0.6", + "resolved": "https://registry.npmjs.org/ignore/-/ignore-4.0.6.tgz", + "integrity": "sha512-cyFDKrqc/YdcWFniJhzI42+AzS+gNwmUzOSFcRCQYwySuBBBy/KjuxWLZ/FHEH6Moq1NizMOBWyTcv8O4OZIMg==" + }, + "import-cwd": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/import-cwd/-/import-cwd-2.1.0.tgz", + "integrity": "sha1-qmzzbnInYShcs3HsZRn1PiQ1sKk=", + "requires": { + "import-from": "^2.1.0" + } + }, + "import-fresh": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-3.0.0.tgz", + "integrity": "sha512-pOnA9tfM3Uwics+SaBLCNyZZZbK+4PTu0OPZtLlMIrv17EdBoC15S9Kn8ckJ9TZTyKb3ywNE5y1yeDxxGA7nTQ==", + "requires": { + "parent-module": "^1.0.0", + "resolve-from": "^4.0.0" + } + }, + "import-from": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/import-from/-/import-from-2.1.0.tgz", + "integrity": "sha1-M1238qev/VOqpHHUuAId7ja387E=", + "requires": { + "resolve-from": "^3.0.0" + }, + "dependencies": { + "resolve-from": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-3.0.0.tgz", + "integrity": "sha1-six699nWiBvItuZTM17rywoYh0g=" + } + } + }, + "import-local": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/import-local/-/import-local-2.0.0.tgz", + "integrity": "sha512-b6s04m3O+s3CGSbqDIyP4R6aAwAeYlVq9+WUWep6iHa8ETRf9yei1U48C5MmfJmV9AiLYYBKPMq/W+/WRpQmCQ==", + "requires": { + "pkg-dir": "^3.0.0", + "resolve-cwd": "^2.0.0" + } + }, + "imurmurhash": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/imurmurhash/-/imurmurhash-0.1.4.tgz", + "integrity": "sha1-khi5srkoojixPcT7a21XbyMUU+o=" + }, + "indexes-of": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/indexes-of/-/indexes-of-1.0.1.tgz", + "integrity": "sha1-8w9xbI4r00bHtn0985FVZqfAVgc=" + }, + "indexof": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/indexof/-/indexof-0.0.1.tgz", + "integrity": "sha1-gtwzbSMrkGIXnQWrMpOmYFn9Q10=" + }, + "inflight": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", + "integrity": "sha1-Sb1jMdfQLQwJvJEKEHW6gWW1bfk=", + "requires": { + "once": "^1.3.0", + "wrappy": "1" + } + }, + "inherits": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.3.tgz", + "integrity": "sha1-Yzwsg+PaQqUC9SRmAiSA9CCCYd4=" + }, + "ini": { + "version": "1.3.5", + "resolved": "https://registry.npmjs.org/ini/-/ini-1.3.5.tgz", + "integrity": "sha512-RZY5huIKCMRWDUqZlEi72f/lmXKMvuszcMBduliQ3nnWbx9X/ZBQO7DijMEYS9EhHBb2qacRUMtC7svLwe0lcw==" + }, + "inquirer": { + "version": "6.2.2", + "resolved": "https://registry.npmjs.org/inquirer/-/inquirer-6.2.2.tgz", + "integrity": "sha512-Z2rREiXA6cHRR9KBOarR3WuLlFzlIfAEIiB45ll5SSadMg7WqOh1MKEjjndfuH5ewXdixWCxqnVfGOQzPeiztA==", + "requires": { + "ansi-escapes": "^3.2.0", + "chalk": "^2.4.2", + "cli-cursor": "^2.1.0", + "cli-width": "^2.0.0", + "external-editor": "^3.0.3", + "figures": "^2.0.0", + "lodash": "^4.17.11", + "mute-stream": "0.0.7", + "run-async": "^2.2.0", + "rxjs": "^6.4.0", + "string-width": "^2.1.0", + "strip-ansi": "^5.0.0", + "through": "^2.3.6" + }, + "dependencies": { + "ansi-regex": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-4.1.0.tgz", + "integrity": "sha512-1apePfXM1UOSqw0o9IiFAovVz9M5S1Dg+4TrDwfMewQ6p/rmMueb7tWZjQ1rx4Loy1ArBggoqGpfqqdI4rondg==" + }, + "strip-ansi": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-5.2.0.tgz", + "integrity": "sha512-DuRs1gKbBqsMKIZlrffwlug8MHkcnpjs5VPmL1PAh+mA30U0DTotfDZ0d2UUsXpPmPmMMJ6W773MaA3J+lbiWA==", + "requires": { + "ansi-regex": "^4.1.0" + } + } + } + }, + "internal-ip": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/internal-ip/-/internal-ip-4.3.0.tgz", + "integrity": "sha512-S1zBo1D6zcsyuC6PMmY5+55YMILQ9av8lotMx447Bq6SAgo/sDK6y6uUKmuYhW7eacnIhFfsPmCNYdDzsnnDCg==", + "requires": { + "default-gateway": "^4.2.0", + "ipaddr.js": "^1.9.0" + }, + "dependencies": { + "ipaddr.js": { + "version": "1.9.0", + "resolved": "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-1.9.0.tgz", + "integrity": "sha512-M4Sjn6N/+O6/IXSJseKqHoFc+5FdGJ22sXqnjTpdZweHK64MzEPAyQZyEU3R/KRv2GLoa7nNtg/C2Ev6m7z+eA==" + } + } + }, + "interpret": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/interpret/-/interpret-1.2.0.tgz", + "integrity": "sha512-mT34yGKMNceBQUoVn7iCDKDntA7SC6gycMAWzGx1z/CMCTV7b2AAtXlo3nRyHZ1FelRkQbQjprHSYGwzLtkVbw==" + }, + "invariant": { + "version": "2.2.4", + "resolved": "https://registry.npmjs.org/invariant/-/invariant-2.2.4.tgz", + "integrity": "sha512-phJfQVBuaJM5raOpJjSfkiD6BpbCE4Ns//LaXl6wGYtUBY83nWS6Rf9tXm2e8VaK60JEjYldbPif/A2B1C2gNA==", + "requires": { + "loose-envify": "^1.0.0" + } + }, + "invert-kv": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/invert-kv/-/invert-kv-2.0.0.tgz", + "integrity": "sha512-wPVv/y/QQ/Uiirj/vh3oP+1Ww+AWehmi1g5fFWGPF6IpCBCDVrhgHRMvrLfdYcwDh3QJbGXDW4JAuzxElLSqKA==" + }, + "ip": { + "version": "1.1.5", + "resolved": "https://registry.npmjs.org/ip/-/ip-1.1.5.tgz", + "integrity": "sha1-vd7XARQpCCjAoDnnLvJfWq7ENUo=" + }, + "ip-regex": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/ip-regex/-/ip-regex-2.1.0.tgz", + "integrity": "sha1-+ni/XS5pE8kRzp+BnuUUa7bYROk=" + }, + "ipaddr.js": { + "version": "1.8.0", + "resolved": "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-1.8.0.tgz", + "integrity": "sha1-6qM9bd16zo9/b+DJygRA5wZzix4=" + }, + "is-accessor-descriptor": { + "version": "0.1.6", + "resolved": "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-0.1.6.tgz", + "integrity": "sha1-qeEss66Nh2cn7u84Q/igiXtcmNY=", + "requires": { + "kind-of": "^3.0.2" + }, + "dependencies": { + "kind-of": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", + "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", + "requires": { + "is-buffer": "^1.1.5" + } + } + } + }, + "is-arrayish": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.2.1.tgz", + "integrity": "sha1-d8mYQFJ6qOyxqLppe4BkWnqSap0=" + }, + "is-binary-path": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/is-binary-path/-/is-binary-path-1.0.1.tgz", + "integrity": "sha1-dfFmQrSA8YenEcgUFh/TpKdlWJg=", + "requires": { + "binary-extensions": "^1.0.0" + } + }, + "is-buffer": { + "version": "1.1.6", + "resolved": "https://registry.npmjs.org/is-buffer/-/is-buffer-1.1.6.tgz", + "integrity": "sha512-NcdALwpXkTm5Zvvbk7owOUSvVvBKDgKP5/ewfXEznmQFfs4ZRmanOeKBTjRVjka3QFoN6XJ+9F3USqfHqTaU5w==" + }, + "is-callable": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/is-callable/-/is-callable-1.1.4.tgz", + "integrity": "sha512-r5p9sxJjYnArLjObpjA4xu5EKI3CuKHkJXMhT7kwbpUyIFD1n5PMAsoPvWnvtZiNz7LjkYDRZhd7FlI0eMijEA==" + }, + "is-ci": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/is-ci/-/is-ci-2.0.0.tgz", + "integrity": "sha512-YfJT7rkpQB0updsdHLGWrvhBJfcfzNNawYDNIyQXJz0IViGf75O8EBPKSdvw2rF+LGCsX4FZ8tcr3b19LcZq4w==", + "requires": { + "ci-info": "^2.0.0" + } + }, + "is-data-descriptor": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-0.1.4.tgz", + "integrity": "sha1-C17mSDiOLIYCgueT8YVv7D8wG1Y=", + "requires": { + "kind-of": "^3.0.2" + }, + "dependencies": { + "kind-of": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", + "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", + "requires": { + "is-buffer": "^1.1.5" + } + } + } + }, + "is-date-object": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/is-date-object/-/is-date-object-1.0.1.tgz", + "integrity": "sha1-mqIOtq7rv/d/vTPnTKAbM1gdOhY=" + }, + "is-descriptor": { + "version": "0.1.6", + "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-0.1.6.tgz", + "integrity": "sha512-avDYr0SB3DwO9zsMov0gKCESFYqCnE4hq/4z3TdUlukEy5t9C0YRq7HLrsN52NAcqXKaepeCD0n+B0arnVG3Hg==", + "requires": { + "is-accessor-descriptor": "^0.1.6", + "is-data-descriptor": "^0.1.4", + "kind-of": "^5.0.0" + }, + "dependencies": { + "kind-of": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-5.1.0.tgz", + "integrity": "sha512-NGEErnH6F2vUuXDh+OlbcKW7/wOcfdRHaZ7VWtqCztfHri/++YKmP51OdWeGPuqCOba6kk2OTe5d02VmTB80Pw==" + } + } + }, + "is-directory": { + "version": "0.3.1", + "resolved": "https://registry.npmjs.org/is-directory/-/is-directory-0.3.1.tgz", + "integrity": "sha1-YTObbyR1/Hcv2cnYP1yFddwVSuE=" + }, + "is-extendable": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/is-extendable/-/is-extendable-0.1.1.tgz", + "integrity": "sha1-YrEQ4omkcUGOPsNqYX1HLjAd/Ik=" + }, + "is-extglob": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", + "integrity": "sha1-qIwCU1eR8C7TfHahueqXc8gz+MI=" + }, + "is-fullwidth-code-point": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-2.0.0.tgz", + "integrity": "sha1-o7MKXE8ZkYMWeqq5O+764937ZU8=" + }, + "is-generator-fn": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/is-generator-fn/-/is-generator-fn-2.1.0.tgz", + "integrity": "sha512-cTIB4yPYL/Grw0EaSzASzg6bBy9gqCofvWN8okThAYIxKJZC+udlRAmGbM0XLeniEJSs8uEgHPGuHSe1XsOLSQ==" + }, + "is-glob": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.0.tgz", + "integrity": "sha1-lSHHaEXMJhCoUgPd8ICpWML/q8A=", + "requires": { + "is-extglob": "^2.1.1" + } + }, + "is-number": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-number/-/is-number-3.0.0.tgz", + "integrity": "sha1-JP1iAaR4LPUFYcgQJ2r8fRLXEZU=", + "requires": { + "kind-of": "^3.0.2" + }, + "dependencies": { + "kind-of": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", + "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", + "requires": { + "is-buffer": "^1.1.5" + } + } + } + }, + "is-path-cwd": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/is-path-cwd/-/is-path-cwd-2.0.0.tgz", + "integrity": "sha512-m5dHHzpOXEiv18JEORttBO64UgTEypx99vCxQLjbBvGhOJxnTNglYoFXxwo6AbsQb79sqqycQEHv2hWkHZAijA==" + }, + "is-path-in-cwd": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/is-path-in-cwd/-/is-path-in-cwd-2.0.0.tgz", + "integrity": "sha512-6Vz5Gc9s/sDA3JBVu0FzWufm8xaBsqy1zn8Q6gmvGP6nSDMw78aS4poBNeatWjaRpTpxxLn1WOndAiOlk+qY8A==", + "requires": { + "is-path-inside": "^1.0.0" + } + }, + "is-path-inside": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/is-path-inside/-/is-path-inside-1.0.1.tgz", + "integrity": "sha1-jvW33lBDej/cprToZe96pVy0gDY=", + "requires": { + "path-is-inside": "^1.0.1" + } + }, + "is-plain-object": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/is-plain-object/-/is-plain-object-2.0.4.tgz", + "integrity": "sha512-h5PpgXkWitc38BBMYawTYMWJHFZJVnBquFE57xFpjB8pJFiF6gZ+bU+WyI/yqXiFR5mdLsgYNaPe8uao6Uv9Og==", + "requires": { + "isobject": "^3.0.1" + } + }, + "is-promise": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/is-promise/-/is-promise-2.1.0.tgz", + "integrity": "sha1-eaKp7OfwlugPNtKy87wWwf9L8/o=" + }, + "is-regex": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/is-regex/-/is-regex-1.0.4.tgz", + "integrity": "sha1-VRdIm1RwkbCTDglWVM7SXul+lJE=", + "requires": { + "has": "^1.0.1" + } + }, + "is-stream": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-1.1.0.tgz", + "integrity": "sha1-EtSj3U5o4Lec6428hBc66A2RykQ=" + }, + "is-symbol": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/is-symbol/-/is-symbol-1.0.2.tgz", + "integrity": "sha512-HS8bZ9ox60yCJLH9snBpIwv9pYUAkcuLhSA1oero1UB5y9aiQpRA8y2ex945AOtCZL1lJDeIk3G5LthswI46Lw==", + "requires": { + "has-symbols": "^1.0.0" + } + }, + "is-typedarray": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-typedarray/-/is-typedarray-1.0.0.tgz", + "integrity": "sha1-5HnICFjfDBsR3dppQPlgEfzaSpo=" + }, + "is-windows": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/is-windows/-/is-windows-1.0.2.tgz", + "integrity": "sha512-eXK1UInq2bPmjyX6e3VHIzMLobc4J94i4AWn+Hpq3OU5KkrRC96OAcR3PRJ/pGu6m8TRnBHP9dkXQVsT/COVIA==" + }, + "is-wsl": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/is-wsl/-/is-wsl-1.1.0.tgz", + "integrity": "sha1-HxbkqiKwTRM2tmGIpmrzxgDDpm0=" + }, + "isarray": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", + "integrity": "sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE=" + }, + "isexe": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", + "integrity": "sha1-6PvzdNxVb/iUehDcsFctYz8s+hA=" + }, + "isobject": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/isobject/-/isobject-3.0.1.tgz", + "integrity": "sha1-TkMekrEalzFjaqH5yNHMvP2reN8=" + }, + "isstream": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/isstream/-/isstream-0.1.2.tgz", + "integrity": "sha1-R+Y/evVa+m+S4VAOaQ64uFKcCZo=" + }, + "istanbul-api": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/istanbul-api/-/istanbul-api-2.1.1.tgz", + "integrity": "sha512-kVmYrehiwyeBAk/wE71tW6emzLiHGjYIiDrc8sfyty4F8M02/lrgXSm+R1kXysmF20zArvmZXjlE/mg24TVPJw==", + "requires": { + "async": "^2.6.1", + "compare-versions": "^3.2.1", + "fileset": "^2.0.3", + "istanbul-lib-coverage": "^2.0.3", + "istanbul-lib-hook": "^2.0.3", + "istanbul-lib-instrument": "^3.1.0", + "istanbul-lib-report": "^2.0.4", + "istanbul-lib-source-maps": "^3.0.2", + "istanbul-reports": "^2.1.1", + "js-yaml": "^3.12.0", + "make-dir": "^1.3.0", + "minimatch": "^3.0.4", + "once": "^1.4.0" + } + }, + "istanbul-lib-coverage": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/istanbul-lib-coverage/-/istanbul-lib-coverage-2.0.3.tgz", + "integrity": "sha512-dKWuzRGCs4G+67VfW9pBFFz2Jpi4vSp/k7zBcJ888ofV5Mi1g5CUML5GvMvV6u9Cjybftu+E8Cgp+k0dI1E5lw==" + }, + "istanbul-lib-hook": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/istanbul-lib-hook/-/istanbul-lib-hook-2.0.3.tgz", + "integrity": "sha512-CLmEqwEhuCYtGcpNVJjLV1DQyVnIqavMLFHV/DP+np/g3qvdxu3gsPqYoJMXm15sN84xOlckFB3VNvRbf5yEgA==", + "requires": { + "append-transform": "^1.0.0" + } + }, + "istanbul-lib-instrument": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/istanbul-lib-instrument/-/istanbul-lib-instrument-3.1.0.tgz", + "integrity": "sha512-ooVllVGT38HIk8MxDj/OIHXSYvH+1tq/Vb38s8ixt9GoJadXska4WkGY+0wkmtYCZNYtaARniH/DixUGGLZ0uA==", + "requires": { + "@babel/generator": "^7.0.0", + "@babel/parser": "^7.0.0", + "@babel/template": "^7.0.0", + "@babel/traverse": "^7.0.0", + "@babel/types": "^7.0.0", + "istanbul-lib-coverage": "^2.0.3", + "semver": "^5.5.0" + } + }, + "istanbul-lib-report": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/istanbul-lib-report/-/istanbul-lib-report-2.0.4.tgz", + "integrity": "sha512-sOiLZLAWpA0+3b5w5/dq0cjm2rrNdAfHWaGhmn7XEFW6X++IV9Ohn+pnELAl9K3rfpaeBfbmH9JU5sejacdLeA==", + "requires": { + "istanbul-lib-coverage": "^2.0.3", + "make-dir": "^1.3.0", + "supports-color": "^6.0.0" + }, + "dependencies": { + "supports-color": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-6.1.0.tgz", + "integrity": "sha512-qe1jfm1Mg7Nq/NSh6XE24gPXROEVsWHxC1LIx//XNlD9iw7YZQGjZNjYN7xGaEG6iKdA8EtNFW6R0gjnVXp+wQ==", + "requires": { + "has-flag": "^3.0.0" + } + } + } + }, + "istanbul-lib-source-maps": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/istanbul-lib-source-maps/-/istanbul-lib-source-maps-3.0.2.tgz", + "integrity": "sha512-JX4v0CiKTGp9fZPmoxpu9YEkPbEqCqBbO3403VabKjH+NRXo72HafD5UgnjTEqHL2SAjaZK1XDuDOkn6I5QVfQ==", + "requires": { + "debug": "^4.1.1", + "istanbul-lib-coverage": "^2.0.3", + "make-dir": "^1.3.0", + "rimraf": "^2.6.2", + "source-map": "^0.6.1" + }, + "dependencies": { + "debug": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.1.1.tgz", + "integrity": "sha512-pYAIzeRo8J6KPEaJ0VWOh5Pzkbw/RetuzehGM7QRRX5he4fPHx2rdKMB256ehJCkX+XRQm16eZLqLNS8RSZXZw==", + "requires": { + "ms": "^2.1.1" + } + }, + "ms": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.1.tgz", + "integrity": "sha512-tgp+dl5cGk28utYktBsrFqA7HKgrhgPsg6Z/EfhWI4gl1Hwq8B/GmY/0oXZ6nF8hDVesS/FpnYaD/kOWhYQvyg==" + }, + "source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==" + } + } + }, + "istanbul-reports": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/istanbul-reports/-/istanbul-reports-2.1.1.tgz", + "integrity": "sha512-FzNahnidyEPBCI0HcufJoSEoKykesRlFcSzQqjH9x0+LC8tnnE/p/90PBLu8iZTxr8yYZNyTtiAujUqyN+CIxw==", + "requires": { + "handlebars": "^4.1.0" + } + }, + "jest": { + "version": "24.7.1", + "resolved": "https://registry.npmjs.org/jest/-/jest-24.7.1.tgz", + "integrity": "sha512-AbvRar5r++izmqo5gdbAjTeA6uNRGoNRuj5vHB0OnDXo2DXWZJVuaObiGgtlvhKb+cWy2oYbQSfxv7Q7GjnAtA==", + "requires": { + "import-local": "^2.0.0", + "jest-cli": "^24.7.1" + }, + "dependencies": { + "jest-cli": { + "version": "24.7.1", + "resolved": "https://registry.npmjs.org/jest-cli/-/jest-cli-24.7.1.tgz", + "integrity": "sha512-32OBoSCVPzcTslGFl6yVCMzB2SqX3IrWwZCY5mZYkb0D2WsogmU3eV2o8z7+gRQa4o4sZPX/k7GU+II7CxM6WQ==", + "requires": { + "@jest/core": "^24.7.1", + "@jest/test-result": "^24.7.1", + "@jest/types": "^24.7.0", + "chalk": "^2.0.1", + "exit": "^0.1.2", + "import-local": "^2.0.0", + "is-ci": "^2.0.0", + "jest-config": "^24.7.1", + "jest-util": "^24.7.1", + "jest-validate": "^24.7.0", + "prompts": "^2.0.1", + "realpath-native": "^1.1.0", + "yargs": "^12.0.2" + } + } + } + }, + "jest-changed-files": { + "version": "24.7.0", + "resolved": "https://registry.npmjs.org/jest-changed-files/-/jest-changed-files-24.7.0.tgz", + "integrity": "sha512-33BgewurnwSfJrW7T5/ZAXGE44o7swLslwh8aUckzq2e17/2Os1V0QU506ZNik3hjs8MgnEMKNkcud442NCDTw==", + "requires": { + "@jest/types": "^24.7.0", + "execa": "^1.0.0", + "throat": "^4.0.0" + } + }, + "jest-config": { + "version": "24.7.1", + "resolved": "https://registry.npmjs.org/jest-config/-/jest-config-24.7.1.tgz", + "integrity": "sha512-8FlJNLI+X+MU37j7j8RE4DnJkvAghXmBWdArVzypW6WxfGuxiL/CCkzBg0gHtXhD2rxla3IMOSUAHylSKYJ83g==", + "requires": { + "@babel/core": "^7.1.0", + "@jest/test-sequencer": "^24.7.1", + "@jest/types": "^24.7.0", + "babel-jest": "^24.7.1", + "chalk": "^2.0.1", + "glob": "^7.1.1", + "jest-environment-jsdom": "^24.7.1", + "jest-environment-node": "^24.7.1", + "jest-get-type": "^24.3.0", + "jest-jasmine2": "^24.7.1", + "jest-regex-util": "^24.3.0", + "jest-resolve": "^24.7.1", + "jest-util": "^24.7.1", + "jest-validate": "^24.7.0", + "micromatch": "^3.1.10", + "pretty-format": "^24.7.0", + "realpath-native": "^1.1.0" + } + }, + "jest-diff": { + "version": "24.7.0", + "resolved": "https://registry.npmjs.org/jest-diff/-/jest-diff-24.7.0.tgz", + "integrity": "sha512-ULQZ5B1lWpH70O4xsANC4tf4Ko6RrpwhE3PtG6ERjMg1TiYTC2Wp4IntJVGro6a8HG9luYHhhmF4grF0Pltckg==", + "requires": { + "chalk": "^2.0.1", + "diff-sequences": "^24.3.0", + "jest-get-type": "^24.3.0", + "pretty-format": "^24.7.0" + } + }, + "jest-docblock": { + "version": "24.3.0", + "resolved": "https://registry.npmjs.org/jest-docblock/-/jest-docblock-24.3.0.tgz", + "integrity": "sha512-nlANmF9Yq1dufhFlKG9rasfQlrY7wINJbo3q01tu56Jv5eBU5jirylhF2O5ZBnLxzOVBGRDz/9NAwNyBtG4Nyg==", + "requires": { + "detect-newline": "^2.1.0" + } + }, + "jest-each": { + "version": "24.7.1", + "resolved": "https://registry.npmjs.org/jest-each/-/jest-each-24.7.1.tgz", + "integrity": "sha512-4fsS8fEfLa3lfnI1Jw6NxjhyRTgfpuOVTeUZZFyVYqeTa4hPhr2YkToUhouuLTrL2eMGOfpbdMyRx0GQ/VooKA==", + "requires": { + "@jest/types": "^24.7.0", + "chalk": "^2.0.1", + "jest-get-type": "^24.3.0", + "jest-util": "^24.7.1", + "pretty-format": "^24.7.0" + } + }, + "jest-environment-jsdom": { + "version": "24.7.1", + "resolved": "https://registry.npmjs.org/jest-environment-jsdom/-/jest-environment-jsdom-24.7.1.tgz", + "integrity": "sha512-Gnhb+RqE2JuQGb3kJsLF8vfqjt3PHKSstq4Xc8ic+ax7QKo4Z0RWGucU3YV+DwKR3T9SYc+3YCUQEJs8r7+Jxg==", + "requires": { + "@jest/environment": "^24.7.1", + "@jest/fake-timers": "^24.7.1", + "@jest/types": "^24.7.0", + "jest-mock": "^24.7.0", + "jest-util": "^24.7.1", + "jsdom": "^11.5.1" + } + }, + "jest-environment-node": { + "version": "24.7.1", + "resolved": "https://registry.npmjs.org/jest-environment-node/-/jest-environment-node-24.7.1.tgz", + "integrity": "sha512-GJJQt1p9/C6aj6yNZMvovZuxTUd+BEJprETdvTKSb4kHcw4mFj8777USQV0FJoJ4V3djpOwA5eWyPwfq//PFBA==", + "requires": { + "@jest/environment": "^24.7.1", + "@jest/fake-timers": "^24.7.1", + "@jest/types": "^24.7.0", + "jest-mock": "^24.7.0", + "jest-util": "^24.7.1" + } + }, + "jest-get-type": { + "version": "24.3.0", + "resolved": "https://registry.npmjs.org/jest-get-type/-/jest-get-type-24.3.0.tgz", + "integrity": "sha512-HYF6pry72YUlVcvUx3sEpMRwXEWGEPlJ0bSPVnB3b3n++j4phUEoSPcS6GC0pPJ9rpyPSe4cb5muFo6D39cXow==" + }, + "jest-haste-map": { + "version": "24.7.1", + "resolved": "https://registry.npmjs.org/jest-haste-map/-/jest-haste-map-24.7.1.tgz", + "integrity": "sha512-g0tWkzjpHD2qa03mTKhlydbmmYiA2KdcJe762SbfFo/7NIMgBWAA0XqQlApPwkWOF7Cxoi/gUqL0i6DIoLpMBw==", + "requires": { + "@jest/types": "^24.7.0", + "anymatch": "^2.0.0", + "fb-watchman": "^2.0.0", + "fsevents": "^1.2.7", + "graceful-fs": "^4.1.15", + "invariant": "^2.2.4", + "jest-serializer": "^24.4.0", + "jest-util": "^24.7.1", + "jest-worker": "^24.6.0", + "micromatch": "^3.1.10", + "sane": "^4.0.3", + "walker": "^1.0.7" + } + }, + "jest-jasmine2": { + "version": "24.7.1", + "resolved": "https://registry.npmjs.org/jest-jasmine2/-/jest-jasmine2-24.7.1.tgz", + "integrity": "sha512-Y/9AOJDV1XS44wNwCaThq4Pw3gBPiOv/s6NcbOAkVRRUEPu+36L2xoPsqQXsDrxoBerqeyslpn2TpCI8Zr6J2w==", + "requires": { + "@babel/traverse": "^7.1.0", + "@jest/environment": "^24.7.1", + "@jest/test-result": "^24.7.1", + "@jest/types": "^24.7.0", + "chalk": "^2.0.1", + "co": "^4.6.0", + "expect": "^24.7.1", + "is-generator-fn": "^2.0.0", + "jest-each": "^24.7.1", + "jest-matcher-utils": "^24.7.0", + "jest-message-util": "^24.7.1", + "jest-runtime": "^24.7.1", + "jest-snapshot": "^24.7.1", + "jest-util": "^24.7.1", + "pretty-format": "^24.7.0", + "throat": "^4.0.0" + } + }, + "jest-leak-detector": { + "version": "24.7.0", + "resolved": "https://registry.npmjs.org/jest-leak-detector/-/jest-leak-detector-24.7.0.tgz", + "integrity": "sha512-zV0qHKZGXtmPVVzT99CVEcHE9XDf+8LwiE0Ob7jjezERiGVljmqKFWpV2IkG+rkFIEUHFEkMiICu7wnoPM/RoQ==", + "requires": { + "pretty-format": "^24.7.0" + } + }, + "jest-matcher-utils": { + "version": "24.7.0", + "resolved": "https://registry.npmjs.org/jest-matcher-utils/-/jest-matcher-utils-24.7.0.tgz", + "integrity": "sha512-158ieSgk3LNXeUhbVJYRXyTPSCqNgVXOp/GT7O94mYd3pk/8+odKTyR1JLtNOQSPzNi8NFYVONtvSWA/e1RDXg==", + "requires": { + "chalk": "^2.0.1", + "jest-diff": "^24.7.0", + "jest-get-type": "^24.3.0", + "pretty-format": "^24.7.0" + } + }, + "jest-message-util": { + "version": "24.7.1", + "resolved": "https://registry.npmjs.org/jest-message-util/-/jest-message-util-24.7.1.tgz", + "integrity": "sha512-dk0gqVtyqezCHbcbk60CdIf+8UHgD+lmRHifeH3JRcnAqh4nEyPytSc9/L1+cQyxC+ceaeP696N4ATe7L+omcg==", + "requires": { + "@babel/code-frame": "^7.0.0", + "@jest/test-result": "^24.7.1", + "@jest/types": "^24.7.0", + "@types/stack-utils": "^1.0.1", + "chalk": "^2.0.1", + "micromatch": "^3.1.10", + "slash": "^2.0.0", + "stack-utils": "^1.0.1" + } + }, + "jest-mock": { + "version": "24.7.0", + "resolved": "https://registry.npmjs.org/jest-mock/-/jest-mock-24.7.0.tgz", + "integrity": "sha512-6taW4B4WUcEiT2V9BbOmwyGuwuAFT2G8yghF7nyNW1/2gq5+6aTqSPcS9lS6ArvEkX55vbPAS/Jarx5LSm4Fng==", + "requires": { + "@jest/types": "^24.7.0" + } + }, + "jest-pnp-resolver": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/jest-pnp-resolver/-/jest-pnp-resolver-1.2.1.tgz", + "integrity": "sha512-pgFw2tm54fzgYvc/OHrnysABEObZCUNFnhjoRjaVOCN8NYc032/gVjPaHD4Aq6ApkSieWtfKAFQtmDKAmhupnQ==" + }, + "jest-regex-util": { + "version": "24.3.0", + "resolved": "https://registry.npmjs.org/jest-regex-util/-/jest-regex-util-24.3.0.tgz", + "integrity": "sha512-tXQR1NEOyGlfylyEjg1ImtScwMq8Oh3iJbGTjN7p0J23EuVX1MA8rwU69K4sLbCmwzgCUbVkm0FkSF9TdzOhtg==" + }, + "jest-resolve": { + "version": "24.7.1", + "resolved": "https://registry.npmjs.org/jest-resolve/-/jest-resolve-24.7.1.tgz", + "integrity": "sha512-Bgrc+/UUZpGJ4323sQyj85hV9d+ANyPNu6XfRDUcyFNX1QrZpSoM0kE4Mb2vZMAYTJZsBFzYe8X1UaOkOELSbw==", + "requires": { + "@jest/types": "^24.7.0", + "browser-resolve": "^1.11.3", + "chalk": "^2.0.1", + "jest-pnp-resolver": "^1.2.1", + "realpath-native": "^1.1.0" + } + }, + "jest-resolve-dependencies": { + "version": "24.7.1", + "resolved": "https://registry.npmjs.org/jest-resolve-dependencies/-/jest-resolve-dependencies-24.7.1.tgz", + "integrity": "sha512-2Eyh5LJB2liNzfk4eo7bD1ZyBbqEJIyyrFtZG555cSWW9xVHxII2NuOkSl1yUYTAYCAmM2f2aIT5A7HzNmubyg==", + "requires": { + "@jest/types": "^24.7.0", + "jest-regex-util": "^24.3.0", + "jest-snapshot": "^24.7.1" + } + }, + "jest-runner": { + "version": "24.7.1", + "resolved": "https://registry.npmjs.org/jest-runner/-/jest-runner-24.7.1.tgz", + "integrity": "sha512-aNFc9liWU/xt+G9pobdKZ4qTeG/wnJrJna3VqunziDNsWT3EBpmxXZRBMKCsNMyfy+A/XHiV+tsMLufdsNdgCw==", + "requires": { + "@jest/console": "^24.7.1", + "@jest/environment": "^24.7.1", + "@jest/test-result": "^24.7.1", + "@jest/types": "^24.7.0", + "chalk": "^2.4.2", + "exit": "^0.1.2", + "graceful-fs": "^4.1.15", + "jest-config": "^24.7.1", + "jest-docblock": "^24.3.0", + "jest-haste-map": "^24.7.1", + "jest-jasmine2": "^24.7.1", + "jest-leak-detector": "^24.7.0", + "jest-message-util": "^24.7.1", + "jest-resolve": "^24.7.1", + "jest-runtime": "^24.7.1", + "jest-util": "^24.7.1", + "jest-worker": "^24.6.0", + "source-map-support": "^0.5.6", + "throat": "^4.0.0" + } + }, + "jest-runtime": { + "version": "24.7.1", + "resolved": "https://registry.npmjs.org/jest-runtime/-/jest-runtime-24.7.1.tgz", + "integrity": "sha512-0VAbyBy7tll3R+82IPJpf6QZkokzXPIS71aDeqh+WzPRXRCNz6StQ45otFariPdJ4FmXpDiArdhZrzNAC3sj6A==", + "requires": { + "@jest/console": "^24.7.1", + "@jest/environment": "^24.7.1", + "@jest/source-map": "^24.3.0", + "@jest/transform": "^24.7.1", + "@jest/types": "^24.7.0", + "@types/yargs": "^12.0.2", + "chalk": "^2.0.1", + "exit": "^0.1.2", + "glob": "^7.1.3", + "graceful-fs": "^4.1.15", + "jest-config": "^24.7.1", + "jest-haste-map": "^24.7.1", + "jest-message-util": "^24.7.1", + "jest-mock": "^24.7.0", + "jest-regex-util": "^24.3.0", + "jest-resolve": "^24.7.1", + "jest-snapshot": "^24.7.1", + "jest-util": "^24.7.1", + "jest-validate": "^24.7.0", + "realpath-native": "^1.1.0", + "slash": "^2.0.0", + "strip-bom": "^3.0.0", + "yargs": "^12.0.2" + } + }, + "jest-serializer": { + "version": "24.4.0", + "resolved": "https://registry.npmjs.org/jest-serializer/-/jest-serializer-24.4.0.tgz", + "integrity": "sha512-k//0DtglVstc1fv+GY/VHDIjrtNjdYvYjMlbLUed4kxrE92sIUewOi5Hj3vrpB8CXfkJntRPDRjCrCvUhBdL8Q==" + }, + "jest-snapshot": { + "version": "24.7.1", + "resolved": "https://registry.npmjs.org/jest-snapshot/-/jest-snapshot-24.7.1.tgz", + "integrity": "sha512-8Xk5O4p+JsZZn4RCNUS3pxA+ORKpEKepE+a5ejIKrId9CwrVN0NY+vkqEkXqlstA5NMBkNahXkR/4qEBy0t5yA==", + "requires": { + "@babel/types": "^7.0.0", + "@jest/types": "^24.7.0", + "chalk": "^2.0.1", + "expect": "^24.7.1", + "jest-diff": "^24.7.0", + "jest-matcher-utils": "^24.7.0", + "jest-message-util": "^24.7.1", + "jest-resolve": "^24.7.1", + "mkdirp": "^0.5.1", + "natural-compare": "^1.4.0", + "pretty-format": "^24.7.0", + "semver": "^5.5.0" + } + }, + "jest-util": { + "version": "24.7.1", + "resolved": "https://registry.npmjs.org/jest-util/-/jest-util-24.7.1.tgz", + "integrity": "sha512-/KilOue2n2rZ5AnEBYoxOXkeTu6vi7cjgQ8MXEkih0oeAXT6JkS3fr7/j8+engCjciOU1Nq5loMSKe0A1oeX0A==", + "requires": { + "@jest/console": "^24.7.1", + "@jest/fake-timers": "^24.7.1", + "@jest/source-map": "^24.3.0", + "@jest/test-result": "^24.7.1", + "@jest/types": "^24.7.0", + "callsites": "^3.0.0", + "chalk": "^2.0.1", + "graceful-fs": "^4.1.15", + "is-ci": "^2.0.0", + "mkdirp": "^0.5.1", + "slash": "^2.0.0", + "source-map": "^0.6.0" + }, + "dependencies": { + "source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==" + } + } + }, + "jest-validate": { + "version": "24.7.0", + "resolved": "https://registry.npmjs.org/jest-validate/-/jest-validate-24.7.0.tgz", + "integrity": "sha512-cgai/gts9B2chz1rqVdmLhzYxQbgQurh1PEQSvSgPZ8KGa1AqXsqC45W5wKEwzxKrWqypuQrQxnF4+G9VejJJA==", + "requires": { + "@jest/types": "^24.7.0", + "camelcase": "^5.0.0", + "chalk": "^2.0.1", + "jest-get-type": "^24.3.0", + "leven": "^2.1.0", + "pretty-format": "^24.7.0" + } + }, + "jest-watcher": { + "version": "24.7.1", + "resolved": "https://registry.npmjs.org/jest-watcher/-/jest-watcher-24.7.1.tgz", + "integrity": "sha512-Wd6TepHLRHVKLNPacEsBwlp9raeBIO+01xrN24Dek4ggTS8HHnOzYSFnvp+6MtkkJ3KfMzy220KTi95e2rRkrw==", + "requires": { + "@jest/test-result": "^24.7.1", + "@jest/types": "^24.7.0", + "@types/yargs": "^12.0.9", + "ansi-escapes": "^3.0.0", + "chalk": "^2.0.1", + "jest-util": "^24.7.1", + "string-length": "^2.0.0" + } + }, + "jest-worker": { + "version": "24.6.0", + "resolved": "https://registry.npmjs.org/jest-worker/-/jest-worker-24.6.0.tgz", + "integrity": "sha512-jDwgW5W9qGNvpI1tNnvajh0a5IE/PuGLFmHk6aR/BZFz8tSgGw17GsDPXAJ6p91IvYDjOw8GpFbvvZGAK+DPQQ==", + "requires": { + "merge-stream": "^1.0.1", + "supports-color": "^6.1.0" + }, + "dependencies": { + "supports-color": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-6.1.0.tgz", + "integrity": "sha512-qe1jfm1Mg7Nq/NSh6XE24gPXROEVsWHxC1LIx//XNlD9iw7YZQGjZNjYN7xGaEG6iKdA8EtNFW6R0gjnVXp+wQ==", + "requires": { + "has-flag": "^3.0.0" + } + } + } + }, + "js-tokens": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz", + "integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==" + }, + "js-yaml": { + "version": "3.13.1", + "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.13.1.tgz", + "integrity": "sha512-YfbcO7jXDdyj0DGxYVSlSeQNHbD7XPWvrVWeVUujrQEoZzWJIRrCPoyk6kL6IAjAG2IolMK4T0hNUe0HOUs5Jw==", + "requires": { + "argparse": "^1.0.7", + "esprima": "^4.0.0" + } + }, + "jsbn": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/jsbn/-/jsbn-0.1.1.tgz", + "integrity": "sha1-peZUwuWi3rXyAdls77yoDA7y9RM=" + }, + "jsdom": { + "version": "11.12.0", + "resolved": "https://registry.npmjs.org/jsdom/-/jsdom-11.12.0.tgz", + "integrity": "sha512-y8Px43oyiBM13Zc1z780FrfNLJCXTL40EWlty/LXUtcjykRBNgLlCjWXpfSPBl2iv+N7koQN+dvqszHZgT/Fjw==", + "requires": { + "abab": "^2.0.0", + "acorn": "^5.5.3", + "acorn-globals": "^4.1.0", + "array-equal": "^1.0.0", + "cssom": ">= 0.3.2 < 0.4.0", + "cssstyle": "^1.0.0", + "data-urls": "^1.0.0", + "domexception": "^1.0.1", + "escodegen": "^1.9.1", + "html-encoding-sniffer": "^1.0.2", + "left-pad": "^1.3.0", + "nwsapi": "^2.0.7", + "parse5": "4.0.0", + "pn": "^1.1.0", + "request": "^2.87.0", + "request-promise-native": "^1.0.5", + "sax": "^1.2.4", + "symbol-tree": "^3.2.2", + "tough-cookie": "^2.3.4", + "w3c-hr-time": "^1.0.1", + "webidl-conversions": "^4.0.2", + "whatwg-encoding": "^1.0.3", + "whatwg-mimetype": "^2.1.0", + "whatwg-url": "^6.4.1", + "ws": "^5.2.0", + "xml-name-validator": "^3.0.0" + }, + "dependencies": { + "acorn": { + "version": "5.7.3", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-5.7.3.tgz", + "integrity": "sha512-T/zvzYRfbVojPWahDsE5evJdHb3oJoQfFbsrKM7w5Zcs++Tr257tia3BmMP8XYVjp1S9RZXQMh7gao96BlqZOw==" + } + } + }, + "jsesc": { + "version": "2.5.2", + "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-2.5.2.tgz", + "integrity": "sha512-OYu7XEzjkCQ3C5Ps3QIZsQfNpqoJyZZA99wd9aWd05NCtC5pWOkShK2mkL6HXQR6/Cy2lbNdPlZBpuQHXE63gA==" + }, + "json-parse-better-errors": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/json-parse-better-errors/-/json-parse-better-errors-1.0.2.tgz", + "integrity": "sha512-mrqyZKfX5EhL7hvqcV6WG1yYjnjeuYDzDhhcAAUrq8Po85NBQBJP+ZDUT75qZQ98IkUoBqdkExkukOU7Ts2wrw==" + }, + "json-schema": { + "version": "0.2.3", + "resolved": "https://registry.npmjs.org/json-schema/-/json-schema-0.2.3.tgz", + "integrity": "sha1-tIDIkuWaLwWVTOcnvT8qTogvnhM=" + }, + "json-schema-traverse": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", + "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==" + }, + "json-stable-stringify-without-jsonify": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/json-stable-stringify-without-jsonify/-/json-stable-stringify-without-jsonify-1.0.1.tgz", + "integrity": "sha1-nbe1lJatPzz+8wp1FC0tkwrXJlE=" + }, + "json-stringify-safe": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/json-stringify-safe/-/json-stringify-safe-5.0.1.tgz", + "integrity": "sha1-Epai1Y/UXxmg9s4B1lcB4sc1tus=" + }, + "json3": { + "version": "3.3.2", + "resolved": "https://registry.npmjs.org/json3/-/json3-3.3.2.tgz", + "integrity": "sha1-PAQ0dD35Pi9cQq7nsZvLSDV19OE=" + }, + "json5": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/json5/-/json5-1.0.1.tgz", + "integrity": "sha512-aKS4WQjPenRxiQsC93MNfjx+nbF4PAdYzmd/1JIj8HYzqfbu86beTuNgXDzPknWk0n0uARlyewZo4s++ES36Ow==", + "requires": { + "minimist": "^1.2.0" + } + }, + "jsprim": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/jsprim/-/jsprim-1.4.1.tgz", + "integrity": "sha1-MT5mvB5cwG5Di8G3SZwuXFastqI=", + "requires": { + "assert-plus": "1.0.0", + "extsprintf": "1.3.0", + "json-schema": "0.2.3", + "verror": "1.10.0" + } + }, + "killable": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/killable/-/killable-1.0.1.tgz", + "integrity": "sha512-LzqtLKlUwirEUyl/nicirVmNiPvYs7l5n8wOPP7fyJVpUPkvCnW/vuiXGpylGUlnPDnB7311rARzAt3Mhswpjg==" + }, + "kind-of": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-6.0.2.tgz", + "integrity": "sha512-s5kLOcnH0XqDO+FvuaLX8DDjZ18CGFk7VygH40QoKPUQhW4e2rvM0rwUq0t8IQDOwYSeLK01U90OjzBTme2QqA==" + }, + "kleur": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/kleur/-/kleur-3.0.3.tgz", + "integrity": "sha512-eTIzlVOSUR+JxdDFepEYcBMtZ9Qqdef+rnzWdRZuMbOywu5tO2w2N7rqjoANZ5k9vywhL6Br1VRjUIgTQx4E8w==" + }, + "lcid": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/lcid/-/lcid-2.0.0.tgz", + "integrity": "sha512-avPEb8P8EGnwXKClwsNUgryVjllcRqtMYa49NTsbQagYuT1DcXnl1915oxWjoyGrXR6zH/Y0Zc96xWsPcoDKeA==", + "requires": { + "invert-kv": "^2.0.0" + } + }, + "left-pad": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/left-pad/-/left-pad-1.3.0.tgz", + "integrity": "sha512-XI5MPzVNApjAyhQzphX8BkmKsKUxD4LdyK24iZeQGinBN9yTQT3bFlCBy/aVx2HrNcqQGsdot8ghrjyrvMCoEA==" + }, + "leven": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/leven/-/leven-2.1.0.tgz", + "integrity": "sha1-wuep93IJTe6dNCAq6KzORoeHVYA=" + }, + "levn": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/levn/-/levn-0.3.0.tgz", + "integrity": "sha1-OwmSTt+fCDwEkP3UwLxEIeBHZO4=", + "requires": { + "prelude-ls": "~1.1.2", + "type-check": "~0.3.2" + } + }, + "load-json-file": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/load-json-file/-/load-json-file-4.0.0.tgz", + "integrity": "sha1-L19Fq5HjMhYjT9U62rZo607AmTs=", + "requires": { + "graceful-fs": "^4.1.2", + "parse-json": "^4.0.0", + "pify": "^3.0.0", + "strip-bom": "^3.0.0" + } + }, + "loader-runner": { + "version": "2.4.0", + "resolved": "https://registry.npmjs.org/loader-runner/-/loader-runner-2.4.0.tgz", + "integrity": "sha512-Jsmr89RcXGIwivFY21FcRrisYZfvLMTWx5kOLc+JTxtpBOG6xML0vzbc6SEQG2FO9/4Fc3wW4LVcB5DmGflaRw==" + }, + "loader-utils": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/loader-utils/-/loader-utils-1.2.3.tgz", + "integrity": "sha512-fkpz8ejdnEMG3s37wGL07iSBDg99O9D5yflE9RGNH3hRdx9SOwYfnGYdZOUIZitN8E+E2vkq3MUMYMvPYl5ZZA==", + "requires": { + "big.js": "^5.2.2", + "emojis-list": "^2.0.0", + "json5": "^1.0.1" + } + }, + "locate-path": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-3.0.0.tgz", + "integrity": "sha512-7AO748wWnIhNqAuaty2ZWHkQHRSNfPVIsPIfwEOWO22AmaoVrWavlOcMR5nzTLNYvp36X220/maaRsrec1G65A==", + "requires": { + "p-locate": "^3.0.0", + "path-exists": "^3.0.0" + } + }, + "lodash": { + "version": "4.17.11", + "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.11.tgz", + "integrity": "sha512-cQKh8igo5QUhZ7lg38DYWAxMvjSAKG0A8wGSVimP07SIUEK2UO+arSRKbRZWtelMtN5V0Hkwh5ryOto/SshYIg==" + }, + "lodash.sortby": { + "version": "4.7.0", + "resolved": "https://registry.npmjs.org/lodash.sortby/-/lodash.sortby-4.7.0.tgz", + "integrity": "sha1-7dFMgk4sycHgsKG0K7UhBRakJDg=" + }, + "lodash.unescape": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/lodash.unescape/-/lodash.unescape-4.0.1.tgz", + "integrity": "sha1-vyJJiGzlFM2hEvrpIYzcBlIR/Jw=" + }, + "log-symbols": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/log-symbols/-/log-symbols-2.2.0.tgz", + "integrity": "sha512-VeIAFslyIerEJLXHziedo2basKbMKtTw3vfn5IzG0XTjhAVEJyNHnL2p7vc+wBDSdQuUpNw3M2u6xb9QsAY5Eg==", + "requires": { + "chalk": "^2.0.1" + } + }, + "loglevel": { + "version": "1.6.1", + "resolved": "https://registry.npmjs.org/loglevel/-/loglevel-1.6.1.tgz", + "integrity": "sha1-4PyVEztu8nbNyIh82vJKpvFW+Po=" + }, + "loglevelnext": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/loglevelnext/-/loglevelnext-1.0.5.tgz", + "integrity": "sha512-V/73qkPuJmx4BcBF19xPBr+0ZRVBhc4POxvZTZdMeXpJ4NItXSJ/MSwuFT0kQJlCbXvdlZoQQ/418bS1y9Jh6A==", + "requires": { + "es6-symbol": "^3.1.1", + "object.assign": "^4.1.0" + } + }, + "loose-envify": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/loose-envify/-/loose-envify-1.4.0.tgz", + "integrity": "sha512-lyuxPGr/Wfhrlem2CL/UcnUc1zcqKAImBDzukY7Y5F/yQiNdko6+fRLevlw1HgMySw7f611UIY408EtxRSoK3Q==", + "requires": { + "js-tokens": "^3.0.0 || ^4.0.0" + } + }, + "lru-cache": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-5.1.1.tgz", + "integrity": "sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w==", + "requires": { + "yallist": "^3.0.2" + } + }, + "make-dir": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/make-dir/-/make-dir-1.3.0.tgz", + "integrity": "sha512-2w31R7SJtieJJnQtGc7RVL2StM2vGYVfqUOvUDxH6bC6aJTxPxTF0GnIgCyu7tjockiUWAYQRbxa7vKn34s5sQ==", + "requires": { + "pify": "^3.0.0" + } + }, + "make-error": { + "version": "1.3.5", + "resolved": "https://registry.npmjs.org/make-error/-/make-error-1.3.5.tgz", + "integrity": "sha512-c3sIjNUow0+8swNwVpqoH4YCShKNFkMaw6oH1mNS2haDZQqkeZFlHS3dhoeEbKKmJB4vXpJucU6oH75aDYeE9g==" + }, + "makeerror": { + "version": "1.0.11", + "resolved": "https://registry.npmjs.org/makeerror/-/makeerror-1.0.11.tgz", + "integrity": "sha1-4BpckQnyr3lmDk6LlYd5AYT1qWw=", + "requires": { + "tmpl": "1.0.x" + } + }, + "mamacro": { + "version": "0.0.3", + "resolved": "https://registry.npmjs.org/mamacro/-/mamacro-0.0.3.tgz", + "integrity": "sha512-qMEwh+UujcQ+kbz3T6V+wAmO2U8veoq2w+3wY8MquqwVA3jChfwY+Tk52GZKDfACEPjuZ7r2oJLejwpt8jtwTA==" + }, + "map-age-cleaner": { + "version": "0.1.3", + "resolved": "https://registry.npmjs.org/map-age-cleaner/-/map-age-cleaner-0.1.3.tgz", + "integrity": "sha512-bJzx6nMoP6PDLPBFmg7+xRKeFZvFboMrGlxmNj9ClvX53KrmvM5bXFXEWjbz4cz1AFn+jWJ9z/DJSz7hrs0w3w==", + "requires": { + "p-defer": "^1.0.0" + } + }, + "map-cache": { + "version": "0.2.2", + "resolved": "https://registry.npmjs.org/map-cache/-/map-cache-0.2.2.tgz", + "integrity": "sha1-wyq9C9ZSXZsFFkW7TyasXcmKDb8=" + }, + "map-visit": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/map-visit/-/map-visit-1.0.0.tgz", + "integrity": "sha1-7Nyo8TFE5mDxtb1B8S80edmN+48=", + "requires": { + "object-visit": "^1.0.0" + } + }, + "md5.js": { + "version": "1.3.5", + "resolved": "https://registry.npmjs.org/md5.js/-/md5.js-1.3.5.tgz", + "integrity": "sha512-xitP+WxNPcTTOgnTJcrhM0xvdPepipPSf3I8EIpGKeFLjt3PlJLIDG3u8EX53ZIubkb+5U2+3rELYpEhHhzdkg==", + "requires": { + "hash-base": "^3.0.0", + "inherits": "^2.0.1", + "safe-buffer": "^5.1.2" + } + }, + "media-typer": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/media-typer/-/media-typer-0.3.0.tgz", + "integrity": "sha1-hxDXrwqmJvj/+hzgAWhUUmMlV0g=" + }, + "mem": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/mem/-/mem-4.1.0.tgz", + "integrity": "sha512-I5u6Q1x7wxO0kdOpYBB28xueHADYps5uty/zg936CiG8NTe5sJL8EjrCuLneuDW3PlMdZBGDIn8BirEVdovZvg==", + "requires": { + "map-age-cleaner": "^0.1.1", + "mimic-fn": "^1.0.0", + "p-is-promise": "^2.0.0" + } + }, + "memory-fs": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/memory-fs/-/memory-fs-0.4.1.tgz", + "integrity": "sha1-OpoguEYlI+RHz7x+i7gO1me/xVI=", + "requires": { + "errno": "^0.1.3", + "readable-stream": "^2.0.1" + } + }, + "merge-descriptors": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/merge-descriptors/-/merge-descriptors-1.0.1.tgz", + "integrity": "sha1-sAqqVW3YtEVoFQ7J0blT8/kMu2E=" + }, + "merge-stream": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/merge-stream/-/merge-stream-1.0.1.tgz", + "integrity": "sha1-QEEgLVCKNCugAXQAjfDCUbjBNeE=", + "requires": { + "readable-stream": "^2.0.1" + } + }, + "methods": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/methods/-/methods-1.1.2.tgz", + "integrity": "sha1-VSmk1nZUE07cxSZmVoNbD4Ua/O4=" + }, + "micromatch": { + "version": "3.1.10", + "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-3.1.10.tgz", + "integrity": "sha512-MWikgl9n9M3w+bpsY3He8L+w9eF9338xRl8IAO5viDizwSzziFEyUzo2xrrloB64ADbTf8uA8vRqqttDTOmccg==", + "requires": { + "arr-diff": "^4.0.0", + "array-unique": "^0.3.2", + "braces": "^2.3.1", + "define-property": "^2.0.2", + "extend-shallow": "^3.0.2", + "extglob": "^2.0.4", + "fragment-cache": "^0.2.1", + "kind-of": "^6.0.2", + "nanomatch": "^1.2.9", + "object.pick": "^1.3.0", + "regex-not": "^1.0.0", + "snapdragon": "^0.8.1", + "to-regex": "^3.0.2" + } + }, + "miller-rabin": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/miller-rabin/-/miller-rabin-4.0.1.tgz", + "integrity": "sha512-115fLhvZVqWwHPbClyntxEVfVDfl9DLLTuJvq3g2O/Oxi8AiNouAHvDSzHS0viUJc+V5vm3eq91Xwqn9dp4jRA==", + "requires": { + "bn.js": "^4.0.0", + "brorand": "^1.0.1" + } + }, + "mime": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/mime/-/mime-1.4.1.tgz", + "integrity": "sha512-KI1+qOZu5DcW6wayYHSzR/tXKCDC5Om4s1z2QJjDULzLcmf3DvzS7oluY4HCTrc+9FiKmWUgeNLg7W3uIQvxtQ==" + }, + "mime-db": { + "version": "1.38.0", + "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.38.0.tgz", + "integrity": "sha512-bqVioMFFzc2awcdJZIzR3HjZFX20QhilVS7hytkKrv7xFAn8bM1gzc/FOX2awLISvWe0PV8ptFKcon+wZ5qYkg==" + }, + "mime-types": { + "version": "2.1.22", + "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.22.tgz", + "integrity": "sha512-aGl6TZGnhm/li6F7yx82bJiBZwgiEa4Hf6CNr8YO+r5UHr53tSTYZb102zyU50DOWWKeOv0uQLRL0/9EiKWCog==", + "requires": { + "mime-db": "~1.38.0" + } + }, + "mimic-fn": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/mimic-fn/-/mimic-fn-1.2.0.tgz", + "integrity": "sha512-jf84uxzwiuiIVKiOLpfYk7N46TSy8ubTonmneY9vrpHNAnp0QBt2BxWV9dO3/j+BoVAb+a5G6YDPW3M5HOdMWQ==" + }, + "mini-css-extract-plugin": { + "version": "0.5.0", + "resolved": "https://registry.npmjs.org/mini-css-extract-plugin/-/mini-css-extract-plugin-0.5.0.tgz", + "integrity": "sha512-IuaLjruM0vMKhUUT51fQdQzBYTX49dLj8w68ALEAe2A4iYNpIC4eMac67mt3NzycvjOlf07/kYxJDc0RTl1Wqw==", + "requires": { + "loader-utils": "^1.1.0", + "schema-utils": "^1.0.0", + "webpack-sources": "^1.1.0" + } + }, + "minimalistic-assert": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/minimalistic-assert/-/minimalistic-assert-1.0.1.tgz", + "integrity": "sha512-UtJcAD4yEaGtjPezWuO9wC4nwUnVH/8/Im3yEHQP4b67cXlD/Qr9hdITCU1xDbSEXg2XKNaP8jsReV7vQd00/A==" + }, + "minimalistic-crypto-utils": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/minimalistic-crypto-utils/-/minimalistic-crypto-utils-1.0.1.tgz", + "integrity": "sha1-9sAMHAsIIkblxNmd+4x8CDsrWCo=" + }, + "minimatch": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.4.tgz", + "integrity": "sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA==", + "requires": { + "brace-expansion": "^1.1.7" + } + }, + "minimist": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.0.tgz", + "integrity": "sha1-o1AIsg9BOD7sH7kU9M1d95omQoQ=" + }, + "mississippi": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/mississippi/-/mississippi-3.0.0.tgz", + "integrity": "sha512-x471SsVjUtBRtcvd4BzKE9kFC+/2TeWgKCgw0bZcw1b9l2X3QX5vCWgF+KaZaYm87Ss//rHnWryupDrgLvmSkA==", + "requires": { + "concat-stream": "^1.5.0", + "duplexify": "^3.4.2", + "end-of-stream": "^1.1.0", + "flush-write-stream": "^1.0.0", + "from2": "^2.1.0", + "parallel-transform": "^1.1.0", + "pump": "^3.0.0", + "pumpify": "^1.3.3", + "stream-each": "^1.1.0", + "through2": "^2.0.0" + } + }, + "mixin-deep": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/mixin-deep/-/mixin-deep-1.3.1.tgz", + "integrity": "sha512-8ZItLHeEgaqEvd5lYBXfm4EZSFCX29Jb9K+lAHhDKzReKBQKj3R+7NOF6tjqYi9t4oI8VUfaWITJQm86wnXGNQ==", + "requires": { + "for-in": "^1.0.2", + "is-extendable": "^1.0.1" + }, + "dependencies": { + "is-extendable": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/is-extendable/-/is-extendable-1.0.1.tgz", + "integrity": "sha512-arnXMxT1hhoKo9k1LZdmlNyJdDDfy2v0fXjFlmok4+i8ul/6WlbVge9bhM74OpNPQPMGUToDtz+KXa1PneJxOA==", + "requires": { + "is-plain-object": "^2.0.4" + } + } + } + }, + "mkdirp": { + "version": "0.5.1", + "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.1.tgz", + "integrity": "sha1-MAV0OOrGz3+MR2fzhkjWaX11yQM=", + "requires": { + "minimist": "0.0.8" + }, + "dependencies": { + "minimist": { + "version": "0.0.8", + "resolved": "https://registry.npmjs.org/minimist/-/minimist-0.0.8.tgz", + "integrity": "sha1-hX/Kv8M5fSYluCKCYuhqp6ARsF0=" + } + } + }, + "move-concurrently": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/move-concurrently/-/move-concurrently-1.0.1.tgz", + "integrity": "sha1-viwAX9oy4LKa8fBdfEszIUxwH5I=", + "requires": { + "aproba": "^1.1.1", + "copy-concurrently": "^1.0.0", + "fs-write-stream-atomic": "^1.0.8", + "mkdirp": "^0.5.1", + "rimraf": "^2.5.4", + "run-queue": "^1.0.3" + } + }, + "ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=" + }, + "multicast-dns": { + "version": "6.2.3", + "resolved": "https://registry.npmjs.org/multicast-dns/-/multicast-dns-6.2.3.tgz", + "integrity": "sha512-ji6J5enbMyGRHIAkAOu3WdV8nggqviKCEKtXcOqfphZZtQrmHKycfynJ2V7eVPUA4NhJ6V7Wf4TmGbTwKE9B6g==", + "requires": { + "dns-packet": "^1.3.1", + "thunky": "^1.0.2" + } + }, + "multicast-dns-service-types": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/multicast-dns-service-types/-/multicast-dns-service-types-1.1.0.tgz", + "integrity": "sha1-iZ8R2WhuXgXLkbNdXw5jt3PPyQE=" + }, + "mute-stream": { + "version": "0.0.7", + "resolved": "https://registry.npmjs.org/mute-stream/-/mute-stream-0.0.7.tgz", + "integrity": "sha1-MHXOk7whuPq0PhvE2n6BFe0ee6s=" + }, + "nan": { + "version": "2.12.1", + "resolved": "https://registry.npmjs.org/nan/-/nan-2.12.1.tgz", + "integrity": "sha512-JY7V6lRkStKcKTvHO5NVSQRv+RV+FIL5pvDoLiAtSL9pKlC5x9PKQcZDsq7m4FO4d57mkhC6Z+QhAh3Jdk5JFw==", + "optional": true + }, + "nanomatch": { + "version": "1.2.13", + "resolved": "https://registry.npmjs.org/nanomatch/-/nanomatch-1.2.13.tgz", + "integrity": "sha512-fpoe2T0RbHwBTBUOftAfBPaDEi06ufaUai0mE6Yn1kacc3SnTErfb/h+X94VXzI64rKFHYImXSvdwGGCmwOqCA==", + "requires": { + "arr-diff": "^4.0.0", + "array-unique": "^0.3.2", + "define-property": "^2.0.2", + "extend-shallow": "^3.0.2", + "fragment-cache": "^0.2.1", + "is-windows": "^1.0.2", + "kind-of": "^6.0.2", + "object.pick": "^1.3.0", + "regex-not": "^1.0.0", + "snapdragon": "^0.8.1", + "to-regex": "^3.0.1" + } + }, + "natural-compare": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/natural-compare/-/natural-compare-1.4.0.tgz", + "integrity": "sha1-Sr6/7tdUHywnrPspvbvRXI1bpPc=" + }, + "negotiator": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-0.6.1.tgz", + "integrity": "sha1-KzJxhOiZIQEXeyhWP7XnECrNDKk=" + }, + "neo-async": { + "version": "2.6.0", + "resolved": "https://registry.npmjs.org/neo-async/-/neo-async-2.6.0.tgz", + "integrity": "sha512-MFh0d/Wa7vkKO3Y3LlacqAEeHK0mckVqzDieUKTT+KGxi+zIpeVsFxymkIiRpbpDziHc290Xr9A1O4Om7otoRA==" + }, + "next-tick": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/next-tick/-/next-tick-1.0.0.tgz", + "integrity": "sha1-yobR/ogoFpsBICCOPchCS524NCw=" + }, + "nice-try": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/nice-try/-/nice-try-1.0.5.tgz", + "integrity": "sha512-1nh45deeb5olNY7eX82BkPO7SSxR5SSYJiPTrTdFUVYwAl8CKMA5N9PjTYkHiRjisVcxcQ1HXdLhx2qxxJzLNQ==" + }, + "node-forge": { + "version": "0.7.5", + "resolved": "https://registry.npmjs.org/node-forge/-/node-forge-0.7.5.tgz", + "integrity": "sha512-MmbQJ2MTESTjt3Gi/3yG1wGpIMhUfcIypUCGtTizFR9IiccFwxSpfp0vtIZlkFclEqERemxfnSdZEMR9VqqEFQ==" + }, + "node-int64": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/node-int64/-/node-int64-0.4.0.tgz", + "integrity": "sha1-h6kGXNs1XTGC2PlM4RGIuCXGijs=" + }, + "node-libs-browser": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/node-libs-browser/-/node-libs-browser-2.2.0.tgz", + "integrity": "sha512-5MQunG/oyOaBdttrL40dA7bUfPORLRWMUJLQtMg7nluxUvk5XwnLdL9twQHFAjRx/y7mIMkLKT9++qPbbk6BZA==", + "requires": { + "assert": "^1.1.1", + "browserify-zlib": "^0.2.0", + "buffer": "^4.3.0", + "console-browserify": "^1.1.0", + "constants-browserify": "^1.0.0", + "crypto-browserify": "^3.11.0", + "domain-browser": "^1.1.1", + "events": "^3.0.0", + "https-browserify": "^1.0.0", + "os-browserify": "^0.3.0", + "path-browserify": "0.0.0", + "process": "^0.11.10", + "punycode": "^1.2.4", + "querystring-es3": "^0.2.0", + "readable-stream": "^2.3.3", + "stream-browserify": "^2.0.1", + "stream-http": "^2.7.2", + "string_decoder": "^1.0.0", + "timers-browserify": "^2.0.4", + "tty-browserify": "0.0.0", + "url": "^0.11.0", + "util": "^0.11.0", + "vm-browserify": "0.0.4" + }, + "dependencies": { + "punycode": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/punycode/-/punycode-1.4.1.tgz", + "integrity": "sha1-wNWmOycYgArY4esPpSachN1BhF4=" + } + } + }, + "node-modules-regexp": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/node-modules-regexp/-/node-modules-regexp-1.0.0.tgz", + "integrity": "sha1-jZ2+KJZKSsVxLpExZCEHxx6Q7EA=" + }, + "node-notifier": { + "version": "5.4.0", + "resolved": "https://registry.npmjs.org/node-notifier/-/node-notifier-5.4.0.tgz", + "integrity": "sha512-SUDEb+o71XR5lXSTyivXd9J7fCloE3SyP4lSgt3lU2oSANiox+SxlNRGPjDKrwU1YN3ix2KN/VGGCg0t01rttQ==", + "requires": { + "growly": "^1.3.0", + "is-wsl": "^1.1.0", + "semver": "^5.5.0", + "shellwords": "^0.1.1", + "which": "^1.3.0" + } + }, + "normalize-package-data": { + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/normalize-package-data/-/normalize-package-data-2.5.0.tgz", + "integrity": "sha512-/5CMN3T0R4XTj4DcGaexo+roZSdSFW/0AOOTROrjxzCG1wrWXEsGbRKevjlIL+ZDE4sZlJr5ED4YW0yqmkK+eA==", + "requires": { + "hosted-git-info": "^2.1.4", + "resolve": "^1.10.0", + "semver": "2 || 3 || 4 || 5", + "validate-npm-package-license": "^3.0.1" + } + }, + "normalize-path": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz", + "integrity": "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==" + }, + "npm-run-path": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/npm-run-path/-/npm-run-path-2.0.2.tgz", + "integrity": "sha1-NakjLfo11wZ7TLLd8jV7GHFTbF8=", + "requires": { + "path-key": "^2.0.0" + } + }, + "number-is-nan": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/number-is-nan/-/number-is-nan-1.0.1.tgz", + "integrity": "sha1-CXtgK1NCKlIsGvuHkDGDNpQaAR0=" + }, + "nwsapi": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/nwsapi/-/nwsapi-2.1.3.tgz", + "integrity": "sha512-RowAaJGEgYXEZfQ7tvvdtAQUKPyTR6T6wNu0fwlNsGQYr/h3yQc6oI8WnVZh3Y/Sylwc+dtAlvPqfFZjhTyk3A==" + }, + "oauth-sign": { + "version": "0.9.0", + "resolved": "https://registry.npmjs.org/oauth-sign/-/oauth-sign-0.9.0.tgz", + "integrity": "sha512-fexhUFFPTGV8ybAtSIGbV6gOkSv8UtRbDBnAyLQw4QPKkgNlsH2ByPGtMUqdWkos6YCRmAqViwgZrJc/mRDzZQ==" + }, + "object-assign": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", + "integrity": "sha1-IQmtx5ZYh8/AXLvUQsrIv7s2CGM=" + }, + "object-copy": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/object-copy/-/object-copy-0.1.0.tgz", + "integrity": "sha1-fn2Fi3gb18mRpBupde04EnVOmYw=", + "requires": { + "copy-descriptor": "^0.1.0", + "define-property": "^0.2.5", + "kind-of": "^3.0.3" + }, + "dependencies": { + "define-property": { + "version": "0.2.5", + "resolved": "https://registry.npmjs.org/define-property/-/define-property-0.2.5.tgz", + "integrity": "sha1-w1se+RjsPJkPmlvFe+BKrOxcgRY=", + "requires": { + "is-descriptor": "^0.1.0" + } + }, + "kind-of": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", + "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", + "requires": { + "is-buffer": "^1.1.5" + } + } + } + }, + "object-keys": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/object-keys/-/object-keys-1.1.0.tgz", + "integrity": "sha512-6OO5X1+2tYkNyNEx6TsCxEqFfRWaqx6EtMiSbGrw8Ob8v9Ne+Hl8rBAgLBZn5wjEz3s/s6U1WXFUFOcxxAwUpg==" + }, + "object-visit": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/object-visit/-/object-visit-1.0.1.tgz", + "integrity": "sha1-95xEk68MU3e1n+OdOV5BBC3QRbs=", + "requires": { + "isobject": "^3.0.0" + } + }, + "object.assign": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/object.assign/-/object.assign-4.1.0.tgz", + "integrity": "sha512-exHJeq6kBKj58mqGyTQ9DFvrZC/eR6OwxzoM9YRoGBqrXYonaFyGiFMuc9VZrXf7DarreEwMpurG3dd+CNyW5w==", + "requires": { + "define-properties": "^1.1.2", + "function-bind": "^1.1.1", + "has-symbols": "^1.0.0", + "object-keys": "^1.0.11" + } + }, + "object.getownpropertydescriptors": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/object.getownpropertydescriptors/-/object.getownpropertydescriptors-2.0.3.tgz", + "integrity": "sha1-h1jIRvW0B62rDyNuCYbxSwUcqhY=", + "requires": { + "define-properties": "^1.1.2", + "es-abstract": "^1.5.1" + } + }, + "object.pick": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/object.pick/-/object.pick-1.3.0.tgz", + "integrity": "sha1-h6EKxMFpS9Lhy/U1kaZhQftd10c=", + "requires": { + "isobject": "^3.0.1" + } + }, + "obuf": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/obuf/-/obuf-1.1.2.tgz", + "integrity": "sha512-PX1wu0AmAdPqOL1mWhqmlOd8kOIZQwGZw6rh7uby9fTc5lhaOWFLX3I6R1hrF9k3zUY40e6igsLGkDXK92LJNg==" + }, + "on-finished": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/on-finished/-/on-finished-2.3.0.tgz", + "integrity": "sha1-IPEzZIGwg811M3mSoWlxqi2QaUc=", + "requires": { + "ee-first": "1.1.1" + } + }, + "on-headers": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/on-headers/-/on-headers-1.0.2.tgz", + "integrity": "sha512-pZAE+FJLoyITytdqK0U5s+FIpjN0JP3OzFi/u8Rx+EV5/W+JTWGXG8xFzevE7AjBfDqHv/8vL8qQsIhHnqRkrA==" + }, + "once": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", + "integrity": "sha1-WDsap3WWHUsROsF9nFC6753Xa9E=", + "requires": { + "wrappy": "1" + } + }, + "onetime": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/onetime/-/onetime-2.0.1.tgz", + "integrity": "sha1-BnQoIw/WdEOyeUsiu6UotoZ5YtQ=", + "requires": { + "mimic-fn": "^1.0.0" + } + }, + "opn": { + "version": "5.5.0", + "resolved": "https://registry.npmjs.org/opn/-/opn-5.5.0.tgz", + "integrity": "sha512-PqHpggC9bLV0VeWcdKhkpxY+3JTzetLSqTCWL/z/tFIbI6G8JCjondXklT1JinczLz2Xib62sSp0T/gKT4KksA==", + "requires": { + "is-wsl": "^1.1.0" + } + }, + "optimist": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/optimist/-/optimist-0.6.1.tgz", + "integrity": "sha1-2j6nRob6IaGaERwybpDrFaAZZoY=", + "requires": { + "minimist": "~0.0.1", + "wordwrap": "~0.0.2" + }, + "dependencies": { + "minimist": { + "version": "0.0.10", + "resolved": "https://registry.npmjs.org/minimist/-/minimist-0.0.10.tgz", + "integrity": "sha1-3j+YVD2/lggr5IrRoMfNqDYwHc8=" + }, + "wordwrap": { + "version": "0.0.3", + "resolved": "https://registry.npmjs.org/wordwrap/-/wordwrap-0.0.3.tgz", + "integrity": "sha1-o9XabNXAvAAI03I0u68b7WMFkQc=" + } + } + }, + "optionator": { + "version": "0.8.2", + "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.8.2.tgz", + "integrity": "sha1-NkxeQJ0/TWMB1sC0wFu6UBgK62Q=", + "requires": { + "deep-is": "~0.1.3", + "fast-levenshtein": "~2.0.4", + "levn": "~0.3.0", + "prelude-ls": "~1.1.2", + "type-check": "~0.3.2", + "wordwrap": "~1.0.0" + } + }, + "original": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/original/-/original-1.0.2.tgz", + "integrity": "sha512-hyBVl6iqqUOJ8FqRe+l/gS8H+kKYjrEndd5Pm1MfBtsEKA038HkkdbAl/72EAXGyonD/PFsvmVG+EvcIpliMBg==", + "requires": { + "url-parse": "^1.4.3" + } + }, + "os-browserify": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/os-browserify/-/os-browserify-0.3.0.tgz", + "integrity": "sha1-hUNzx/XCMVkU/Jv8a9gjj92h7Cc=" + }, + "os-locale": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/os-locale/-/os-locale-3.1.0.tgz", + "integrity": "sha512-Z8l3R4wYWM40/52Z+S265okfFj8Kt2cC2MKY+xNi3kFs+XGI7WXu/I309QQQYbRW4ijiZ+yxs9pqEhJh0DqW3Q==", + "requires": { + "execa": "^1.0.0", + "lcid": "^2.0.0", + "mem": "^4.0.0" + } + }, + "os-tmpdir": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/os-tmpdir/-/os-tmpdir-1.0.2.tgz", + "integrity": "sha1-u+Z0BseaqFxc/sdm/lc0VV36EnQ=" + }, + "p-defer": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/p-defer/-/p-defer-1.0.0.tgz", + "integrity": "sha1-n26xgvbJqozXQwBKfU+WsZaw+ww=" + }, + "p-each-series": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/p-each-series/-/p-each-series-1.0.0.tgz", + "integrity": "sha1-kw89Et0fUOdDRFeiLNbwSsatf3E=", + "requires": { + "p-reduce": "^1.0.0" + } + }, + "p-finally": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/p-finally/-/p-finally-1.0.0.tgz", + "integrity": "sha1-P7z7FbiZpEEjs0ttzBi3JDNqLK4=" + }, + "p-is-promise": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/p-is-promise/-/p-is-promise-2.0.0.tgz", + "integrity": "sha512-pzQPhYMCAgLAKPWD2jC3Se9fEfrD9npNos0y150EeqZll7akhEgGhTW/slB6lHku8AvYGiJ+YJ5hfHKePPgFWg==" + }, + "p-limit": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.1.0.tgz", + "integrity": "sha512-NhURkNcrVB+8hNfLuysU8enY5xn2KXphsHBaC2YmRNTZRc7RWusw6apSpdEj3jo4CMb6W9nrF6tTnsJsJeyu6g==", + "requires": { + "p-try": "^2.0.0" + } + }, + "p-locate": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-3.0.0.tgz", + "integrity": "sha512-x+12w/To+4GFfgJhBEpiDcLozRJGegY+Ei7/z0tSLkMmxGZNybVMSfWj9aJn8Z5Fc7dBUNJOOVgPv2H7IwulSQ==", + "requires": { + "p-limit": "^2.0.0" + } + }, + "p-map": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/p-map/-/p-map-2.1.0.tgz", + "integrity": "sha512-y3b8Kpd8OAN444hxfBbFfj1FY/RjtTd8tzYwhUqNYXx0fXx2iX4maP4Qr6qhIKbQXI02wTLAda4fYUbDagTUFw==" + }, + "p-reduce": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/p-reduce/-/p-reduce-1.0.0.tgz", + "integrity": "sha1-GMKw3ZNqRpClKfgjH1ig/bakffo=" + }, + "p-try": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/p-try/-/p-try-2.0.0.tgz", + "integrity": "sha512-hMp0onDKIajHfIkdRk3P4CdCmErkYAxxDtP3Wx/4nZ3aGlau2VKh3mZpcuFkH27WQkL/3WBCPOktzA9ZOAnMQQ==" + }, + "pako": { + "version": "1.0.10", + "resolved": "https://registry.npmjs.org/pako/-/pako-1.0.10.tgz", + "integrity": "sha512-0DTvPVU3ed8+HNXOu5Bs+o//Mbdj9VNQMUOe9oKCwh8l0GNwpTDMKCWbRjgtD291AWnkAgkqA/LOnQS8AmS1tw==" + }, + "parallel-transform": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/parallel-transform/-/parallel-transform-1.1.0.tgz", + "integrity": "sha1-1BDwZbBdojCB/NEPKIVMKb2jOwY=", + "requires": { + "cyclist": "~0.2.2", + "inherits": "^2.0.3", + "readable-stream": "^2.1.5" + } + }, + "parent-module": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/parent-module/-/parent-module-1.0.1.tgz", + "integrity": "sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g==", + "requires": { + "callsites": "^3.0.0" + } + }, + "parse-asn1": { + "version": "5.1.4", + "resolved": "https://registry.npmjs.org/parse-asn1/-/parse-asn1-5.1.4.tgz", + "integrity": "sha512-Qs5duJcuvNExRfFZ99HDD3z4mAi3r9Wl/FOjEOijlxwCZs7E7mW2vjTpgQ4J8LpTF8x5v+1Vn5UQFejmWT11aw==", + "requires": { + "asn1.js": "^4.0.0", + "browserify-aes": "^1.0.0", + "create-hash": "^1.1.0", + "evp_bytestokey": "^1.0.0", + "pbkdf2": "^3.0.3", + "safe-buffer": "^5.1.1" + } + }, + "parse-json": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-4.0.0.tgz", + "integrity": "sha1-vjX1Qlvh9/bHRxhPmKeIy5lHfuA=", + "requires": { + "error-ex": "^1.3.1", + "json-parse-better-errors": "^1.0.1" + } + }, + "parse-passwd": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/parse-passwd/-/parse-passwd-1.0.0.tgz", + "integrity": "sha1-bVuTSkVpk7I9N/QKOC1vFmao5cY=" + }, + "parse5": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/parse5/-/parse5-4.0.0.tgz", + "integrity": "sha512-VrZ7eOd3T1Fk4XWNXMgiGBK/z0MG48BWG2uQNU4I72fkQuKUTZpl+u9k+CxEG0twMVzSmXEEz12z5Fnw1jIQFA==" + }, + "parseurl": { + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/parseurl/-/parseurl-1.3.2.tgz", + "integrity": "sha1-/CidTtiZMRlGDBViUyYs3I3mW/M=" + }, + "pascalcase": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/pascalcase/-/pascalcase-0.1.1.tgz", + "integrity": "sha1-s2PlXoAGym/iF4TS2yK9FdeRfxQ=" + }, + "path-browserify": { + "version": "0.0.0", + "resolved": "https://registry.npmjs.org/path-browserify/-/path-browserify-0.0.0.tgz", + "integrity": "sha1-oLhwcpquIUAFt9UDLsLLuw+0RRo=" + }, + "path-dirname": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/path-dirname/-/path-dirname-1.0.2.tgz", + "integrity": "sha1-zDPSTVJeCZpTiMAzbG4yuRYGCeA=" + }, + "path-exists": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-3.0.0.tgz", + "integrity": "sha1-zg6+ql94yxiSXqfYENe1mwEP1RU=" + }, + "path-is-absolute": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", + "integrity": "sha1-F0uSaHNVNP+8es5r9TpanhtcX18=" + }, + "path-is-inside": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/path-is-inside/-/path-is-inside-1.0.2.tgz", + "integrity": "sha1-NlQX3t5EQw0cEa9hAn+s8HS9/FM=" + }, + "path-key": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/path-key/-/path-key-2.0.1.tgz", + "integrity": "sha1-QRyttXTFoUDTpLGRDUDYDMn0C0A=" + }, + "path-parse": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.6.tgz", + "integrity": "sha512-GSmOT2EbHrINBf9SR7CDELwlJ8AENk3Qn7OikK4nFYAu3Ote2+JYNVvkpAEQm3/TLNEJFD/xZJjzyxg3KBWOzw==" + }, + "path-to-regexp": { + "version": "0.1.7", + "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-0.1.7.tgz", + "integrity": "sha1-32BBeABfUi8V60SQ5yR6G/qmf4w=" + }, + "path-type": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/path-type/-/path-type-3.0.0.tgz", + "integrity": "sha512-T2ZUsdZFHgA3u4e5PfPbjd7HDDpxPnQb5jN0SrDsjNSuVXHJqtwTnWqG0B1jZrgmJ/7lj1EmVIByWt1gxGkWvg==", + "requires": { + "pify": "^3.0.0" + } + }, + "pbkdf2": { + "version": "3.0.17", + "resolved": "https://registry.npmjs.org/pbkdf2/-/pbkdf2-3.0.17.tgz", + "integrity": "sha512-U/il5MsrZp7mGg3mSQfn742na2T+1/vHDCG5/iTI3X9MKUuYUZVLQhyRsg06mCgDBTd57TxzgZt7P+fYfjRLtA==", + "requires": { + "create-hash": "^1.1.2", + "create-hmac": "^1.1.4", + "ripemd160": "^2.0.1", + "safe-buffer": "^5.0.1", + "sha.js": "^2.4.8" + } + }, + "performance-now": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/performance-now/-/performance-now-2.1.0.tgz", + "integrity": "sha1-Ywn04OX6kT7BxpMHrjZLSzd8nns=" + }, + "pify": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/pify/-/pify-3.0.0.tgz", + "integrity": "sha1-5aSs0sEB/fPZpNB/DbxNtJ3SgXY=" + }, + "pinkie": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/pinkie/-/pinkie-2.0.4.tgz", + "integrity": "sha1-clVrgM+g1IqXToDnckjoDtT3+HA=" + }, + "pinkie-promise": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/pinkie-promise/-/pinkie-promise-2.0.1.tgz", + "integrity": "sha1-ITXW36ejWMBprJsXh3YogihFD/o=", + "requires": { + "pinkie": "^2.0.0" + } + }, + "pirates": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/pirates/-/pirates-4.0.1.tgz", + "integrity": "sha512-WuNqLTbMI3tmfef2TKxlQmAiLHKtFhlsCZnPIpuv2Ow0RDVO8lfy1Opf4NUzlMXLjPl+Men7AuVdX6TA+s+uGA==", + "requires": { + "node-modules-regexp": "^1.0.0" + } + }, + "pkg-dir": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/pkg-dir/-/pkg-dir-3.0.0.tgz", + "integrity": "sha512-/E57AYkoeQ25qkxMj5PBOVgF8Kiu/h7cYS30Z5+R7WaiCCBfLq58ZI/dSeaEKb9WVJV5n/03QwrN3IeWIFllvw==", + "requires": { + "find-up": "^3.0.0" + } + }, + "pn": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/pn/-/pn-1.1.0.tgz", + "integrity": "sha512-2qHaIQr2VLRFoxe2nASzsV6ef4yOOH+Fi9FBOVH6cqeSgUnoyySPZkxzLuzd+RYOQTRpROA0ztTMqxROKSb/nA==" + }, + "portfinder": { + "version": "1.0.20", + "resolved": "https://registry.npmjs.org/portfinder/-/portfinder-1.0.20.tgz", + "integrity": "sha512-Yxe4mTyDzTd59PZJY4ojZR8F+E5e97iq2ZOHPz3HDgSvYC5siNad2tLooQ5y5QHyQhc3xVqvyk/eNA3wuoa7Sw==", + "requires": { + "async": "^1.5.2", + "debug": "^2.2.0", + "mkdirp": "0.5.x" + }, + "dependencies": { + "async": { + "version": "1.5.2", + "resolved": "https://registry.npmjs.org/async/-/async-1.5.2.tgz", + "integrity": "sha1-7GphrlZIDAw8skHJVhjiCJL5Zyo=" + } + } + }, + "posix-character-classes": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/posix-character-classes/-/posix-character-classes-0.1.1.tgz", + "integrity": "sha1-AerA/jta9xoqbAL+q7jB/vfgDqs=" + }, + "postcss": { + "version": "7.0.14", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-7.0.14.tgz", + "integrity": "sha512-NsbD6XUUMZvBxtQAJuWDJeeC4QFsmWsfozWxCJPWf3M55K9iu2iMDaKqyoOdTJ1R4usBXuxlVFAIo8rZPQD4Bg==", + "requires": { + "chalk": "^2.4.2", + "source-map": "^0.6.1", + "supports-color": "^6.1.0" + }, + "dependencies": { + "source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==" + }, + "supports-color": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-6.1.0.tgz", + "integrity": "sha512-qe1jfm1Mg7Nq/NSh6XE24gPXROEVsWHxC1LIx//XNlD9iw7YZQGjZNjYN7xGaEG6iKdA8EtNFW6R0gjnVXp+wQ==", + "requires": { + "has-flag": "^3.0.0" + } + } + } + }, + "postcss-load-config": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/postcss-load-config/-/postcss-load-config-2.0.0.tgz", + "integrity": "sha512-V5JBLzw406BB8UIfsAWSK2KSwIJ5yoEIVFb4gVkXci0QdKgA24jLmHZ/ghe/GgX0lJ0/D1uUK1ejhzEY94MChQ==", + "requires": { + "cosmiconfig": "^4.0.0", + "import-cwd": "^2.0.0" + } + }, + "postcss-loader": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/postcss-loader/-/postcss-loader-3.0.0.tgz", + "integrity": "sha512-cLWoDEY5OwHcAjDnkyRQzAXfs2jrKjXpO/HQFcc5b5u/r7aa471wdmChmwfnv7x2u840iat/wi0lQ5nbRgSkUA==", + "requires": { + "loader-utils": "^1.1.0", + "postcss": "^7.0.0", + "postcss-load-config": "^2.0.0", + "schema-utils": "^1.0.0" + } + }, + "postcss-modules-extract-imports": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/postcss-modules-extract-imports/-/postcss-modules-extract-imports-2.0.0.tgz", + "integrity": "sha512-LaYLDNS4SG8Q5WAWqIJgdHPJrDDr/Lv775rMBFUbgjTz6j34lUznACHcdRWroPvXANP2Vj7yNK57vp9eFqzLWQ==", + "requires": { + "postcss": "^7.0.5" + } + }, + "postcss-modules-local-by-default": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/postcss-modules-local-by-default/-/postcss-modules-local-by-default-2.0.6.tgz", + "integrity": "sha512-oLUV5YNkeIBa0yQl7EYnxMgy4N6noxmiwZStaEJUSe2xPMcdNc8WmBQuQCx18H5psYbVxz8zoHk0RAAYZXP9gA==", + "requires": { + "postcss": "^7.0.6", + "postcss-selector-parser": "^6.0.0", + "postcss-value-parser": "^3.3.1" + } + }, + "postcss-modules-scope": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/postcss-modules-scope/-/postcss-modules-scope-2.1.0.tgz", + "integrity": "sha512-91Rjps0JnmtUB0cujlc8KIKCsJXWjzuxGeT/+Q2i2HXKZ7nBUeF9YQTZZTNvHVoNYj1AthsjnGLtqDUE0Op79A==", + "requires": { + "postcss": "^7.0.6", + "postcss-selector-parser": "^6.0.0" + } + }, + "postcss-modules-values": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/postcss-modules-values/-/postcss-modules-values-2.0.0.tgz", + "integrity": "sha512-Ki7JZa7ff1N3EIMlPnGTZfUMe69FFwiQPnVSXC9mnn3jozCRBYIxiZd44yJOV2AmabOo4qFf8s0dC/+lweG7+w==", + "requires": { + "icss-replace-symbols": "^1.1.0", + "postcss": "^7.0.6" + } + }, + "postcss-selector-parser": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/postcss-selector-parser/-/postcss-selector-parser-6.0.2.tgz", + "integrity": "sha512-36P2QR59jDTOAiIkqEprfJDsoNrvwFei3eCqKd1Y0tUsBimsq39BLp7RD+JWny3WgB1zGhJX8XVePwm9k4wdBg==", + "requires": { + "cssesc": "^3.0.0", + "indexes-of": "^1.0.1", + "uniq": "^1.0.1" + } + }, + "postcss-value-parser": { + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/postcss-value-parser/-/postcss-value-parser-3.3.1.tgz", + "integrity": "sha512-pISE66AbVkp4fDQ7VHBwRNXzAAKJjw4Vw7nWI/+Q3vuly7SNfgYXvm6i5IgFylHGK5sP/xHAbB7N49OS4gWNyQ==" + }, + "prelude-ls": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.1.2.tgz", + "integrity": "sha1-IZMqVJ9eUv/ZqCf1cOBL5iqX2lQ=" + }, + "prettier": { + "version": "1.16.4", + "resolved": "https://registry.npmjs.org/prettier/-/prettier-1.16.4.tgz", + "integrity": "sha512-ZzWuos7TI5CKUeQAtFd6Zhm2s6EpAD/ZLApIhsF9pRvRtM1RFo61dM/4MSRUA0SuLugA/zgrZD8m0BaY46Og7g==" + }, + "prettier-linter-helpers": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/prettier-linter-helpers/-/prettier-linter-helpers-1.0.0.tgz", + "integrity": "sha512-GbK2cP9nraSSUF9N2XwUwqfzlAFlMNYYl+ShE/V+H8a9uNl/oUqB1w2EL54Jh0OlyRSd8RfWYJ3coVS4TROP2w==", + "requires": { + "fast-diff": "^1.1.2" + } + }, + "pretty-format": { + "version": "24.7.0", + "resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-24.7.0.tgz", + "integrity": "sha512-apen5cjf/U4dj7tHetpC7UEFCvtAgnNZnBDkfPv3fokzIqyOJckAG9OlAPC1BlFALnqT/lGB2tl9EJjlK6eCsA==", + "requires": { + "@jest/types": "^24.7.0", + "ansi-regex": "^4.0.0", + "ansi-styles": "^3.2.0", + "react-is": "^16.8.4" + }, + "dependencies": { + "ansi-regex": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-4.1.0.tgz", + "integrity": "sha512-1apePfXM1UOSqw0o9IiFAovVz9M5S1Dg+4TrDwfMewQ6p/rmMueb7tWZjQ1rx4Loy1ArBggoqGpfqqdI4rondg==" + } + } + }, + "process": { + "version": "0.11.10", + "resolved": "https://registry.npmjs.org/process/-/process-0.11.10.tgz", + "integrity": "sha1-czIwDoQBYb2j5podHZGn1LwW8YI=" + }, + "process-nextick-args": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-2.0.0.tgz", + "integrity": "sha512-MtEC1TqN0EU5nephaJ4rAtThHtC86dNN9qCuEhtshvpVBkAW5ZO7BASN9REnF9eoXGcRub+pFuKEpOHE+HbEMw==" + }, + "progress": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/progress/-/progress-2.0.3.tgz", + "integrity": "sha512-7PiHtLll5LdnKIMw100I+8xJXR5gW2QwWYkT6iJva0bXitZKa/XMrSbdmg3r2Xnaidz9Qumd0VPaMrZlF9V9sA==" + }, + "promise-inflight": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/promise-inflight/-/promise-inflight-1.0.1.tgz", + "integrity": "sha1-mEcocL8igTL8vdhoEputEsPAKeM=" + }, + "prompts": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/prompts/-/prompts-2.0.4.tgz", + "integrity": "sha512-HTzM3UWp/99A0gk51gAegwo1QRYA7xjcZufMNe33rCclFszUYAuHe1fIN/3ZmiHeGPkUsNaRyQm1hHOfM0PKxA==", + "requires": { + "kleur": "^3.0.2", + "sisteransi": "^1.0.0" + } + }, + "proxy-addr": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/proxy-addr/-/proxy-addr-2.0.4.tgz", + "integrity": "sha512-5erio2h9jp5CHGwcybmxmVqHmnCBZeewlfJ0pex+UW7Qny7OOZXTtH56TGNyBizkgiOwhJtMKrVzDTeKcySZwA==", + "requires": { + "forwarded": "~0.1.2", + "ipaddr.js": "1.8.0" + } + }, + "prr": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/prr/-/prr-1.0.1.tgz", + "integrity": "sha1-0/wRS6BplaRexok/SEzrHXj19HY=" + }, + "psl": { + "version": "1.1.31", + "resolved": "https://registry.npmjs.org/psl/-/psl-1.1.31.tgz", + "integrity": "sha512-/6pt4+C+T+wZUieKR620OpzN/LlnNKuWjy1iFLQ/UG35JqHlR/89MP1d96dUfkf6Dne3TuLQzOYEYshJ+Hx8mw==" + }, + "public-encrypt": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/public-encrypt/-/public-encrypt-4.0.3.tgz", + "integrity": "sha512-zVpa8oKZSz5bTMTFClc1fQOnyyEzpl5ozpi1B5YcvBrdohMjH2rfsBtyXcuNuwjsDIXmBYlF2N5FlJYhR29t8Q==", + "requires": { + "bn.js": "^4.1.0", + "browserify-rsa": "^4.0.0", + "create-hash": "^1.1.0", + "parse-asn1": "^5.0.0", + "randombytes": "^2.0.1", + "safe-buffer": "^5.1.2" + } + }, + "pump": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/pump/-/pump-3.0.0.tgz", + "integrity": "sha512-LwZy+p3SFs1Pytd/jYct4wpv49HiYCqd9Rlc5ZVdk0V+8Yzv6jR5Blk3TRmPL1ft69TxP0IMZGJ+WPFU2BFhww==", + "requires": { + "end-of-stream": "^1.1.0", + "once": "^1.3.1" + } + }, + "pumpify": { + "version": "1.5.1", + "resolved": "https://registry.npmjs.org/pumpify/-/pumpify-1.5.1.tgz", + "integrity": "sha512-oClZI37HvuUJJxSKKrC17bZ9Cu0ZYhEAGPsPUy9KlMUmv9dKX2o77RUmq7f3XjIxbwyGwYzbzQ1L2Ks8sIradQ==", + "requires": { + "duplexify": "^3.6.0", + "inherits": "^2.0.3", + "pump": "^2.0.0" + }, + "dependencies": { + "pump": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/pump/-/pump-2.0.1.tgz", + "integrity": "sha512-ruPMNRkN3MHP1cWJc9OWr+T/xDP0jhXYCLfJcBuX54hhfIBnaQmAUMfDcG4DM5UMWByBbJY69QSphm3jtDKIkA==", + "requires": { + "end-of-stream": "^1.1.0", + "once": "^1.3.1" + } + } + } + }, + "punycode": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.1.1.tgz", + "integrity": "sha512-XRsRjdf+j5ml+y/6GKHPZbrF/8p2Yga0JPtdqTIY2Xe5ohJPD9saDJJLPvp9+NSBprVvevdXZybnj2cv8OEd0A==" + }, + "qs": { + "version": "6.5.2", + "resolved": "https://registry.npmjs.org/qs/-/qs-6.5.2.tgz", + "integrity": "sha512-N5ZAX4/LxJmF+7wN74pUD6qAh9/wnvdQcjq9TZjevvXzSUo7bfmw91saqMjzGS2xq91/odN2dW/WOl7qQHNDGA==" + }, + "querystring": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/querystring/-/querystring-0.2.0.tgz", + "integrity": "sha1-sgmEkgO7Jd+CDadW50cAWHhSFiA=" + }, + "querystring-es3": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/querystring-es3/-/querystring-es3-0.2.1.tgz", + "integrity": "sha1-nsYfeQSYdXB9aUFFlv2Qek1xHnM=" + }, + "querystringify": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/querystringify/-/querystringify-2.1.1.tgz", + "integrity": "sha512-w7fLxIRCRT7U8Qu53jQnJyPkYZIaR4n5151KMfcJlO/A9397Wxb1amJvROTK6TOnp7PfoAmg/qXiNHI+08jRfA==" + }, + "randombytes": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/randombytes/-/randombytes-2.1.0.tgz", + "integrity": "sha512-vYl3iOX+4CKUWuxGi9Ukhie6fsqXqS9FE2Zaic4tNFD2N2QQaXOMFbuKK4QmDHC0JO6B1Zp41J0LpT0oR68amQ==", + "requires": { + "safe-buffer": "^5.1.0" + } + }, + "randomfill": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/randomfill/-/randomfill-1.0.4.tgz", + "integrity": "sha512-87lcbR8+MhcWcUiQ+9e+Rwx8MyR2P7qnt15ynUlbm3TU/fjbgz4GsvfSUDTemtCCtVCqb4ZcEFlyPNTh9bBTLw==", + "requires": { + "randombytes": "^2.0.5", + "safe-buffer": "^5.1.0" + } + }, + "range-parser": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/range-parser/-/range-parser-1.2.0.tgz", + "integrity": "sha1-9JvmtIeJTdxA3MlKMi9hEJLgDV4=" + }, + "raw-body": { + "version": "2.3.3", + "resolved": "https://registry.npmjs.org/raw-body/-/raw-body-2.3.3.tgz", + "integrity": "sha512-9esiElv1BrZoI3rCDuOuKCBRbuApGGaDPQfjSflGxdy4oyzqghxu6klEkkVIvBje+FF0BX9coEv8KqW6X/7njw==", + "requires": { + "bytes": "3.0.0", + "http-errors": "1.6.3", + "iconv-lite": "0.4.23", + "unpipe": "1.0.0" + }, + "dependencies": { + "iconv-lite": { + "version": "0.4.23", + "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.23.tgz", + "integrity": "sha512-neyTUVFtahjf0mB3dZT77u+8O0QB89jFdnBkd5P1JgYPbPaia3gXXOVL2fq8VyU2gMMD7SaN7QukTB/pmXYvDA==", + "requires": { + "safer-buffer": ">= 2.1.2 < 3" + } + } + } + }, + "react-is": { + "version": "16.8.6", + "resolved": "https://registry.npmjs.org/react-is/-/react-is-16.8.6.tgz", + "integrity": "sha512-aUk3bHfZ2bRSVFFbbeVS4i+lNPZr3/WM5jT2J5omUVV1zzcs1nAaf3l51ctA5FFvCRbhrH0bdAsRRQddFJZPtA==" + }, + "read-pkg": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/read-pkg/-/read-pkg-3.0.0.tgz", + "integrity": "sha1-nLxoaXj+5l0WwA4rGcI3/Pbjg4k=", + "requires": { + "load-json-file": "^4.0.0", + "normalize-package-data": "^2.3.2", + "path-type": "^3.0.0" + } + }, + "read-pkg-up": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/read-pkg-up/-/read-pkg-up-4.0.0.tgz", + "integrity": "sha512-6etQSH7nJGsK0RbG/2TeDzZFa8shjQ1um+SwQQ5cwKy0dhSXdOncEhb1CPpvQG4h7FyOV6EB6YlV0yJvZQNAkA==", + "requires": { + "find-up": "^3.0.0", + "read-pkg": "^3.0.0" + } + }, + "readable-stream": { + "version": "2.3.6", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.6.tgz", + "integrity": "sha512-tQtKA9WIAhBF3+VLAseyMqZeBjW0AHJoxOtYqSUZNJxauErmLbVm2FW1y+J/YA9dUrAC39ITejlZWhVIwawkKw==", + "requires": { + "core-util-is": "~1.0.0", + "inherits": "~2.0.3", + "isarray": "~1.0.0", + "process-nextick-args": "~2.0.0", + "safe-buffer": "~5.1.1", + "string_decoder": "~1.1.1", + "util-deprecate": "~1.0.1" + } + }, + "readdirp": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-2.2.1.tgz", + "integrity": "sha512-1JU/8q+VgFZyxwrJ+SVIOsh+KywWGpds3NTqikiKpDMZWScmAYyKIgqkO+ARvNWJfXeXR1zxz7aHF4u4CyH6vQ==", + "requires": { + "graceful-fs": "^4.1.11", + "micromatch": "^3.1.10", + "readable-stream": "^2.0.2" + } + }, + "realpath-native": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/realpath-native/-/realpath-native-1.1.0.tgz", + "integrity": "sha512-wlgPA6cCIIg9gKz0fgAPjnzh4yR/LnXovwuo9hvyGvx3h8nX4+/iLZplfUWasXpqD8BdnGnP5njOFjkUwPzvjA==", + "requires": { + "util.promisify": "^1.0.0" + } + }, + "regex-not": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/regex-not/-/regex-not-1.0.2.tgz", + "integrity": "sha512-J6SDjUgDxQj5NusnOtdFxDwN/+HWykR8GELwctJ7mdqhcyy1xEc4SRFHUXvxTp661YaVKAjfRLZ9cCqS6tn32A==", + "requires": { + "extend-shallow": "^3.0.2", + "safe-regex": "^1.1.0" + } + }, + "regexpp": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/regexpp/-/regexpp-2.0.1.tgz", + "integrity": "sha512-lv0M6+TkDVniA3aD1Eg0DVpfU/booSu7Eev3TDO/mZKHBfVjgCGTV4t4buppESEYDtkArYFOxTJWv6S5C+iaNw==" + }, + "remove-trailing-separator": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/remove-trailing-separator/-/remove-trailing-separator-1.1.0.tgz", + "integrity": "sha1-wkvOKig62tW8P1jg1IJJuSN52O8=" + }, + "repeat-element": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/repeat-element/-/repeat-element-1.1.3.tgz", + "integrity": "sha512-ahGq0ZnV5m5XtZLMb+vP76kcAM5nkLqk0lpqAuojSKGgQtn4eRi4ZZGm2olo2zKFH+sMsWaqOCW1dqAnOru72g==" + }, + "repeat-string": { + "version": "1.6.1", + "resolved": "https://registry.npmjs.org/repeat-string/-/repeat-string-1.6.1.tgz", + "integrity": "sha1-jcrkcOHIirwtYA//Sndihtp15jc=" + }, + "request": { + "version": "2.88.0", + "resolved": "https://registry.npmjs.org/request/-/request-2.88.0.tgz", + "integrity": "sha512-NAqBSrijGLZdM0WZNsInLJpkJokL72XYjUpnB0iwsRgxh7dB6COrHnTBNwN0E+lHDAJzu7kLAkDeY08z2/A0hg==", + "requires": { + "aws-sign2": "~0.7.0", + "aws4": "^1.8.0", + "caseless": "~0.12.0", + "combined-stream": "~1.0.6", + "extend": "~3.0.2", + "forever-agent": "~0.6.1", + "form-data": "~2.3.2", + "har-validator": "~5.1.0", + "http-signature": "~1.2.0", + "is-typedarray": "~1.0.0", + "isstream": "~0.1.2", + "json-stringify-safe": "~5.0.1", + "mime-types": "~2.1.19", + "oauth-sign": "~0.9.0", + "performance-now": "^2.1.0", + "qs": "~6.5.2", + "safe-buffer": "^5.1.2", + "tough-cookie": "~2.4.3", + "tunnel-agent": "^0.6.0", + "uuid": "^3.3.2" + }, + "dependencies": { + "punycode": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/punycode/-/punycode-1.4.1.tgz", + "integrity": "sha1-wNWmOycYgArY4esPpSachN1BhF4=" + }, + "tough-cookie": { + "version": "2.4.3", + "resolved": "https://registry.npmjs.org/tough-cookie/-/tough-cookie-2.4.3.tgz", + "integrity": "sha512-Q5srk/4vDM54WJsJio3XNn6K2sCG+CQ8G5Wz6bZhRZoAe/+TxjWB/GlFAnYEbkYVlON9FMk/fE3h2RLpPXo4lQ==", + "requires": { + "psl": "^1.1.24", + "punycode": "^1.4.1" + } + } + } + }, + "request-promise-core": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/request-promise-core/-/request-promise-core-1.1.2.tgz", + "integrity": "sha512-UHYyq1MO8GsefGEt7EprS8UrXsm1TxEvFUX1IMTuSLU2Rh7fTIdFtl8xD7JiEYiWU2dl+NYAjCTksTehQUxPag==", + "requires": { + "lodash": "^4.17.11" + } + }, + "request-promise-native": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/request-promise-native/-/request-promise-native-1.0.7.tgz", + "integrity": "sha512-rIMnbBdgNViL37nZ1b3L/VfPOpSi0TqVDQPAvO6U14lMzOLrt5nilxCQqtDKhZeDiW0/hkCXGoQjhgJd/tCh6w==", + "requires": { + "request-promise-core": "1.1.2", + "stealthy-require": "^1.1.1", + "tough-cookie": "^2.3.3" + } + }, + "require-directory": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz", + "integrity": "sha1-jGStX9MNqxyXbiNE/+f3kqam30I=" + }, + "require-from-string": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/require-from-string/-/require-from-string-2.0.2.tgz", + "integrity": "sha512-Xf0nWe6RseziFMu+Ap9biiUbmplq6S9/p+7w7YXP/JBHhrUDDUhwa+vANyubuqfZWTveU//DYVGsDG7RKL/vEw==" + }, + "require-main-filename": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/require-main-filename/-/require-main-filename-1.0.1.tgz", + "integrity": "sha1-l/cXtp1IeE9fUmpsWqj/3aBVpNE=" + }, + "requireindex": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/requireindex/-/requireindex-1.2.0.tgz", + "integrity": "sha512-L9jEkOi3ASd9PYit2cwRfyppc9NoABujTP8/5gFcbERmo5jUoAKovIC3fsF17pkTnGsrByysqX+Kxd2OTNI1ww==" + }, + "requires-port": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/requires-port/-/requires-port-1.0.0.tgz", + "integrity": "sha1-kl0mAdOaxIXgkc8NpcbmlNw9yv8=" + }, + "resolve": { + "version": "1.10.0", + "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.10.0.tgz", + "integrity": "sha512-3sUr9aq5OfSg2S9pNtPA9hL1FVEAjvfOC4leW0SNf/mpnaakz2a9femSd6LqAww2RaFctwyf1lCqnTHuF1rxDg==", + "requires": { + "path-parse": "^1.0.6" + } + }, + "resolve-cwd": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/resolve-cwd/-/resolve-cwd-2.0.0.tgz", + "integrity": "sha1-AKn3OHVW4nA46uIyyqNypqWbZlo=", + "requires": { + "resolve-from": "^3.0.0" + }, + "dependencies": { + "resolve-from": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-3.0.0.tgz", + "integrity": "sha1-six699nWiBvItuZTM17rywoYh0g=" + } + } + }, + "resolve-dir": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/resolve-dir/-/resolve-dir-1.0.1.tgz", + "integrity": "sha1-eaQGRMNivoLybv/nOcm7U4IEb0M=", + "requires": { + "expand-tilde": "^2.0.0", + "global-modules": "^1.0.0" + } + }, + "resolve-from": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-4.0.0.tgz", + "integrity": "sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==" + }, + "resolve-url": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/resolve-url/-/resolve-url-0.2.1.tgz", + "integrity": "sha1-LGN/53yJOv0qZj/iGqkIAGjiBSo=" + }, + "restore-cursor": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/restore-cursor/-/restore-cursor-2.0.0.tgz", + "integrity": "sha1-n37ih/gv0ybU/RYpI9YhKe7g368=", + "requires": { + "onetime": "^2.0.0", + "signal-exit": "^3.0.2" + } + }, + "ret": { + "version": "0.1.15", + "resolved": "https://registry.npmjs.org/ret/-/ret-0.1.15.tgz", + "integrity": "sha512-TTlYpa+OL+vMMNG24xSlQGEJ3B/RzEfUlLct7b5G/ytav+wPrplCpVMFuwzXbkecJrb6IYo1iFb0S9v37754mg==" + }, + "rimraf": { + "version": "2.6.3", + "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.6.3.tgz", + "integrity": "sha512-mwqeW5XsA2qAejG46gYdENaxXjx9onRNCfn7L0duuP4hCuTIi/QO7PDK07KJfp1d+izWPrzEJDcSqBa0OZQriA==", + "requires": { + "glob": "^7.1.3" + } + }, + "ripemd160": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/ripemd160/-/ripemd160-2.0.2.tgz", + "integrity": "sha512-ii4iagi25WusVoiC4B4lq7pbXfAp3D9v5CwfkY33vffw2+pkDjY1D8GaN7spsxvCSx8dkPqOZCEZyfxcmJG2IA==", + "requires": { + "hash-base": "^3.0.0", + "inherits": "^2.0.1" + } + }, + "rsvp": { + "version": "4.8.4", + "resolved": "https://registry.npmjs.org/rsvp/-/rsvp-4.8.4.tgz", + "integrity": "sha512-6FomvYPfs+Jy9TfXmBpBuMWNH94SgCsZmJKcanySzgNNP6LjWxBvyLTa9KaMfDDM5oxRfrKDB0r/qeRsLwnBfA==" + }, + "run-async": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/run-async/-/run-async-2.3.0.tgz", + "integrity": "sha1-A3GrSuC91yDUFm19/aZP96RFpsA=", + "requires": { + "is-promise": "^2.1.0" + } + }, + "run-queue": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/run-queue/-/run-queue-1.0.3.tgz", + "integrity": "sha1-6Eg5bwV9Ij8kOGkkYY4laUFh7Ec=", + "requires": { + "aproba": "^1.1.1" + } + }, + "rxjs": { + "version": "6.4.0", + "resolved": "https://registry.npmjs.org/rxjs/-/rxjs-6.4.0.tgz", + "integrity": "sha512-Z9Yfa11F6B9Sg/BK9MnqnQ+aQYicPLtilXBp2yUtDt2JRCE0h26d33EnfO3ZxoNxG0T92OUucP3Ct7cpfkdFfw==", + "requires": { + "tslib": "^1.9.0" + } + }, + "safe-buffer": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", + "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==" + }, + "safe-regex": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/safe-regex/-/safe-regex-1.1.0.tgz", + "integrity": "sha1-QKNmnzsHfR6UPURinhV91IAjvy4=", + "requires": { + "ret": "~0.1.10" + } + }, + "safer-buffer": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz", + "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==" + }, + "sane": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/sane/-/sane-4.1.0.tgz", + "integrity": "sha512-hhbzAgTIX8O7SHfp2c8/kREfEn4qO/9q8C9beyY6+tvZ87EpoZ3i1RIEvp27YBswnNbY9mWd6paKVmKbAgLfZA==", + "requires": { + "@cnakazawa/watch": "^1.0.3", + "anymatch": "^2.0.0", + "capture-exit": "^2.0.0", + "exec-sh": "^0.3.2", + "execa": "^1.0.0", + "fb-watchman": "^2.0.0", + "micromatch": "^3.1.4", + "minimist": "^1.1.1", + "walker": "~1.0.5" + } + }, + "sax": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/sax/-/sax-1.2.4.tgz", + "integrity": "sha512-NqVDv9TpANUjFm0N8uM5GxL36UgKi9/atZw+x7YFnQ8ckwFGKrl4xX4yWtrey3UJm5nP1kUbnYgLopqWNSRhWw==" + }, + "schema-utils": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-1.0.0.tgz", + "integrity": "sha512-i27Mic4KovM/lnGsy8whRCHhc7VicJajAjTrYg11K9zfZXnYIt4k5F+kZkwjnrhKzLic/HLU4j11mjsz2G/75g==", + "requires": { + "ajv": "^6.1.0", + "ajv-errors": "^1.0.0", + "ajv-keywords": "^3.1.0" + } + }, + "select-hose": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/select-hose/-/select-hose-2.0.0.tgz", + "integrity": "sha1-Yl2GWPhlr0Psliv8N2o3NZpJlMo=" + }, + "selfsigned": { + "version": "1.10.4", + "resolved": "https://registry.npmjs.org/selfsigned/-/selfsigned-1.10.4.tgz", + "integrity": "sha512-9AukTiDmHXGXWtWjembZ5NDmVvP2695EtpgbCsxCa68w3c88B+alqbmZ4O3hZ4VWGXeGWzEVdvqgAJD8DQPCDw==", + "requires": { + "node-forge": "0.7.5" + } + }, + "semver": { + "version": "5.5.0", + "resolved": "https://registry.npmjs.org/semver/-/semver-5.5.0.tgz", + "integrity": "sha512-4SJ3dm0WAwWy/NVeioZh5AntkdJoWKxHxcmyP622fOkgHa4z3R0TdBJICINyaSDE6uNwVc8gZr+ZinwZAH4xIA==" + }, + "send": { + "version": "0.16.2", + "resolved": "https://registry.npmjs.org/send/-/send-0.16.2.tgz", + "integrity": "sha512-E64YFPUssFHEFBvpbbjr44NCLtI1AohxQ8ZSiJjQLskAdKuriYEP6VyGEsRDH8ScozGpkaX1BGvhanqCwkcEZw==", + "requires": { + "debug": "2.6.9", + "depd": "~1.1.2", + "destroy": "~1.0.4", + "encodeurl": "~1.0.2", + "escape-html": "~1.0.3", + "etag": "~1.8.1", + "fresh": "0.5.2", + "http-errors": "~1.6.2", + "mime": "1.4.1", + "ms": "2.0.0", + "on-finished": "~2.3.0", + "range-parser": "~1.2.0", + "statuses": "~1.4.0" + } + }, + "serialize-javascript": { + "version": "1.6.1", + "resolved": "https://registry.npmjs.org/serialize-javascript/-/serialize-javascript-1.6.1.tgz", + "integrity": "sha512-A5MOagrPFga4YaKQSWHryl7AXvbQkEqpw4NNYMTNYUNV51bA8ABHgYFpqKx+YFFrw59xMV1qGH1R4AgoNIVgCw==" + }, + "serve-index": { + "version": "1.9.1", + "resolved": "https://registry.npmjs.org/serve-index/-/serve-index-1.9.1.tgz", + "integrity": "sha1-03aNabHn2C5c4FD/9bRTvqEqkjk=", + "requires": { + "accepts": "~1.3.4", + "batch": "0.6.1", + "debug": "2.6.9", + "escape-html": "~1.0.3", + "http-errors": "~1.6.2", + "mime-types": "~2.1.17", + "parseurl": "~1.3.2" + } + }, + "serve-static": { + "version": "1.13.2", + "resolved": "https://registry.npmjs.org/serve-static/-/serve-static-1.13.2.tgz", + "integrity": "sha512-p/tdJrO4U387R9oMjb1oj7qSMaMfmOyd4j9hOFoxZe2baQszgHcSWjuya/CiT5kgZZKRudHNOA0pYXOl8rQ5nw==", + "requires": { + "encodeurl": "~1.0.2", + "escape-html": "~1.0.3", + "parseurl": "~1.3.2", + "send": "0.16.2" + } + }, + "set-blocking": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/set-blocking/-/set-blocking-2.0.0.tgz", + "integrity": "sha1-BF+XgtARrppoA93TgrJDkrPYkPc=" + }, + "set-value": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/set-value/-/set-value-2.0.0.tgz", + "integrity": "sha512-hw0yxk9GT/Hr5yJEYnHNKYXkIA8mVJgd9ditYZCe16ZczcaELYYcfvaXesNACk2O8O0nTiPQcQhGUQj8JLzeeg==", + "requires": { + "extend-shallow": "^2.0.1", + "is-extendable": "^0.1.1", + "is-plain-object": "^2.0.3", + "split-string": "^3.0.1" + }, + "dependencies": { + "extend-shallow": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", + "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", + "requires": { + "is-extendable": "^0.1.0" + } + } + } + }, + "setimmediate": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/setimmediate/-/setimmediate-1.0.5.tgz", + "integrity": "sha1-KQy7Iy4waULX1+qbg3Mqt4VvgoU=" + }, + "setprototypeof": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.1.0.tgz", + "integrity": "sha512-BvE/TwpZX4FXExxOxZyRGQQv651MSwmWKZGqvmPcRIjDqWub67kTKuIMx43cZZrS/cBBzwBcNDWoFxt2XEFIpQ==" + }, + "sha.js": { + "version": "2.4.11", + "resolved": "https://registry.npmjs.org/sha.js/-/sha.js-2.4.11.tgz", + "integrity": "sha512-QMEp5B7cftE7APOjk5Y6xgrbWu+WkLVQwk8JNjZ8nKRciZaByEW6MubieAiToS7+dwvrjGhH8jRXz3MVd0AYqQ==", + "requires": { + "inherits": "^2.0.1", + "safe-buffer": "^5.0.1" + } + }, + "shebang-command": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-1.2.0.tgz", + "integrity": "sha1-RKrGW2lbAzmJaMOfNj/uXer98eo=", + "requires": { + "shebang-regex": "^1.0.0" + } + }, + "shebang-regex": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-1.0.0.tgz", + "integrity": "sha1-2kL0l0DAtC2yypcoVxyxkMmO/qM=" + }, + "shellwords": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/shellwords/-/shellwords-0.1.1.tgz", + "integrity": "sha512-vFwSUfQvqybiICwZY5+DAWIPLKsWO31Q91JSKl3UYv+K5c2QRPzn0qzec6QPu1Qc9eHYItiP3NdJqNVqetYAww==" + }, + "signal-exit": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.2.tgz", + "integrity": "sha1-tf3AjxKH6hF4Yo5BXiUTK3NkbG0=" + }, + "sisteransi": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/sisteransi/-/sisteransi-1.0.0.tgz", + "integrity": "sha512-N+z4pHB4AmUv0SjveWRd6q1Nj5w62m5jodv+GD8lvmbY/83T/rpbJGZOnK5T149OldDj4Db07BSv9xY4K6NTPQ==" + }, + "slash": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/slash/-/slash-2.0.0.tgz", + "integrity": "sha512-ZYKh3Wh2z1PpEXWr0MpSBZ0V6mZHAQfYevttO11c51CaWjGTaadiKZ+wVt1PbMlDV5qhMFslpZCemhwOK7C89A==" + }, + "slice-ansi": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/slice-ansi/-/slice-ansi-2.1.0.tgz", + "integrity": "sha512-Qu+VC3EwYLldKa1fCxuuvULvSJOKEgk9pi8dZeCVK7TqBfUNTH4sFkk4joj8afVSfAYgJoSOetjx9QWOJ5mYoQ==", + "requires": { + "ansi-styles": "^3.2.0", + "astral-regex": "^1.0.0", + "is-fullwidth-code-point": "^2.0.0" + } + }, + "snapdragon": { + "version": "0.8.2", + "resolved": "https://registry.npmjs.org/snapdragon/-/snapdragon-0.8.2.tgz", + "integrity": "sha512-FtyOnWN/wCHTVXOMwvSv26d+ko5vWlIDD6zoUJ7LW8vh+ZBC8QdljveRP+crNrtBwioEUWy/4dMtbBjA4ioNlg==", + "requires": { + "base": "^0.11.1", + "debug": "^2.2.0", + "define-property": "^0.2.5", + "extend-shallow": "^2.0.1", + "map-cache": "^0.2.2", + "source-map": "^0.5.6", + "source-map-resolve": "^0.5.0", + "use": "^3.1.0" + }, + "dependencies": { + "define-property": { + "version": "0.2.5", + "resolved": "https://registry.npmjs.org/define-property/-/define-property-0.2.5.tgz", + "integrity": "sha1-w1se+RjsPJkPmlvFe+BKrOxcgRY=", + "requires": { + "is-descriptor": "^0.1.0" + } + }, + "extend-shallow": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", + "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", + "requires": { + "is-extendable": "^0.1.0" + } + } + } + }, + "snapdragon-node": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/snapdragon-node/-/snapdragon-node-2.1.1.tgz", + "integrity": "sha512-O27l4xaMYt/RSQ5TR3vpWCAB5Kb/czIcqUFOM/C4fYcLnbZUc1PkjTAMjof2pBWaSTwOUd6qUHcFGVGj7aIwnw==", + "requires": { + "define-property": "^1.0.0", + "isobject": "^3.0.0", + "snapdragon-util": "^3.0.1" + }, + "dependencies": { + "define-property": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/define-property/-/define-property-1.0.0.tgz", + "integrity": "sha1-dp66rz9KY6rTr56NMEybvnm/sOY=", + "requires": { + "is-descriptor": "^1.0.0" + } + }, + "is-accessor-descriptor": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-1.0.0.tgz", + "integrity": "sha512-m5hnHTkcVsPfqx3AKlyttIPb7J+XykHvJP2B9bZDjlhLIoEq4XoK64Vg7boZlVWYK6LUY94dYPEE7Lh0ZkZKcQ==", + "requires": { + "kind-of": "^6.0.0" + } + }, + "is-data-descriptor": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-1.0.0.tgz", + "integrity": "sha512-jbRXy1FmtAoCjQkVmIVYwuuqDFUbaOeDjmed1tOGPrsMhtJA4rD9tkgA0F1qJ3gRFRXcHYVkdeaP50Q5rE/jLQ==", + "requires": { + "kind-of": "^6.0.0" + } + }, + "is-descriptor": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-1.0.2.tgz", + "integrity": "sha512-2eis5WqQGV7peooDyLmNEPUrps9+SXX5c9pL3xEB+4e9HnGuDa7mB7kHxHw4CbqS9k1T2hOH3miL8n8WtiYVtg==", + "requires": { + "is-accessor-descriptor": "^1.0.0", + "is-data-descriptor": "^1.0.0", + "kind-of": "^6.0.2" + } + } + } + }, + "snapdragon-util": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/snapdragon-util/-/snapdragon-util-3.0.1.tgz", + "integrity": "sha512-mbKkMdQKsjX4BAL4bRYTj21edOf8cN7XHdYUJEe+Zn99hVEYcMvKPct1IqNe7+AZPirn8BCDOQBHQZknqmKlZQ==", + "requires": { + "kind-of": "^3.2.0" + }, + "dependencies": { + "kind-of": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", + "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", + "requires": { + "is-buffer": "^1.1.5" + } + } + } + }, + "sockjs": { + "version": "0.3.19", + "resolved": "https://registry.npmjs.org/sockjs/-/sockjs-0.3.19.tgz", + "integrity": "sha512-V48klKZl8T6MzatbLlzzRNhMepEys9Y4oGFpypBFFn1gLI/QQ9HtLLyWJNbPlwGLelOVOEijUbTTJeLLI59jLw==", + "requires": { + "faye-websocket": "^0.10.0", + "uuid": "^3.0.1" + } + }, + "sockjs-client": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/sockjs-client/-/sockjs-client-1.3.0.tgz", + "integrity": "sha512-R9jxEzhnnrdxLCNln0xg5uGHqMnkhPSTzUZH2eXcR03S/On9Yvoq2wyUZILRUhZCNVu2PmwWVoyuiPz8th8zbg==", + "requires": { + "debug": "^3.2.5", + "eventsource": "^1.0.7", + "faye-websocket": "~0.11.1", + "inherits": "^2.0.3", + "json3": "^3.3.2", + "url-parse": "^1.4.3" + }, + "dependencies": { + "debug": { + "version": "3.2.6", + "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.6.tgz", + "integrity": "sha512-mel+jf7nrtEl5Pn1Qx46zARXKDpBbvzezse7p7LqINmdoIk8PYP5SySaxEmYv6TZ0JyEKA1hsCId6DIhgITtWQ==", + "requires": { + "ms": "^2.1.1" + } + }, + "faye-websocket": { + "version": "0.11.1", + "resolved": "https://registry.npmjs.org/faye-websocket/-/faye-websocket-0.11.1.tgz", + "integrity": "sha1-8O/hjE9W5PQK/H4Gxxn9XuYYjzg=", + "requires": { + "websocket-driver": ">=0.5.1" + } + }, + "ms": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.1.tgz", + "integrity": "sha512-tgp+dl5cGk28utYktBsrFqA7HKgrhgPsg6Z/EfhWI4gl1Hwq8B/GmY/0oXZ6nF8hDVesS/FpnYaD/kOWhYQvyg==" + } + } + }, + "source-list-map": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/source-list-map/-/source-list-map-2.0.1.tgz", + "integrity": "sha512-qnQ7gVMxGNxsiL4lEuJwe/To8UnK7fAnmbGEEH8RpLouuKbeEm0lhbQVFIrNSuB+G7tVrAlVsZgETT5nljf+Iw==" + }, + "source-map": { + "version": "0.5.7", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.5.7.tgz", + "integrity": "sha1-igOdLRAh0i0eoUyA2OpGi6LvP8w=" + }, + "source-map-resolve": { + "version": "0.5.2", + "resolved": "https://registry.npmjs.org/source-map-resolve/-/source-map-resolve-0.5.2.tgz", + "integrity": "sha512-MjqsvNwyz1s0k81Goz/9vRBe9SZdB09Bdw+/zYyO+3CuPk6fouTaxscHkgtE8jKvf01kVfl8riHzERQ/kefaSA==", + "requires": { + "atob": "^2.1.1", + "decode-uri-component": "^0.2.0", + "resolve-url": "^0.2.1", + "source-map-url": "^0.4.0", + "urix": "^0.1.0" + } + }, + "source-map-support": { + "version": "0.5.10", + "resolved": "https://registry.npmjs.org/source-map-support/-/source-map-support-0.5.10.tgz", + "integrity": "sha512-YfQ3tQFTK/yzlGJuX8pTwa4tifQj4QS2Mj7UegOu8jAz59MqIiMGPXxQhVQiIMNzayuUSF/jEuVnfFF5JqybmQ==", + "requires": { + "buffer-from": "^1.0.0", + "source-map": "^0.6.0" + }, + "dependencies": { + "source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==" + } + } + }, + "source-map-url": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/source-map-url/-/source-map-url-0.4.0.tgz", + "integrity": "sha1-PpNdfd1zYxuXZZlW1VEo6HtQhKM=" + }, + "spdx-correct": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/spdx-correct/-/spdx-correct-3.1.0.tgz", + "integrity": "sha512-lr2EZCctC2BNR7j7WzJ2FpDznxky1sjfxvvYEyzxNyb6lZXHODmEoJeFu4JupYlkfha1KZpJyoqiJ7pgA1qq8Q==", + "requires": { + "spdx-expression-parse": "^3.0.0", + "spdx-license-ids": "^3.0.0" + } + }, + "spdx-exceptions": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/spdx-exceptions/-/spdx-exceptions-2.2.0.tgz", + "integrity": "sha512-2XQACfElKi9SlVb1CYadKDXvoajPgBVPn/gOQLrTvHdElaVhr7ZEbqJaRnJLVNeaI4cMEAgVCeBMKF6MWRDCRA==" + }, + "spdx-expression-parse": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/spdx-expression-parse/-/spdx-expression-parse-3.0.0.tgz", + "integrity": "sha512-Yg6D3XpRD4kkOmTpdgbUiEJFKghJH03fiC1OPll5h/0sO6neh2jqRDVHOQ4o/LMea0tgCkbMgea5ip/e+MkWyg==", + "requires": { + "spdx-exceptions": "^2.1.0", + "spdx-license-ids": "^3.0.0" + } + }, + "spdx-license-ids": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/spdx-license-ids/-/spdx-license-ids-3.0.4.tgz", + "integrity": "sha512-7j8LYJLeY/Yb6ACbQ7F76qy5jHkp0U6jgBfJsk97bwWlVUnUWsAgpyaCvo17h0/RQGnQ036tVDomiwoI4pDkQA==" + }, + "spdy": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/spdy/-/spdy-4.0.0.tgz", + "integrity": "sha512-ot0oEGT/PGUpzf/6uk4AWLqkq+irlqHXkrdbk51oWONh3bxQmBuljxPNl66zlRRcIJStWq0QkLUCPOPjgjvU0Q==", + "requires": { + "debug": "^4.1.0", + "handle-thing": "^2.0.0", + "http-deceiver": "^1.2.7", + "select-hose": "^2.0.0", + "spdy-transport": "^3.0.0" + }, + "dependencies": { + "debug": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.1.1.tgz", + "integrity": "sha512-pYAIzeRo8J6KPEaJ0VWOh5Pzkbw/RetuzehGM7QRRX5he4fPHx2rdKMB256ehJCkX+XRQm16eZLqLNS8RSZXZw==", + "requires": { + "ms": "^2.1.1" + } + }, + "ms": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.1.tgz", + "integrity": "sha512-tgp+dl5cGk28utYktBsrFqA7HKgrhgPsg6Z/EfhWI4gl1Hwq8B/GmY/0oXZ6nF8hDVesS/FpnYaD/kOWhYQvyg==" + } + } + }, + "spdy-transport": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/spdy-transport/-/spdy-transport-3.0.0.tgz", + "integrity": "sha512-hsLVFE5SjA6TCisWeJXFKniGGOpBgMLmerfO2aCyCU5s7nJ/rpAepqmFifv/GCbSbueEeAJJnmSQ2rKC/g8Fcw==", + "requires": { + "debug": "^4.1.0", + "detect-node": "^2.0.4", + "hpack.js": "^2.1.6", + "obuf": "^1.1.2", + "readable-stream": "^3.0.6", + "wbuf": "^1.7.3" + }, + "dependencies": { + "debug": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.1.1.tgz", + "integrity": "sha512-pYAIzeRo8J6KPEaJ0VWOh5Pzkbw/RetuzehGM7QRRX5he4fPHx2rdKMB256ehJCkX+XRQm16eZLqLNS8RSZXZw==", + "requires": { + "ms": "^2.1.1" + } + }, + "ms": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.1.tgz", + "integrity": "sha512-tgp+dl5cGk28utYktBsrFqA7HKgrhgPsg6Z/EfhWI4gl1Hwq8B/GmY/0oXZ6nF8hDVesS/FpnYaD/kOWhYQvyg==" + }, + "readable-stream": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.3.0.tgz", + "integrity": "sha512-EsI+s3k3XsW+fU8fQACLN59ky34AZ14LoeVZpYwmZvldCFo0r0gnelwF2TcMjLor/BTL5aDJVBMkss0dthToPw==", + "requires": { + "inherits": "^2.0.3", + "string_decoder": "^1.1.1", + "util-deprecate": "^1.0.1" + } + } + } + }, + "split-string": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/split-string/-/split-string-3.1.0.tgz", + "integrity": "sha512-NzNVhJDYpwceVVii8/Hu6DKfD2G+NrQHlS/V/qgv763EYudVwEcMQNxd2lh+0VrUByXN/oJkl5grOhYWvQUYiw==", + "requires": { + "extend-shallow": "^3.0.0" + } + }, + "sprintf-js": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.0.3.tgz", + "integrity": "sha1-BOaSb2YolTVPPdAVIDYzuFcpfiw=" + }, + "sshpk": { + "version": "1.16.1", + "resolved": "https://registry.npmjs.org/sshpk/-/sshpk-1.16.1.tgz", + "integrity": "sha512-HXXqVUq7+pcKeLqqZj6mHFUMvXtOJt1uoUx09pFW6011inTMxqI8BA8PM95myrIyyKwdnzjdFjLiE6KBPVtJIg==", + "requires": { + "asn1": "~0.2.3", + "assert-plus": "^1.0.0", + "bcrypt-pbkdf": "^1.0.0", + "dashdash": "^1.12.0", + "ecc-jsbn": "~0.1.1", + "getpass": "^0.1.1", + "jsbn": "~0.1.0", + "safer-buffer": "^2.0.2", + "tweetnacl": "~0.14.0" + } + }, + "ssri": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/ssri/-/ssri-6.0.1.tgz", + "integrity": "sha512-3Wge10hNcT1Kur4PDFwEieXSCMCJs/7WvSACcrMYrNp+b8kDL1/0wJch5Ni2WrtwEa2IO8OsVfeKIciKCDx/QA==", + "requires": { + "figgy-pudding": "^3.5.1" + } + }, + "stack-utils": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/stack-utils/-/stack-utils-1.0.2.tgz", + "integrity": "sha512-MTX+MeG5U994cazkjd/9KNAapsHnibjMLnfXodlkXw76JEea0UiNzrqidzo1emMwk7w5Qhc9jd4Bn9TBb1MFwA==" + }, + "static-extend": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/static-extend/-/static-extend-0.1.2.tgz", + "integrity": "sha1-YICcOcv/VTNyJv1eC1IPNB8ftcY=", + "requires": { + "define-property": "^0.2.5", + "object-copy": "^0.1.0" + }, + "dependencies": { + "define-property": { + "version": "0.2.5", + "resolved": "https://registry.npmjs.org/define-property/-/define-property-0.2.5.tgz", + "integrity": "sha1-w1se+RjsPJkPmlvFe+BKrOxcgRY=", + "requires": { + "is-descriptor": "^0.1.0" + } + } + } + }, + "statuses": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/statuses/-/statuses-1.4.0.tgz", + "integrity": "sha512-zhSCtt8v2NDrRlPQpCNtw/heZLtfUDqxBM1udqikb/Hbk52LK4nQSwr10u77iopCW5LsyHpuXS0GnEc48mLeew==" + }, + "stealthy-require": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/stealthy-require/-/stealthy-require-1.1.1.tgz", + "integrity": "sha1-NbCYdbT/SfJqd35QmzCQoyJr8ks=" + }, + "stream-browserify": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/stream-browserify/-/stream-browserify-2.0.2.tgz", + "integrity": "sha512-nX6hmklHs/gr2FuxYDltq8fJA1GDlxKQCz8O/IM4atRqBH8OORmBNgfvW5gG10GT/qQ9u0CzIvr2X5Pkt6ntqg==", + "requires": { + "inherits": "~2.0.1", + "readable-stream": "^2.0.2" + } + }, + "stream-each": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/stream-each/-/stream-each-1.2.3.tgz", + "integrity": "sha512-vlMC2f8I2u/bZGqkdfLQW/13Zihpej/7PmSiMQsbYddxuTsJp8vRe2x2FvVExZg7FaOds43ROAuFJwPR4MTZLw==", + "requires": { + "end-of-stream": "^1.1.0", + "stream-shift": "^1.0.0" + } + }, + "stream-http": { + "version": "2.8.3", + "resolved": "https://registry.npmjs.org/stream-http/-/stream-http-2.8.3.tgz", + "integrity": "sha512-+TSkfINHDo4J+ZobQLWiMouQYB+UVYFttRA94FpEzzJ7ZdqcL4uUUQ7WkdkI4DSozGmgBUE/a47L+38PenXhUw==", + "requires": { + "builtin-status-codes": "^3.0.0", + "inherits": "^2.0.1", + "readable-stream": "^2.3.6", + "to-arraybuffer": "^1.0.0", + "xtend": "^4.0.0" + } + }, + "stream-shift": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/stream-shift/-/stream-shift-1.0.0.tgz", + "integrity": "sha1-1cdSgl5TZ+eG944Y5EXqIjoVWVI=" + }, + "string-length": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/string-length/-/string-length-2.0.0.tgz", + "integrity": "sha1-1A27aGo6zpYMHP/KVivyxF+DY+0=", + "requires": { + "astral-regex": "^1.0.0", + "strip-ansi": "^4.0.0" + } + }, + "string-width": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-2.1.1.tgz", + "integrity": "sha512-nOqH59deCq9SRHlxq1Aw85Jnt4w6KvLKqWVik6oA9ZklXLNIOlqg4F2yrT1MVaTjAqvVwdfeZ7w7aCvJD7ugkw==", + "requires": { + "is-fullwidth-code-point": "^2.0.0", + "strip-ansi": "^4.0.0" + } + }, + "string_decoder": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", + "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", + "requires": { + "safe-buffer": "~5.1.0" + } + }, + "strip-ansi": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-4.0.0.tgz", + "integrity": "sha1-qEeQIusaw2iocTibY1JixQXuNo8=", + "requires": { + "ansi-regex": "^3.0.0" + } + }, + "strip-bom": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-3.0.0.tgz", + "integrity": "sha1-IzTBjpx1n3vdVv3vfprj1YjmjtM=" + }, + "strip-eof": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/strip-eof/-/strip-eof-1.0.0.tgz", + "integrity": "sha1-u0P/VZim6wXYm1n80SnJgzE2Br8=" + }, + "strip-json-comments": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-2.0.1.tgz", + "integrity": "sha1-PFMZQukIwml8DsNEhYwobHygpgo=" + }, + "supports-color": { + "version": "5.5.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", + "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", + "requires": { + "has-flag": "^3.0.0" + } + }, + "symbol-tree": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/symbol-tree/-/symbol-tree-3.2.2.tgz", + "integrity": "sha1-rifbOPZgp64uHDt9G8KQgZuFGeY=" + }, + "table": { + "version": "5.2.3", + "resolved": "https://registry.npmjs.org/table/-/table-5.2.3.tgz", + "integrity": "sha512-N2RsDAMvDLvYwFcwbPyF3VmVSSkuF+G1e+8inhBLtHpvwXGw4QRPEZhihQNeEN0i1up6/f6ObCJXNdlRG3YVyQ==", + "requires": { + "ajv": "^6.9.1", + "lodash": "^4.17.11", + "slice-ansi": "^2.1.0", + "string-width": "^3.0.0" + }, + "dependencies": { + "ansi-regex": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-4.1.0.tgz", + "integrity": "sha512-1apePfXM1UOSqw0o9IiFAovVz9M5S1Dg+4TrDwfMewQ6p/rmMueb7tWZjQ1rx4Loy1ArBggoqGpfqqdI4rondg==" + }, + "string-width": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-3.1.0.tgz", + "integrity": "sha512-vafcv6KjVZKSgz06oM/H6GDBrAtz8vdhQakGjFIvNrHA6y3HCF1CInLy+QLq8dTJPQ1b+KDUqDFctkdRW44e1w==", + "requires": { + "emoji-regex": "^7.0.1", + "is-fullwidth-code-point": "^2.0.0", + "strip-ansi": "^5.1.0" + } + }, + "strip-ansi": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-5.2.0.tgz", + "integrity": "sha512-DuRs1gKbBqsMKIZlrffwlug8MHkcnpjs5VPmL1PAh+mA30U0DTotfDZ0d2UUsXpPmPmMMJ6W773MaA3J+lbiWA==", + "requires": { + "ansi-regex": "^4.1.0" + } + } + } + }, + "tapable": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/tapable/-/tapable-1.1.1.tgz", + "integrity": "sha512-9I2ydhj8Z9veORCw5PRm4u9uebCn0mcCa6scWoNcbZ6dAtoo2618u9UUzxgmsCOreJpqDDuv61LvwofW7hLcBA==" + }, + "terser": { + "version": "3.17.0", + "resolved": "https://registry.npmjs.org/terser/-/terser-3.17.0.tgz", + "integrity": "sha512-/FQzzPJmCpjAH9Xvk2paiWrFq+5M6aVOf+2KRbwhByISDX/EujxsK+BAvrhb6H+2rtrLCHK9N01wO014vrIwVQ==", + "requires": { + "commander": "^2.19.0", + "source-map": "~0.6.1", + "source-map-support": "~0.5.10" + }, + "dependencies": { + "source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==" + } + } + }, + "terser-webpack-plugin": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/terser-webpack-plugin/-/terser-webpack-plugin-1.2.3.tgz", + "integrity": "sha512-GOK7q85oAb/5kE12fMuLdn2btOS9OBZn4VsecpHDywoUC/jLhSAKOiYo0ezx7ss2EXPMzyEWFoE0s1WLE+4+oA==", + "requires": { + "cacache": "^11.0.2", + "find-cache-dir": "^2.0.0", + "schema-utils": "^1.0.0", + "serialize-javascript": "^1.4.0", + "source-map": "^0.6.1", + "terser": "^3.16.1", + "webpack-sources": "^1.1.0", + "worker-farm": "^1.5.2" + }, + "dependencies": { + "source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==" + } + } + }, + "test-exclude": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/test-exclude/-/test-exclude-5.1.0.tgz", + "integrity": "sha512-gwf0S2fFsANC55fSeSqpb8BYk6w3FDvwZxfNjeF6FRgvFa43r+7wRiA/Q0IxoRU37wB/LE8IQ4221BsNucTaCA==", + "requires": { + "arrify": "^1.0.1", + "minimatch": "^3.0.4", + "read-pkg-up": "^4.0.0", + "require-main-filename": "^1.0.1" + } + }, + "text-table": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/text-table/-/text-table-0.2.0.tgz", + "integrity": "sha1-f17oI66AUgfACvLfSoTsP8+lcLQ=" + }, + "throat": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/throat/-/throat-4.1.0.tgz", + "integrity": "sha1-iQN8vJLFarGJJua6TLsgDhVnKmo=" + }, + "through": { + "version": "2.3.8", + "resolved": "https://registry.npmjs.org/through/-/through-2.3.8.tgz", + "integrity": "sha1-DdTJ/6q8NXlgsbckEV1+Doai4fU=" + }, + "through2": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/through2/-/through2-2.0.5.tgz", + "integrity": "sha512-/mrRod8xqpA+IHSLyGCQ2s8SPHiCDEeQJSep1jqLYeEUClOFG2Qsh+4FU6G9VeqpZnGW/Su8LQGc4YKni5rYSQ==", + "requires": { + "readable-stream": "~2.3.6", + "xtend": "~4.0.1" + } + }, + "thunky": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/thunky/-/thunky-1.0.3.tgz", + "integrity": "sha512-YwT8pjmNcAXBZqrubu22P4FYsh2D4dxRmnWBOL8Jk8bUcRUtc5326kx32tuTmFDAZtLOGEVNl8POAR8j896Iow==" + }, + "timers-browserify": { + "version": "2.0.10", + "resolved": "https://registry.npmjs.org/timers-browserify/-/timers-browserify-2.0.10.tgz", + "integrity": "sha512-YvC1SV1XdOUaL6gx5CoGroT3Gu49pK9+TZ38ErPldOWW4j49GI1HKs9DV+KGq/w6y+LZ72W1c8cKz2vzY+qpzg==", + "requires": { + "setimmediate": "^1.0.4" + } + }, + "tmp": { + "version": "0.0.33", + "resolved": "https://registry.npmjs.org/tmp/-/tmp-0.0.33.tgz", + "integrity": "sha512-jRCJlojKnZ3addtTOjdIqoRuPEKBvNXcGYqzO6zWZX8KfKEpnGY5jfggJQ3EjKuu8D4bJRr0y+cYJFmYbImXGw==", + "requires": { + "os-tmpdir": "~1.0.2" + } + }, + "tmpl": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/tmpl/-/tmpl-1.0.4.tgz", + "integrity": "sha1-I2QN17QtAEM5ERQIIOXPRA5SHdE=" + }, + "to-arraybuffer": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/to-arraybuffer/-/to-arraybuffer-1.0.1.tgz", + "integrity": "sha1-fSKbH8xjfkZsoIEYCDanqr/4P0M=" + }, + "to-fast-properties": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/to-fast-properties/-/to-fast-properties-2.0.0.tgz", + "integrity": "sha1-3F5pjL0HkmW8c+A3doGk5Og/YW4=" + }, + "to-object-path": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/to-object-path/-/to-object-path-0.3.0.tgz", + "integrity": "sha1-KXWIt7Dn4KwI4E5nL4XB9JmeF68=", + "requires": { + "kind-of": "^3.0.2" + }, + "dependencies": { + "kind-of": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", + "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", + "requires": { + "is-buffer": "^1.1.5" + } + } + } + }, + "to-regex": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/to-regex/-/to-regex-3.0.2.tgz", + "integrity": "sha512-FWtleNAtZ/Ki2qtqej2CXTOayOH9bHDQF+Q48VpWyDXjbYxA4Yz8iDB31zXOBUlOHHKidDbqGVrTUvQMPmBGBw==", + "requires": { + "define-property": "^2.0.2", + "extend-shallow": "^3.0.2", + "regex-not": "^1.0.2", + "safe-regex": "^1.1.0" + } + }, + "to-regex-range": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-2.1.1.tgz", + "integrity": "sha1-fIDBe53+vlmeJzZ+DU3VWQFB2zg=", + "requires": { + "is-number": "^3.0.0", + "repeat-string": "^1.6.1" + } + }, + "tough-cookie": { + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/tough-cookie/-/tough-cookie-2.5.0.tgz", + "integrity": "sha512-nlLsUzgm1kfLXSXfRZMc1KLAugd4hqJHDTvc2hDIwS3mZAfMEuMbc03SujMF+GEcpaX/qboeycw6iO8JwVv2+g==", + "requires": { + "psl": "^1.1.28", + "punycode": "^2.1.1" + } + }, + "tr46": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/tr46/-/tr46-1.0.1.tgz", + "integrity": "sha1-qLE/1r/SSJUZZ0zN5VujaTtwbQk=", + "requires": { + "punycode": "^2.1.0" + } + }, + "trim-right": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/trim-right/-/trim-right-1.0.1.tgz", + "integrity": "sha1-yy4SAwZ+DI3h9hQJS5/kVwTqYAM=" + }, + "ts-jest": { + "version": "24.0.2", + "resolved": "https://registry.npmjs.org/ts-jest/-/ts-jest-24.0.2.tgz", + "integrity": "sha512-h6ZCZiA1EQgjczxq+uGLXQlNgeg02WWJBbeT8j6nyIBRQdglqbvzDoHahTEIiS6Eor6x8mK6PfZ7brQ9Q6tzHw==", + "requires": { + "bs-logger": "0.x", + "buffer-from": "1.x", + "fast-json-stable-stringify": "2.x", + "json5": "2.x", + "make-error": "1.x", + "mkdirp": "0.x", + "resolve": "1.x", + "semver": "^5.5", + "yargs-parser": "10.x" + }, + "dependencies": { + "camelcase": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-4.1.0.tgz", + "integrity": "sha1-1UVjW+HjPFQmScaRc+Xeas+uNN0=" + }, + "json5": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/json5/-/json5-2.1.0.tgz", + "integrity": "sha512-8Mh9h6xViijj36g7Dxi+Y4S6hNGV96vcJZr/SrlHh1LR/pEn/8j/+qIBbs44YKl69Lrfctp4QD+AdWLTMqEZAQ==", + "requires": { + "minimist": "^1.2.0" + } + }, + "yargs-parser": { + "version": "10.1.0", + "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-10.1.0.tgz", + "integrity": "sha512-VCIyR1wJoEBZUqk5PA+oOBF6ypbwh5aNB3I50guxAL/quggdfs4TtNHQrSazFA3fYZ+tEqfs0zIGlv0c/rgjbQ==", + "requires": { + "camelcase": "^4.1.0" + } + } + } + }, + "tslib": { + "version": "1.9.3", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.9.3.tgz", + "integrity": "sha512-4krF8scpejhaOgqzBEcGM7yDIEfi0/8+8zDRZhNZZ2kjmHJ4hv3zCbQWxoJGz1iw5U0Jl0nma13xzHXcncMavQ==" + }, + "tsutils": { + "version": "3.10.0", + "resolved": "https://registry.npmjs.org/tsutils/-/tsutils-3.10.0.tgz", + "integrity": "sha512-q20XSMq7jutbGB8luhKKsQldRKWvyBO2BGqni3p4yq8Ys9bEP/xQw3KepKmMRt9gJ4lvQSScrihJrcKdKoSU7Q==", + "requires": { + "tslib": "^1.8.1" + } + }, + "tty-browserify": { + "version": "0.0.0", + "resolved": "https://registry.npmjs.org/tty-browserify/-/tty-browserify-0.0.0.tgz", + "integrity": "sha1-oVe6QC2iTpv5V/mqadUk7tQpAaY=" + }, + "tunnel-agent": { + "version": "0.6.0", + "resolved": "https://registry.npmjs.org/tunnel-agent/-/tunnel-agent-0.6.0.tgz", + "integrity": "sha1-J6XeoGs2sEoKmWZ3SykIaPD8QP0=", + "requires": { + "safe-buffer": "^5.0.1" + } + }, + "tweetnacl": { + "version": "0.14.5", + "resolved": "https://registry.npmjs.org/tweetnacl/-/tweetnacl-0.14.5.tgz", + "integrity": "sha1-WuaBd/GS1EViadEIr6k/+HQ/T2Q=" + }, + "type-check": { + "version": "0.3.2", + "resolved": "https://registry.npmjs.org/type-check/-/type-check-0.3.2.tgz", + "integrity": "sha1-WITKtRLPHTVeP7eE8wgEsrUg23I=", + "requires": { + "prelude-ls": "~1.1.2" + } + }, + "type-is": { + "version": "1.6.16", + "resolved": "https://registry.npmjs.org/type-is/-/type-is-1.6.16.tgz", + "integrity": "sha512-HRkVv/5qY2G6I8iab9cI7v1bOIdhm94dVjQCPFElW9W+3GeDOSHmy2EBYe4VTApuzolPcmgFTN3ftVJRKR2J9Q==", + "requires": { + "media-typer": "0.3.0", + "mime-types": "~2.1.18" + } + }, + "typedarray": { + "version": "0.0.6", + "resolved": "https://registry.npmjs.org/typedarray/-/typedarray-0.0.6.tgz", + "integrity": "sha1-hnrHTjhkGHsdPUfZlqeOxciDB3c=" + }, + "typescript": { + "version": "3.4.3", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-3.4.3.tgz", + "integrity": "sha512-FFgHdPt4T/duxx6Ndf7hwgMZZjZpB+U0nMNGVCYPq0rEzWKjEDobm4J6yb3CS7naZ0yURFqdw9Gwc7UOh/P9oQ==" + }, + "uglify-js": { + "version": "3.5.4", + "resolved": "https://registry.npmjs.org/uglify-js/-/uglify-js-3.5.4.tgz", + "integrity": "sha512-GpKo28q/7Bm5BcX9vOu4S46FwisbPbAmkkqPnGIpKvKTM96I85N6XHQV+k4I6FA2wxgLhcsSyHoNhzucwCflvA==", + "optional": true, + "requires": { + "commander": "~2.20.0", + "source-map": "~0.6.1" + }, + "dependencies": { + "commander": { + "version": "2.20.0", + "resolved": "https://registry.npmjs.org/commander/-/commander-2.20.0.tgz", + "integrity": "sha512-7j2y+40w61zy6YC2iRNpUe/NwhNyoXrYpHMrSunaMG64nRnaf96zO/KMQR4OyN/UnE5KLyEBnKHd4aG3rskjpQ==", + "optional": true + }, + "source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "optional": true + } + } + }, + "union-value": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/union-value/-/union-value-1.0.0.tgz", + "integrity": "sha1-XHHDTLW61dzr4+oM0IIHulqhrqQ=", + "requires": { + "arr-union": "^3.1.0", + "get-value": "^2.0.6", + "is-extendable": "^0.1.1", + "set-value": "^0.4.3" + }, + "dependencies": { + "extend-shallow": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", + "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", + "requires": { + "is-extendable": "^0.1.0" + } + }, + "set-value": { + "version": "0.4.3", + "resolved": "https://registry.npmjs.org/set-value/-/set-value-0.4.3.tgz", + "integrity": "sha1-fbCPnT0i3H945Trzw79GZuzfzPE=", + "requires": { + "extend-shallow": "^2.0.1", + "is-extendable": "^0.1.1", + "is-plain-object": "^2.0.1", + "to-object-path": "^0.3.0" + } + } + } + }, + "uniq": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/uniq/-/uniq-1.0.1.tgz", + "integrity": "sha1-sxxa6CVIRKOoKBVBzisEuGWnNP8=" + }, + "unique-filename": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/unique-filename/-/unique-filename-1.1.1.tgz", + "integrity": "sha512-Vmp0jIp2ln35UTXuryvjzkjGdRyf9b2lTXuSYUiPmzRcl3FDtYqAwOnTJkAngD9SWhnoJzDbTKwaOrZ+STtxNQ==", + "requires": { + "unique-slug": "^2.0.0" + } + }, + "unique-slug": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/unique-slug/-/unique-slug-2.0.1.tgz", + "integrity": "sha512-n9cU6+gITaVu7VGj1Z8feKMmfAjEAQGhwD9fE3zvpRRa0wEIx8ODYkVGfSc94M2OX00tUFV8wH3zYbm1I8mxFg==", + "requires": { + "imurmurhash": "^0.1.4" + } + }, + "unpipe": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/unpipe/-/unpipe-1.0.0.tgz", + "integrity": "sha1-sr9O6FFKrmFltIF4KdIbLvSZBOw=" + }, + "unset-value": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/unset-value/-/unset-value-1.0.0.tgz", + "integrity": "sha1-g3aHP30jNRef+x5vw6jtDfyKtVk=", + "requires": { + "has-value": "^0.3.1", + "isobject": "^3.0.0" + }, + "dependencies": { + "has-value": { + "version": "0.3.1", + "resolved": "https://registry.npmjs.org/has-value/-/has-value-0.3.1.tgz", + "integrity": "sha1-ex9YutpiyoJ+wKIHgCVlSEWZXh8=", + "requires": { + "get-value": "^2.0.3", + "has-values": "^0.1.4", + "isobject": "^2.0.0" + }, + "dependencies": { + "isobject": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/isobject/-/isobject-2.1.0.tgz", + "integrity": "sha1-8GVWEJaj8dou9GJy+BXIQNh+DIk=", + "requires": { + "isarray": "1.0.0" + } + } + } + }, + "has-values": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/has-values/-/has-values-0.1.4.tgz", + "integrity": "sha1-bWHeldkd/Km5oCCJrThL/49it3E=" + } + } + }, + "upath": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/upath/-/upath-1.1.0.tgz", + "integrity": "sha512-bzpH/oBhoS/QI/YtbkqCg6VEiPYjSZtrHQM6/QnJS6OL9pKUFLqb3aFh4Scvwm45+7iAgiMkLhSbaZxUqmrprw==" + }, + "uri-js": { + "version": "4.2.2", + "resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.2.2.tgz", + "integrity": "sha512-KY9Frmirql91X2Qgjry0Wd4Y+YTdrdZheS8TFwvkbLWf/G5KNJDCh6pKL5OZctEW4+0Baa5idK2ZQuELRwPznQ==", + "requires": { + "punycode": "^2.1.0" + } + }, + "urix": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/urix/-/urix-0.1.0.tgz", + "integrity": "sha1-2pN/emLiH+wf0Y1Js1wpNQZ6bHI=" + }, + "url": { + "version": "0.11.0", + "resolved": "https://registry.npmjs.org/url/-/url-0.11.0.tgz", + "integrity": "sha1-ODjpfPxgUh63PFJajlW/3Z4uKPE=", + "requires": { + "punycode": "1.3.2", + "querystring": "0.2.0" + }, + "dependencies": { + "punycode": { + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/punycode/-/punycode-1.3.2.tgz", + "integrity": "sha1-llOgNvt8HuQjQvIyXM7v6jkmxI0=" + } + } + }, + "url-loader": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/url-loader/-/url-loader-1.1.2.tgz", + "integrity": "sha512-dXHkKmw8FhPqu8asTc1puBfe3TehOCo2+RmOOev5suNCIYBcT626kxiWg1NBVkwc4rO8BGa7gP70W7VXuqHrjg==", + "requires": { + "loader-utils": "^1.1.0", + "mime": "^2.0.3", + "schema-utils": "^1.0.0" + }, + "dependencies": { + "mime": { + "version": "2.4.0", + "resolved": "https://registry.npmjs.org/mime/-/mime-2.4.0.tgz", + "integrity": "sha512-ikBcWwyqXQSHKtciCcctu9YfPbFYZ4+gbHEmE0Q8jzcTYQg5dHCr3g2wwAZjPoJfQVXZq6KXAjpXOTf5/cjT7w==" + } + } + }, + "url-parse": { + "version": "1.4.5", + "resolved": "https://registry.npmjs.org/url-parse/-/url-parse-1.4.5.tgz", + "integrity": "sha512-4XDvC5vZRjEpjP0L4znrWeoH8P8F0XGBlfLdABi/6oV4o8xUVbTpyrxWHxkK2bT0pSIpcjdIzSoWUhlUfawCAQ==", + "requires": { + "querystringify": "^2.0.0", + "requires-port": "^1.0.0" + } + }, + "use": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/use/-/use-3.1.1.tgz", + "integrity": "sha512-cwESVXlO3url9YWlFW/TA9cshCEhtu7IKJ/p5soJ/gGpj7vbvFrAY/eIioQ6Dw23KjZhYgiIo8HOs1nQ2vr/oQ==" + }, + "util": { + "version": "0.11.1", + "resolved": "https://registry.npmjs.org/util/-/util-0.11.1.tgz", + "integrity": "sha512-HShAsny+zS2TZfaXxD9tYj4HQGlBezXZMZuM/S5PKLLoZkShZiGk9o5CzukI1LVHZvjdvZ2Sj1aW/Ndn2NB/HQ==", + "requires": { + "inherits": "2.0.3" + } + }, + "util-deprecate": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", + "integrity": "sha1-RQ1Nyfpw3nMnYvvS1KKJgUGaDM8=" + }, + "util.promisify": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/util.promisify/-/util.promisify-1.0.0.tgz", + "integrity": "sha512-i+6qA2MPhvoKLuxnJNpXAGhg7HphQOSUq2LKMZD0m15EiskXUkMvKdF4Uui0WYeCUGea+o2cw/ZuwehtfsrNkA==", + "requires": { + "define-properties": "^1.1.2", + "object.getownpropertydescriptors": "^2.0.3" + } + }, + "utils-merge": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/utils-merge/-/utils-merge-1.0.1.tgz", + "integrity": "sha1-n5VxD1CiZ5R7LMwSR0HBAoQn5xM=" + }, + "uuid": { + "version": "3.3.2", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-3.3.2.tgz", + "integrity": "sha512-yXJmeNaw3DnnKAOKJE51sL/ZaYfWJRl1pK9dr19YFCu0ObS231AB1/LbqTKRAQ5kw8A90rA6fr4riOUpTZvQZA==" + }, + "v8-compile-cache": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/v8-compile-cache/-/v8-compile-cache-2.0.2.tgz", + "integrity": "sha512-1wFuMUIM16MDJRCrpbpuEPTUGmM5QMUg0cr3KFwra2XgOgFcPGDQHDh3CszSCD2Zewc/dh/pamNEW8CbfDebUw==" + }, + "validate-npm-package-license": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/validate-npm-package-license/-/validate-npm-package-license-3.0.4.tgz", + "integrity": "sha512-DpKm2Ui/xN7/HQKCtpZxoRWBhZ9Z0kqtygG8XCgNQ8ZlDnxuQmWhj566j8fN4Cu3/JmbhsDo7fcAJq4s9h27Ew==", + "requires": { + "spdx-correct": "^3.0.0", + "spdx-expression-parse": "^3.0.0" + } + }, + "vary": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/vary/-/vary-1.1.2.tgz", + "integrity": "sha1-IpnwLG3tMNSllhsLn3RSShj2NPw=" + }, + "verror": { + "version": "1.10.0", + "resolved": "https://registry.npmjs.org/verror/-/verror-1.10.0.tgz", + "integrity": "sha1-OhBcoXBTr1XW4nDB+CiGguGNpAA=", + "requires": { + "assert-plus": "^1.0.0", + "core-util-is": "1.0.2", + "extsprintf": "^1.2.0" + } + }, + "vm-browserify": { + "version": "0.0.4", + "resolved": "https://registry.npmjs.org/vm-browserify/-/vm-browserify-0.0.4.tgz", + "integrity": "sha1-XX6kW7755Kb/ZflUOOCofDV9WnM=", + "requires": { + "indexof": "0.0.1" + } + }, + "w3c-hr-time": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/w3c-hr-time/-/w3c-hr-time-1.0.1.tgz", + "integrity": "sha1-gqwr/2PZUOqeMYmlimViX+3xkEU=", + "requires": { + "browser-process-hrtime": "^0.1.2" + } + }, + "walker": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/walker/-/walker-1.0.7.tgz", + "integrity": "sha1-L3+bj9ENZ3JisYqITijRlhjgKPs=", + "requires": { + "makeerror": "1.0.x" + } + }, + "watchpack": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/watchpack/-/watchpack-1.6.0.tgz", + "integrity": "sha512-i6dHe3EyLjMmDlU1/bGQpEw25XSjkJULPuAVKCbNRefQVq48yXKUpwg538F7AZTf9kyr57zj++pQFltUa5H7yA==", + "requires": { + "chokidar": "^2.0.2", + "graceful-fs": "^4.1.2", + "neo-async": "^2.5.0" + } + }, + "wbuf": { + "version": "1.7.3", + "resolved": "https://registry.npmjs.org/wbuf/-/wbuf-1.7.3.tgz", + "integrity": "sha512-O84QOnr0icsbFGLS0O3bI5FswxzRr8/gHwWkDlQFskhSPryQXvrTMxjxGP4+iWYoauLoBvfDpkrOauZ+0iZpDA==", + "requires": { + "minimalistic-assert": "^1.0.0" + } + }, + "webidl-conversions": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-4.0.2.tgz", + "integrity": "sha512-YQ+BmxuTgd6UXZW3+ICGfyqRyHXVlD5GtQr5+qjiNW7bF0cqrzX500HVXPBOvgXb5YnzDd+h0zqyv61KUD7+Sg==" + }, + "webpack": { + "version": "4.29.6", + "resolved": "https://registry.npmjs.org/webpack/-/webpack-4.29.6.tgz", + "integrity": "sha512-MwBwpiE1BQpMDkbnUUaW6K8RFZjljJHArC6tWQJoFm0oQtfoSebtg4Y7/QHnJ/SddtjYLHaKGX64CFjG5rehJw==", + "requires": { + "@webassemblyjs/ast": "1.8.5", + "@webassemblyjs/helper-module-context": "1.8.5", + "@webassemblyjs/wasm-edit": "1.8.5", + "@webassemblyjs/wasm-parser": "1.8.5", + "acorn": "^6.0.5", + "acorn-dynamic-import": "^4.0.0", + "ajv": "^6.1.0", + "ajv-keywords": "^3.1.0", + "chrome-trace-event": "^1.0.0", + "enhanced-resolve": "^4.1.0", + "eslint-scope": "^4.0.0", + "json-parse-better-errors": "^1.0.2", + "loader-runner": "^2.3.0", + "loader-utils": "^1.1.0", + "memory-fs": "~0.4.1", + "micromatch": "^3.1.8", + "mkdirp": "~0.5.0", + "neo-async": "^2.5.0", + "node-libs-browser": "^2.0.0", + "schema-utils": "^1.0.0", + "tapable": "^1.1.0", + "terser-webpack-plugin": "^1.1.0", + "watchpack": "^1.5.0", + "webpack-sources": "^1.3.0" + } + }, + "webpack-cli": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/webpack-cli/-/webpack-cli-3.3.0.tgz", + "integrity": "sha512-t1M7G4z5FhHKJ92WRKwZ1rtvi7rHc0NZoZRbSkol0YKl4HvcC8+DsmGDmK7MmZxHSAetHagiOsjOB6MmzC2TUw==", + "requires": { + "chalk": "^2.4.1", + "cross-spawn": "^6.0.5", + "enhanced-resolve": "^4.1.0", + "findup-sync": "^2.0.0", + "global-modules": "^1.0.0", + "import-local": "^2.0.0", + "interpret": "^1.1.0", + "loader-utils": "^1.1.0", + "supports-color": "^5.5.0", + "v8-compile-cache": "^2.0.2", + "yargs": "^12.0.5" + } + }, + "webpack-dev-middleware": { + "version": "3.6.2", + "resolved": "https://registry.npmjs.org/webpack-dev-middleware/-/webpack-dev-middleware-3.6.2.tgz", + "integrity": "sha512-A47I5SX60IkHrMmZUlB0ZKSWi29TZTcPz7cha1Z75yYOsgWh/1AcPmQEbC8ZIbU3A1ytSv1PMU0PyPz2Lmz2jg==", + "requires": { + "memory-fs": "^0.4.1", + "mime": "^2.3.1", + "range-parser": "^1.0.3", + "webpack-log": "^2.0.0" + }, + "dependencies": { + "mime": { + "version": "2.4.2", + "resolved": "https://registry.npmjs.org/mime/-/mime-2.4.2.tgz", + "integrity": "sha512-zJBfZDkwRu+j3Pdd2aHsR5GfH2jIWhmL1ZzBoc+X+3JEti2hbArWcyJ+1laC1D2/U/W1a/+Cegj0/OnEU2ybjg==" + }, + "webpack-log": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/webpack-log/-/webpack-log-2.0.0.tgz", + "integrity": "sha512-cX8G2vR/85UYG59FgkoMamwHUIkSSlV3bBMRsbxVXVUk2j6NleCKjQ/WE9eYg9WY4w25O9w8wKP4rzNZFmUcUg==", + "requires": { + "ansi-colors": "^3.0.0", + "uuid": "^3.3.2" + } + } + } + }, + "webpack-dev-server": { + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/webpack-dev-server/-/webpack-dev-server-3.3.1.tgz", + "integrity": "sha512-jY09LikOyGZrxVTXK0mgIq9y2IhCoJ05848dKZqX1gAGLU1YDqgpOT71+W53JH/wI4v6ky4hm+KvSyW14JEs5A==", + "requires": { + "ansi-html": "0.0.7", + "bonjour": "^3.5.0", + "chokidar": "^2.1.5", + "compression": "^1.7.4", + "connect-history-api-fallback": "^1.6.0", + "debug": "^4.1.1", + "del": "^4.1.0", + "express": "^4.16.4", + "html-entities": "^1.2.1", + "http-proxy-middleware": "^0.19.1", + "import-local": "^2.0.0", + "internal-ip": "^4.2.0", + "ip": "^1.1.5", + "killable": "^1.0.1", + "loglevel": "^1.6.1", + "opn": "^5.5.0", + "portfinder": "^1.0.20", + "schema-utils": "^1.0.0", + "selfsigned": "^1.10.4", + "semver": "^6.0.0", + "serve-index": "^1.9.1", + "sockjs": "0.3.19", + "sockjs-client": "1.3.0", + "spdy": "^4.0.0", + "strip-ansi": "^3.0.1", + "supports-color": "^6.1.0", + "url": "^0.11.0", + "webpack-dev-middleware": "^3.6.2", + "webpack-log": "^2.0.0", + "yargs": "12.0.5" + }, + "dependencies": { + "ansi-regex": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-2.1.1.tgz", + "integrity": "sha1-w7M6te42DYbg5ijwRorn7yfWVN8=" + }, + "chokidar": { + "version": "2.1.5", + "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-2.1.5.tgz", + "integrity": "sha512-i0TprVWp+Kj4WRPtInjexJ8Q+BqTE909VpH8xVhXrJkoc5QC8VO9TryGOqTr+2hljzc1sC62t22h5tZePodM/A==", + "requires": { + "anymatch": "^2.0.0", + "async-each": "^1.0.1", + "braces": "^2.3.2", + "fsevents": "^1.2.7", + "glob-parent": "^3.1.0", + "inherits": "^2.0.3", + "is-binary-path": "^1.0.0", + "is-glob": "^4.0.0", + "normalize-path": "^3.0.0", + "path-is-absolute": "^1.0.0", + "readdirp": "^2.2.1", + "upath": "^1.1.1" + } + }, + "debug": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.1.1.tgz", + "integrity": "sha512-pYAIzeRo8J6KPEaJ0VWOh5Pzkbw/RetuzehGM7QRRX5he4fPHx2rdKMB256ehJCkX+XRQm16eZLqLNS8RSZXZw==", + "requires": { + "ms": "^2.1.1" + } + }, + "ms": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.1.tgz", + "integrity": "sha512-tgp+dl5cGk28utYktBsrFqA7HKgrhgPsg6Z/EfhWI4gl1Hwq8B/GmY/0oXZ6nF8hDVesS/FpnYaD/kOWhYQvyg==" + }, + "semver": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.0.0.tgz", + "integrity": "sha512-0UewU+9rFapKFnlbirLi3byoOuhrSsli/z/ihNnvM24vgF+8sNBiI1LZPBSH9wJKUwaUbw+s3hToDLCXkrghrQ==" + }, + "strip-ansi": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-3.0.1.tgz", + "integrity": "sha1-ajhfuIU9lS1f8F0Oiq+UJ43GPc8=", + "requires": { + "ansi-regex": "^2.0.0" + } + }, + "supports-color": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-6.1.0.tgz", + "integrity": "sha512-qe1jfm1Mg7Nq/NSh6XE24gPXROEVsWHxC1LIx//XNlD9iw7YZQGjZNjYN7xGaEG6iKdA8EtNFW6R0gjnVXp+wQ==", + "requires": { + "has-flag": "^3.0.0" + } + }, + "upath": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/upath/-/upath-1.1.2.tgz", + "integrity": "sha512-kXpym8nmDmlCBr7nKdIx8P2jNBa+pBpIUFRnKJ4dr8htyYGJFokkr2ZvERRtUN+9SY+JqXouNgUPtv6JQva/2Q==" + }, + "webpack-log": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/webpack-log/-/webpack-log-2.0.0.tgz", + "integrity": "sha512-cX8G2vR/85UYG59FgkoMamwHUIkSSlV3bBMRsbxVXVUk2j6NleCKjQ/WE9eYg9WY4w25O9w8wKP4rzNZFmUcUg==", + "requires": { + "ansi-colors": "^3.0.0", + "uuid": "^3.3.2" + } + } + } + }, + "webpack-log": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/webpack-log/-/webpack-log-1.2.0.tgz", + "integrity": "sha512-U9AnICnu50HXtiqiDxuli5gLB5PGBo7VvcHx36jRZHwK4vzOYLbImqT4lwWwoMHdQWwEKw736fCHEekokTEKHA==", + "requires": { + "chalk": "^2.1.0", + "log-symbols": "^2.1.0", + "loglevelnext": "^1.0.1", + "uuid": "^3.1.0" + } + }, + "webpack-sources": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/webpack-sources/-/webpack-sources-1.3.0.tgz", + "integrity": "sha512-OiVgSrbGu7NEnEvQJJgdSFPl2qWKkWq5lHMhgiToIiN9w34EBnjYzSYs+VbL5KoYiLNtFFa7BZIKxRED3I32pA==", + "requires": { + "source-list-map": "^2.0.0", + "source-map": "~0.6.1" + }, + "dependencies": { + "source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==" + } + } + }, + "websocket-driver": { + "version": "0.7.0", + "resolved": "https://registry.npmjs.org/websocket-driver/-/websocket-driver-0.7.0.tgz", + "integrity": "sha1-DK+dLXVdk67gSdS90NP+LMoqJOs=", + "requires": { + "http-parser-js": ">=0.4.0", + "websocket-extensions": ">=0.1.1" + } + }, + "websocket-extensions": { + "version": "0.1.3", + "resolved": "https://registry.npmjs.org/websocket-extensions/-/websocket-extensions-0.1.3.tgz", + "integrity": "sha512-nqHUnMXmBzT0w570r2JpJxfiSD1IzoI+HGVdd3aZ0yNi3ngvQ4jv1dtHt5VGxfI2yj5yqImPhOK4vmIh2xMbGg==" + }, + "whatwg-encoding": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/whatwg-encoding/-/whatwg-encoding-1.0.5.tgz", + "integrity": "sha512-b5lim54JOPN9HtzvK9HFXvBma/rnfFeqsic0hSpjtDbVxR3dJKLc+KB4V6GgiGOvl7CY/KNh8rxSo9DKQrnUEw==", + "requires": { + "iconv-lite": "0.4.24" + } + }, + "whatwg-mimetype": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/whatwg-mimetype/-/whatwg-mimetype-2.3.0.tgz", + "integrity": "sha512-M4yMwr6mAnQz76TbJm914+gPpB/nCwvZbJU28cUD6dR004SAxDLOOSUaB1JDRqLtaOV/vi0IC5lEAGFgrjGv/g==" + }, + "whatwg-url": { + "version": "6.5.0", + "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-6.5.0.tgz", + "integrity": "sha512-rhRZRqx/TLJQWUpQ6bmrt2UV4f0HCQ463yQuONJqC6fO2VoEb1pTYddbe59SkYq87aoM5A3bdhMZiUiVws+fzQ==", + "requires": { + "lodash.sortby": "^4.7.0", + "tr46": "^1.0.1", + "webidl-conversions": "^4.0.2" + } + }, + "which": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/which/-/which-1.3.1.tgz", + "integrity": "sha512-HxJdYWq1MTIQbJ3nw0cqssHoTNU267KlrDuGZ1WYlxDStUtKUhOaJmh112/TZmHxxUfuJqPXSOm7tDyas0OSIQ==", + "requires": { + "isexe": "^2.0.0" + } + }, + "which-module": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/which-module/-/which-module-2.0.0.tgz", + "integrity": "sha1-2e8H3Od7mQK4o6j6SzHD4/fm6Ho=" + }, + "wordwrap": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/wordwrap/-/wordwrap-1.0.0.tgz", + "integrity": "sha1-J1hIEIkUVqQXHI0CJkQa3pDLyus=" + }, + "worker-farm": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/worker-farm/-/worker-farm-1.6.0.tgz", + "integrity": "sha512-6w+3tHbM87WnSWnENBUvA2pxJPLhQUg5LKwUQHq3r+XPhIM+Gh2R5ycbwPCyuGbNg+lPgdcnQUhuC02kJCvffQ==", + "requires": { + "errno": "~0.1.7" + } + }, + "wrap-ansi": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-2.1.0.tgz", + "integrity": "sha1-2Pw9KE3QV5T+hJc8rs3Rz4JP3YU=", + "requires": { + "string-width": "^1.0.1", + "strip-ansi": "^3.0.1" + }, + "dependencies": { + "ansi-regex": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-2.1.1.tgz", + "integrity": "sha1-w7M6te42DYbg5ijwRorn7yfWVN8=" + }, + "is-fullwidth-code-point": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-1.0.0.tgz", + "integrity": "sha1-754xOG8DGn8NZDr4L95QxFfvAMs=", + "requires": { + "number-is-nan": "^1.0.0" + } + }, + "string-width": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-1.0.2.tgz", + "integrity": "sha1-EYvfW4zcUaKn5w0hHgfisLmxB9M=", + "requires": { + "code-point-at": "^1.0.0", + "is-fullwidth-code-point": "^1.0.0", + "strip-ansi": "^3.0.0" + } + }, + "strip-ansi": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-3.0.1.tgz", + "integrity": "sha1-ajhfuIU9lS1f8F0Oiq+UJ43GPc8=", + "requires": { + "ansi-regex": "^2.0.0" + } + } + } + }, + "wrappy": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", + "integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8=" + }, + "write": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/write/-/write-1.0.3.tgz", + "integrity": "sha512-/lg70HAjtkUgWPVZhZcm+T4hkL8Zbtp1nFNOn3lRrxnlv50SRBv7cR7RqR+GMsd3hUXy9hWBo4CHTbFTcOYwig==", + "requires": { + "mkdirp": "^0.5.1" + } + }, + "write-file-atomic": { + "version": "2.4.1", + "resolved": "https://registry.npmjs.org/write-file-atomic/-/write-file-atomic-2.4.1.tgz", + "integrity": "sha512-TGHFeZEZMnv+gBFRfjAcxL5bPHrsGKtnb4qsFAws7/vlh+QfwAaySIw4AXP9ZskTTh5GWu3FLuJhsWVdiJPGvg==", + "requires": { + "graceful-fs": "^4.1.11", + "imurmurhash": "^0.1.4", + "signal-exit": "^3.0.2" + } + }, + "ws": { + "version": "5.2.2", + "resolved": "https://registry.npmjs.org/ws/-/ws-5.2.2.tgz", + "integrity": "sha512-jaHFD6PFv6UgoIVda6qZllptQsMlDEJkTQcybzzXDYM1XO9Y8em691FGMPmM46WGyLU4z9KMgQN+qrux/nhlHA==", + "requires": { + "async-limiter": "~1.0.0" + } + }, + "xml-name-validator": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/xml-name-validator/-/xml-name-validator-3.0.0.tgz", + "integrity": "sha512-A5CUptxDsvxKJEU3yO6DuWBSJz/qizqzJKOMIfUJHETbBw/sFaDxgd6fxm1ewUaM0jZ444Fc5vC5ROYurg/4Pw==" + }, + "xtend": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/xtend/-/xtend-4.0.1.tgz", + "integrity": "sha1-pcbVMr5lbiPbgg77lDofBJmNY68=" + }, + "y18n": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/y18n/-/y18n-4.0.0.tgz", + "integrity": "sha512-r9S/ZyXu/Xu9q1tYlpsLIsa3EeLXXk0VwlxqTcFRfg9EhMW+17kbt9G0NrgCmhGb5vT2hyhJZLfDGx+7+5Uj/w==" + }, + "yallist": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-3.0.3.tgz", + "integrity": "sha512-S+Zk8DEWE6oKpV+vI3qWkaK+jSbIK86pCwe2IF/xwIpQ8jEuxpw9NyaGjmp9+BoJv5FV2piqCDcoCtStppiq2A==" + }, + "yargs": { + "version": "12.0.5", + "resolved": "https://registry.npmjs.org/yargs/-/yargs-12.0.5.tgz", + "integrity": "sha512-Lhz8TLaYnxq/2ObqHDql8dX8CJi97oHxrjUcYtzKbbykPtVW9WB+poxI+NM2UIzsMgNCZTIf0AQwsjK5yMAqZw==", + "requires": { + "cliui": "^4.0.0", + "decamelize": "^1.2.0", + "find-up": "^3.0.0", + "get-caller-file": "^1.0.1", + "os-locale": "^3.0.0", + "require-directory": "^2.1.1", + "require-main-filename": "^1.0.1", + "set-blocking": "^2.0.0", + "string-width": "^2.0.0", + "which-module": "^2.0.0", + "y18n": "^3.2.1 || ^4.0.0", + "yargs-parser": "^11.1.1" + } + }, + "yargs-parser": { + "version": "11.1.1", + "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-11.1.1.tgz", + "integrity": "sha512-C6kB/WJDiaxONLJQnF8ccx9SEeoTTLek8RVbaOIsrAUS8VrBEXfmeSnCZxygc+XC2sNMBIwOOnfcxiynjHsVSQ==", + "requires": { + "camelcase": "^5.0.0", + "decamelize": "^1.2.0" + } + } + } +} diff --git a/visual_console_client/package.json b/visual_console_client/package.json new file mode 100644 index 0000000000..d049a39b10 --- /dev/null +++ b/visual_console_client/package.json @@ -0,0 +1,50 @@ +{ + "name": "pandora-fms-visual-console", + "version": "1.0.0", + "description": "Visual Console", + "scripts": { + "build": "BUILD_PATH=\"pandora_console/include/visual-console-client\" NODE_ENV=production webpack", + "build:dev": "BUILD_PATH=\"pandora_console/include/visual-console-client\" NODE_ENV=development webpack", + "build:watch": "npm run build -- --watch", + "format": "prettier", + "lint": "eslint \"src/**/*.ts\"", + "start": "webpack-dev-server --color --mode=development", + "test": "jest", + "test:watch": "jest --watch" + }, + "repository": { + "type": "git", + "url": "git+https://github.com/pandorafms/pandorafms.git" + }, + "author": "Alejandro Gallardo Escobar ", + "license": "GPL2", + "private": true, + "bugs": { + "url": "https://github.com/pandorafms/pandorafms/issues" + }, + "homepage": "https://github.com/pandorafms/pandorafms#readme", + "dependencies": { + "@types/d3-shape": "^1.3.1", + "@types/jest": "^24.0.11", + "@typescript-eslint/eslint-plugin": "^1.6.0", + "@typescript-eslint/parser": "^1.6.0", + "awesome-typescript-loader": "^5.2.1", + "clean-webpack-plugin": "^2.0.1", + "css-loader": "^2.1.1", + "d3-shape": "^1.3.5", + "eslint": "^5.16.0", + "eslint-config-prettier": "^4.1.0", + "eslint-plugin-prettier": "^3.0.1", + "file-loader": "^3.0.1", + "jest": "^24.7.1", + "mini-css-extract-plugin": "^0.5.0", + "postcss-loader": "^3.0.0", + "prettier": "^1.16.1", + "ts-jest": "^24.0.2", + "typescript": "^3.4.3", + "url-loader": "^1.1.2", + "webpack": "^4.29.6", + "webpack-cli": "^3.3.0", + "webpack-dev-server": "^3.3.1" + } +} diff --git a/visual_console_client/playground/index.html b/visual_console_client/playground/index.html new file mode 100644 index 0000000000..a42ddd5907 --- /dev/null +++ b/visual_console_client/playground/index.html @@ -0,0 +1,365 @@ + + + + + + Visual Console Sandbox + + + + + +
+ + + + diff --git a/visual_console_client/src/Item.ts b/visual_console_client/src/Item.ts new file mode 100644 index 0000000000..94dab962b1 --- /dev/null +++ b/visual_console_client/src/Item.ts @@ -0,0 +1,545 @@ +import { Position, Size, UnknownObject, WithModuleProps } from "./types"; +import { + sizePropsDecoder, + positionPropsDecoder, + parseIntOr, + parseBoolean, + notEmptyStringOr, + replaceMacros, + humanDate, + humanTime +} from "./lib"; +import TypedEvent, { Listener, Disposable } from "./TypedEvent"; + +// Enum: https://www.typescriptlang.org/docs/handbook/enums.html. +export const enum ItemType { + STATIC_GRAPH = 0, + MODULE_GRAPH = 1, + SIMPLE_VALUE = 2, + PERCENTILE_BAR = 3, + LABEL = 4, + ICON = 5, + SIMPLE_VALUE_MAX = 6, + SIMPLE_VALUE_MIN = 7, + SIMPLE_VALUE_AVG = 8, + PERCENTILE_BUBBLE = 9, + SERVICE = 10, + GROUP_ITEM = 11, + BOX_ITEM = 12, + LINE_ITEM = 13, + AUTO_SLA_GRAPH = 14, + CIRCULAR_PROGRESS_BAR = 15, + CIRCULAR_INTERIOR_PROGRESS_BAR = 16, + DONUT_GRAPH = 17, + BARS_GRAPH = 18, + CLOCK = 19, + COLOR_CLOUD = 20 +} + +// Base item properties. This interface should be extended by the item implementations. +export interface ItemProps extends Position, Size { + readonly id: number; + readonly type: ItemType; + label: string | null; + labelPosition: "up" | "right" | "down" | "left"; + isLinkEnabled: boolean; + link: string | null; + isOnTop: boolean; + parentId: number | null; + aclGroupId: number | null; +} + +// FIXME: Fix type compatibility. +export interface ItemClickEvent { + // data: Props; + data: UnknownObject; + nativeEvent: Event; +} + +// FIXME: Fix type compatibility. +export interface ItemRemoveEvent { + // data: Props; + data: UnknownObject; +} + +/** + * Extract a valid enum value from a raw label positi9on value. + * @param labelPosition Raw value. + */ +const parseLabelPosition = ( + labelPosition: any // eslint-disable-line @typescript-eslint/no-explicit-any +): ItemProps["labelPosition"] => { + switch (labelPosition) { + case "up": + case "right": + case "down": + case "left": + return labelPosition; + default: + return "down"; + } +}; + +/** + * Build a valid typed object from a raw object. + * This will allow us to ensure the type safety. + * + * @param data Raw object. + * @return An object representing the item props. + * @throws Will throw a TypeError if some property + * is missing from the raw object or have an invalid type. + */ +export function itemBasePropsDecoder(data: UnknownObject): ItemProps | never { + if (data.id == null || isNaN(parseInt(data.id))) { + throw new TypeError("invalid id."); + } + if (data.type == null || isNaN(parseInt(data.type))) { + throw new TypeError("invalid type."); + } + + return { + id: parseInt(data.id), + type: parseInt(data.type), + label: notEmptyStringOr(data.label, null), + labelPosition: parseLabelPosition(data.labelPosition), + isLinkEnabled: parseBoolean(data.isLinkEnabled), + link: notEmptyStringOr(data.link, null), + isOnTop: parseBoolean(data.isOnTop), + parentId: parseIntOr(data.parentId, null), + aclGroupId: parseIntOr(data.aclGroupId, null), + ...sizePropsDecoder(data), // Object spread. It will merge the properties of the two objects. + ...positionPropsDecoder(data) // Object spread. It will merge the properties of the two objects. + }; +} + +/** + * Base class of the visual console items. Should be extended to use its capabilities. + */ +abstract class VisualConsoleItem { + // Properties of the item. + private itemProps: Props; + // Reference to the DOM element which will contain the item. + public elementRef: HTMLElement; + public readonly labelElementRef: HTMLElement; + // Reference to the DOM element which will contain the view of the item which extends this class. + protected readonly childElementRef: HTMLElement; + // Event manager for click events. + private readonly clickEventManager = new TypedEvent>(); + // Event manager for remove events. + private readonly removeEventManager = new TypedEvent< + ItemRemoveEvent + >(); + // List of references to clean the event listeners. + private readonly disposables: Disposable[] = []; + + /** + * To create a new element which will be inside the item box. + * @return Item. + */ + protected abstract createDomElement(): HTMLElement; + + public constructor(props: Props) { + this.itemProps = props; + + /* + * Get a HTMLElement which represents the container box + * of the Visual Console item. This element will manage + * all the common things like click events, show a border + * when hovered, etc. + */ + this.elementRef = this.createContainerDomElement(); + this.labelElementRef = this.createLabelDomElement(); + + /* + * Get a HTMLElement which represents the custom view + * of the Visual Console item. This element will be + * different depending on the item implementation. + */ + this.childElementRef = this.createDomElement(); + + // Insert the elements into the container. + this.elementRef.append(this.childElementRef, this.labelElementRef); + + // Resize element. + this.resizeElement(props.width, props.height); + // Set label position. + this.changeLabelPosition(props.labelPosition); + } + + /** + * To create a new box for the visual console item. + * @return Item box. + */ + private createContainerDomElement(): HTMLElement { + let box; + if (this.props.isLinkEnabled) { + box = document.createElement("a"); + box as HTMLAnchorElement; + if (this.props.link) box.href = this.props.link; + } else { + box = document.createElement("div"); + box as HTMLDivElement; + } + + box.className = "visual-console-item"; + box.style.zIndex = this.props.isOnTop ? "2" : "1"; + box.style.left = `${this.props.x}px`; + box.style.top = `${this.props.y}px`; + box.onclick = e => + this.clickEventManager.emit({ data: this.props, nativeEvent: e }); + + return box; + } + + /** + * To create a new label for the visual console item. + * @return Item label. + */ + protected createLabelDomElement(): HTMLElement { + const element = document.createElement("div"); + element.className = "visual-console-item-label"; + // Add the label if it exists. + const label = this.getLabelWithMacrosReplaced(); + if (label.length > 0) { + // Ugly table we need to use to replicate the legacy style. + const table = document.createElement("table"); + const row = document.createElement("tr"); + const emptyRow1 = document.createElement("tr"); + const emptyRow2 = document.createElement("tr"); + const cell = document.createElement("td"); + + cell.innerHTML = label; + row.append(cell); + table.append(emptyRow1, row, emptyRow2); + table.style.textAlign = "center"; + + // Change the table size depending on its position. + switch (this.props.labelPosition) { + case "up": + case "down": + if (this.props.width > 0) { + table.style.width = `${this.props.width}px`; + table.style.height = null; + } + break; + case "left": + case "right": + if (this.props.height > 0) { + table.style.width = null; + table.style.height = `${this.props.height}px`; + } + break; + } + + // element.innerHTML = this.props.label; + element.append(table); + } + + return element; + } + + /** + * Return the label stored into the props with some macros replaced. + */ + protected getLabelWithMacrosReplaced(): string { + // We assert that the props may have some needed properties. + const props = this.props as Partial; + + return replaceMacros( + [ + { + macro: "_date_", + value: humanDate(new Date()) + }, + { + macro: "_time_", + value: humanTime(new Date()) + }, + { + macro: "_agent_", + value: props.agentAlias != null ? props.agentAlias : "" + }, + { + macro: "_agentdescription_", + value: props.agentDescription != null ? props.agentDescription : "" + }, + { + macro: "_address_", + value: props.agentAddress != null ? props.agentAddress : "" + }, + { + macro: "_module_", + value: props.moduleName != null ? props.moduleName : "" + }, + { + macro: "_moduledescription_", + value: props.moduleDescription != null ? props.moduleDescription : "" + } + ], + this.props.label || "" + ); + } + + /** + * To update the content element. + * @return Item. + */ + protected updateDomElement(element: HTMLElement): void { + element.innerHTML = this.createDomElement().innerHTML; + } + + /** + * Public accessor of the `props` property. + * @return Properties. + */ + public get props(): Props { + return { ...this.itemProps }; // Return a copy. + } + + /** + * Public setter of the `props` property. + * If the new props are different enough than the + * stored props, a render would be fired. + * @param newProps + */ + public set props(newProps: Props) { + const prevProps = this.props; + // Update the internal props. + this.itemProps = newProps; + + // From this point, things which rely on this.props can access to the changes. + + // Check if we should re-render. + if (this.shouldBeUpdated(prevProps, newProps)) this.render(prevProps); + } + + /** + * To compare the previous and the new props and returns a boolean value + * in case the difference is meaningfull enough to perform DOM changes. + * + * Here, the only comparision is done by reference. + * + * Override this function to perform a different comparision depending on the item needs. + * + * @param prevProps + * @param newProps + * @return Whether the difference is meaningful enough to perform DOM changes or not. + */ + protected shouldBeUpdated(prevProps: Props, newProps: Props): boolean { + return prevProps !== newProps; + } + + /** + * To recreate or update the HTMLElement which represents the item into the DOM. + * @param prevProps If exists it will be used to only perform DOM updates instead of a full replace. + */ + public render(prevProps: Props | null = null): void { + this.updateDomElement(this.childElementRef); + + // Move box. + if (!prevProps || this.positionChanged(prevProps, this.props)) { + this.moveElement(this.props.x, this.props.y); + } + // Resize box. + if (!prevProps || this.sizeChanged(prevProps, this.props)) { + this.resizeElement(this.props.width, this.props.height); + } + // Change label. + const oldLabelHtml = this.labelElementRef.innerHTML; + const newLabelHtml = this.createLabelDomElement().innerHTML; + if (oldLabelHtml !== newLabelHtml) { + this.labelElementRef.innerHTML = newLabelHtml; + } + // Change label position. + if (!prevProps || prevProps.labelPosition !== this.props.labelPosition) { + this.changeLabelPosition(this.props.labelPosition); + } + // Change link. + if ( + prevProps && + (prevProps.isLinkEnabled !== this.props.isLinkEnabled || + (this.props.isLinkEnabled && prevProps.link !== this.props.link)) + ) { + const container = this.createContainerDomElement(); + container.innerHTML = this.elementRef.innerHTML; + + if (this.elementRef.parentNode !== null) { + this.elementRef.parentNode.replaceChild(container, this.elementRef); + } + + // Changed the reference to the main element. It's ugly, but needed. + this.elementRef = container; + } + } + + /** + * To remove the event listeners and the elements from the DOM. + */ + public remove(): void { + // Call the remove event. + this.removeEventManager.emit({ data: this.props }); + // Event listeners. + this.disposables.forEach(disposable => { + try { + disposable.dispose(); + } catch (ignored) {} // eslint-disable-line no-empty + }); + // VisualConsoleItem DOM element. + this.elementRef.remove(); + } + + /** + * Compare the previous and the new position and return + * a boolean value in case the position changed. + * @param prevPosition + * @param newPosition + * @return Whether the position changed or not. + */ + protected positionChanged( + prevPosition: Position, + newPosition: Position + ): boolean { + return prevPosition.x !== newPosition.x || prevPosition.y !== newPosition.y; + } + + /** + * Move the label around the item content. + * @param position Label position. + */ + protected changeLabelPosition(position: Props["labelPosition"]): void { + switch (position) { + case "up": + this.elementRef.style.flexDirection = "column-reverse"; + break; + case "left": + this.elementRef.style.flexDirection = "row-reverse"; + break; + case "right": + this.elementRef.style.flexDirection = "row"; + break; + case "down": + default: + this.elementRef.style.flexDirection = "column"; + break; + } + + // Ugly table to show the label as its legacy counterpart. + const tables = this.labelElementRef.getElementsByTagName("table"); + const table = tables.length > 0 ? tables.item(0) : null; + // Change the table size depending on its position. + if (table) { + switch (this.props.labelPosition) { + case "up": + case "down": + if (this.props.width > 0) { + table.style.width = `${this.props.width}px`; + table.style.height = null; + } + break; + case "left": + case "right": + if (this.props.height > 0) { + table.style.width = null; + table.style.height = `${this.props.height}px`; + } + break; + } + } + } + + /** + * Move the DOM container. + * @param x Horizontal axis position. + * @param y Vertical axis position. + */ + protected moveElement(x: number, y: number): void { + this.elementRef.style.left = `${x}px`; + this.elementRef.style.top = `${y}px`; + } + + /** + * Update the position into the properties and move the DOM container. + * @param x Horizontal axis position. + * @param y Vertical axis position. + */ + public move(x: number, y: number): void { + this.moveElement(x, y); + this.itemProps = { + ...this.props, // Object spread: http://es6-features.org/#SpreadOperator + x, + y + }; + } + + /** + * Compare the previous and the new size and return + * a boolean value in case the size changed. + * @param prevSize + * @param newSize + * @return Whether the size changed or not. + */ + protected sizeChanged(prevSize: Size, newSize: Size): boolean { + return ( + prevSize.width !== newSize.width || prevSize.height !== newSize.height + ); + } + + /** + * Resize the DOM content container. + * @param width + * @param height + */ + protected resizeElement(width: number, height: number): void { + // The most valuable size is the content size. + this.childElementRef.style.width = width > 0 ? `${width}px` : null; + this.childElementRef.style.height = height > 0 ? `${height}px` : null; + } + + /** + * Update the size into the properties and resize the DOM container. + * @param width + * @param height + */ + public resize(width: number, height: number): void { + this.resizeElement(width, height); + this.itemProps = { + ...this.props, // Object spread: http://es6-features.org/#SpreadOperator + width, + height + }; + } + + /** + * To add an event handler to the click of the linked visual console elements. + * @param listener Function which is going to be executed when a linked console is clicked. + */ + public onClick(listener: Listener>): Disposable { + /* + * The '.on' function returns a function which will clean the event + * listener when executed. We store all the 'dispose' functions to + * call them when the item should be cleared. + */ + const disposable = this.clickEventManager.on(listener); + this.disposables.push(disposable); + + return disposable; + } + + /** + * To add an event handler to the removal of the item. + * @param listener Function which is going to be executed when a item is removed. + */ + public onRemove(listener: Listener>): Disposable { + /* + * The '.on' function returns a function which will clean the event + * listener when executed. We store all the 'dispose' functions to + * call them when the item should be cleared. + */ + const disposable = this.removeEventManager.on(listener); + this.disposables.push(disposable); + + return disposable; + } +} + +export default VisualConsoleItem; diff --git a/visual_console_client/src/TypedEvent.ts b/visual_console_client/src/TypedEvent.ts new file mode 100644 index 0000000000..e199470f6c --- /dev/null +++ b/visual_console_client/src/TypedEvent.ts @@ -0,0 +1,40 @@ +export interface Listener { + (event: T): void; +} + +export interface Disposable { + dispose: () => void; +} + +/** passes through events as they happen. You will not get events from before you start listening */ +export default class TypedEvent { + private listeners: Listener[] = []; + private listenersOncer: Listener[] = []; + + public on = (listener: Listener): Disposable => { + this.listeners.push(listener); + return { + dispose: () => this.off(listener) + }; + }; + + public once = (listener: Listener): void => { + this.listenersOncer.push(listener); + }; + + public off = (listener: Listener): void => { + const callbackIndex = this.listeners.indexOf(listener); + if (callbackIndex > -1) this.listeners.splice(callbackIndex, 1); + }; + + public emit = (event: T): void => { + /** Update any general listeners */ + this.listeners.forEach(listener => listener(event)); + + /** Clear the `once` queue */ + this.listenersOncer.forEach(listener => listener(event)); + this.listenersOncer = []; + }; + + public pipe = (te: TypedEvent): Disposable => this.on(e => te.emit(e)); +} diff --git a/visual_console_client/src/VisualConsole.ts b/visual_console_client/src/VisualConsole.ts new file mode 100644 index 0000000000..ffaed36fcf --- /dev/null +++ b/visual_console_client/src/VisualConsole.ts @@ -0,0 +1,560 @@ +import { UnknownObject, Size } from "./types"; +import { + parseBoolean, + sizePropsDecoder, + parseIntOr, + notEmptyStringOr +} from "./lib"; +import Item, { + ItemType, + ItemProps, + ItemClickEvent, + ItemRemoveEvent +} from "./Item"; +import StaticGraph, { staticGraphPropsDecoder } from "./items/StaticGraph"; +import Icon, { iconPropsDecoder } from "./items/Icon"; +import ColorCloud, { colorCloudPropsDecoder } from "./items/ColorCloud"; +import Group, { groupPropsDecoder } from "./items/Group"; +import Clock, { clockPropsDecoder } from "./items/Clock"; +import Box, { boxPropsDecoder } from "./items/Box"; +import Line, { linePropsDecoder } from "./items/Line"; +import Label, { labelPropsDecoder } from "./items/Label"; +import SimpleValue, { simpleValuePropsDecoder } from "./items/SimpleValue"; +import EventsHistory, { + eventsHistoryPropsDecoder +} from "./items/EventsHistory"; +import Percentile, { percentilePropsDecoder } from "./items/Percentile"; +import TypedEvent, { Disposable, Listener } from "./TypedEvent"; +import DonutGraph, { donutGraphPropsDecoder } from "./items/DonutGraph"; +import BarsGraph, { barsGraphPropsDecoder } from "./items/BarsGraph"; +import ModuleGraph, { moduleGraphPropsDecoder } from "./items/ModuleGraph"; +import Service, { servicePropsDecoder } from "./items/Service"; + +// TODO: Document. +// eslint-disable-next-line @typescript-eslint/explicit-function-return-type +function itemInstanceFrom(data: UnknownObject) { + const type = parseIntOr(data.type, null); + if (type == null) throw new TypeError("missing item type."); + + switch (type as ItemType) { + case ItemType.STATIC_GRAPH: + return new StaticGraph(staticGraphPropsDecoder(data)); + case ItemType.MODULE_GRAPH: + return new ModuleGraph(moduleGraphPropsDecoder(data)); + case ItemType.SIMPLE_VALUE: + case ItemType.SIMPLE_VALUE_MAX: + case ItemType.SIMPLE_VALUE_MIN: + case ItemType.SIMPLE_VALUE_AVG: + return new SimpleValue(simpleValuePropsDecoder(data)); + case ItemType.PERCENTILE_BAR: + case ItemType.PERCENTILE_BUBBLE: + case ItemType.CIRCULAR_PROGRESS_BAR: + case ItemType.CIRCULAR_INTERIOR_PROGRESS_BAR: + return new Percentile(percentilePropsDecoder(data)); + case ItemType.LABEL: + return new Label(labelPropsDecoder(data)); + case ItemType.ICON: + return new Icon(iconPropsDecoder(data)); + case ItemType.SERVICE: + return new Service(servicePropsDecoder(data)); + case ItemType.GROUP_ITEM: + return new Group(groupPropsDecoder(data)); + case ItemType.BOX_ITEM: + return new Box(boxPropsDecoder(data)); + case ItemType.LINE_ITEM: + return new Line(linePropsDecoder(data)); + case ItemType.AUTO_SLA_GRAPH: + return new EventsHistory(eventsHistoryPropsDecoder(data)); + case ItemType.DONUT_GRAPH: + return new DonutGraph(donutGraphPropsDecoder(data)); + case ItemType.BARS_GRAPH: + return new BarsGraph(barsGraphPropsDecoder(data)); + case ItemType.CLOCK: + return new Clock(clockPropsDecoder(data)); + case ItemType.COLOR_CLOUD: + return new ColorCloud(colorCloudPropsDecoder(data)); + default: + throw new TypeError("item not found"); + } +} + +// TODO: Document. +// eslint-disable-next-line @typescript-eslint/explicit-function-return-type +function decodeProps(data: UnknownObject) { + const type = parseIntOr(data.type, null); + if (type == null) throw new TypeError("missing item type."); + + switch (type as ItemType) { + case ItemType.STATIC_GRAPH: + return staticGraphPropsDecoder(data); + case ItemType.MODULE_GRAPH: + return moduleGraphPropsDecoder(data); + case ItemType.SIMPLE_VALUE: + case ItemType.SIMPLE_VALUE_MAX: + case ItemType.SIMPLE_VALUE_MIN: + case ItemType.SIMPLE_VALUE_AVG: + return simpleValuePropsDecoder(data); + case ItemType.PERCENTILE_BAR: + case ItemType.PERCENTILE_BUBBLE: + case ItemType.CIRCULAR_PROGRESS_BAR: + case ItemType.CIRCULAR_INTERIOR_PROGRESS_BAR: + return percentilePropsDecoder(data); + case ItemType.LABEL: + return labelPropsDecoder(data); + case ItemType.ICON: + return iconPropsDecoder(data); + case ItemType.SERVICE: + return servicePropsDecoder(data); + case ItemType.GROUP_ITEM: + return groupPropsDecoder(data); + case ItemType.BOX_ITEM: + return boxPropsDecoder(data); + case ItemType.LINE_ITEM: + return linePropsDecoder(data); + case ItemType.AUTO_SLA_GRAPH: + return eventsHistoryPropsDecoder(data); + case ItemType.DONUT_GRAPH: + return donutGraphPropsDecoder(data); + case ItemType.BARS_GRAPH: + return barsGraphPropsDecoder(data); + case ItemType.CLOCK: + return clockPropsDecoder(data); + case ItemType.COLOR_CLOUD: + return colorCloudPropsDecoder(data); + default: + throw new TypeError("decoder not found"); + } +} + +// Base properties. +export interface VisualConsoleProps extends Size { + readonly id: number; + name: string; + groupId: number; + backgroundURL: string | null; // URL? + backgroundColor: string | null; + isFavorite: boolean; + relationLineWidth: number; +} + +/** + * Build a valid typed object from a raw object. + * This will allow us to ensure the type safety. + * + * @param data Raw object. + * @return An object representing the Visual Console props. + * @throws Will throw a TypeError if some property + * is missing from the raw object or have an invalid type. + */ +export function visualConsolePropsDecoder( + data: UnknownObject +): VisualConsoleProps | never { + // Object destructuring: http://es6-features.org/#ObjectMatchingShorthandNotation + const { + id, + name, + groupId, + backgroundURL, + backgroundColor, + isFavorite, + relationLineWidth + } = data; + + if (id == null || isNaN(parseInt(id))) { + throw new TypeError("invalid Id."); + } + if (typeof name !== "string" || name.length === 0) { + throw new TypeError("invalid name."); + } + if (groupId == null || isNaN(parseInt(groupId))) { + throw new TypeError("invalid group Id."); + } + + return { + id: parseInt(id), + name, + groupId: parseInt(groupId), + backgroundURL: notEmptyStringOr(backgroundURL, null), + backgroundColor: notEmptyStringOr(backgroundColor, null), + isFavorite: parseBoolean(isFavorite), + relationLineWidth: parseIntOr(relationLineWidth, 0), + ...sizePropsDecoder(data) + }; +} + +export default class VisualConsole { + // Reference to the DOM element which will contain the items. + private readonly containerRef: HTMLElement; + // Properties. + private _props: VisualConsoleProps; + // Visual Console Item instances by their Id. + private elementsById: { + [key: number]: Item; + } = {}; + // Visual Console Item Ids. + private elementIds: ItemProps["id"][] = []; + // Dictionary which store the created lines. + private relations: { + [key: string]: Line; + } = {}; + // Event manager for click events. + private readonly clickEventManager = new TypedEvent< + ItemClickEvent + >(); + // List of references to clean the event listeners. + private readonly disposables: Disposable[] = []; + + /** + * React to a click on an element. + * @param e Event object. + */ + private handleElementClick: (e: ItemClickEvent) => void = e => { + this.clickEventManager.emit(e); + // console.log(`Clicked element #${e.data.id}`, e); + }; + + /** + * Clear some element references. + * @param e Event object. + */ + private handleElementRemove: (e: ItemRemoveEvent) => void = e => { + // Remove the element from the list and its relations. + this.elementIds = this.elementIds.filter(id => id !== e.data.id); + delete this.elementsById[e.data.id]; + this.clearRelations(e.data.id); + }; + + public constructor( + container: HTMLElement, + props: UnknownObject, + items: UnknownObject[] + ) { + this.containerRef = container; + this._props = visualConsolePropsDecoder(props); + + // Force the first render. + this.render(); + + // Sort by isOnTop, id ASC + items = items.sort(function(a, b) { + if ( + a.isOnTop == null || + b.isOnTop == null || + a.id == null || + b.id == null + ) { + return 0; + } + + if (a.isOnTop && !b.isOnTop) return 1; + else if (!a.isOnTop && b.isOnTop) return -1; + else if (a.id < b.id) return 1; + else return -1; + }); + + // Initialize the items. + items.forEach(item => { + try { + const itemInstance = itemInstanceFrom(item); + // Add the item to the list. + this.elementsById[itemInstance.props.id] = itemInstance; + this.elementIds.push(itemInstance.props.id); + // Item event handlers. + itemInstance.onClick(this.handleElementClick); + itemInstance.onRemove(this.handleElementRemove); + // Add the item to the DOM. + this.containerRef.append(itemInstance.elementRef); + } catch (error) { + console.log("Error creating a new element:", error.message); + } + }); + + // Create lines. + this.buildRelations(); + } + + /** + * Public accessor of the `elements` property. + * @return Properties. + */ + public get elements(): Item[] { + // Ensure the type cause Typescript doesn't know the filter removes null items. + return this.elementIds + .map(id => this.elementsById[id]) + .filter(_ => _ != null) as Item[]; + } + + /** + * Public setter of the `elements` property. + * @param items. + */ + public updateElements(items: UnknownObject[]): void { + const itemIds = items.map(item => item.id || null).filter(id => id != null); + itemIds as number[]; // Tell the type system to rely on us. + // Get the elements we should delete. + const deletedIds: number[] = this.elementIds.filter( + id => itemIds.indexOf(id) < 0 + ); + // Delete the elements. + deletedIds.forEach(id => { + if (this.elementsById[id] != null) { + this.elementsById[id].remove(); + delete this.elementsById[id]; + } + }); + // Replace the element ids. + this.elementIds = itemIds; + + // Initialize the items. + items.forEach(item => { + if (item.id) { + if (this.elementsById[item.id] == null) { + // New item. + try { + const itemInstance = itemInstanceFrom(item); + // Add the item to the list. + this.elementsById[itemInstance.props.id] = itemInstance; + // Item event handlers. + itemInstance.onClick(this.handleElementClick); + itemInstance.onRemove(this.handleElementRemove); + // Add the item to the DOM. + this.containerRef.append(itemInstance.elementRef); + } catch (error) { + console.log("Error creating a new element:", error.message); + } + } else { + // Update item. + try { + this.elementsById[item.id].props = decodeProps(item); + } catch (error) { + console.log("Error updating an element:", error.message); + } + } + } + }); + + // Re-build relations. + this.buildRelations(); + } + + /** + * Public accessor of the `props` property. + * @return Properties. + */ + public get props(): VisualConsoleProps { + return { ...this._props }; // Return a copy. + } + + /** + * Public setter of the `props` property. + * If the new props are different enough than the + * stored props, a render would be fired. + * @param newProps + */ + public set props(newProps: VisualConsoleProps) { + const prevProps = this.props; + // Update the internal props. + this._props = newProps; + + // From this point, things which rely on this.props can access to the changes. + + // Re-render. + this.render(prevProps); + } + + /** + * Recreate or update the HTMLElement which represents the Visual Console into the DOM. + * @param prevProps If exists it will be used to only DOM updates instead of a full replace. + */ + public render(prevProps: VisualConsoleProps | null = null): void { + if (prevProps) { + if (prevProps.backgroundURL !== this.props.backgroundURL) { + this.containerRef.style.backgroundImage = + this.props.backgroundURL !== null + ? `url(${this.props.backgroundURL})` + : null; + } + if (prevProps.backgroundColor !== this.props.backgroundColor) { + this.containerRef.style.backgroundColor = this.props.backgroundColor; + } + if (this.sizeChanged(prevProps, this.props)) { + this.resizeElement(this.props.width, this.props.height); + } + } else { + this.containerRef.style.backgroundImage = + this.props.backgroundURL !== null + ? `url(${this.props.backgroundURL})` + : null; + + this.containerRef.style.backgroundColor = this.props.backgroundColor; + this.resizeElement(this.props.width, this.props.height); + } + } + + /** + * Compare the previous and the new size and return + * a boolean value in case the size changed. + * @param prevSize + * @param newSize + * @return Whether the size changed or not. + */ + public sizeChanged(prevSize: Size, newSize: Size): boolean { + return ( + prevSize.width !== newSize.width || prevSize.height !== newSize.height + ); + } + + /** + * Resize the DOM container. + * @param width + * @param height + */ + public resizeElement(width: number, height: number): void { + this.containerRef.style.width = `${width}px`; + this.containerRef.style.height = `${height}px`; + } + + /** + * Update the size into the properties and resize the DOM container. + * @param width + * @param height + */ + public resize(width: number, height: number): void { + this.props = { + ...this.props, // Object spread: http://es6-features.org/#SpreadOperator + width, + height + }; + } + + /** + * To remove the event listeners and the elements from the DOM. + */ + public remove(): void { + this.disposables.forEach(d => d.dispose()); // Arrow function. + this.elements.forEach(e => e.remove()); // Arrow function. + this.elementsById = {}; + this.elementIds = []; + // Clear relations. + this.clearRelations(); + // Clean container. + this.containerRef.innerHTML = ""; + } + + /** + * Create line elements which connect the elements with their parents. + */ + private buildRelations(): void { + // Clear relations. + this.clearRelations(); + // Add relations. + this.elements.forEach(item => { + if (item.props.parentId !== null) { + const parent = this.elementsById[item.props.parentId]; + const child = this.elementsById[item.props.id]; + if (parent && child) this.addRelationLine(parent, child); + } + }); + } + + /** + * @param itemId Optional identifier of a parent or child item. + * Remove the line elements which connect the elements with their parents. + */ + private clearRelations(itemId?: number): void { + if (itemId != null) { + for (let key in this.relations) { + const ids = key.split("|"); + const parentId = Number.parseInt(ids[0]); + const childId = Number.parseInt(ids[1]); + + if (itemId === parentId || itemId === childId) { + this.relations[key].remove(); + delete this.relations[key]; + } + } + } else { + for (let key in this.relations) { + this.relations[key].remove(); + delete this.relations[key]; + } + } + } + + /** + * Retrieve the line element which represent the relation between items. + * @param parentId Identifier of the parent item. + * @param childId Itentifier of the child item. + * @return The line element or nothing. + */ + private getRelationLine(parentId: number, childId: number): Line | null { + const identifier = `${parentId}|${childId}`; + return this.relations[identifier] || null; + } + + /** + * Add a new line item to represent a relation between the items. + * @param parent Parent item. + * @param child Child item. + * @return Whether the line was added or not. + */ + private addRelationLine( + parent: Item, + child: Item + ): Line { + const identifier = `${parent.props.id}|${child.props.id}`; + if (this.relations[identifier] != null) { + this.relations[identifier].remove(); + } + + // Get the items center. + const startX = parent.props.x + parent.elementRef.clientWidth / 2; + const startY = + parent.props.y + + (parent.elementRef.clientHeight - parent.labelElementRef.clientHeight) / + 2; + const endX = child.props.x + child.elementRef.clientWidth / 2; + const endY = + child.props.y + + (child.elementRef.clientHeight - child.labelElementRef.clientHeight) / 2; + + const line = new Line( + linePropsDecoder({ + id: 0, + type: ItemType.LINE_ITEM, + startX, + startY, + endX, + endY, + width: 0, + height: 0, + lineWidth: this.props.relationLineWidth, + color: "#CCCCCC" + }) + ); + // Save a reference to the line item. + this.relations[identifier] = line; + + // Add the line to the DOM. + line.elementRef.style.zIndex = "0"; + this.containerRef.append(line.elementRef); + + return line; + } + + /** + * Add an event handler to the click of the linked visual console elements. + * @param listener Function which is going to be executed when a linked console is clicked. + */ + public onClick(listener: Listener>): Disposable { + /* + * The '.on' function returns a function which will clean the event + * listener when executed. We store all the 'dispose' functions to + * call them when the item should be cleared. + */ + const disposable = this.clickEventManager.on(listener); + this.disposables.push(disposable); + + return disposable; + } +} diff --git a/visual_console_client/src/index.ts b/visual_console_client/src/index.ts new file mode 100644 index 0000000000..a7e8656d8d --- /dev/null +++ b/visual_console_client/src/index.ts @@ -0,0 +1,14 @@ +/* + * Useful resources. + * http://es6-features.org/ + * http://exploringjs.com/es6 + * https://www.typescriptlang.org/ + */ + +import "./main.css"; // CSS import. +import VisualConsole from "./VisualConsole"; + +// Export the VisualConsole class to the global object. + +// eslint-disable-next-line +(window as any).VisualConsole = VisualConsole; diff --git a/visual_console_client/src/items/BarsGraph.ts b/visual_console_client/src/items/BarsGraph.ts new file mode 100644 index 0000000000..d1a6fe97a5 --- /dev/null +++ b/visual_console_client/src/items/BarsGraph.ts @@ -0,0 +1,67 @@ +import { UnknownObject, WithModuleProps } from "../types"; +import { modulePropsDecoder, decodeBase64, stringIsEmpty } from "../lib"; +import Item, { ItemType, ItemProps, itemBasePropsDecoder } from "../Item"; + +export type BarsGraphProps = { + type: ItemType.BARS_GRAPH; + html: string; +} & ItemProps & + WithModuleProps; + +/** + * Build a valid typed object from a raw object. + * This will allow us to ensure the type safety. + * + * @param data Raw object. + * @return An object representing the bars graph props. + * @throws Will throw a TypeError if some property + * is missing from the raw object or have an invalid type. + */ +export function barsGraphPropsDecoder( + data: UnknownObject +): BarsGraphProps | never { + if (stringIsEmpty(data.html) && stringIsEmpty(data.encodedHtml)) { + throw new TypeError("missing html content."); + } + + return { + ...itemBasePropsDecoder(data), // Object spread. It will merge the properties of the two objects. + type: ItemType.BARS_GRAPH, + html: !stringIsEmpty(data.html) + ? data.html + : decodeBase64(data.encodedHtml), + ...modulePropsDecoder(data) // Object spread. It will merge the properties of the two objects. + }; +} + +export default class BarsGraph extends Item { + protected createDomElement(): HTMLElement { + const element = document.createElement("div"); + element.className = "bars-graph"; + element.innerHTML = this.props.html; + + // Hack to execute the JS after the HTML is added to the DOM. + const scripts = element.getElementsByTagName("script"); + for (let i = 0; i < scripts.length; i++) { + setTimeout(() => { + if (scripts[i].src.length === 0) eval(scripts[i].innerHTML.trim()); + }, 0); + } + + return element; + } + + protected updateDomElement(element: HTMLElement): void { + element.innerHTML = this.props.html; + + // Hack to execute the JS after the HTML is added to the DOM. + const aux = document.createElement("div"); + aux.innerHTML = this.props.html; + const scripts = aux.getElementsByTagName("script"); + for (let i = 0; i < scripts.length; i++) { + if (scripts[i].src.length === 0) { + eval(scripts[i].innerHTML.trim()); + } + } + } +} diff --git a/visual_console_client/src/items/Box.ts b/visual_console_client/src/items/Box.ts new file mode 100644 index 0000000000..ca3078c0a6 --- /dev/null +++ b/visual_console_client/src/items/Box.ts @@ -0,0 +1,68 @@ +import { UnknownObject } from "../types"; +import { parseIntOr, notEmptyStringOr } from "../lib"; +import Item, { ItemType, ItemProps, itemBasePropsDecoder } from "../Item"; + +interface BoxProps extends ItemProps { + // Overrided properties. + readonly type: ItemType.BOX_ITEM; + label: null; + isLinkEnabled: false; + parentId: null; + aclGroupId: null; + // Custom properties. + borderWidth: number; + borderColor: string | null; + fillColor: string | null; +} + +/** + * Build a valid typed object from a raw object. + * This will allow us to ensure the type safety. + * + * @param data Raw object. + * @return An object representing the item props. + * @throws Will throw a TypeError if some property + * is missing from the raw object or have an invalid type. + */ +export function boxPropsDecoder(data: UnknownObject): BoxProps | never { + return { + ...itemBasePropsDecoder(data), // Object spread. It will merge the properties of the two objects. + type: ItemType.BOX_ITEM, + label: null, + isLinkEnabled: false, + parentId: null, + aclGroupId: null, + // Custom properties. + borderWidth: parseIntOr(data.borderWidth, 0), + borderColor: notEmptyStringOr(data.borderColor, null), + fillColor: notEmptyStringOr(data.fillColor, null) + }; +} + +export default class Box extends Item { + protected createDomElement(): HTMLElement { + const box: HTMLDivElement = document.createElement("div"); + box.className = "box"; + // To prevent this item to expand beyond its parent. + box.style.boxSizing = "border-box"; + + if (this.props.fillColor) { + box.style.backgroundColor = this.props.fillColor; + } + + // Border. + if (this.props.borderWidth > 0) { + box.style.borderStyle = "solid"; + // Control the max width to prevent this item to expand beyond its parent. + const maxBorderWidth = Math.min(this.props.width, this.props.height) / 2; + const borderWidth = Math.min(this.props.borderWidth, maxBorderWidth); + box.style.borderWidth = `${borderWidth}px`; + + if (this.props.borderColor) { + box.style.borderColor = this.props.borderColor; + } + } + + return box; + } +} diff --git a/visual_console_client/src/items/Clock/alarm-clock.ttf b/visual_console_client/src/items/Clock/alarm-clock.ttf new file mode 100755 index 0000000000..9e9b593459 Binary files /dev/null and b/visual_console_client/src/items/Clock/alarm-clock.ttf differ diff --git a/visual_console_client/src/items/Clock/index.ts b/visual_console_client/src/items/Clock/index.ts new file mode 100644 index 0000000000..a65f188659 --- /dev/null +++ b/visual_console_client/src/items/Clock/index.ts @@ -0,0 +1,601 @@ +import "./styles.css"; + +import { LinkedVisualConsoleProps, UnknownObject, Size } from "../../types"; +import { + linkedVCPropsDecoder, + parseIntOr, + parseBoolean, + prefixedCssRules, + notEmptyStringOr, + humanDate, + humanTime +} from "../../lib"; +import Item, { ItemProps, itemBasePropsDecoder, ItemType } from "../../Item"; + +export type ClockProps = { + type: ItemType.CLOCK; + clockType: "analogic" | "digital"; + clockFormat: "datetime" | "time"; + clockTimezone: string; + clockTimezoneOffset: number; // Offset of the timezone to UTC in seconds. + showClockTimezone: boolean; + color?: string | null; +} & ItemProps & + LinkedVisualConsoleProps; + +/** + * Extract a valid enum value from a raw unknown value. + * @param clockType Raw value. + */ +const parseClockType = ( + clockType: any // eslint-disable-line @typescript-eslint/no-explicit-any +): ClockProps["clockType"] => { + switch (clockType) { + case "analogic": + case "digital": + return clockType; + default: + return "analogic"; + } +}; + +/** + * Extract a valid enum value from a raw unknown value. + * @param clockFormat Raw value. + */ +const parseClockFormat = ( + clockFormat: any // eslint-disable-line @typescript-eslint/no-explicit-any +): ClockProps["clockFormat"] => { + switch (clockFormat) { + case "datetime": + case "date": + case "time": + return clockFormat; + default: + return "datetime"; + } +}; + +/** + * Build a valid typed object from a raw object. + * This will allow us to ensure the type safety. + * + * @param data Raw object. + * @return An object representing the clock props. + * @throws Will throw a TypeError if some property + * is missing from the raw object or have an invalid type. + */ +export function clockPropsDecoder(data: UnknownObject): ClockProps | never { + if ( + typeof data.clockTimezone !== "string" || + data.clockTimezone.length === 0 + ) { + throw new TypeError("invalid timezone."); + } + + return { + ...itemBasePropsDecoder(data), // Object spread. It will merge the properties of the two objects. + type: ItemType.CLOCK, + clockType: parseClockType(data.clockType), + clockFormat: parseClockFormat(data.clockFormat), + clockTimezone: data.clockTimezone, + clockTimezoneOffset: parseIntOr(data.clockTimezoneOffset, 0), + showClockTimezone: parseBoolean(data.showClockTimezone), + color: notEmptyStringOr(data.color, null), + ...linkedVCPropsDecoder(data) // Object spread. It will merge the properties of the two objects. + }; +} + +export default class Clock extends Item { + public static readonly TICK_INTERVAL = 1000; // In ms. + private intervalRef: number | null = null; + + public constructor(props: ClockProps) { + // Call the superclass constructor. + super(props); + + /* The item is already loaded and inserted into the DOM. + * The class properties are now initialized. + * Now you can modify the item, add event handlers, timers, etc. + */ + + /* The use of the arrow function is important here. startTick will + * use the function passed as an argument to call the global setInterval + * function. The interval, timeout or event functions, among other, are + * called into another execution loop and using a different context. + * The arrow functions, unlike the classic functions, doesn't create + * their own context (this), so their context at execution time will be + * use the current context at the declaration time. + * http://es6-features.org/#Lexicalthis + */ + this.startTick( + () => { + // Replace the old element with the updated date. + this.childElementRef.innerHTML = this.createClock().innerHTML; + }, + /* The analogic clock doesn't need to tick, + * but it will be refreshed every 20 seconds + * to avoid a desync caused by page freezes. + */ + this.props.clockType === "analogic" ? 20000 : Clock.TICK_INTERVAL + ); + } + + /** + * Wrap a window.clearInterval call. + */ + private stopTick(): void { + if (this.intervalRef !== null) { + window.clearInterval(this.intervalRef); + this.intervalRef = null; + } + } + + /** + * Wrap a window.setInterval call. + * @param handler Function to be called every time the interval + * timer is reached. + * @param interval Number in milliseconds for the interval timer. + */ + private startTick( + handler: TimerHandler, + interval: number = Clock.TICK_INTERVAL + ): void { + this.stopTick(); + this.intervalRef = window.setInterval(handler, interval); + } + + /** + * Create a element which contains the DOM representation of the item. + * @return DOM Element. + * @override + */ + protected createDomElement(): HTMLElement | never { + return this.createClock(); + } + + /** + * To remove the event listeners and the elements from the DOM. + * @override + */ + public remove(): void { + // Clear the interval. + this.stopTick(); + // Call to the parent clean function. + super.remove(); + } + + /** + * @override Item.resizeElement + * Resize the DOM content container. + * @param width + * @param height + */ + protected resizeElement(width: number, height: number): void { + const { width: newWidth, height: newHeight } = this.getElementSize( + width, + height + ); // Destructuring assigment: http://es6-features.org/#ObjectMatchingShorthandNotation + super.resizeElement(newWidth, newHeight); + // Re-render the item to force it calculate a new font size. + if (this.props.clockType === "digital") { + // Replace the old element with the updated date. + this.childElementRef.innerHTML = this.createClock().innerHTML; + } + } + + /** + * Create a element which contains a representation of a clock. + * It choose between the clock types. + * @return DOM Element. + * @throws Error. + */ + private createClock(): HTMLElement | never { + switch (this.props.clockType) { + case "analogic": + return this.createAnalogicClock(); + case "digital": + return this.createDigitalClock(); + default: + throw new Error("invalid clock type."); + } + } + + /** + * Create a element which contains a representation of an analogic clock. + * @return DOM Element. + */ + private createAnalogicClock(): HTMLElement { + const svgNS = "http://www.w3.org/2000/svg"; + const colors = { + watchFace: "#FFFFF0", + watchFaceBorder: "#242124", + mark: "#242124", + handDark: "#242124", + handLight: "#525252", + secondHand: "#DC143C" + }; + + const { width, height } = this.getElementSize(); // Destructuring assigment: http://es6-features.org/#ObjectMatchingShorthandNotation + + const div = document.createElement("div"); + div.className = "analogic-clock"; + div.style.width = `${width}px`; + div.style.height = `${height}px`; + + // SVG container. + const svg = document.createElementNS(svgNS, "svg"); + // Auto resize SVG using the view box magic: https://css-tricks.com/scale-svg/ + svg.setAttribute("viewBox", "0 0 100 100"); + + // Clock face. + const clockFace = document.createElementNS(svgNS, "g"); + clockFace.setAttribute("class", "clockface"); + const clockFaceBackground = document.createElementNS(svgNS, "circle"); + clockFaceBackground.setAttribute("cx", "50"); + clockFaceBackground.setAttribute("cy", "50"); + clockFaceBackground.setAttribute("r", "48"); + clockFaceBackground.setAttribute("fill", colors.watchFace); + clockFaceBackground.setAttribute("stroke", colors.watchFaceBorder); + clockFaceBackground.setAttribute("stroke-width", "2"); + clockFaceBackground.setAttribute("stroke-linecap", "round"); + // Insert the clockface background into the clockface group. + clockFace.append(clockFaceBackground); + + // Timezone complication. + const city = this.getHumanTimezone(); + if (city.length > 0) { + const timezoneComplication = document.createElementNS(svgNS, "text"); + timezoneComplication.setAttribute("text-anchor", "middle"); + timezoneComplication.setAttribute("font-size", "8"); + timezoneComplication.setAttribute( + "transform", + "translate(30 50) rotate(90)" // Rotate to counter the clock rotation. + ); + timezoneComplication.setAttribute("fill", colors.mark); + timezoneComplication.textContent = city; + clockFace.append(timezoneComplication); + } + + // Marks group. + const marksGroup = document.createElementNS(svgNS, "g"); + marksGroup.setAttribute("class", "marks"); + // Build the 12 hours mark. + const mainMarkGroup = document.createElementNS(svgNS, "g"); + mainMarkGroup.setAttribute("class", "mark"); + mainMarkGroup.setAttribute("transform", "translate(50 50)"); + const mark1a = document.createElementNS(svgNS, "line"); + mark1a.setAttribute("x1", "36"); + mark1a.setAttribute("y1", "0"); + mark1a.setAttribute("x2", "46"); + mark1a.setAttribute("y2", "0"); + mark1a.setAttribute("stroke", colors.mark); + mark1a.setAttribute("stroke-width", "5"); + const mark1b = document.createElementNS(svgNS, "line"); + mark1b.setAttribute("x1", "36"); + mark1b.setAttribute("y1", "0"); + mark1b.setAttribute("x2", "46"); + mark1b.setAttribute("y2", "0"); + mark1b.setAttribute("stroke", colors.watchFace); + mark1b.setAttribute("stroke-width", "1"); + // Insert the 12 mark lines into their group. + mainMarkGroup.append(mark1a, mark1b); + // Insert the main mark into the marks group. + marksGroup.append(mainMarkGroup); + // Build the rest of the marks. + for (let i = 1; i < 60; i++) { + const mark = document.createElementNS(svgNS, "line"); + mark.setAttribute("y1", "0"); + mark.setAttribute("y2", "0"); + mark.setAttribute("stroke", colors.mark); + mark.setAttribute("transform", `translate(50 50) rotate(${i * 6})`); + + if (i % 5 === 0) { + mark.setAttribute("x1", "38"); + mark.setAttribute("x2", "46"); + mark.setAttribute("stroke-width", i % 15 === 0 ? "2" : "1"); + } else { + mark.setAttribute("x1", "42"); + mark.setAttribute("x2", "46"); + mark.setAttribute("stroke-width", "0.5"); + } + + // Insert the mark into the marks group. + marksGroup.append(mark); + } + + /* Clock hands */ + + // Hour hand. + const hourHand = document.createElementNS(svgNS, "g"); + hourHand.setAttribute("class", "hour-hand"); + hourHand.setAttribute("transform", "translate(50 50)"); + // This will go back and will act like a border. + const hourHandA = document.createElementNS(svgNS, "line"); + hourHandA.setAttribute("class", "hour-hand-a"); + hourHandA.setAttribute("x1", "0"); + hourHandA.setAttribute("y1", "0"); + hourHandA.setAttribute("x2", "30"); + hourHandA.setAttribute("y2", "0"); + hourHandA.setAttribute("stroke", colors.handLight); + hourHandA.setAttribute("stroke-width", "4"); + hourHandA.setAttribute("stroke-linecap", "round"); + // This will go in front of the previous line. + const hourHandB = document.createElementNS(svgNS, "line"); + hourHandB.setAttribute("class", "hour-hand-b"); + hourHandB.setAttribute("x1", "0"); + hourHandB.setAttribute("y1", "0"); + hourHandB.setAttribute("x2", "29.9"); + hourHandB.setAttribute("y2", "0"); + hourHandB.setAttribute("stroke", colors.handDark); + hourHandB.setAttribute("stroke-width", "3.1"); + hourHandB.setAttribute("stroke-linecap", "round"); + // Append the elements to finish the hour hand. + hourHand.append(hourHandA, hourHandB); + + // Minute hand. + const minuteHand = document.createElementNS(svgNS, "g"); + minuteHand.setAttribute("class", "minute-hand"); + minuteHand.setAttribute("transform", "translate(50 50)"); + // This will go back and will act like a border. + const minuteHandA = document.createElementNS(svgNS, "line"); + minuteHandA.setAttribute("class", "minute-hand-a"); + minuteHandA.setAttribute("x1", "0"); + minuteHandA.setAttribute("y1", "0"); + minuteHandA.setAttribute("x2", "40"); + minuteHandA.setAttribute("y2", "0"); + minuteHandA.setAttribute("stroke", colors.handLight); + minuteHandA.setAttribute("stroke-width", "2"); + minuteHandA.setAttribute("stroke-linecap", "round"); + // This will go in front of the previous line. + const minuteHandB = document.createElementNS(svgNS, "line"); + minuteHandB.setAttribute("class", "minute-hand-b"); + minuteHandB.setAttribute("x1", "0"); + minuteHandB.setAttribute("y1", "0"); + minuteHandB.setAttribute("x2", "39.9"); + minuteHandB.setAttribute("y2", "0"); + minuteHandB.setAttribute("stroke", colors.handDark); + minuteHandB.setAttribute("stroke-width", "1.5"); + minuteHandB.setAttribute("stroke-linecap", "round"); + const minuteHandPin = document.createElementNS(svgNS, "circle"); + minuteHandPin.setAttribute("r", "3"); + minuteHandPin.setAttribute("fill", colors.handDark); + // Append the elements to finish the minute hand. + minuteHand.append(minuteHandA, minuteHandB, minuteHandPin); + + // Second hand. + const secondHand = document.createElementNS(svgNS, "g"); + secondHand.setAttribute("class", "second-hand"); + secondHand.setAttribute("transform", "translate(50 50)"); + const secondHandBar = document.createElementNS(svgNS, "line"); + secondHandBar.setAttribute("x1", "0"); + secondHandBar.setAttribute("y1", "0"); + secondHandBar.setAttribute("x2", "46"); + secondHandBar.setAttribute("y2", "0"); + secondHandBar.setAttribute("stroke", colors.secondHand); + secondHandBar.setAttribute("stroke-width", "1"); + secondHandBar.setAttribute("stroke-linecap", "round"); + const secondHandPin = document.createElementNS(svgNS, "circle"); + secondHandPin.setAttribute("r", "2"); + secondHandPin.setAttribute("fill", colors.secondHand); + // Append the elements to finish the second hand. + secondHand.append(secondHandBar, secondHandPin); + + // Pin. + const pin = document.createElementNS(svgNS, "circle"); + pin.setAttribute("cx", "50"); + pin.setAttribute("cy", "50"); + pin.setAttribute("r", "0.3"); + pin.setAttribute("fill", colors.handDark); + + // Get the hand angles. + const date = this.getOriginDate(); + const seconds = date.getSeconds(); + const minutes = date.getMinutes(); + const hours = date.getHours(); + const secAngle = (360 / 60) * seconds; + const minuteAngle = (360 / 60) * minutes + (360 / 60) * (seconds / 60); + const hourAngle = (360 / 12) * hours + (360 / 12) * (minutes / 60); + // Set the clock time by moving the hands. + hourHand.setAttribute("transform", `translate(50 50) rotate(${hourAngle})`); + minuteHand.setAttribute( + "transform", + `translate(50 50) rotate(${minuteAngle})` + ); + secondHand.setAttribute( + "transform", + `translate(50 50) rotate(${secAngle})` + ); + + // Build the clock + svg.append(clockFace, marksGroup, hourHand, minuteHand, secondHand, pin); + // Rotate the clock to its normal position. + svg.setAttribute("transform", "rotate(-90)"); + + /* Add the animation declaration to the container. + * Since the animation keyframes need to know the + * start angle, this angle is dynamic (current time), + * and we can't edit keyframes through javascript + * safely and with backwards compatibility, we need + * to inject it. + */ + div.innerHTML = ` + + `; + // Add the clock to the container + div.append(svg); + + return div; + } + + /** + * Create a element which contains a representation of a digital clock. + * @return DOM Element. + */ + private createDigitalClock(): HTMLElement { + const element: HTMLDivElement = document.createElement("div"); + element.className = "digital-clock"; + + const { width } = this.getElementSize(); // Destructuring assigment: http://es6-features.org/#ObjectMatchingShorthandNotation + + // Calculate font size to adapt the font to the item size. + const baseTimeFontSize = 20; // Per 100px of width. + const dateFontSizeMultiplier = 0.5; + const tzFontSizeMultiplier = 6 / this.props.clockTimezone.length; + const timeFontSize = (baseTimeFontSize * width) / 100; + const dateFontSize = + (baseTimeFontSize * dateFontSizeMultiplier * width) / 100; + const tzFontSize = Math.min( + (baseTimeFontSize * tzFontSizeMultiplier * width) / 100, + (width / 100) * 10 + ); + + // Date calculated using the original timezone. + const date = this.getOriginDate(); + + // Date. + if (this.props.clockFormat === "datetime") { + const dateElem: HTMLSpanElement = document.createElement("span"); + dateElem.className = "date"; + dateElem.textContent = humanDate(date, "default"); + dateElem.style.fontSize = `${dateFontSize}px`; + if (this.props.color) dateElem.style.color = this.props.color; + element.append(dateElem); + } + + // Time. + const timeElem: HTMLSpanElement = document.createElement("span"); + timeElem.className = "time"; + timeElem.textContent = humanTime(date); + timeElem.style.fontSize = `${timeFontSize}px`; + if (this.props.color) timeElem.style.color = this.props.color; + element.append(timeElem); + + // City name. + const city = this.getHumanTimezone(); + if (city.length > 0) { + const tzElem: HTMLSpanElement = document.createElement("span"); + tzElem.className = "timezone"; + tzElem.textContent = city; + tzElem.style.fontSize = `${tzFontSize}px`; + if (this.props.color) tzElem.style.color = this.props.color; + element.append(tzElem); + } + + return element; + } + + /** + * Generate the current date using the timezone offset stored into the properties. + * @return The current date. + */ + private getOriginDate(initialDate: Date | null = null): Date { + const d = initialDate ? initialDate : new Date(); + const targetTZOffset = this.props.clockTimezoneOffset * 1000; // In ms. + const localTZOffset = d.getTimezoneOffset() * 60 * 1000; // In ms. + const utimestamp = d.getTime() + targetTZOffset + localTZOffset; + + return new Date(utimestamp); + } + + /** + * Extract a human readable city name from the timezone text. + * @param timezone Timezone text. + */ + public getHumanTimezone(timezone: string = this.props.clockTimezone): string { + const [, city = ""] = timezone.split("/"); + return city.replace("_", " "); + } + + /** + * Generate a element size using the current size and the default values. + * @return The size. + */ + private getElementSize( + width: number = this.props.width, + height: number = this.props.height + ): Size { + switch (this.props.clockType) { + case "analogic": { + let diameter = 100; // Default value. + + if (width > 0 && height > 0) { + diameter = Math.min(width, height); + } else if (width > 0) { + diameter = width; + } else if (height > 0) { + diameter = height; + } + + return { + width: diameter, + height: diameter + }; + } + case "digital": { + if (width > 0 && height > 0) { + // The proportion of the clock should be (width = height / 2) aproximately. + height = width / 2 < height ? width / 2 : height; + } else if (width > 0) { + height = width / 2; + } else if (height > 0) { + // The proportion of the clock should be (height * 2 = width) aproximately. + width = height * 2; + } else { + width = 100; // Default value. + height = 50; // Default value. + } + + return { + width, + height + }; + } + default: + throw new Error("invalid clock type."); + } + } +} diff --git a/visual_console_client/src/items/Clock/spec.ts b/visual_console_client/src/items/Clock/spec.ts new file mode 100644 index 0000000000..7380f98468 --- /dev/null +++ b/visual_console_client/src/items/Clock/spec.ts @@ -0,0 +1,67 @@ +import Clock, { clockPropsDecoder } from "."; + +const genericRawProps = { + id: 1, + type: 19, // Clock item = 19 + label: null, + isLinkEnabled: false, + isOnTop: false, + parentId: null, + aclGroupId: null +}; + +const positionRawProps = { + x: 100, + y: 50 +}; + +const sizeRawProps = { + width: 100, + height: 100 +}; + +const digitalClockProps = { + clockType: "digital", + clockFormat: "datetime", + clockTimezone: "Madrid", + clockTimezoneOffset: 60, + showClockTimezone: true, + color: "white" +}; + +const linkedModuleProps = { + // Agent props. + agentId: null, + agentName: null, + // Module props. + moduleId: null, + moduleName: null +}; + +describe("Clock item", () => { + const clockInstance = new Clock( + clockPropsDecoder({ + ...genericRawProps, + ...positionRawProps, + ...sizeRawProps, + ...linkedModuleProps, + ...digitalClockProps + }) + ); + + it("should have the digital-clock class", () => { + expect( + clockInstance.elementRef.getElementsByClassName("digital-clock").length + ).toBeGreaterThan(0); + }); + + describe("getHumanTimezone function", () => { + it("should return a better timezone", () => { + expect(clockInstance.getHumanTimezone("America/New_York")).toBe( + "New York" + ); + expect(clockInstance.getHumanTimezone("Europe/Madrid")).toBe("Madrid"); + expect(clockInstance.getHumanTimezone("Asia/Tokyo")).toBe("Tokyo"); + }); + }); +}); diff --git a/visual_console_client/src/items/Clock/styles.css b/visual_console_client/src/items/Clock/styles.css new file mode 100644 index 0000000000..cfa6f11eb5 --- /dev/null +++ b/visual_console_client/src/items/Clock/styles.css @@ -0,0 +1,48 @@ +@font-face { + font-family: Alarm Clock; + src: url(./alarm-clock.ttf); +} + +/* Digital clock */ + +.visual-console-item .digital-clock { + display: flex; + flex-direction: column; + justify-content: center; + justify-items: center; + align-content: center; + align-items: center; +} + +.visual-console-item .digital-clock > span { + font-family: "Alarm Clock", "Courier New", Courier, monospace; + font-size: 50px; + + /* To improve legibility */ + -webkit-font-smoothing: antialiased; + -moz-osx-font-smoothing: grayscale; + text-rendering: optimizeLegibility; + text-shadow: rgba(0, 0, 0, 0.01) 0 0 1px; +} + +.visual-console-item .digital-clock > span.date { + font-size: 25px; +} + +.visual-console-item .digital-clock > span.timezone { + font-size: 25px; +} + +/* Analog clock */ + +.visual-console-item .analogic-clock .hour-hand { + animation: rotate-hour 43200s infinite linear; +} + +.visual-console-item .analogic-clock .minute-hand { + animation: rotate-minute 3600s infinite linear; +} + +.visual-console-item .analogic-clock .second-hand { + animation: rotate-second 60s infinite linear; +} diff --git a/visual_console_client/src/items/ColorCloud.spec.ts b/visual_console_client/src/items/ColorCloud.spec.ts new file mode 100644 index 0000000000..103850b04b --- /dev/null +++ b/visual_console_client/src/items/ColorCloud.spec.ts @@ -0,0 +1,64 @@ +import ColorCloud, { colorCloudPropsDecoder } from "./ColorCloud"; + +const genericRawProps = { + id: 1, + type: 20, // COlor cloud item = 20 + label: null, + isLinkEnabled: false, + isOnTop: false, + parentId: null, + aclGroupId: null +}; + +const positionRawProps = { + x: 100, + y: 50 +}; + +const sizeRawProps = { + width: 100, + height: 100 +}; + +const colorCloudProps = { + color: "rgb(100, 50, 245)" +}; + +const linkedModuleProps = { + // Agent props. + agentId: null, + agentName: null, + // Module props. + moduleId: null, + moduleName: null +}; + +describe("Color cloud item", () => { + const colorCloudInstance = new ColorCloud( + colorCloudPropsDecoder({ + ...genericRawProps, + ...positionRawProps, + ...sizeRawProps, + ...linkedModuleProps, + ...colorCloudProps + }) + ); + + it("should throw when using an invalid color into the props decoder", () => { + expect(() => + colorCloudPropsDecoder({ + ...genericRawProps, + ...positionRawProps, + ...sizeRawProps, + ...linkedModuleProps, + color: null + }) + ).toThrowError(TypeError); + }); + + it("should have the color-cloud class", () => { + expect( + colorCloudInstance.elementRef.getElementsByClassName("color-cloud").length + ).toBeGreaterThan(0); + }); +}); diff --git a/visual_console_client/src/items/ColorCloud.ts b/visual_console_client/src/items/ColorCloud.ts new file mode 100644 index 0000000000..0b5dfe9948 --- /dev/null +++ b/visual_console_client/src/items/ColorCloud.ts @@ -0,0 +1,100 @@ +import { + WithModuleProps, + LinkedVisualConsoleProps, + UnknownObject +} from "../types"; +import { modulePropsDecoder, linkedVCPropsDecoder } from "../lib"; +import Item, { itemBasePropsDecoder, ItemType, ItemProps } from "../Item"; + +export type ColorCloudProps = { + type: ItemType.COLOR_CLOUD; + color: string; + // TODO: Add the rest of the color cloud values? +} & ItemProps & + WithModuleProps & + LinkedVisualConsoleProps; + +/** + * Build a valid typed object from a raw object. + * This will allow us to ensure the type safety. + * + * @param data Raw object. + * @return An object representing the static graph props. + * @throws Will throw a TypeError if some property + * is missing from the raw object or have an invalid type. + */ +export function colorCloudPropsDecoder( + data: UnknownObject +): ColorCloudProps | never { + // TODO: Validate the color. + if (typeof data.color !== "string" || data.color.length === 0) { + throw new TypeError("invalid color."); + } + + return { + ...itemBasePropsDecoder(data), // Object spread. It will merge the properties of the two objects. + type: ItemType.COLOR_CLOUD, + color: data.color, + ...modulePropsDecoder(data), // Object spread. It will merge the properties of the two objects. + ...linkedVCPropsDecoder(data) // Object spread. It will merge the properties of the two objects. + }; +} + +const svgNS = "http://www.w3.org/2000/svg"; + +export default class ColorCloud extends Item { + protected createDomElement(): HTMLElement { + const container: HTMLDivElement = document.createElement("div"); + container.className = "color-cloud"; + + // Add the SVG. + container.append(this.createSvgElement()); + + return container; + } + + public createSvgElement(): SVGSVGElement { + const gradientId = `grad_${this.props.id}`; + // SVG container. + const svg = document.createElementNS(svgNS, "svg"); + // Auto resize SVG using the view box magic: https://css-tricks.com/scale-svg/ + svg.setAttribute("viewBox", "0 0 100 100"); + + // Defs. + const defs = document.createElementNS(svgNS, "defs"); + // Radial gradient. + const radialGradient = document.createElementNS(svgNS, "radialGradient"); + radialGradient.setAttribute("id", gradientId); + radialGradient.setAttribute("cx", "50%"); + radialGradient.setAttribute("cy", "50%"); + radialGradient.setAttribute("r", "50%"); + radialGradient.setAttribute("fx", "50%"); + radialGradient.setAttribute("fy", "50%"); + // Stops. + const stop0 = document.createElementNS(svgNS, "stop"); + stop0.setAttribute("offset", "0%"); + stop0.setAttribute( + "style", + `stop-color:${this.props.color};stop-opacity:0.9` + ); + const stop100 = document.createElementNS(svgNS, "stop"); + stop100.setAttribute("offset", "100%"); + stop100.setAttribute( + "style", + `stop-color:${this.props.color};stop-opacity:0` + ); + // Circle. + const circle = document.createElementNS(svgNS, "circle"); + circle.setAttribute("fill", `url(#${gradientId})`); + circle.setAttribute("cx", "50%"); + circle.setAttribute("cy", "50%"); + circle.setAttribute("r", "50%"); + + // Append elements. + radialGradient.append(stop0, stop100); + defs.append(radialGradient); + svg.append(defs, circle); + + return svg; + } +} diff --git a/visual_console_client/src/items/DonutGraph.ts b/visual_console_client/src/items/DonutGraph.ts new file mode 100644 index 0000000000..d60f268567 --- /dev/null +++ b/visual_console_client/src/items/DonutGraph.ts @@ -0,0 +1,78 @@ +import { + LinkedVisualConsoleProps, + UnknownObject, + WithModuleProps +} from "../types"; +import { + linkedVCPropsDecoder, + modulePropsDecoder, + decodeBase64, + stringIsEmpty +} from "../lib"; +import Item, { ItemType, ItemProps, itemBasePropsDecoder } from "../Item"; + +export type DonutGraphProps = { + type: ItemType.DONUT_GRAPH; + html: string; +} & ItemProps & + WithModuleProps & + LinkedVisualConsoleProps; + +/** + * Build a valid typed object from a raw object. + * This will allow us to ensure the type safety. + * + * @param data Raw object. + * @return An object representing the donut graph props. + * @throws Will throw a TypeError if some property + * is missing from the raw object or have an invalid type. + */ +export function donutGraphPropsDecoder( + data: UnknownObject +): DonutGraphProps | never { + if (stringIsEmpty(data.html) && stringIsEmpty(data.encodedHtml)) { + throw new TypeError("missing html content."); + } + + return { + ...itemBasePropsDecoder(data), // Object spread. It will merge the properties of the two objects. + type: ItemType.DONUT_GRAPH, + html: !stringIsEmpty(data.html) + ? data.html + : decodeBase64(data.encodedHtml), + ...modulePropsDecoder(data), // Object spread. It will merge the properties of the two objects. + ...linkedVCPropsDecoder(data) // Object spread. It will merge the properties of the two objects. + }; +} + +export default class DonutGraph extends Item { + protected createDomElement(): HTMLElement { + const element = document.createElement("div"); + element.className = "donut-graph"; + element.innerHTML = this.props.html; + + // Hack to execute the JS after the HTML is added to the DOM. + const scripts = element.getElementsByTagName("script"); + for (let i = 0; i < scripts.length; i++) { + setTimeout(() => { + if (scripts[i].src.length === 0) eval(scripts[i].innerHTML.trim()); + }, 0); + } + + return element; + } + + protected updateDomElement(element: HTMLElement): void { + element.innerHTML = this.props.html; + + // Hack to execute the JS after the HTML is added to the DOM. + const aux = document.createElement("div"); + aux.innerHTML = this.props.html; + const scripts = aux.getElementsByTagName("script"); + for (let i = 0; i < scripts.length; i++) { + if (scripts[i].src.length === 0) { + eval(scripts[i].innerHTML.trim()); + } + } + } +} diff --git a/visual_console_client/src/items/EventsHistory.ts b/visual_console_client/src/items/EventsHistory.ts new file mode 100644 index 0000000000..7e9db0ae9b --- /dev/null +++ b/visual_console_client/src/items/EventsHistory.ts @@ -0,0 +1,78 @@ +import { UnknownObject, WithModuleProps } from "../types"; +import { + modulePropsDecoder, + parseIntOr, + decodeBase64, + stringIsEmpty +} from "../lib"; +import Item, { ItemType, ItemProps, itemBasePropsDecoder } from "../Item"; + +export type EventsHistoryProps = { + type: ItemType.AUTO_SLA_GRAPH; + maxTime: number | null; + html: string; +} & ItemProps & + WithModuleProps; + +/** + * Build a valid typed object from a raw object. + * This will allow us to ensure the type safety. + * + * @param data Raw object. + * @return An object representing the events history props. + * @throws Will throw a TypeError if some property + * is missing from the raw object or have an invalid type. + */ +export function eventsHistoryPropsDecoder( + data: UnknownObject +): EventsHistoryProps | never { + if (stringIsEmpty(data.html) && stringIsEmpty(data.encodedHtml)) { + throw new TypeError("missing html content."); + } + + return { + ...itemBasePropsDecoder(data), // Object spread. It will merge the properties of the two objects. + type: ItemType.AUTO_SLA_GRAPH, + maxTime: parseIntOr(data.maxTime, null), + html: !stringIsEmpty(data.html) + ? data.html + : decodeBase64(data.encodedHtml), + ...modulePropsDecoder(data) // Object spread. It will merge the properties of the two objects. + }; +} + +export default class EventsHistory extends Item { + protected createDomElement(): HTMLElement { + const element = document.createElement("div"); + element.className = "events-history"; + element.innerHTML = this.props.html; + + // Hack to execute the JS after the HTML is added to the DOM. + const scripts = element.getElementsByTagName("script"); + for (let i = 0; i < scripts.length; i++) { + if (scripts[i].src.length === 0) { + setTimeout(() => { + try { + eval(scripts[i].innerHTML.trim()); + } catch (ignored) {} // eslint-disable-line no-empty + }, 0); + } + } + + return element; + } + + protected updateDomElement(element: HTMLElement): void { + element.innerHTML = this.props.html; + + // Hack to execute the JS after the HTML is added to the DOM. + const aux = document.createElement("div"); + aux.innerHTML = this.props.html; + const scripts = aux.getElementsByTagName("script"); + for (let i = 0; i < scripts.length; i++) { + if (scripts[i].src.length === 0) { + eval(scripts[i].innerHTML.trim()); + } + } + } +} diff --git a/visual_console_client/src/items/Group.spec.ts b/visual_console_client/src/items/Group.spec.ts new file mode 100644 index 0000000000..e00a5cf327 --- /dev/null +++ b/visual_console_client/src/items/Group.spec.ts @@ -0,0 +1,44 @@ +import Group, { groupPropsDecoder } from "./Group"; + +const genericRawProps = { + id: 1, + type: 11, // Group item = 11 + label: null, + isLinkEnabled: false, + isOnTop: false, + parentId: null, + aclGroupId: null +}; + +const positionRawProps = { + x: 100, + y: 50 +}; + +const sizeRawProps = { + width: 100, + height: 100 +}; + +const groupRawProps = { + imageSrc: + "https://brutus.artica.lan:8081/uploads/-/system/project/avatar/1/1.png", + groupId: 1 +}; + +describe("Group item", () => { + const groupInstance = new Group( + groupPropsDecoder({ + ...genericRawProps, + ...positionRawProps, + ...sizeRawProps, + ...groupRawProps + }) + ); + + it("should have the group class", () => { + expect( + groupInstance.elementRef.getElementsByClassName("group").length + ).toBeGreaterThan(0); + }); +}); diff --git a/visual_console_client/src/items/Group.ts b/visual_console_client/src/items/Group.ts new file mode 100644 index 0000000000..fa97f69a62 --- /dev/null +++ b/visual_console_client/src/items/Group.ts @@ -0,0 +1,80 @@ +import { LinkedVisualConsoleProps, UnknownObject } from "../types"; +import { + linkedVCPropsDecoder, + parseIntOr, + notEmptyStringOr, + stringIsEmpty, + decodeBase64, + parseBoolean +} from "../lib"; +import Item, { ItemProps, itemBasePropsDecoder, ItemType } from "../Item"; + +export type GroupProps = { + type: ItemType.GROUP_ITEM; + groupId: number; + imageSrc: string | null; // URL? + statusImageSrc: string | null; + showStatistics: boolean; + html?: string | null; +} & ItemProps & + LinkedVisualConsoleProps; + +function extractHtml(data: UnknownObject): string | null { + if (!stringIsEmpty(data.html)) return data.html; + if (!stringIsEmpty(data.encodedHtml)) return decodeBase64(data.encodedHtml); + return null; +} + +/** + * Build a valid typed object from a raw object. + * This will allow us to ensure the type safety. + * + * @param data Raw object. + * @return An object representing the group props. + * @throws Will throw a TypeError if some property + * is missing from the raw object or have an invalid type. + */ +export function groupPropsDecoder(data: UnknownObject): GroupProps | never { + if ( + (typeof data.imageSrc !== "string" || data.imageSrc.length === 0) && + data.encodedHtml === null + ) { + throw new TypeError("invalid image src."); + } + if (parseIntOr(data.groupId, null) === null) { + throw new TypeError("invalid group Id."); + } + + const showStatistics = parseBoolean(data.showStatistics); + const html = showStatistics ? extractHtml(data) : null; + + return { + ...itemBasePropsDecoder(data), // Object spread. It will merge the properties of the two objects. + type: ItemType.GROUP_ITEM, + groupId: parseInt(data.groupId), + imageSrc: notEmptyStringOr(data.imageSrc, null), + statusImageSrc: notEmptyStringOr(data.statusImageSrc, null), + showStatistics, + html, + ...linkedVCPropsDecoder(data) // Object spread. It will merge the properties of the two objects. + }; +} + +export default class Group extends Item { + protected createDomElement(): HTMLElement { + const element = document.createElement("div"); + element.className = "group"; + + if (!this.props.showStatistics && this.props.statusImageSrc !== null) { + // Icon with status. + element.style.background = `url(${this.props.statusImageSrc}) no-repeat`; + element.style.backgroundSize = "contain"; + element.style.backgroundPosition = "center"; + } else if (this.props.showStatistics && this.props.html != null) { + // Stats table. + element.innerHTML = this.props.html; + } + + return element; + } +} diff --git a/visual_console_client/src/items/Icon.ts b/visual_console_client/src/items/Icon.ts new file mode 100644 index 0000000000..12c1b035c7 --- /dev/null +++ b/visual_console_client/src/items/Icon.ts @@ -0,0 +1,43 @@ +import { LinkedVisualConsoleProps, UnknownObject } from "../types"; +import { linkedVCPropsDecoder } from "../lib"; +import Item, { ItemType, ItemProps, itemBasePropsDecoder } from "../Item"; + +export type IconProps = { + type: ItemType.ICON; + imageSrc: string; // URL? +} & ItemProps & + LinkedVisualConsoleProps; + +/** + * Build a valid typed object from a raw object. + * This will allow us to ensure the type safety. + * + * @param data Raw object. + * @return An object representing the icon props. + * @throws Will throw a TypeError if some property + * is missing from the raw object or have an invalid type. + */ +export function iconPropsDecoder(data: UnknownObject): IconProps | never { + if (typeof data.imageSrc !== "string" || data.imageSrc.length === 0) { + throw new TypeError("invalid image src."); + } + + return { + ...itemBasePropsDecoder(data), // Object spread. It will merge the properties of the two objects. + type: ItemType.ICON, + imageSrc: data.imageSrc, + ...linkedVCPropsDecoder(data) // Object spread. It will merge the properties of the two objects. + }; +} + +export default class Icon extends Item { + protected createDomElement(): HTMLElement { + const element = document.createElement("div"); + element.className = "icon"; + element.style.background = `url(${this.props.imageSrc}) no-repeat`; + element.style.backgroundSize = "contain"; + element.style.backgroundPosition = "center"; + + return element; + } +} diff --git a/visual_console_client/src/items/Label.ts b/visual_console_client/src/items/Label.ts new file mode 100644 index 0000000000..c8de572c15 --- /dev/null +++ b/visual_console_client/src/items/Label.ts @@ -0,0 +1,47 @@ +import { LinkedVisualConsoleProps, UnknownObject } from "../types"; +import { linkedVCPropsDecoder } from "../lib"; +import Item, { ItemType, ItemProps, itemBasePropsDecoder } from "../Item"; + +export type LabelProps = { + type: ItemType.LABEL; +} & ItemProps & + LinkedVisualConsoleProps; + +/** + * Build a valid typed object from a raw object. + * This will allow us to ensure the type safety. + * + * @param data Raw object. + * @return An object representing the label props. + * @throws Will throw a TypeError if some property + * is missing from the raw object or have an invalid type. + */ +export function labelPropsDecoder(data: UnknownObject): LabelProps | never { + return { + ...itemBasePropsDecoder(data), // Object spread. It will merge the properties of the two objects. + type: ItemType.LABEL, + ...linkedVCPropsDecoder(data) // Object spread. It will merge the properties of the two objects. + }; +} + +export default class Label extends Item { + protected createDomElement(): HTMLElement { + const element = document.createElement("div"); + element.className = "label"; + element.innerHTML = this.getLabelWithMacrosReplaced(); + + return element; + } + + /** + * @override Item.createLabelDomElement + * Create a new label for the visual console item. + * @return Item label. + */ + public createLabelDomElement(): HTMLElement { + const element = document.createElement("div"); + element.className = "visual-console-item-label"; + // Always return an empty label. + return element; + } +} diff --git a/visual_console_client/src/items/Line.ts b/visual_console_client/src/items/Line.ts new file mode 100644 index 0000000000..20532e44ea --- /dev/null +++ b/visual_console_client/src/items/Line.ts @@ -0,0 +1,147 @@ +import { UnknownObject, Position, Size } from "../types"; +import { parseIntOr, notEmptyStringOr } from "../lib"; +import Item, { ItemType, ItemProps, itemBasePropsDecoder } from "../Item"; + +interface LineProps extends ItemProps { + // Overrided properties. + readonly type: ItemType.LINE_ITEM; + label: null; + isLinkEnabled: false; + parentId: null; + aclGroupId: null; + // Custom properties. + startPosition: Position; + endPosition: Position; + lineWidth: number; + color: string | null; +} + +/** + * Build a valid typed object from a raw object. + * This will allow us to ensure the type safety. + * + * @param data Raw object. + * @return An object representing the item props. + * @throws Will throw a TypeError if some property + * is missing from the raw object or have an invalid type. + */ +export function linePropsDecoder(data: UnknownObject): LineProps | never { + const props: LineProps = { + ...itemBasePropsDecoder({ ...data, width: 1, height: 1 }), // Object spread. It will merge the properties of the two objects. + type: ItemType.LINE_ITEM, + label: null, + isLinkEnabled: false, + parentId: null, + aclGroupId: null, + // Initialize Position & Size. + x: 0, + y: 0, + width: 0, + height: 0, + // Custom properties. + startPosition: { + x: parseIntOr(data.startX, 0), + y: parseIntOr(data.startY, 0) + }, + endPosition: { + x: parseIntOr(data.endX, 0), + y: parseIntOr(data.endY, 0) + }, + lineWidth: parseIntOr(data.lineWidth || data.borderWidth, 1), + color: notEmptyStringOr(data.borderColor || data.color, null) + }; + + /* + * We need to enhance the props with the extracted size and position + * of the box cause there are missing at the props update. A better + * solution would be overriding the props setter to do it there, but + * the language doesn't allow it while targetting ES5. + * TODO: We need to figure out a more consistent solution. + */ + + return { + ...props, + // Enhance the props extracting the box size and position. + // eslint-disable-next-line @typescript-eslint/no-use-before-define + ...Line.extractBoxSizeAndPosition(props) + }; +} + +export default class Line extends Item { + /** + * @override + */ + public constructor(props: LineProps) { + /* + * We need to override the constructor cause we need to obtain + * the + * box size and position from the start and finish points + * of the line. + */ + super({ + ...props, + ...Line.extractBoxSizeAndPosition(props) + }); + } + + /** + * @override + * To create the item's DOM representation. + * @return Item. + */ + protected createDomElement(): HTMLElement { + const element: HTMLDivElement = document.createElement("div"); + element.className = "line"; + + const svgNS = "http://www.w3.org/2000/svg"; + // SVG container. + const svg = document.createElementNS(svgNS, "svg"); + // Set SVG size. + svg.setAttribute( + "width", + (this.props.width + this.props.lineWidth).toString() + ); + svg.setAttribute( + "height", + (this.props.height + this.props.lineWidth).toString() + ); + const line = document.createElementNS(svgNS, "line"); + line.setAttribute( + "x1", + `${this.props.startPosition.x - this.props.x + this.props.lineWidth / 2}` + ); + line.setAttribute( + "y1", + `${this.props.startPosition.y - this.props.y + this.props.lineWidth / 2}` + ); + line.setAttribute( + "x2", + `${this.props.endPosition.x - this.props.x + this.props.lineWidth / 2}` + ); + line.setAttribute( + "y2", + `${this.props.endPosition.y - this.props.y + this.props.lineWidth / 2}` + ); + line.setAttribute("stroke", this.props.color || "black"); + line.setAttribute("stroke-width", this.props.lineWidth.toString()); + + svg.append(line); + element.append(svg); + + return element; + } + + /** + * Extract the size and position of the box from + * the start and the finish of the line. + * @param props Item properties. + */ + public static extractBoxSizeAndPosition(props: LineProps): Size & Position { + return { + width: Math.abs(props.startPosition.x - props.endPosition.x), + height: Math.abs(props.startPosition.y - props.endPosition.y), + x: Math.min(props.startPosition.x, props.endPosition.x), + y: Math.min(props.startPosition.y, props.endPosition.y) + }; + } +} diff --git a/visual_console_client/src/items/ModuleGraph.ts b/visual_console_client/src/items/ModuleGraph.ts new file mode 100644 index 0000000000..3440496d19 --- /dev/null +++ b/visual_console_client/src/items/ModuleGraph.ts @@ -0,0 +1,118 @@ +import { + LinkedVisualConsoleProps, + UnknownObject, + WithModuleProps +} from "../types"; +import { + linkedVCPropsDecoder, + modulePropsDecoder, + decodeBase64, + stringIsEmpty +} from "../lib"; +import Item, { ItemType, ItemProps, itemBasePropsDecoder } from "../Item"; + +export type ModuleGraphProps = { + type: ItemType.MODULE_GRAPH; + html: string; +} & ItemProps & + WithModuleProps & + LinkedVisualConsoleProps; + +/** + * Build a valid typed object from a raw object. + * This will allow us to ensure the type safety. + * + * @param data Raw object. + * @return An object representing the module graph props. + * @throws Will throw a TypeError if some property + * is missing from the raw object or have an invalid type. + */ +export function moduleGraphPropsDecoder( + data: UnknownObject +): ModuleGraphProps | never { + if (stringIsEmpty(data.html) && stringIsEmpty(data.encodedHtml)) { + throw new TypeError("missing html content."); + } + + return { + ...itemBasePropsDecoder(data), // Object spread. It will merge the properties of the two objects. + type: ItemType.MODULE_GRAPH, + html: !stringIsEmpty(data.html) + ? data.html + : decodeBase64(data.encodedHtml), + ...modulePropsDecoder(data), // Object spread. It will merge the properties of the two objects. + ...linkedVCPropsDecoder(data) // Object spread. It will merge the properties of the two objects. + }; +} + +export default class ModuleGraph extends Item { + /** + * @override Item.resizeElement. + * Resize the DOM content container. + * We need to override the resize function cause this item's height + * is larger than the configured and the graph is over the label. + * @param width + * @param height + */ + protected resizeElement(width: number): void { + super.resizeElement(width, 0); + } + + protected createDomElement(): HTMLElement { + const element = document.createElement("div"); + element.className = "module-graph"; + element.innerHTML = this.props.html; + + // Remove the overview graph. + const legendP = element.getElementsByTagName("p"); + for (let i = 0; i < legendP.length; i++) { + legendP[i].style.margin = "0px"; + } + + // Remove the overview graph. + const overviewGraphs = element.getElementsByClassName("overview_graph"); + for (let i = 0; i < overviewGraphs.length; i++) { + overviewGraphs[i].remove(); + } + + // Hack to execute the JS after the HTML is added to the DOM. + const scripts = element.getElementsByTagName("script"); + for (let i = 0; i < scripts.length; i++) { + if (scripts[i].src.length === 0) { + setTimeout(() => { + try { + eval(scripts[i].innerHTML.trim()); + } catch (ignored) {} // eslint-disable-line no-empty + }, 0); + } + } + + return element; + } + + protected updateDomElement(element: HTMLElement): void { + element.innerHTML = this.props.html; + + // Remove the overview graph. + const legendP = element.getElementsByTagName("p"); + for (let i = 0; i < legendP.length; i++) { + legendP[i].style.margin = "0px"; + } + + // Remove the overview graph. + const overviewGraphs = element.getElementsByClassName("overview_graph"); + for (let i = 0; i < overviewGraphs.length; i++) { + overviewGraphs[i].remove(); + } + + // Hack to execute the JS after the HTML is added to the DOM. + const aux = document.createElement("div"); + aux.innerHTML = this.props.html; + const scripts = aux.getElementsByTagName("script"); + for (let i = 0; i < scripts.length; i++) { + if (scripts[i].src.length === 0) { + eval(scripts[i].innerHTML.trim()); + } + } + } +} diff --git a/visual_console_client/src/items/Percentile.ts b/visual_console_client/src/items/Percentile.ts new file mode 100644 index 0000000000..25b5cc4a43 --- /dev/null +++ b/visual_console_client/src/items/Percentile.ts @@ -0,0 +1,260 @@ +import { arc as arcFactory } from "d3-shape"; + +import { + LinkedVisualConsoleProps, + UnknownObject, + WithModuleProps +} from "../types"; +import { + linkedVCPropsDecoder, + modulePropsDecoder, + notEmptyStringOr, + parseIntOr, + parseFloatOr +} from "../lib"; +import Item, { ItemType, ItemProps, itemBasePropsDecoder } from "../Item"; + +export type PercentileProps = { + type: ItemType.PERCENTILE_BAR; + percentileType: + | "progress-bar" + | "bubble" + | "circular-progress-bar" + | "circular-progress-bar-alt"; + valueType: "percent" | "value"; + minValue: number | null; + maxValue: number | null; + color: string | null; + labelColor: string | null; + value: number | null; + unit: string | null; +} & ItemProps & + WithModuleProps & + LinkedVisualConsoleProps; + +/** + * Extract a valid enum value from a raw type value. + * @param type Raw value. + */ +// eslint-disable-next-line @typescript-eslint/no-explicit-any +function extractPercentileType(type: any): PercentileProps["percentileType"] { + switch (type) { + case "progress-bar": + case "bubble": + case "circular-progress-bar": + case "circular-progress-bar-alt": + return type; + default: + case ItemType.PERCENTILE_BAR: + return "progress-bar"; + case ItemType.PERCENTILE_BUBBLE: + return "bubble"; + case ItemType.CIRCULAR_PROGRESS_BAR: + return "circular-progress-bar"; + case ItemType.CIRCULAR_INTERIOR_PROGRESS_BAR: + return "circular-progress-bar-alt"; + } +} + +/** + * Extract a valid enum value from a raw value type value. + * @param type Raw value. + */ +// eslint-disable-next-line @typescript-eslint/no-explicit-any +function extractValueType(valueType: any): PercentileProps["valueType"] { + switch (valueType) { + case "percent": + case "value": + return valueType; + default: + return "percent"; + } +} + +/** + * Build a valid typed object from a raw object. + * This will allow us to ensure the type safety. + * + * @param data Raw object. + * @return An object representing the percentile props. + * @throws Will throw a TypeError if some property + * is missing from the raw object or have an invalid type. + */ +export function percentilePropsDecoder( + data: UnknownObject +): PercentileProps | never { + return { + ...itemBasePropsDecoder(data), // Object spread. It will merge the properties of the two objects. + type: ItemType.PERCENTILE_BAR, + percentileType: extractPercentileType(data.percentileType || data.type), + valueType: extractValueType(data.valueType), + minValue: parseIntOr(data.minValue, null), + maxValue: parseIntOr(data.maxValue, null), + color: notEmptyStringOr(data.color, null), + labelColor: notEmptyStringOr(data.labelColor, null), + value: parseFloatOr(data.value, null), + unit: notEmptyStringOr(data.unit, null), + ...modulePropsDecoder(data), // Object spread. It will merge the properties of the two objects. + ...linkedVCPropsDecoder(data) // Object spread. It will merge the properties of the two objects. + }; +} + +const svgNS = "http://www.w3.org/2000/svg"; + +export default class Percentile extends Item { + protected createDomElement(): HTMLElement { + const colors = { + background: "#000000", + progress: this.props.color || "#F0F0F0", + text: this.props.labelColor || "#444444" + }; + // Progress. + const progress = this.getProgress(); + // Main element. + const element = document.createElement("div"); + // SVG container. + const svg = document.createElementNS(svgNS, "svg"); + + switch (this.props.percentileType) { + case "progress-bar": + { + const backgroundRect = document.createElementNS(svgNS, "rect"); + backgroundRect.setAttribute("fill", colors.background); + backgroundRect.setAttribute("fill-opacity", "0.5"); + backgroundRect.setAttribute("width", "100"); + backgroundRect.setAttribute("height", "20"); + backgroundRect.setAttribute("rx", "5"); + backgroundRect.setAttribute("ry", "5"); + const progressRect = document.createElementNS(svgNS, "rect"); + progressRect.setAttribute("fill", colors.progress); + progressRect.setAttribute("fill-opacity", "1"); + progressRect.setAttribute("width", `${progress}`); + progressRect.setAttribute("height", "20"); + progressRect.setAttribute("rx", "5"); + progressRect.setAttribute("ry", "5"); + const text = document.createElementNS(svgNS, "text"); + text.setAttribute("text-anchor", "middle"); + text.setAttribute("alignment-baseline", "middle"); + text.setAttribute("font-size", "12"); + text.setAttribute("font-family", "arial"); + text.setAttribute("font-weight", "bold"); + text.setAttribute("transform", "translate(50 11)"); + text.setAttribute("fill", colors.text); + + if (this.props.valueType === "value") { + text.textContent = this.props.unit + ? `${this.props.value} ${this.props.unit}` + : `${this.props.value}`; + } else { + text.textContent = `${progress}%`; + } + + // Auto resize SVG using the view box magic: https://css-tricks.com/scale-svg/ + svg.setAttribute("viewBox", "0 0 100 20"); + svg.append(backgroundRect, progressRect, text); + } + break; + case "bubble": + case "circular-progress-bar": + case "circular-progress-bar-alt": + { + // Auto resize SVG using the view box magic: https://css-tricks.com/scale-svg/ + svg.setAttribute("viewBox", "0 0 100 100"); + + if (this.props.percentileType === "bubble") { + // Create and append the circles. + const backgroundCircle = document.createElementNS(svgNS, "circle"); + backgroundCircle.setAttribute("transform", "translate(50 50)"); + backgroundCircle.setAttribute("fill", colors.background); + backgroundCircle.setAttribute("fill-opacity", "0.5"); + backgroundCircle.setAttribute("r", "50"); + const progressCircle = document.createElementNS(svgNS, "circle"); + progressCircle.setAttribute("transform", "translate(50 50)"); + progressCircle.setAttribute("fill", colors.progress); + progressCircle.setAttribute("fill-opacity", "1"); + progressCircle.setAttribute("r", `${progress / 2}`); + + svg.append(backgroundCircle, progressCircle); + } else { + // Create and append the circles. + const arcProps = { + innerRadius: + this.props.percentileType === "circular-progress-bar" ? 30 : 0, + outerRadius: 50, + startAngle: 0, + endAngle: Math.PI * 2 + }; + const arc = arcFactory(); + + const backgroundCircle = document.createElementNS(svgNS, "path"); + backgroundCircle.setAttribute("transform", "translate(50 50)"); + backgroundCircle.setAttribute("fill", colors.background); + backgroundCircle.setAttribute("fill-opacity", "0.5"); + backgroundCircle.setAttribute("d", `${arc(arcProps)}`); + const progressCircle = document.createElementNS(svgNS, "path"); + progressCircle.setAttribute("transform", "translate(50 50)"); + progressCircle.setAttribute("fill", colors.progress); + progressCircle.setAttribute("fill-opacity", "1"); + progressCircle.setAttribute( + "d", + `${arc({ + ...arcProps, + endAngle: arcProps.endAngle * (progress / 100) + })}` + ); + + svg.append(backgroundCircle, progressCircle); + } + + // Create and append the text. + const text = document.createElementNS(svgNS, "text"); + text.setAttribute("text-anchor", "middle"); + text.setAttribute("alignment-baseline", "middle"); + text.setAttribute("font-size", "16"); + text.setAttribute("font-family", "arial"); + text.setAttribute("font-weight", "bold"); + text.setAttribute("fill", colors.text); + + if (this.props.valueType === "value") { + // Show value and unit in 1 (no unit) or 2 lines. + if (this.props.unit && this.props.unit.length > 0) { + const value = document.createElementNS(svgNS, "tspan"); + value.setAttribute("x", "0"); + value.setAttribute("dy", "1em"); + value.textContent = `${this.props.value}`; + const unit = document.createElementNS(svgNS, "tspan"); + unit.setAttribute("x", "0"); + unit.setAttribute("dy", "1em"); + unit.textContent = `${this.props.unit}`; + text.append(value, unit); + text.setAttribute("transform", "translate(50 33)"); + } else { + text.textContent = `${this.props.value}`; + text.setAttribute("transform", "translate(50 50)"); + } + } else { + // Percentage. + text.textContent = `${progress}%`; + text.setAttribute("transform", "translate(50 50)"); + } + + svg.append(text); + } + break; + } + + element.append(svg); + + return element; + } + + private getProgress(): number { + const minValue = this.props.minValue || 0; + const maxValue = this.props.maxValue || 100; + const value = this.props.value || 100; + + if (value <= minValue) return 0; + else if (value >= maxValue) return 100; + else return ((value - minValue) / (maxValue - minValue)) * 100; + } +} diff --git a/visual_console_client/src/items/Service.ts b/visual_console_client/src/items/Service.ts new file mode 100644 index 0000000000..9440503e80 --- /dev/null +++ b/visual_console_client/src/items/Service.ts @@ -0,0 +1,70 @@ +import { UnknownObject } from "../types"; +import { + stringIsEmpty, + notEmptyStringOr, + decodeBase64, + parseIntOr +} from "../lib"; +import Item, { ItemType, ItemProps, itemBasePropsDecoder } from "../Item"; + +export type ServiceProps = { + type: ItemType.SERVICE; + serviceId: number; + imageSrc: string | null; + statusImageSrc: string | null; + encodedTitle: string | null; +} & ItemProps; + +/** + * Build a valid typed object from a raw object. + * This will allow us to ensure the type safety. + * + * @param data Raw object. + * @return An object representing the service props. + * @throws Will throw a TypeError if some property + * is missing from the raw object or have an invalid type. + */ +export function servicePropsDecoder(data: UnknownObject): ServiceProps | never { + if (data.imageSrc !== null) { + if ( + typeof data.statusImageSrc !== "string" || + data.imageSrc.statusImageSrc === 0 + ) { + throw new TypeError("invalid status image src."); + } + } else { + if (stringIsEmpty(data.encodedTitle)) { + throw new TypeError("missing encode tittle content."); + } + } + + if (parseIntOr(data.serviceId, null) === null) { + throw new TypeError("invalid service id."); + } + + return { + ...itemBasePropsDecoder(data), // Object spread. It will merge the properties of the two objects. + type: ItemType.SERVICE, + serviceId: data.serviceId, + imageSrc: notEmptyStringOr(data.imageSrc, null), + statusImageSrc: notEmptyStringOr(data.statusImageSrc, null), + encodedTitle: notEmptyStringOr(data.encodedTitle, null) + }; +} + +export default class Service extends Item { + public createDomElement(): HTMLElement { + const element = document.createElement("div"); + element.className = "service"; + + if (this.props.statusImageSrc !== null) { + element.style.background = `url(${this.props.statusImageSrc}) no-repeat`; + element.style.backgroundSize = "contain"; + element.style.backgroundPosition = "center"; + } else if (this.props.encodedTitle !== null) { + element.innerHTML = decodeBase64(this.props.encodedTitle); + } + + return element; + } +} diff --git a/visual_console_client/src/items/SimpleValue.ts b/visual_console_client/src/items/SimpleValue.ts new file mode 100644 index 0000000000..8464881a61 --- /dev/null +++ b/visual_console_client/src/items/SimpleValue.ts @@ -0,0 +1,129 @@ +import { + LinkedVisualConsoleProps, + UnknownObject, + WithModuleProps +} from "../types"; +import { + linkedVCPropsDecoder, + parseIntOr, + modulePropsDecoder, + replaceMacros +} from "../lib"; +import Item, { ItemType, ItemProps, itemBasePropsDecoder } from "../Item"; + +export type SimpleValueProps = { + type: ItemType.SIMPLE_VALUE; + valueType: "string" | "image"; + value: string; +} & ( + | { + processValue: "none"; + } + | { + processValue: "avg" | "max" | "min"; + period: number; + }) & + ItemProps & + WithModuleProps & + LinkedVisualConsoleProps; + +/** + * Extract a valid enum value from a raw value type. + * @param valueType Raw value. + */ +const parseValueType = ( + valueType: any // eslint-disable-line @typescript-eslint/no-explicit-any +): SimpleValueProps["valueType"] => { + switch (valueType) { + case "string": + case "image": + return valueType; + default: + return "string"; + } +}; + +/** + * Extract a valid enum value from a raw process value. + * @param processValue Raw value. + */ +const parseProcessValue = ( + processValue: any // eslint-disable-line @typescript-eslint/no-explicit-any +): SimpleValueProps["processValue"] => { + switch (processValue) { + case "none": + case "avg": + case "max": + case "min": + return processValue; + default: + return "none"; + } +}; + +/** + * Build a valid typed object from a raw object. + * This will allow us to ensure the type safety. + * + * @param data Raw object. + * @return An object representing the simple value props. + * @throws Will throw a TypeError if some property + * is missing from the raw object or have an invalid type. + */ +export function simpleValuePropsDecoder( + data: UnknownObject +): SimpleValueProps | never { + if (typeof data.value !== "string" || data.value.length === 0) { + throw new TypeError("invalid value"); + } + + const processValue = parseProcessValue(data.processValue); + + return { + ...itemBasePropsDecoder(data), // Object spread. It will merge the properties of the two objects. + type: ItemType.SIMPLE_VALUE, + valueType: parseValueType(data.valueType), + value: data.value, + ...(processValue === "none" + ? { processValue } + : { processValue, period: parseIntOr(data.period, 0) }), // Object spread. It will merge the properties of the two objects. + ...modulePropsDecoder(data), // Object spread. It will merge the properties of the two objects. + ...linkedVCPropsDecoder(data) // Object spread. It will merge the properties of the two objects. + }; +} + +export default class SimpleValue extends Item { + protected createDomElement(): HTMLElement { + const element = document.createElement("div"); + element.className = "simple-value"; + + if (this.props.valueType === "image") { + const img = document.createElement("img"); + img.src = this.props.value; + element.append(img); + } else { + // Add the value to the label and show it. + let text = this.props.value; + let label = this.getLabelWithMacrosReplaced(); + if (label.length > 0) { + text = replaceMacros([{ macro: /\(?_VALUE_\)?/i, value: text }], label); + } + + element.innerHTML = text; + } + + return element; + } + + /** + * @override Item.createLabelDomElement + * Create a new label for the visual console item. + * @return Item label. + */ + protected createLabelDomElement(): HTMLElement { + const element = document.createElement("div"); + element.className = "visual-console-item-label"; + // Always return an empty label. + return element; + } +} diff --git a/visual_console_client/src/items/StaticGraph.ts b/visual_console_client/src/items/StaticGraph.ts new file mode 100644 index 0000000000..0e713dc5f3 --- /dev/null +++ b/visual_console_client/src/items/StaticGraph.ts @@ -0,0 +1,89 @@ +import { + WithModuleProps, + LinkedVisualConsoleProps, + UnknownObject +} from "../types"; + +import { + modulePropsDecoder, + linkedVCPropsDecoder, + notEmptyStringOr +} from "../lib"; +import Item, { ItemType, ItemProps, itemBasePropsDecoder } from "../Item"; + +export type StaticGraphProps = { + type: ItemType.STATIC_GRAPH; + imageSrc: string; // URL? + showLastValueTooltip: "default" | "enabled" | "disabled"; + statusImageSrc: string | null; // URL? + lastValue: string | null; +} & ItemProps & + (WithModuleProps | LinkedVisualConsoleProps); + +/** + * Extract a valid enum value from a raw unknown value. + * @param showLastValueTooltip Raw value. + */ +const parseShowLastValueTooltip = ( + showLastValueTooltip: any // eslint-disable-line @typescript-eslint/no-explicit-any +): StaticGraphProps["showLastValueTooltip"] => { + switch (showLastValueTooltip) { + case "default": + case "enabled": + case "disabled": + return showLastValueTooltip; + default: + return "default"; + } +}; + +/** + * Build a valid typed object from a raw object. + * This will allow us to ensure the type safety. + * + * @param data Raw object. + * @return An object representing the static graph props. + * @throws Will throw a TypeError if some property + * is missing from the raw object or have an invalid type. + */ +export function staticGraphPropsDecoder( + data: UnknownObject +): StaticGraphProps | never { + if (typeof data.imageSrc !== "string" || data.imageSrc.length === 0) { + throw new TypeError("invalid image src."); + } + + return { + ...itemBasePropsDecoder(data), // Object spread. It will merge the properties of the two objects. + type: ItemType.STATIC_GRAPH, + imageSrc: data.imageSrc, + showLastValueTooltip: parseShowLastValueTooltip(data.showLastValueTooltip), + statusImageSrc: notEmptyStringOr(data.statusImageSrc, null), + lastValue: notEmptyStringOr(data.lastValue, null), + ...modulePropsDecoder(data), // Object spread. It will merge the properties of the two objects. + ...linkedVCPropsDecoder(data) // Object spread. It will merge the properties of the two objects. + }; +} + +export default class StaticGraph extends Item { + protected createDomElement(): HTMLElement { + const imgSrc = this.props.statusImageSrc || this.props.imageSrc; + const element = document.createElement("div"); + element.className = "static-graph"; + element.style.background = `url(${imgSrc}) no-repeat`; + element.style.backgroundSize = "contain"; + element.style.backgroundPosition = "center"; + + // Show last value in a tooltip. + if ( + this.props.lastValue !== null && + this.props.showLastValueTooltip !== "disabled" + ) { + element.className = "static-graph image forced_title"; + element.setAttribute("data-use_title_for_force_title", "1"); + element.setAttribute("data-title", this.props.lastValue); + } + + return element; + } +} diff --git a/visual_console_client/src/lib.spec.ts b/visual_console_client/src/lib.spec.ts new file mode 100644 index 0000000000..3330773be7 --- /dev/null +++ b/visual_console_client/src/lib.spec.ts @@ -0,0 +1,120 @@ +import { + parseIntOr, + stringIsEmpty, + notEmptyStringOr, + leftPad, + prefixedCssRules, + decodeBase64, + humanDate, + humanTime, + replaceMacros +} from "./lib"; + +describe("function parseIntOr", () => { + it("should retrieve valid int or a default value", () => { + expect(parseIntOr("Foo", null)).toBe(null); + expect(parseIntOr("1a", null)).toBe(1); + expect(parseIntOr("a1", null)).toBe(null); + expect(parseIntOr("1", null)).toBe(1); + expect(parseIntOr(false, null)).toBe(null); + expect(parseIntOr(true, null)).toBe(null); + expect(parseIntOr(1, null)).toBe(1); + }); +}); + +describe("function stringIsEmpty", () => { + it("should check properly if a string is empry or not", () => { + expect(stringIsEmpty()).toBe(true); + expect(stringIsEmpty("")).toBe(true); + expect(stringIsEmpty("foo")).toBe(false); + expect(stringIsEmpty("bar")).toBe(false); + }); +}); + +describe("function notEmptyStringOr", () => { + it("should retrieve not empty string or a default value", () => { + expect(notEmptyStringOr("", null)).toBe(null); + expect(notEmptyStringOr("Foo", null)).toBe("Foo"); + expect(notEmptyStringOr(1, 1)).toBe(1); + expect(notEmptyStringOr(1, 0)).toBe(0); + expect(notEmptyStringOr("", 0)).toBe(0); + expect(notEmptyStringOr("Foo", "Bar")).toBe("Foo"); + expect(notEmptyStringOr(0, "Bar")).toBe("Bar"); + }); +}); + +describe("function leftPad", () => { + it("should pad properly", () => { + expect(leftPad(1, 2, 0)).toBe("01"); + expect(leftPad(1, 4, 0)).toBe("0001"); + expect(leftPad(1, 4, "0")).toBe("0001"); + expect(leftPad("1", 4, "0")).toBe("0001"); + expect(leftPad(10, 4, 0)).toBe("0010"); + expect(leftPad("bar", 6, "foo")).toBe("foobar"); + expect(leftPad("bar", 11, "foo")).toBe("foofoofobar"); + expect(leftPad("bar", 4, "foo")).toBe("fbar"); + expect(leftPad("bar", 2, "foo")).toBe("ar"); + expect(leftPad("bar", 3, "foo")).toBe("bar"); + }); +}); + +describe("function prefixedCssRules", () => { + it("should add the prefixes to the rules", () => { + const rules = prefixedCssRules("transform", "rotate(0)"); + expect(rules).toContainEqual("transform: rotate(0);"); + expect(rules).toContainEqual("-webkit-transform: rotate(0);"); + expect(rules).toContainEqual("-moz-transform: rotate(0);"); + expect(rules).toContainEqual("-ms-transform: rotate(0);"); + expect(rules).toContainEqual("-o-transform: rotate(0);"); + expect(rules).toHaveLength(5); + }); +}); + +describe("function decodeBase64", () => { + it("should decode the base64 without errors", () => { + expect(decodeBase64("SGkgSSdtIGRlY29kZWQ=")).toEqual("Hi I'm decoded"); + expect(decodeBase64("Rk9PQkFSQkFa")).toEqual("FOOBARBAZ"); + expect(decodeBase64("eyJpZCI6MSwibmFtZSI6ImZvbyJ9")).toEqual( + '{"id":1,"name":"foo"}' + ); + expect( + decodeBase64("PGRpdj5Cb3ggPHA+UGFyYWdyYXBoPC9wPjxociAvPjwvZGl2Pg==") + ).toEqual("
Box

Paragraph


"); + }); +}); + +describe("humanDate function", () => { + it("should return the date with padded 0's", () => { + const expected = "01/02/0123"; + const date = new Date(`02/01/0123 12:00:00`); + const digitalDate = humanDate(date); + expect(digitalDate).toBe(expected); + }); +}); + +describe("humanTime function", () => { + it("should return the time with padded 0's when hours/minutes/seconds are less than 10", () => { + const expected = "01:02:03"; + const date = new Date(`01/01/1970 ${expected}`); + const digitalTime = humanTime(date); + expect(digitalTime).toBe(expected); + }); +}); + +describe("replaceMacros function", () => { + const macros = [ + { macro: "_foo_", value: "foo" }, + { macro: "_bar_", value: "bar" }, + { macro: "_baz_", value: "baz" } + ]; + + it("should not replace anything if it doesn't find any macro", () => { + const text = "Lorem Ipsum"; + expect(replaceMacros(macros, text)).toBe(text); + }); + + it("should replace the macros", () => { + const text = "Lorem _foo_ Ipsum _baz_"; + expect(replaceMacros(macros, text)).toBe("Lorem foo Ipsum baz"); + }); +}); diff --git a/visual_console_client/src/lib.ts b/visual_console_client/src/lib.ts new file mode 100644 index 0000000000..57b4f1c221 --- /dev/null +++ b/visual_console_client/src/lib.ts @@ -0,0 +1,335 @@ +import { + UnknownObject, + Position, + Size, + WithAgentProps, + WithModuleProps, + LinkedVisualConsoleProps, + LinkedVisualConsolePropsStatus +} from "./types"; + +/** + * Return a number or a default value from a raw value. + * @param value Raw value from which we will try to extract a valid number. + * @param defaultValue Default value to use if we cannot extract a valid number. + * @return A valid number or the default value. + */ +// eslint-disable-next-line @typescript-eslint/no-explicit-any +export function parseIntOr(value: any, defaultValue: T): number | T { + if (typeof value === "number") return value; + if (typeof value === "string" && value.length > 0 && !isNaN(parseInt(value))) + return parseInt(value); + else return defaultValue; +} + +/** + * Return a number or a default value from a raw value. + * @param value Raw value from which we will try to extract a valid number. + * @param defaultValue Default value to use if we cannot extract a valid number. + * @return A valid number or the default value. + */ +// eslint-disable-next-line @typescript-eslint/no-explicit-any +export function parseFloatOr(value: any, defaultValue: T): number | T { + if (typeof value === "number") return value; + if ( + typeof value === "string" && + value.length > 0 && + !isNaN(parseFloat(value)) + ) + return parseFloat(value); + else return defaultValue; +} + +/** + * Check if a string exists and it's not empty. + * @param value Value to check. + * @return The check result. + */ +export function stringIsEmpty(value?: string | null): boolean { + return value == null || value.length === 0; +} + +/** + * Return a not empty string or a default value from a raw value. + * @param value Raw value from which we will try to extract a non empty string. + * @param defaultValue Default value to use if we cannot extract a non empty string. + * @return A non empty string or the default value. + */ +// eslint-disable-next-line @typescript-eslint/no-explicit-any +export function notEmptyStringOr(value: any, defaultValue: T): string | T { + return typeof value === "string" && value.length > 0 ? value : defaultValue; +} + +/** + * Return a boolean from a raw value. + * @param value Raw value from which we will try to extract the boolean. + * @return A valid boolean value. false by default. + */ +// eslint-disable-next-line @typescript-eslint/no-explicit-any +export function parseBoolean(value: any): boolean { + if (typeof value === "boolean") return value; + else if (typeof value === "number") return value > 0; + else if (typeof value === "string") return value === "1" || value === "true"; + else return false; +} + +/** + * Pad the current string with another string (multiple times, if needed) + * until the resulting string reaches the given length. + * The padding is applied from the start (left) of the current string. + * @param value Text that needs to be padded. + * @param length Length of the returned text. + * @param pad Text to add. + * @return Padded text. + */ +export function leftPad( + value: string | number, + length: number, + pad: string | number = " " +): string { + if (typeof value === "number") value = `${value}`; + if (typeof pad === "number") pad = `${pad}`; + + const diffLength = length - value.length; + if (diffLength === 0) return value; + if (diffLength < 0) return value.substr(Math.abs(diffLength)); + + if (diffLength === pad.length) return `${pad}${value}`; + if (diffLength < pad.length) return `${pad.substring(0, diffLength)}${value}`; + + const repeatTimes = Math.floor(diffLength / pad.length); + const restLength = diffLength - pad.length * repeatTimes; + + let newPad = ""; + for (let i = 0; i < repeatTimes; i++) newPad += pad; + + if (restLength === 0) return `${newPad}${value}`; + return `${newPad}${pad.substring(0, restLength)}${value}`; +} + +/* Decoders */ + +/** + * Build a valid typed object from a raw object. + * @param data Raw object. + * @return An object representing the position. + */ +export function positionPropsDecoder(data: UnknownObject): Position { + return { + x: parseIntOr(data.x, 0), + y: parseIntOr(data.y, 0) + }; +} + +/** + * Build a valid typed object from a raw object. + * @param data Raw object. + * @return An object representing the size. + * @throws Will throw a TypeError if the width and height are not valid numbers. + */ +export function sizePropsDecoder(data: UnknownObject): Size | never { + if ( + data.width == null || + isNaN(parseInt(data.width)) || + data.height == null || + isNaN(parseInt(data.height)) + ) { + throw new TypeError("invalid size."); + } + + return { + width: parseInt(data.width), + height: parseInt(data.height) + }; +} + +/** + * Build a valid typed object from a raw object. + * @param data Raw object. + * @return An object representing the agent properties. + */ +export function agentPropsDecoder(data: UnknownObject): WithAgentProps { + const agentProps: WithAgentProps = { + agentId: parseIntOr(data.agent, null), + agentName: notEmptyStringOr(data.agentName, null), + agentAlias: notEmptyStringOr(data.agentAlias, null), + agentDescription: notEmptyStringOr(data.agentDescription, null), + agentAddress: notEmptyStringOr(data.agentAddress, null) + }; + + return data.metaconsoleId != null + ? { + metaconsoleId: data.metaconsoleId, + ...agentProps // Object spread: http://es6-features.org/#SpreadOperator + } + : agentProps; +} + +/** + * Build a valid typed object from a raw object. + * @param data Raw object. + * @return An object representing the module and agent properties. + */ +export function modulePropsDecoder(data: UnknownObject): WithModuleProps { + return { + moduleId: parseIntOr(data.moduleId, null), + moduleName: notEmptyStringOr(data.moduleName, null), + moduleDescription: notEmptyStringOr(data.moduleDescription, null), + ...agentPropsDecoder(data) // Object spread: http://es6-features.org/#SpreadOperator + }; +} + +/** + * Build a valid typed object from a raw object. + * @param data Raw object. + * @return An object representing the linked visual console properties. + * @throws Will throw a TypeError if the status calculation properties are invalid. + */ +export function linkedVCPropsDecoder( + data: UnknownObject +): LinkedVisualConsoleProps | never { + // Object destructuring: http://es6-features.org/#ObjectMatchingShorthandNotation + const { + metaconsoleId, + linkedLayoutId: id, + linkedLayoutAgentId: agentId + } = data; + + let linkedLayoutStatusProps: LinkedVisualConsolePropsStatus = { + linkedLayoutStatusType: "default" + }; + switch (data.linkedLayoutStatusType) { + case "weight": { + const weight = parseIntOr(data.linkedLayoutStatusTypeWeight, null); + if (weight == null) + throw new TypeError("invalid status calculation properties."); + + if (data.linkedLayoutStatusTypeWeight) + linkedLayoutStatusProps = { + linkedLayoutStatusType: "weight", + linkedLayoutStatusTypeWeight: weight + }; + break; + } + case "service": { + const warningThreshold = parseIntOr( + data.linkedLayoutStatusTypeWarningThreshold, + null + ); + const criticalThreshold = parseIntOr( + data.linkedLayoutStatusTypeCriticalThreshold, + null + ); + if (warningThreshold == null || criticalThreshold == null) { + throw new TypeError("invalid status calculation properties."); + } + + linkedLayoutStatusProps = { + linkedLayoutStatusType: "service", + linkedLayoutStatusTypeWarningThreshold: warningThreshold, + linkedLayoutStatusTypeCriticalThreshold: criticalThreshold + }; + break; + } + } + + const linkedLayoutBaseProps = { + linkedLayoutId: parseIntOr(id, null), + linkedLayoutAgentId: parseIntOr(agentId, null), + ...linkedLayoutStatusProps // Object spread: http://es6-features.org/#SpreadOperator + }; + + return metaconsoleId != null + ? { + metaconsoleId, + ...linkedLayoutBaseProps // Object spread: http://es6-features.org/#SpreadOperator + } + : linkedLayoutBaseProps; +} + +/** + * To get a CSS rule with the most used prefixes. + * @param ruleName Name of the CSS rule. + * @param ruleValue Value of the CSS rule. + * @return An array of rules with the prefixes applied. + */ +export function prefixedCssRules( + ruleName: string, + ruleValue: string +): string[] { + const rule = `${ruleName}: ${ruleValue};`; + return [ + `-webkit-${rule}`, + `-moz-${rule}`, + `-ms-${rule}`, + `-o-${rule}`, + `${rule}` + ]; +} + +/** + * Decode a base64 string. + * @param input Data encoded using base64. + * @return Decoded data. + */ +export function decodeBase64(input: string): string { + return decodeURIComponent(escape(window.atob(input))); +} + +/** + * Generate a date representation with the format 'd/m/Y'. + * @param initialDate Date to be used instead of a generated one. + * @param locale Locale to use if localization is required and available. + * @example 24/02/2020. + * @return Date representation. + */ +export function humanDate(date: Date, locale: string | null = null): string { + if (locale && Intl && Intl.DateTimeFormat) { + // Format using the user locale. + const options: Intl.DateTimeFormatOptions = { + day: "2-digit", + month: "2-digit", + year: "numeric" + }; + return Intl.DateTimeFormat(locale, options).format(date); + } else { + // Use getDate, getDay returns the week day. + const day = leftPad(date.getDate(), 2, 0); + // The getMonth function returns the month starting by 0. + const month = leftPad(date.getMonth() + 1, 2, 0); + const year = leftPad(date.getFullYear(), 4, 0); + + // Format: 'd/m/Y'. + return `${day}/${month}/${year}`; + } +} + +/** + * Generate a time representation with the format 'hh:mm:ss'. + * @param initialDate Date to be used instead of a generated one. + * @example 01:34:09. + * @return Time representation. + */ +export function humanTime(date: Date): string { + const hours = leftPad(date.getHours(), 2, 0); + const minutes = leftPad(date.getMinutes(), 2, 0); + const seconds = leftPad(date.getSeconds(), 2, 0); + + return `${hours}:${minutes}:${seconds}`; +} + +interface Macro { + macro: string | RegExp; + value: string; +} +/** + * Replace the macros of a text. + * @param macros List of macros and their replacements. + * @param text Text in which we need to replace the macros. + */ +export function replaceMacros(macros: Macro[], text: string): string { + return macros.reduce( + (acc, { macro, value }) => acc.replace(macro, value), + text + ); +} diff --git a/visual_console_client/src/main.css b/visual_console_client/src/main.css new file mode 100644 index 0000000000..d61ad48810 --- /dev/null +++ b/visual_console_client/src/main.css @@ -0,0 +1,15 @@ +#visual-console-container { + margin: 0px auto; + position: relative; + background-repeat: no-repeat; + background-size: contain; +} + +.visual-console-item { + position: absolute; + display: flex; + flex-direction: initial; + justify-items: center; + align-items: center; + user-select: text; +} diff --git a/visual_console_client/src/types.ts b/visual_console_client/src/types.ts new file mode 100644 index 0000000000..79dee56e74 --- /dev/null +++ b/visual_console_client/src/types.ts @@ -0,0 +1,47 @@ +export interface UnknownObject { + [key: string]: any; // eslint-disable-line @typescript-eslint/no-explicit-any +} + +export interface Position { + x: number; + y: number; +} + +export interface Size { + width: number; + height: number; +} + +export interface WithAgentProps { + metaconsoleId?: number | null; + agentId: number | null; + agentName: string | null; + agentAlias: string | null; + agentDescription: string | null; + agentAddress: string | null; +} + +export interface WithModuleProps extends WithAgentProps { + moduleId: number | null; + moduleName: string | null; + moduleDescription: string | null; +} + +export type LinkedVisualConsolePropsStatus = + | { + linkedLayoutStatusType: "default"; + } + | { + linkedLayoutStatusType: "weight"; + linkedLayoutStatusTypeWeight: number; + } + | { + linkedLayoutStatusType: "service"; + linkedLayoutStatusTypeWarningThreshold: number; + linkedLayoutStatusTypeCriticalThreshold: number; + }; +export type LinkedVisualConsoleProps = { + metaconsoleId?: number | null; + linkedLayoutId: number | null; + linkedLayoutAgentId: number | null; +} & LinkedVisualConsolePropsStatus; diff --git a/visual_console_client/tsconfig.json b/visual_console_client/tsconfig.json new file mode 100644 index 0000000000..5f047a33d6 --- /dev/null +++ b/visual_console_client/tsconfig.json @@ -0,0 +1,22 @@ +{ + "compilerOptions": { + "target": "es5", + "module": "es6", + "strict": true, + "alwaysStrict": true, + "allowJs": true, + "esModuleInterop": true, + "allowSyntheticDefaultImports": true, + "forceConsistentCasingInFileNames": true, + "moduleResolution": "node", + "resolveJsonModule": true, + "sourceMap": true, + // "isolatedModules": true, + "noEmit": true, + "noImplicitAny": true, + "noImplicitReturns": true, + "pretty": true + }, + "include": ["**/*.ts"], + "exclude": ["node_modules"] +} diff --git a/visual_console_client/webpack.config.js b/visual_console_client/webpack.config.js new file mode 100644 index 0000000000..ffce749d99 --- /dev/null +++ b/visual_console_client/webpack.config.js @@ -0,0 +1,104 @@ +// eslint-disable-next-line @typescript-eslint/no-var-requires +const path = require("path"); +// eslint-disable-next-line @typescript-eslint/no-var-requires +const CleanWebpackPlugin = require("clean-webpack-plugin"); +// eslint-disable-next-line @typescript-eslint/no-var-requires +const MiniCssExtractPlugin = require("mini-css-extract-plugin"); + +const dev = process.env.NODE_ENV !== "production"; +const entry = path.join(__dirname, "src", "index.ts"); +const buildPath = path.join( + __dirname, + "..", + process.env.BUILD_PATH && process.env.BUILD_PATH.length > 0 + ? process.env.BUILD_PATH + : "build" +); + +module.exports = { + mode: dev ? "development" : "production", + entry, // Start from this file. + output: { + path: buildPath, // The files will be created here. + // filename: dev ? "vc.[name].min.js" : "vc.[name].[chunkhash:8].min.js" + filename: "vc.[name].min.js" + }, + devtool: "source-map", + resolve: { + extensions: [".ts", ".js", ".json"] + }, + module: { + rules: [ + // Loader for the Typescript compiler. + { + test: /\.ts$/, + loader: "awesome-typescript-loader" + }, + // This loader builds a main CSS file from all the CSS imports across the files. + { + test: /\.css$/, + loader: [ + // https://github.com/webpack-contrib/mini-css-extract-plugin + { + loader: MiniCssExtractPlugin.loader, + options: { + hot: true, // if you want HMR - we try to automatically inject hot reloading but if it's not working, add it to the config + reloadAll: true // when desperation kicks in - this is a brute force HMR flag + } + }, + // https://webpack.js.org/loaders/css-loader + { + loader: "css-loader", + options: { + sourceMap: true + } + }, + // To post process CSS and add some things like prefixes to the rules. e.g.: -webkit-... + // https://github.com/postcss/postcss-loader + { + loader: "postcss-loader", + options: { + plugins: () => [ + // To improve the support for old browsers. + require("autoprefixer")({ + browsers: ["> 1%", "last 2 versions"] + }) + ] + } + } + ] + }, + // To allow the use of file imports. The imported files are transformed into + // data uris if they are small enough or it returns a path to the file. + // https://webpack.js.org/loaders/url-loader + { + test: /\.(png|jpg|gif|svg|eot|ttf|woff|woff2)$/, + loader: "url-loader", + options: { + limit: 10000, + // name: "[name].[hash:8].[ext]" + name: "[name].[ext]" + } + } + ] + }, + plugins: [ + // This plugin will remove all files inside Webpack's output.path directory, + // as well as all unused webpack assets after every successful rebuild. + new CleanWebpackPlugin(), + // Options for the plugin which extract the CSS files to build a main file. + new MiniCssExtractPlugin({ + // Options similar to the same options in webpackOptions.output + // both options are optional + // filename: dev ? "vc.[name].css" : "vc.[name].[contenthash:8].css", + filename: "vc.[name].css", + // Disable to remove warnings about conflicting order between imports. + orderWarning: true + }) + ], + // Static server which runs the playground on npm start. + devServer: { + open: true, + contentBase: "playground" + } +};