From 2018f0c8313ade05314d2b34c3a49a718f56a85c Mon Sep 17 00:00:00 2001 From: Matt Callanan Date: Mon, 25 Sep 2017 17:01:08 +1000 Subject: [PATCH 01/16] Fix "Rate exceeded" issues for large clusters. - Dockerfile tweaks: - Upgrade docker base image to node:8.5-alpine with fix for "gyp": "gyp ERR! stack Error: Can't find Python executable "python", you can set the PYTHON env variable" - Fixes and tweaks to index.js: - Added delayPromise() to introduce a 1s delay between ECS API calls to avoid "Rate exceeded" back-pressure issues when running within AWS account. - Fix "InvalidParameterException: instanceIds can have at most 100 items" error. - Use internal DEBUG_LOGGING constant to assist when debugging locally - Use template strings and replace some "var"s with "let"s - UI Fixes: - Fix 'onError is not a function' bug. - Fix displaying of server errors. "TypeError: Cannot read property 'length' of undefined at renderGraph (graph.js:103)" - Handle errors caught before 'graph' object is initialised without causing "Uncaught TypeError: Cannot read property 'append' of undefined" error for graph. - Show instances even if there are no tasks. - Added TODO: "Make delay between AWS API calls configurable" --- Dockerfile | 12 ++- TODO.md | 12 ++- public/javascripts/graph.js | 73 ++++++++++-------- routes/index.js | 142 ++++++++++++++++++++++-------------- 4 files changed, 148 insertions(+), 91 deletions(-) diff --git a/Dockerfile b/Dockerfile index 0234e8e..e721785 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,6 +1,16 @@ # https://hub.docker.com/r/library/node/ -FROM node:4.1 +FROM node:8.5-alpine + +# >> FIX: +# Fixes error Ubuntu: "gyp ERR! stack Error: Can't find Python executable "python", you can set the PYTHON env variable" +# REF: https://gist.github.com/vidhill/0a85dc1848feee4171944dc4d7757895 +# REF: https://github.com/nodejs/node-gyp/issues/1105 + +# build base includes g++ and gcc and Make +RUN apk update && apk add python build-base + +# << END FIX # Bundle app source COPY . / diff --git a/TODO.md b/TODO.md index d0189f0..366eac7 100644 --- a/TODO.md +++ b/TODO.md @@ -1,3 +1,4 @@ +## UI * Menubar with: * Selectable Region * Selectable AWS account – requires ability for mutliple config files server side @@ -8,12 +9,19 @@ * Show memory breakdown across containers * Sliding timebar to see historical data for comparison (like google street view) * Show container actual memory utilisation vs reserved memory utilisation +* Provide access to more troubleshooting information (such as docker logs, ECS logs) + +## Server * Write a plugin system that lets adopters plugin their own statistics from favourite monitoring tool * Pluggable backend system that could support other public or private cloud providers -* Provide access to more troubleshooting information (such as docker logs, ECS logs) * Cache responses server-side to reduce AWS API calls * Make the data transfer between client and server more efficient - Separate requests for task and instance data and populate graph asynchronously * Arrow functions: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Functions/Arrow_functions -* Testing + +### Server Configuration +* Make logging levels configurable +* Make delay between AWS API calls configurable + +## Testing * Capture ECS JSON responses for testing and replay with mock AWS ECS server * https://fbflex.wordpress.com/2013/11/18/mocking-out-amazon-aws-sdk-with-the-betamax-recording-proxy-for-testing/ diff --git a/public/javascripts/graph.js b/public/javascripts/graph.js index bb53db0..28efc51 100644 --- a/public/javascripts/graph.js +++ b/public/javascripts/graph.js @@ -92,27 +92,9 @@ function remainingResource(d, resourceType) { } } -function renderGraph(chartDivId, legendDivId, cluster, resourceTypeText, onCompletion, onError) { - var showTaskBreakdown = true; // TODO: Parameterise - try { - var resourceType = parseResourceType(resourceTypeText, ResourceEnum.MEMORY); - var topMargin = 20; - var bottomMargin = 100; - var rightMargin = 50; - var leftMargin = 50; - var graphWidth = (window.apiResponseData.length > 50 ? 1300 : 1000) - leftMargin - rightMargin; //establishes width based on data set size - var graphHeight = 520 - topMargin - bottomMargin; - var totalHeight = 400; - - var colorRange = d3.scale.ordinal().range(colorbrewer.Pastel1[9].concat(colorbrewer.Pastel2[8]).concat(colorbrewer.Set1[9]).concat(colorbrewer.Set2[8]).concat(colorbrewer.Set3[12])); - var xRange = d3.scale.ordinal().rangeRoundBands([10, graphWidth], .1); - var yRange = d3.scale.linear().rangeRound([graphHeight, 0]); - var xAxis = d3.svg.axis().scale(xRange).orient("bottom"); - var yAxis = d3.svg.axis().scale(yRange).orient("left").tickFormat(d3.format(".2s")); - - // Main graph area +function recreateMainGraphElement(chartDivId, graphWidth, leftMargin, rightMargin, totalHeight, topMargin, bottomMargin) { d3.select('#' + chartDivId).select("svg").remove(); - var graph = d3.select('#' + chartDivId) + let graph = d3.select('#' + chartDivId) .append("svg") .attr("class", "cluster-graph") .attr("id", "cluster-graph") @@ -120,12 +102,39 @@ function renderGraph(chartDivId, legendDivId, cluster, resourceTypeText, onCompl .attr("height", totalHeight + topMargin + bottomMargin) .attr("float", "left") .append("g").attr("transform", "translate(" + leftMargin + "," + topMargin + ")"); + return graph; +} - if (window.apiResponseError) { - var errorMsg = "Server Error: " + (window.apiResponseError instanceof XMLHttpRequest ? window.apiResponseError.responseText : JSON.stringify(window.apiResponseError)); - handleError(errorMsg, graph, onError); - return graph; - } +const GRAPH_TOP_MARGIN = 20; +const GRAPH_BOTTOM_MARGIN = 100; +const RIGHT_MARGIN = 50; +const LEFT_MARGIN = 50; +const GRAPH_HEIGHT = 520 - GRAPH_TOP_MARGIN - GRAPH_BOTTOM_MARGIN; +const TOTAL_HEIGHT = 400; +const DEFAULT_GRAPH_WIDTH = 1000; +const EXPANDED_GRAPH_WIDTH = 1300; + +function renderGraph(chartDivId, legendDivId, cluster, resourceTypeText, onCompletion, onError) { + if (window.apiResponseError) { + let errorMsg = "Server Error: " + (window.apiResponseError instanceof XMLHttpRequest ? window.apiResponseError.responseText : JSON.stringify(window.apiResponseError)); + let graph = recreateMainGraphElement(chartDivId, DEFAULT_GRAPH_WIDTH, LEFT_MARGIN, RIGHT_MARGIN, TOTAL_HEIGHT, GRAPH_TOP_MARGIN, GRAPH_BOTTOM_MARGIN); + handleError(errorMsg, graph, onError); + return graph; + } + + let showTaskBreakdown = true; // TODO: Parameterise + + try { + let resourceType = parseResourceType(resourceTypeText, ResourceEnum.MEMORY); + let graphWidth = window.apiResponseError ? DEFAULT_GRAPH_WIDTH : (window.apiResponseData.length > 50 ? EXPANDED_GRAPH_WIDTH : DEFAULT_GRAPH_WIDTH) - LEFT_MARGIN - RIGHT_MARGIN; //establishes width based on data set size + let colorRange = d3.scale.ordinal().range(colorbrewer.Pastel1[9].concat(colorbrewer.Pastel2[8]).concat(colorbrewer.Set1[9]).concat(colorbrewer.Set2[8]).concat(colorbrewer.Set3[12])); + let xRange = d3.scale.ordinal().rangeRoundBands([10, graphWidth], .1); + let yRange = d3.scale.linear().rangeRound([GRAPH_HEIGHT, 0]); + let xAxis = d3.svg.axis().scale(xRange).orient("bottom"); + let yAxis = d3.svg.axis().scale(yRange).orient("left").tickFormat(d3.format(".2s")); + + // Main graph area + let graph = recreateMainGraphElement(chartDivId, graphWidth, LEFT_MARGIN, RIGHT_MARGIN, TOTAL_HEIGHT, GRAPH_TOP_MARGIN, GRAPH_BOTTOM_MARGIN); if (window.apiResponseData.length == 0) { showInfo(graph, "No instances are registered for the '" + cluster + "' cluster."); @@ -133,7 +142,7 @@ function renderGraph(chartDivId, legendDivId, cluster, resourceTypeText, onCompl return graph; } - var uniqueTaskDefs = window.apiResponseData.reduce(function (acc, current) { + let uniqueTaskDefs = window.apiResponseData.reduce(function (acc, current) { return acc.concat(current.tasks.map(function (t) { return taskFamilyAndRevision(t); })) @@ -168,7 +177,7 @@ function renderGraph(chartDivId, legendDivId, cluster, resourceTypeText, onCompl // Draw X axis var xAxisLabels = graph.append("g") .attr("class", "graph-axis") - .attr("transform", "translate(0," + graphHeight + ")") + .attr("transform", "translate(0," + GRAPH_HEIGHT + ")") .call(xAxis); var menu = [ @@ -276,7 +285,7 @@ function renderGraph(chartDivId, legendDivId, cluster, resourceTypeText, onCompl // Draw legend var taskData = uniqueTaskDefs.sort(); - var longestLength = taskData.reduce(function (a, b) { return a.length > b.length ? a : b; }).length; + var longestLength = taskData.reduce(function (a, b) { return a.length > b.length ? a : b; }, []).length; // TODO: Add hover highlight of related blocks d3.select('#' + legendDivId).select("svg").remove(); @@ -295,7 +304,7 @@ function renderGraph(chartDivId, legendDivId, cluster, resourceTypeText, onCompl .enter() .append("rect") .attr("x", 1) - .attr("y", function(d, i) { return ((i * 20) + topMargin); }) + .attr("y", function(d, i) { return ((i * 20) + GRAPH_TOP_MARGIN); }) .attr("width", 18) .attr("height", 18) .style("fill", function (d) { return colorRange(d); }) @@ -309,7 +318,7 @@ function renderGraph(chartDivId, legendDivId, cluster, resourceTypeText, onCompl .attr("x", 25) .attr("width", 5) .attr("height", 5) - .attr("y", function(d, i) { return ((i * 20) + topMargin + 12); }) + .attr("y", function(d, i) { return ((i * 20) + GRAPH_TOP_MARGIN + 12); }) .text(function(d) { return d; }); } else { @@ -355,9 +364,9 @@ function populateGraph(useStaticData, chartDivId, legendDivId, cluster, resource window.apiResponseData = apiResponseData; console.log("For debugging: window.apiResponseData, window.apiResponseError"); - renderGraph(chartDivId, legendDivId, cluster, resourceTypeText, onError); + renderGraph(chartDivId, legendDivId, cluster, resourceTypeText, onCompletion, onError); }); } catch (e) { - handleError("ERROR. Uncaught Exception: " + e, graph, onCompletion, onError); + handleError("ERROR. Uncaught Exception: " + e, graph, onError); } } diff --git a/routes/index.js b/routes/index.js index 1ed6f81..27f3445 100644 --- a/routes/index.js +++ b/routes/index.js @@ -1,3 +1,6 @@ +// TODO: Convert DEBUG_LOGGING to configuration +var DEBUG_LOGGING = false; + var express = require('express'); var router = express.Router(); var fs = require('fs'); @@ -9,10 +12,10 @@ var batchPromises = require('batch-promises'); // Override default credentials with ./aws_config.json if it exists var AWS_CONFIG_FILE = './aws_config.json'; if (fs.existsSync(AWS_CONFIG_FILE)) { - console.log("Updating with settings from '" + AWS_CONFIG_FILE + "'..."); + console.log(`Updating with settings from '${AWS_CONFIG_FILE}'...`); AWS.config.update(JSON.parse(fs.readFileSync(AWS_CONFIG_FILE, 'utf8'))); - console.log(" default region: " + AWS_CONFIG_FILE + ": '" + AWS.config.region + "'"); } +console.log(`Targeting AWS region '${AWS.config.region}'`); var utils = require('./utils'); @@ -33,7 +36,7 @@ router.get('/', function(req, res, next) { */ router.get('/api/instance_summaries_with_tasks', function (req, res, next) { - console.log("/api/instance_summaries_with_tasks: cluster='" + req.query.cluster + "', live=" + !req.query.static + ")"); + console.log(`/api/instance_summaries_with_tasks: cluster='${req.query.cluster}', live=${!req.query.static})`); if (!req.query.cluster) { send400Response("Please provide a 'cluster' parameter", res); } else { @@ -48,33 +51,46 @@ router.get('/api/instance_summaries_with_tasks', function (req, res, next) { return listAllContainerInstances(cluster); }) .then(function (listAllContainerInstanceArns) { + debugLog(`\tFound ${listAllContainerInstanceArns.length} ContainerInstanceARNs...`); if (listAllContainerInstanceArns.length == 0) { return new Promise(function (resolve, reject) { resolve(null); }); } else { - return ecs.describeContainerInstances({ - cluster: cluster, - containerInstances: listAllContainerInstanceArns - }).promise(); + let containerInstanceBatches = listAllContainerInstanceArns.map(function (instances, index) { + return index % maxSize === 0 ? listAllContainerInstanceArns.slice(index, index + maxSize) : null; + }).filter(function (instances) { + return instances; + }); + return batchPromises(1, containerInstanceBatches, containerInstanceBatch => new Promise((resolve, reject) => { + // The containerInstanceBatch iteratee will fire after each batch + debugLog(`\tCalling ecs.describeContainerInstances for Container Instance batch: ${containerInstanceBatch}`); + resolve(ecs.describeContainerInstances({ + cluster: cluster, + containerInstances: containerInstanceBatch + }).promise().then(delayPromise(1000))); + })); } }) - .then(function (describeContainerInstancesResponse) { - if (!describeContainerInstancesResponse) { + .then(function (describeContainerInstancesResponses) { + if (!describeContainerInstancesResponses || describeContainerInstancesResponses.length == 0) { return new Promise(function (resolve, reject) { + console.warn("No Container Instances found"); res.json([]); }); } else { - containerInstances = describeContainerInstancesResponse.data.containerInstances; - var ec2instanceIds = containerInstances.map(function (i) { return i.ec2InstanceId; }); - console.log("\nFound the following", ec2instanceIds.length, "ec2InstanceIds in cluster '", cluster, "':", ec2instanceIds); + let containerInstances = describeContainerInstancesResponses.reduce(function(acc, current){ + return acc.concat(current.data.containerInstances); + }, []); + let ec2instanceIds = containerInstances.map(function (i) { return i.ec2InstanceId; }); + console.log(`Found ${ec2instanceIds.length} ec2InstanceIds: ${ec2instanceIds}`); return ec2.describeInstances({ InstanceIds: ec2instanceIds }).promise() .then(function (ec2Instances) { var instances = [].concat.apply([], ec2Instances.data.Reservations.map(function (r) { return r.Instances })); var privateIpAddresses = instances.map(function (i) {return i.PrivateIpAddress}); - console.log("\nMatching Private IP addressess:", privateIpAddresses); + console.log(`\twith ${privateIpAddresses.length} matching Private IP addresses: ${privateIpAddresses}`); var instanceSummaries = containerInstances.map(function (instance) { var ec2IpAddress = instances.find(function (i) {return i.InstanceId == instance.ec2InstanceId}).PrivateIpAddress; return { @@ -137,6 +153,7 @@ function listContainerInstanceWithToken(cluster, token, instanceArns) { if (token) { params['nextToken'] = token; } + debugLog("Calling ecs.listContainerInstances..."); return ecs.listContainerInstances(params).promise() .then(function(listContainerInstanceResponse) { var containerInstanceArns = instanceArns.concat(listContainerInstanceResponse.data.containerInstanceArns); @@ -163,53 +180,41 @@ function listAllTasks(cluster) { } function listTasksWithToken(cluster, token, tasks) { - var params = {cluster: cluster, maxResults: maxSize}; + let params = {cluster: cluster, maxResults: maxSize}; if (token) { params['nextToken'] = token; } - //console.log("\nCalling ecs.listTasks with token", token); + debugLog(`\tCalling ecs.listTasks with token: ${token} ...`); return ecs.listTasks(params).promise() - .then(function(tasksResponse) { - //console.log("\nReceived tasksResponse", tasksResponse); - var taskArns = tasks.concat(tasksResponse.data.taskArns); - var nextToken = tasksResponse.data.nextToken; - if (taskArns.length == 0) { - return []; - } else if (nextToken) { - return listTasksWithToken(cluster, nextToken, taskArns); - } else { - //console.log("\nReturning taskArns from listTasksWithToken", taskArns); - return taskArns; - } - }); -} - -function findTaskDefinition(task, attempt) { - return ecs.describeTaskDefinition({taskDefinition: task.taskDefinitionArn}).promise().catch(function (err) { - sleep.sleep(attempt); - if (attempt <= 5) { - //console.log("\n Reattempting findTaskDefinition for", task.taskDefinitionArn); - return findTaskDefinition(task, ++attempt); - } else { - //console.log("\n REJECTing findTaskDefinition for", task.taskDefinitionArn, "after", attempt, "attempts"); - return Promise.reject(err); - } - }); + .then(delayPromise(1000)) + .then(function (tasksResponse) { + debugLog(`\t\tReceived tasksResponse with ${tasksResponse.data.taskArns.length} Task ARNs`); + let taskArns = tasks.concat(tasksResponse.data.taskArns); + let nextToken = tasksResponse.data.nextToken; + if (taskArns.length == 0) { + return []; + } else if (nextToken) { + return listTasksWithToken(cluster, nextToken, taskArns); + } else { + debugLog(`\t\tReturning ${taskArns.length} taskArns from listTasksWithToken: ${taskArns}`); + return taskArns; + } + }); } function getTasksWithTaskDefinitions(cluster) { - console.log("Getting Tasks annotated with Task Definitions for cluster '", cluster, "'..."); + console.log(`Getting Tasks annotated with Task Definitions for cluster '${cluster}'...`); return new Promise(function (resolve, reject) { - var tasksArray = []; + let tasksArray = []; listAllTasks(cluster) .then(function (allTaskArns) { if (allTaskArns.length == 0) { - console.log("\nNo Task ARNs found"); + console.warn("\tNo Task ARNs found"); resolve([]); } else { - var taskBatches = allTaskArns.map(function (tasks, index) { - return index % maxSize===0 ? allTaskArns.slice(index, index + maxSize) : null; - }).filter(function(tasks) { + let taskBatches = allTaskArns.map(function (tasks, index) { + return index % maxSize === 0 ? allTaskArns.slice(index, index + maxSize) : null; + }).filter(function (tasks) { return tasks; }); return taskBatches; @@ -219,33 +224,36 @@ function getTasksWithTaskDefinitions(cluster) { // Batch the batches :) - describe up to 2 batches of batches of maxSize ARNs at a time // Without batchPromises, we will fire all ecs.describeTasks calls one after the other and could run into API rate limit issues return batchPromises(2, taskBatches, taskBatch => new Promise((resolve, reject) => { - // The iteratee will fire after each batch - //console.log("\nCalling ecs.describeTasks for", taskBatch); - resolve(ecs.describeTasks({cluster: cluster, tasks: taskBatch}).promise()); + // The iteratee will fire after each batch + debugLog(`\tCalling ecs.describeTasks for Task batch: ${taskBatch}`); + resolve(ecs.describeTasks({cluster: cluster, tasks: taskBatch}).promise() + .then(delayPromise(1000))); })); }) .then(function (describeTasksResponses) { tasksArray = describeTasksResponses.reduce(function(acc, current){ return acc.concat(current.data.tasks); }, []); - console.log("\Found", tasksArray.length, "tasks"); + console.log(`Found ${tasksArray.length} tasks`); // Wait for the responses from 20 describeTaskDefinition calls before invoking another 20 calls // Without batchPromises, we will fire all ecs.describeTaskDefinition calls one after the other and could run into API rate limit issues return batchPromises(20, tasksArray, task => new Promise((resolve, reject) => { - //console.log("Calling describeTaskDefinition for", task.taskDefinitionArn); + debugLog(`\tCalling ecs.describeTaskDefinition for Task Definition ARN: ${task.taskDefinitionArn}`); + // TODO: Don't ask for same task definition more than once resolve(ecs.describeTaskDefinition({taskDefinition: task.taskDefinitionArn}).promise() + .then(delayPromise(1000)) .then(function (taskDefinition) { - //console.log(" Got taskDefinition for", task.taskDefinitionArn); + debugLog(`\t\tReceived taskDefinition for ${task.taskDefinitionArn}`); return taskDefinition; }) .catch(function (err) { - //console.log(" FAILED ecs.describeTaskDefinition() for", task.taskDefinitionArn, ":", err); + debugLog(`\t\tFAILED ecs.describeTaskDefinition call for '${task.taskDefinitionArn}': ${err}`); return Promise.reject(err); })); })); }) .then(function (taskDefs) { - console.log("\nFound", taskDefs.length, "task definitions"); + console.log(`Found ${taskDefs.length} task definitions`); // Fill in task details in tasksArray with taskDefinition details (e.g. memory, cpu) taskDefs.forEach(function(taskDef) { tasksArray @@ -267,7 +275,7 @@ function getTasksWithTaskDefinitions(cluster) { function send400Response(errMsg, res) { console.log(errMsg); - res.status(400).send("Error: " + errMsg); + res.status(400).send(`Error: ${errMsg}`); } function sendErrorResponse(err, res) { @@ -276,4 +284,26 @@ function sendErrorResponse(err, res) { res.status(500).send(err.message); } +function debugLog(msg) { + if (DEBUG_LOGGING) { + console.info(msg); + } +} + +// From: https://blog.raananweber.com/2015/12/01/writing-a-promise-delayer/ +// NOTE: https://www.npmjs.com/package/promise-pause does not work as the aws-sdk-promise library does not return a Promise but a thenable object +function delayPromise(delay) { + //return a function that accepts a single variable + return function(data) { + //this function returns a promise. + return new Promise(function(resolve, reject) { + debugLog(`Pausing for ${delay}ms...`); + setTimeout(function() { + //a promise that is resolved after "delay" milliseconds with the data provided + resolve(data); + }, delay); + }); + } +} + module.exports = router; From e943dfc7505b52462498f41c5a1b7f8b5291aa3f Mon Sep 17 00:00:00 2001 From: Matt Callanan Date: Mon, 2 Oct 2017 22:08:23 +1000 Subject: [PATCH 02/16] Introduce server-side cache for Task Definitions. Update to use ES6 let/const in place of var. --- package.json | 3 +- routes/index.js | 388 +++++++++++++++++++++++++----------------------- 2 files changed, 205 insertions(+), 186 deletions(-) diff --git a/package.json b/package.json index c0b82cc..836e989 100644 --- a/package.json +++ b/package.json @@ -17,7 +17,8 @@ "aws-sdk": "2.77.0", "aws-sdk-promise": "0.0.2", "sleep": "3.0.1", - "batch-promises": "0.0.3" + "batch-promises": "0.0.3", + "memory-cache": "0.2.0" }, "dev-dependencies": { "nodemon": "1.3.7" diff --git a/routes/index.js b/routes/index.js index 27f3445..91fd5ef 100644 --- a/routes/index.js +++ b/routes/index.js @@ -1,33 +1,36 @@ -// TODO: Convert DEBUG_LOGGING to configuration -var DEBUG_LOGGING = false; - -var express = require('express'); -var router = express.Router(); -var fs = require('fs'); +const AWS_API_DELAY = 100; +const debug = require('debug')('api'); +const express = require('express'); +const router = express.Router(); +const fs = require('fs'); // See: https://docs.aws.amazon.com/AWSJavaScriptSDK/guide/node-configuring.html -var AWS = require('aws-sdk-promise'); -var sleep = require('sleep'); -var batchPromises = require('batch-promises'); +const AWS = require('aws-sdk-promise'); +const batchPromises = require('batch-promises'); // AWS variable now has default credentials from Shared Credentials File or Environment Variables. // Override default credentials with ./aws_config.json if it exists -var AWS_CONFIG_FILE = './aws_config.json'; +const AWS_CONFIG_FILE = './aws_config.json'; if (fs.existsSync(AWS_CONFIG_FILE)) { console.log(`Updating with settings from '${AWS_CONFIG_FILE}'...`); AWS.config.update(JSON.parse(fs.readFileSync(AWS_CONFIG_FILE, 'utf8'))); } console.log(`Targeting AWS region '${AWS.config.region}'`); +const utils = require('./utils'); -var utils = require('./utils'); +const taskDefinitionCache = require('memory-cache'); -var ecs = new AWS.ECS(); -var ec2 = new AWS.EC2(); -var maxSize = 100; +const ecs = new AWS.ECS(); +const ec2 = new AWS.EC2(); +const maxSize = 100; /* Home page */ -router.get('/', function(req, res, next) { - res.render('index', { title: 'c3vis - Cloud Container Cluster Visualizer', useStaticData: req.query.static, resourceType: req.query.resourceType ? req.query.resourceType : 'memory' }); +router.get('/', function (req, res, next) { + res.render('index', { + title: 'c3vis - Cloud Container Cluster Visualizer', + useStaticData: req.query.static, + resourceType: req.query.resourceType ? req.query.resourceType : 'memory' + }); }); /* API endpoints @@ -37,78 +40,86 @@ router.get('/', function(req, res, next) { router.get('/api/instance_summaries_with_tasks', function (req, res, next) { console.log(`/api/instance_summaries_with_tasks: cluster='${req.query.cluster}', live=${!req.query.static})`); + debugLog(`Headers: ${JSON.stringify(req.headers, null, 4)}`); if (!req.query.cluster) { send400Response("Please provide a 'cluster' parameter", res); } else { - var cluster = req.query.cluster; - var live = req.query.static != 'true'; + const cluster = req.query.cluster; + const live = req.query.static !== 'true'; if (live) { - var tasksArray = []; - var containerInstances = []; + let tasksArray = []; getTasksWithTaskDefinitions(cluster) .then(function (tasksResult) { tasksArray = tasksResult; - return listAllContainerInstances(cluster); + return listAllContainerInstances(cluster); }) .then(function (listAllContainerInstanceArns) { - debugLog(`\tFound ${listAllContainerInstanceArns.length} ContainerInstanceARNs...`); - if (listAllContainerInstanceArns.length == 0) { + debugLog(`\tFound ${listAllContainerInstanceArns.length} ContainerInstanceARNs...`); + if (listAllContainerInstanceArns.length === 0) { return new Promise(function (resolve, reject) { resolve(null); }); } else { - let containerInstanceBatches = listAllContainerInstanceArns.map(function (instances, index) { - return index % maxSize === 0 ? listAllContainerInstanceArns.slice(index, index + maxSize) : null; - }).filter(function (instances) { - return instances; - }); - return batchPromises(1, containerInstanceBatches, containerInstanceBatch => new Promise((resolve, reject) => { - // The containerInstanceBatch iteratee will fire after each batch - debugLog(`\tCalling ecs.describeContainerInstances for Container Instance batch: ${containerInstanceBatch}`); - resolve(ecs.describeContainerInstances({ - cluster: cluster, - containerInstances: containerInstanceBatch - }).promise().then(delayPromise(1000))); - })); + const containerInstanceBatches = listAllContainerInstanceArns.map(function (instances, index) { + return index % maxSize === 0 ? listAllContainerInstanceArns.slice(index, index + maxSize) : null; + }).filter(function (instances) { + return instances; + }); + return batchPromises(1, containerInstanceBatches, containerInstanceBatch => new Promise((resolve, reject) => { + // The containerInstanceBatch iteratee will fire after each batch + debugLog(`\tCalling ecs.describeContainerInstances for Container Instance batch: ${containerInstanceBatch}`); + resolve(ecs.describeContainerInstances({ + cluster: cluster, + containerInstances: containerInstanceBatch + }).promise().then(delayPromise(AWS_API_DELAY))); + })); } }) .then(function (describeContainerInstancesResponses) { - if (!describeContainerInstancesResponses || describeContainerInstancesResponses.length == 0) { + if (!describeContainerInstancesResponses || describeContainerInstancesResponses.length === 0) { return new Promise(function (resolve, reject) { console.warn("No Container Instances found"); res.json([]); }); } else { - let containerInstances = describeContainerInstancesResponses.reduce(function(acc, current){ + const containerInstances = describeContainerInstancesResponses.reduce(function (acc, current) { return acc.concat(current.data.containerInstances); }, []); - let ec2instanceIds = containerInstances.map(function (i) { return i.ec2InstanceId; }); + const ec2instanceIds = containerInstances.map(function (i) { + return i.ec2InstanceId; + }); console.log(`Found ${ec2instanceIds.length} ec2InstanceIds: ${ec2instanceIds}`); return ec2.describeInstances({ InstanceIds: ec2instanceIds }).promise() - .then(function (ec2Instances) { - var instances = [].concat.apply([], ec2Instances.data.Reservations.map(function (r) { return r.Instances })); - var privateIpAddresses = instances.map(function (i) {return i.PrivateIpAddress}); - console.log(`\twith ${privateIpAddresses.length} matching Private IP addresses: ${privateIpAddresses}`); - var instanceSummaries = containerInstances.map(function (instance) { - var ec2IpAddress = instances.find(function (i) {return i.InstanceId == instance.ec2InstanceId}).PrivateIpAddress; - return { - "ec2IpAddress": ec2IpAddress, - "ec2InstanceId": instance.ec2InstanceId, - "ec2InstanceConsoleUrl": "https://console.aws.amazon.com/ec2/v2/home?region=" + AWS.config.region + "#Instances:instanceId=" + instance.ec2InstanceId, - "ecsInstanceConsoleUrl": "https://console.aws.amazon.com/ecs/home?region=" + AWS.config.region + "#/clusters/" + cluster + "/containerInstances/" + instance["containerInstanceArn"].substring(instance["containerInstanceArn"].lastIndexOf("/") + 1), - "registeredCpu": utils.registeredCpu(instance), - "registeredMemory": utils.registeredMemory(instance), - "remainingCpu": utils.remainingCpu(instance), - "remainingMemory": utils.remainingMemory(instance), - "tasks": tasksArray.filter(function (t) { - return t.containerInstanceArn == instance.containerInstanceArn; - }) - } + .then(function (ec2Instances) { + const instances = [].concat.apply([], ec2Instances.data.Reservations.map(function (r) { + return r.Instances + })); + const privateIpAddresses = instances.map(function (i) { + return i.PrivateIpAddress + }); + console.log(`\twith ${privateIpAddresses.length} matching Private IP addresses: ${privateIpAddresses}`); + const instanceSummaries = containerInstances.map(function (instance) { + const ec2IpAddress = instances.find(function (i) { + return i.InstanceId === instance.ec2InstanceId + }).PrivateIpAddress; + return { + "ec2IpAddress": ec2IpAddress, + "ec2InstanceId": instance.ec2InstanceId, + "ec2InstanceConsoleUrl": "https://console.aws.amazon.com/ec2/v2/home?region=" + AWS.config.region + "#Instances:instanceId=" + instance.ec2InstanceId, + "ecsInstanceConsoleUrl": "https://console.aws.amazon.com/ecs/home?region=" + AWS.config.region + "#/clusters/" + cluster + "/containerInstances/" + instance["containerInstanceArn"].substring(instance["containerInstanceArn"].lastIndexOf("/") + 1), + "registeredCpu": utils.registeredCpu(instance), + "registeredMemory": utils.registeredMemory(instance), + "remainingCpu": utils.remainingCpu(instance), + "remainingMemory": utils.remainingMemory(instance), + "tasks": tasksArray.filter(function (t) { + return t.containerInstanceArn === instance.containerInstanceArn; + }) + } + }); + res.json(instanceSummaries); }); - res.json(instanceSummaries); - }); } }) .catch(function (err) { @@ -116,20 +127,22 @@ router.get('/api/instance_summaries_with_tasks', function (req, res, next) { }); } else { // Return some static instance details with task details - var instanceSummaries = JSON.parse(fs.readFileSync("public/test_data/ecs_instance_summaries_with_tasks-" + cluster + ".json", "utf8")); + const instanceSummaries = JSON.parse(fs.readFileSync("public/test_data/ecs_instance_summaries_with_tasks-" + cluster + ".json", "utf8")); res.json(instanceSummaries); } } }); router.get('/api/cluster_names', function (req, res, next) { - var live = req.query.static != 'true'; + const live = req.query.static !== 'true'; if (live) { - ecs.listClusters({}, function(err, data1) { + ecs.listClusters({}, function (err, data1) { if (err) { sendErrorResponse(err, res); } else { - res.json(data1.clusterArns.map(function(str){return str.substring(str.indexOf("/")+1)})); + res.json(data1.clusterArns.map(function (str) { + return str.substring(str.indexOf("/") + 1) + })); } }); } else { @@ -138,139 +151,145 @@ router.get('/api/cluster_names', function (req, res, next) { }); function listAllContainerInstances(cluster) { - return new Promise(function (resolve, reject) { - listContainerInstanceWithToken(cluster, null, []) - .then (function(containerInstanceArns) { - resolve(containerInstanceArns); - }).catch (function (err) { - reject(err); - }); - }); + return new Promise(function (resolve, reject) { + listContainerInstanceWithToken(cluster, null, []) + .then(function (containerInstanceArns) { + resolve(containerInstanceArns); + }) + .catch(function (err) { + reject(err); + }); + }); } function listContainerInstanceWithToken(cluster, token, instanceArns) { - var params = {cluster: cluster, maxResults: maxSize}; - if (token) { - params['nextToken'] = token; - } - debugLog("Calling ecs.listContainerInstances..."); - return ecs.listContainerInstances(params).promise() - .then(function(listContainerInstanceResponse) { - var containerInstanceArns = instanceArns.concat(listContainerInstanceResponse.data.containerInstanceArns); - var nextToken = listContainerInstanceResponse.data.nextToken; - if (containerInstanceArns.length == 0) { - return []; - } else if (nextToken) { - return listContainerInstanceWithToken(cluster, nextToken, containerInstanceArns); - } else { - return containerInstanceArns; - } - }); + const params = {cluster: cluster, maxResults: maxSize}; + if (token) { + params['nextToken'] = token; + } + debugLog("Calling ecs.listContainerInstances..."); + return ecs.listContainerInstances(params).promise() + .then(function (listContainerInstanceResponse) { + const containerInstanceArns = instanceArns.concat(listContainerInstanceResponse.data.containerInstanceArns); + const nextToken = listContainerInstanceResponse.data.nextToken; + if (containerInstanceArns.length === 0) { + return []; + } else if (nextToken) { + return listContainerInstanceWithToken(cluster, nextToken, containerInstanceArns); + } else { + return containerInstanceArns; + } + }); } function listAllTasks(cluster) { - return new Promise(function (resolve, reject) { - listTasksWithToken(cluster, null, []) - .then (function(allTasks) { - resolve(allTasks); - }).catch (function (err) { - reject(err); - }); + return new Promise(function (resolve, reject) { + listTasksWithToken(cluster, null, []) + .then(function (allTasks) { + resolve(allTasks); + }).catch(function (err) { + reject(err); }); + }); } function listTasksWithToken(cluster, token, tasks) { - let params = {cluster: cluster, maxResults: maxSize}; - if (token) { - params['nextToken'] = token; - } - debugLog(`\tCalling ecs.listTasks with token: ${token} ...`); - return ecs.listTasks(params).promise() - .then(delayPromise(1000)) - .then(function (tasksResponse) { - debugLog(`\t\tReceived tasksResponse with ${tasksResponse.data.taskArns.length} Task ARNs`); - let taskArns = tasks.concat(tasksResponse.data.taskArns); - let nextToken = tasksResponse.data.nextToken; - if (taskArns.length == 0) { - return []; - } else if (nextToken) { - return listTasksWithToken(cluster, nextToken, taskArns); - } else { - debugLog(`\t\tReturning ${taskArns.length} taskArns from listTasksWithToken: ${taskArns}`); - return taskArns; - } - }); + const params = {cluster: cluster, maxResults: maxSize}; + if (token) { + params['nextToken'] = token; + } + debugLog(`\tCalling ecs.listTasks with token: ${token} ...`); + return ecs.listTasks(params).promise() + .then(delayPromise(AWS_API_DELAY)) + .then(function (tasksResponse) { + debugLog(`\t\tReceived tasksResponse with ${tasksResponse.data.taskArns.length} Task ARNs`); + const taskArns = tasks.concat(tasksResponse.data.taskArns); + const nextToken = tasksResponse.data.nextToken; + if (taskArns.length === 0) { + return []; + } else if (nextToken) { + return listTasksWithToken(cluster, nextToken, taskArns); + } else { + debugLog(`\t\tReturning ${taskArns.length} taskArns from listTasksWithToken: ${taskArns}`); + return taskArns; + } + }); } function getTasksWithTaskDefinitions(cluster) { console.log(`Getting Tasks annotated with Task Definitions for cluster '${cluster}'...`); return new Promise(function (resolve, reject) { - let tasksArray = []; - listAllTasks(cluster) - .then(function (allTaskArns) { - if (allTaskArns.length == 0) { - console.warn("\tNo Task ARNs found"); - resolve([]); - } else { - let taskBatches = allTaskArns.map(function (tasks, index) { - return index % maxSize === 0 ? allTaskArns.slice(index, index + maxSize) : null; - }).filter(function (tasks) { - return tasks; - }); - return taskBatches; - } - }) - .then(function (taskBatches) { - // Batch the batches :) - describe up to 2 batches of batches of maxSize ARNs at a time - // Without batchPromises, we will fire all ecs.describeTasks calls one after the other and could run into API rate limit issues - return batchPromises(2, taskBatches, taskBatch => new Promise((resolve, reject) => { - // The iteratee will fire after each batch - debugLog(`\tCalling ecs.describeTasks for Task batch: ${taskBatch}`); - resolve(ecs.describeTasks({cluster: cluster, tasks: taskBatch}).promise() - .then(delayPromise(1000))); + let tasksArray = []; + listAllTasks(cluster) + .then(function (allTaskArns) { + if (allTaskArns.length === 0) { + console.warn("\tNo Task ARNs found"); + resolve([]); + } else { + return allTaskArns.map(function (tasks, index) { + return index % maxSize === 0 ? allTaskArns.slice(index, index + maxSize) : null; + }).filter(function (tasks) { + return tasks; + }); + } + }) + .then(function (taskBatches) { + // Batch the batches :) - describe up to 2 batches of batches of maxSize ARNs at a time + // Without batchPromises, we will fire all ecs.describeTasks calls one after the other and could run into API rate limit issues + return batchPromises(2, taskBatches, taskBatch => new Promise((resolve, reject) => { + // The iteratee will fire after each batch + debugLog(`\tCalling ecs.describeTasks for Task batch: ${taskBatch}`); + resolve(ecs.describeTasks({cluster: cluster, tasks: taskBatch}).promise() + .then(delayPromise(AWS_API_DELAY))); + })); + }) + .then(function (describeTasksResponses) { + tasksArray = describeTasksResponses.reduce(function (acc, current) { + return acc.concat(current.data.tasks); + }, []); + console.log(`Found ${tasksArray.length} tasks`); + // Wait for the responses from 20 describeTaskDefinition calls before invoking another 20 calls + // Without batchPromises, we will fire all ecs.describeTaskDefinition calls one after the other and could run into API rate limit issues + return batchPromises(1, tasksArray, task => new Promise((resolve, reject) => { + const cachedTaskDef = taskDefinitionCache.get(task.taskDefinitionArn); + if (cachedTaskDef) { + debugLog(`\tReusing cached Task Definition for Task Definition ARN: ${task.taskDefinitionArn}`); + resolve(cachedTaskDef); + } else { + debugLog(`\tCalling ecs.describeTaskDefinition for Task Definition ARN: ${task.taskDefinitionArn}`); + resolve(ecs.describeTaskDefinition({taskDefinition: task.taskDefinitionArn}).promise() + .then(delayPromise(AWS_API_DELAY)) + .then(function (taskDefinition) { + debugLog(`\t\tReceived taskDefinition for ARN "${task.taskDefinitionArn}". Caching in memory.`); + taskDefinitionCache.put(task.taskDefinitionArn, taskDefinition); + return taskDefinition; + }) + .catch(function (err) { + debugLog(`\t\tFAILED ecs.describeTaskDefinition call for '${task.taskDefinitionArn}': ${err}`); + return Promise.reject(err); })); - }) - .then(function (describeTasksResponses) { - tasksArray = describeTasksResponses.reduce(function(acc, current){ - return acc.concat(current.data.tasks); - }, []); - console.log(`Found ${tasksArray.length} tasks`); - // Wait for the responses from 20 describeTaskDefinition calls before invoking another 20 calls - // Without batchPromises, we will fire all ecs.describeTaskDefinition calls one after the other and could run into API rate limit issues - return batchPromises(20, tasksArray, task => new Promise((resolve, reject) => { - debugLog(`\tCalling ecs.describeTaskDefinition for Task Definition ARN: ${task.taskDefinitionArn}`); - // TODO: Don't ask for same task definition more than once - resolve(ecs.describeTaskDefinition({taskDefinition: task.taskDefinitionArn}).promise() - .then(delayPromise(1000)) - .then(function (taskDefinition) { - debugLog(`\t\tReceived taskDefinition for ${task.taskDefinitionArn}`); - return taskDefinition; - }) - .catch(function (err) { - debugLog(`\t\tFAILED ecs.describeTaskDefinition call for '${task.taskDefinitionArn}': ${err}`); - return Promise.reject(err); - })); - })); - }) - .then(function (taskDefs) { - console.log(`Found ${taskDefs.length} task definitions`); - // Fill in task details in tasksArray with taskDefinition details (e.g. memory, cpu) - taskDefs.forEach(function(taskDef) { - tasksArray - .filter(function (t) { - return t["taskDefinitionArn"] == taskDef.data.taskDefinition.taskDefinitionArn; - }) - .forEach(function (t) { - t["taskDefinition"] = taskDef.data.taskDefinition; - }); + } + })); + }) + .then(function (taskDefs) { + console.log(`Found ${taskDefs.length} task definitions`); + // Fill in task details in tasksArray with taskDefinition details (e.g. memory, cpu) + taskDefs.forEach(function (taskDef) { + tasksArray + .filter(function (t) { + return t["taskDefinitionArn"] === taskDef.data.taskDefinition.taskDefinitionArn; + }) + .forEach(function (t) { + t["taskDefinition"] = taskDef.data.taskDefinition; }); - resolve(tasksArray); - }) - .catch(function (err) { - console.error("\nCaught error in getTasksWithTaskDefinitions():", err); - reject(err); }); - }); + resolve(tasksArray); + }) + .catch(function (err) { + console.error("\nCaught error in getTasksWithTaskDefinitions():", err); + reject(err); + }); + }); } function send400Response(errMsg, res) { @@ -285,20 +304,19 @@ function sendErrorResponse(err, res) { } function debugLog(msg) { - if (DEBUG_LOGGING) { - console.info(msg); - } + debug(msg); } +// Introduce a delay to prevent Rate Exceeded errors // From: https://blog.raananweber.com/2015/12/01/writing-a-promise-delayer/ // NOTE: https://www.npmjs.com/package/promise-pause does not work as the aws-sdk-promise library does not return a Promise but a thenable object function delayPromise(delay) { //return a function that accepts a single variable - return function(data) { + return function (data) { //this function returns a promise. - return new Promise(function(resolve, reject) { + return new Promise(function (resolve, reject) { debugLog(`Pausing for ${delay}ms...`); - setTimeout(function() { + setTimeout(function () { //a promise that is resolved after "delay" milliseconds with the data provided resolve(data); }, delay); From cc8e1424cda5ceb7c1ee7c738c0d0acc0c4cc74d Mon Sep 17 00:00:00 2001 From: Matt Callanan Date: Mon, 2 Oct 2017 22:09:29 +1000 Subject: [PATCH 03/16] Prevent client-side null error before graph is created. Update to use ES6 let/const in place of var. --- public/javascripts/graph.js | 305 +++++++++++++++++++----------------- 1 file changed, 162 insertions(+), 143 deletions(-) diff --git a/public/javascripts/graph.js b/public/javascripts/graph.js index 28efc51..e109cd7 100644 --- a/public/javascripts/graph.js +++ b/public/javascripts/graph.js @@ -19,7 +19,9 @@ function showError(graph, message) { } function showMessage(graph, message, color) { - graph.append("text").attr("x", 0).attr("y", 20).attr("fill", color).text(message || "Error"); + if (graph !== null) { + graph.append("text").attr("x", 0).attr("y", 20).attr("fill", color).text(message || "Error"); + } } function handleError(errorMsg, graph, onError) { @@ -28,22 +30,28 @@ function handleError(errorMsg, graph, onError) { } function ecsInstanceConsoleUrl(data, ec2IpAddress) { - instance = data.find(function (element, index, array) { return element.ec2IpAddress == ec2IpAddress; }); + const instance = data.find(function (element, index, array) { + return element.ec2IpAddress == ec2IpAddress; + }); return instance != null ? instance.ecsInstanceConsoleUrl : null; } function ec2InstanceConsoleUrl(data, ec2IpAddress) { - instance = data.find(function (element, index, array) { return element.ec2IpAddress == ec2IpAddress; }); + const instance = data.find(function (element, index, array) { + return element.ec2IpAddress == ec2IpAddress; + }); return instance != null ? instance.ec2InstanceConsoleUrl : null; } function ec2InstanceId(data, ec2IpAddress) { - instance = data.find(function (element, index, array) { return element.ec2IpAddress == ec2IpAddress; }); + const instance = data.find(function (element, index, array) { + return element.ec2IpAddress == ec2IpAddress; + }); return instance != null ? instance.ec2InstanceId : null; } function copyToClipboard(text) { - var copyElement = document.createElement('input'); + let copyElement = document.createElement('input'); copyElement.setAttribute('type', 'text'); copyElement.setAttribute('value', text); copyElement = document.body.appendChild(copyElement); @@ -61,8 +69,10 @@ function copyToClipboard(text) { } function addD3DataToTask(task, resourceType, y0) { - var resourceAllocation = task.taskDefinition.containerDefinitions.reduce(function (sum, b) { return sum + (resourceType == ResourceEnum.MEMORY ? b.memory : b.cpu); }, 0); - var y1 = y0 + resourceAllocation; + const resourceAllocation = task.taskDefinition.containerDefinitions.reduce(function (sum, b) { + return sum + (resourceType == ResourceEnum.MEMORY ? b.memory : b.cpu); + }, 0); + const y1 = y0 + resourceAllocation; task.d3Data = { name: taskFamilyAndRevision(task), resourceAllocation: resourceAllocation, // sum of all containers' resource (memory/cpu) allocation @@ -93,16 +103,15 @@ function remainingResource(d, resourceType) { } function recreateMainGraphElement(chartDivId, graphWidth, leftMargin, rightMargin, totalHeight, topMargin, bottomMargin) { - d3.select('#' + chartDivId).select("svg").remove(); - let graph = d3.select('#' + chartDivId) - .append("svg") - .attr("class", "cluster-graph") - .attr("id", "cluster-graph") - .attr("width", graphWidth + leftMargin + rightMargin) - .attr("height", totalHeight + topMargin + bottomMargin) - .attr("float", "left") - .append("g").attr("transform", "translate(" + leftMargin + "," + topMargin + ")"); - return graph; + d3.select('#' + chartDivId).select("svg").remove(); + return d3.select('#' + chartDivId) + .append("svg") + .attr("class", "cluster-graph") + .attr("id", "cluster-graph") + .attr("width", graphWidth + leftMargin + rightMargin) + .attr("height", totalHeight + topMargin + bottomMargin) + .attr("float", "left") + .append("g").attr("transform", "translate(" + leftMargin + "," + topMargin + ")"); } const GRAPH_TOP_MARGIN = 20; @@ -116,25 +125,25 @@ const EXPANDED_GRAPH_WIDTH = 1300; function renderGraph(chartDivId, legendDivId, cluster, resourceTypeText, onCompletion, onError) { if (window.apiResponseError) { - let errorMsg = "Server Error: " + (window.apiResponseError instanceof XMLHttpRequest ? window.apiResponseError.responseText : JSON.stringify(window.apiResponseError)); - let graph = recreateMainGraphElement(chartDivId, DEFAULT_GRAPH_WIDTH, LEFT_MARGIN, RIGHT_MARGIN, TOTAL_HEIGHT, GRAPH_TOP_MARGIN, GRAPH_BOTTOM_MARGIN); + const errorMsg = "Server Error: " + (window.apiResponseError instanceof XMLHttpRequest ? window.apiResponseError.responseText : JSON.stringify(window.apiResponseError)); + const graph = recreateMainGraphElement(chartDivId, DEFAULT_GRAPH_WIDTH, LEFT_MARGIN, RIGHT_MARGIN, TOTAL_HEIGHT, GRAPH_TOP_MARGIN, GRAPH_BOTTOM_MARGIN); handleError(errorMsg, graph, onError); return graph; } - let showTaskBreakdown = true; // TODO: Parameterise + const showTaskBreakdown = true; // TODO: Parameterise try { - let resourceType = parseResourceType(resourceTypeText, ResourceEnum.MEMORY); - let graphWidth = window.apiResponseError ? DEFAULT_GRAPH_WIDTH : (window.apiResponseData.length > 50 ? EXPANDED_GRAPH_WIDTH : DEFAULT_GRAPH_WIDTH) - LEFT_MARGIN - RIGHT_MARGIN; //establishes width based on data set size - let colorRange = d3.scale.ordinal().range(colorbrewer.Pastel1[9].concat(colorbrewer.Pastel2[8]).concat(colorbrewer.Set1[9]).concat(colorbrewer.Set2[8]).concat(colorbrewer.Set3[12])); - let xRange = d3.scale.ordinal().rangeRoundBands([10, graphWidth], .1); - let yRange = d3.scale.linear().rangeRound([GRAPH_HEIGHT, 0]); - let xAxis = d3.svg.axis().scale(xRange).orient("bottom"); - let yAxis = d3.svg.axis().scale(yRange).orient("left").tickFormat(d3.format(".2s")); + const resourceType = parseResourceType(resourceTypeText, ResourceEnum.MEMORY); + const graphWidth = window.apiResponseError ? DEFAULT_GRAPH_WIDTH : (window.apiResponseData.length > 50 ? EXPANDED_GRAPH_WIDTH : DEFAULT_GRAPH_WIDTH) - LEFT_MARGIN - RIGHT_MARGIN; //establishes width based on data set size + const colorRange = d3.scale.ordinal().range(colorbrewer.Pastel1[9].concat(colorbrewer.Pastel2[8]).concat(colorbrewer.Set1[9]).concat(colorbrewer.Set2[8]).concat(colorbrewer.Set3[12])); + const xRange = d3.scale.ordinal().rangeRoundBands([10, graphWidth], .1); + const yRange = d3.scale.linear().rangeRound([GRAPH_HEIGHT, 0]); + const xAxis = d3.svg.axis().scale(xRange).orient("bottom"); + const yAxis = d3.svg.axis().scale(yRange).orient("left").tickFormat(d3.format(".2s")); // Main graph area - let graph = recreateMainGraphElement(chartDivId, graphWidth, LEFT_MARGIN, RIGHT_MARGIN, TOTAL_HEIGHT, GRAPH_TOP_MARGIN, GRAPH_BOTTOM_MARGIN); + const graph = recreateMainGraphElement(chartDivId, graphWidth, LEFT_MARGIN, RIGHT_MARGIN, TOTAL_HEIGHT, GRAPH_TOP_MARGIN, GRAPH_BOTTOM_MARGIN); if (window.apiResponseData.length == 0) { showInfo(graph, "No instances are registered for the '" + cluster + "' cluster."); @@ -156,8 +165,8 @@ function renderGraph(chartDivId, legendDivId, cluster, resourceTypeText, onCompl window.apiResponseData.forEach(function (instance) { // Add d3Data to each task for later display - var y0 = 0; - instance.tasks.forEach(function(task) { + let y0 = 0; + instance.tasks.forEach(function (task) { y0 = addD3DataToTask(task, resourceType, y0); }); }); @@ -168,178 +177,188 @@ function renderGraph(chartDivId, legendDivId, cluster, resourceTypeText, onCompl })); // Calculate maximum resource (memory/cpu) across all servers - var maxResource = d3.max(window.apiResponseData, function (d) { + const maxResource = d3.max(window.apiResponseData, function (d) { return registeredResource(d, resourceType); }); // Set Y axis linear domain range from 0 to maximum memory/cpu in bytes yRange.domain([0, toBytes(maxResource)]); // Draw X axis - var xAxisLabels = graph.append("g") - .attr("class", "graph-axis") - .attr("transform", "translate(0," + GRAPH_HEIGHT + ")") - .call(xAxis); + const xAxisLabels = graph.append("g") + .attr("class", "graph-axis") + .attr("transform", "translate(0," + GRAPH_HEIGHT + ")") + .call(xAxis); - var menu = [ + const menu = [ { title: 'Copy IP Address', - action: function(elm, d, i) { + action: function (elm, d, i) { copyToClipboard(d); } }, { title: 'Open ECS Container Instance Console', - action: function(elm, d, i) { + action: function (elm, d, i) { window.open(ecsInstanceConsoleUrl(window.apiResponseData, d), '_blank'); } }, { title: 'Open EC2 Instance Console', - action: function(elm, d, i) { + action: function (elm, d, i) { window.open(ec2InstanceConsoleUrl(window.apiResponseData, d), '_blank'); } } ]; xAxisLabels.selectAll("text") - .attr("cursor", "pointer") - .on('contextmenu', d3.contextMenu(menu)) - // X axis tooltip - .append("svg:title") - .text(function (d) { - return "Right-click for options"; - }); + .attr("cursor", "pointer") + .on('contextmenu', d3.contextMenu(menu)) + // X axis tooltip + .append("svg:title") + .text(function (d) { + return "Right-click for options"; + }); // Rotate X axis labels 90 degrees if bar is wide enough to cause overlapping if (xRange.rangeBand() < 80) { xAxisLabels.selectAll("text") - .attr("y", 0) - .attr("x", 9) - .attr("dy", ".35em") - .attr("transform", "rotate(90)") - .style("text-anchor", "start"); + .attr("y", 0) + .attr("x", 9) + .attr("dy", ".35em") + .attr("transform", "rotate(90)") + .style("text-anchor", "start"); } // Make the font smaller if bar is wide enough to cause overlapping if (xRange.rangeBand() < 14) { xAxisLabels.selectAll("text") - .attr("class", "graph-axis-small") + .attr("class", "graph-axis-small") } // Draw Y axis graph.append("g") - .attr("class", "graph-axis") - .call(yAxis) - .append("text") - .attr("transform", "rotate(-90)") - .attr("y", 6) - .attr("dy", ".71em") - .style("text-anchor", "end") - .text(resourceLabel(resourceType)); + .attr("class", "graph-axis") + .call(yAxis) + .append("text") + .attr("transform", "rotate(-90)") + .attr("y", 6) + .attr("dy", ".71em") + .style("text-anchor", "end") + .text(resourceLabel(resourceType)); // TODO: Request task data in parallel with instance data. Draw instance outline first then draw task boxes // Create svg elements for each server - var instance = graph.selectAll(".instance") - .data(window.apiResponseData) - .enter().append("g") - .attr("class", "g") - .attr("transform", function (d) { - return "translate(" + xRange(d.ec2IpAddress) + ",0)"; - }); + const instance = graph.selectAll(".instance") + .data(window.apiResponseData) + .enter().append("g") + .attr("class", "g") + .attr("transform", function (d) { + return "translate(" + xRange(d.ec2IpAddress) + ",0)"; + }); // For each server, draw entire resource (memory/cpu) available as grey rect instance.append("rect") - .attr("class", "instance-block") + .attr("class", "instance-block") + .attr("width", xRange.rangeBand()) + .attr("y", function (d) { + return yRange(toBytes(registeredResource(d, resourceType))) + }) + .attr("height", function (d) { + return yRange(toBytes(maxResource - (registeredResource(d, resourceType)))); + }); + + if (showTaskBreakdown) { + // For each task on each server, represent resource (memory/cpu) allocation as a rect + instance.selectAll(".task") + .data(function (d) { + return d.tasks; + }) + .enter().append("rect") + .attr("class", "task-block") .attr("width", xRange.rangeBand()) .attr("y", function (d) { - return yRange(toBytes(registeredResource(d, resourceType))) + return yRange(toBytes(d.d3Data.y1)); }) .attr("height", function (d) { - return yRange(toBytes(maxResource - (registeredResource(d, resourceType)))); + return yRange(toBytes(d.d3Data.y0)) - yRange(toBytes(d.d3Data.y1)); + }) + .style("fill", function (d) { + return colorRange(d.d3Data.name); + }) + // Use name as hover tooltip + .append("svg:title") + .text(function (d) { + return d.d3Data.name + " (" + resourceLabel(resourceType) + ": " + d.d3Data.resourceAllocation + ")"; }); - if (showTaskBreakdown) { - // For each task on each server, represent resource (memory/cpu) allocation as a rect - instance.selectAll(".task") - .data(function (d) { - return d.tasks; - }) - .enter().append("rect") - .attr("class", "task-block") - .attr("width", xRange.rangeBand()) - .attr("y", function (d) { - return yRange(toBytes(d.d3Data.y1)); - }) - .attr("height", function (d) { - return yRange(toBytes(d.d3Data.y0)) - yRange(toBytes(d.d3Data.y1)); - }) - .style("fill", function (d) { - return colorRange(d.d3Data.name); - }) - // Use name as hover tooltip - .append("svg:title") - .text(function (d) { - return d.d3Data.name + " (" + resourceLabel(resourceType) + ": " + d.d3Data.resourceAllocation + ")"; - }); - // Draw legend - var taskData = uniqueTaskDefs.sort(); - var longestLength = taskData.reduce(function (a, b) { return a.length > b.length ? a : b; }, []).length; + const taskData = uniqueTaskDefs.sort(); + const longestLength = taskData.reduce(function (a, b) { + return a.length > b.length ? a : b; + }, []).length; // TODO: Add hover highlight of related blocks d3.select('#' + legendDivId).select("svg").remove(); - var svg2 = d3.select('#' + legendDivId) - .append("svg") - .attr("class", "cluster-legend") - .attr("id", "cluster-legend") - .attr("width", (longestLength * 10) + 20) - .attr("height", (20 * taskData.length) + 20); + const svg2 = d3.select('#' + legendDivId) + .append("svg") + .attr("class", "cluster-legend") + .attr("id", "cluster-legend") + .attr("width", (longestLength * 10) + 20) + .attr("height", (20 * taskData.length) + 20); - var legend = svg2.append("g") - .attr("class", "legend"); + const legend = svg2.append("g") + .attr("class", "legend"); legend.selectAll('rect') - .data(taskData) - .enter() - .append("rect") - .attr("x", 1) - .attr("y", function(d, i) { return ((i * 20) + GRAPH_TOP_MARGIN); }) - .attr("width", 18) - .attr("height", 18) - .style("fill", function (d) { return colorRange(d); }) - .style("stroke-width", 0.5) - .style("stroke", "rgb(51, 51, 51)"); + .data(taskData) + .enter() + .append("rect") + .attr("x", 1) + .attr("y", function (d, i) { + return ((i * 20) + GRAPH_TOP_MARGIN); + }) + .attr("width", 18) + .attr("height", 18) + .style("fill", function (d) { + return colorRange(d); + }) + .style("stroke-width", 0.5) + .style("stroke", "rgb(51, 51, 51)"); legend.selectAll('text') - .data(taskData) - .enter() - .append("text") - .attr("x", 25) - .attr("width", 5) - .attr("height", 5) - .attr("y", function(d, i) { return ((i * 20) + GRAPH_TOP_MARGIN + 12); }) - .text(function(d) { return d; }); + .data(taskData) + .enter() + .append("text") + .attr("x", 25) + .attr("width", 5) + .attr("height", 5) + .attr("y", function (d, i) { + return ((i * 20) + GRAPH_TOP_MARGIN + 12); + }) + .text(function (d) { + return d; + }); } else { // For each each server, represent total cpu allocation as a single orange rect instance.append("rect") - .attr("width", xRange.rangeBand()) - .attr("y", function (d) { - var usedCpu = toBytes(registeredResource(d, resourceType)) - toBytes(remainingResource(d, resourceType)); - return yRange(usedCpu) - }) - .attr("height", function (d) { - return yRange(toBytes(remainingResource(d, resourceType))) - yRange(toBytes(registeredResource(d, resourceType))); - }) - .style("fill", "orange") - .style("stroke", "grey"); + .attr("width", xRange.rangeBand()) + .attr("y", function (d) { + const usedCpu = toBytes(registeredResource(d, resourceType)) - toBytes(remainingResource(d, resourceType)); + return yRange(usedCpu) + }) + .attr("height", function (d) { + return yRange(toBytes(remainingResource(d, resourceType))) - yRange(toBytes(registeredResource(d, resourceType))); + }) + .style("fill", "orange") + .style("stroke", "grey"); } return graph; } catch (e) { - handleError(e.stack ? e.stack : e, graph, onError); + handleError(e.stack ? e.stack : e, null, onError); } finally { onCompletion(); } @@ -348,25 +367,25 @@ function renderGraph(chartDivId, legendDivId, cluster, resourceTypeText, onCompl function populateGraph(useStaticData, chartDivId, legendDivId, cluster, resourceTypeText, onCompletion, onError) { try { if (!cluster && !useStaticData) { - handleError("Please select a cluster.", graph, onError); + handleError("Please select a cluster.", null, onError); return; } - var clusterParam = "cluster=" + (cluster ? cluster : "default"); - var optionalStaticParam = (useStaticData ? "&static=true" : ""); - var c3visApiUrl = "/api/instance_summaries_with_tasks?" + clusterParam + optionalStaticParam; + const clusterParam = "cluster=" + (cluster ? cluster : "default"); + const optionalStaticParam = (useStaticData ? "&static=true" : ""); + const c3visApiUrl = "/api/instance_summaries_with_tasks?" + clusterParam + optionalStaticParam; // after a GET all data from API begin drawing graph d3.json(c3visApiUrl, function (apiResponseError, apiResponseData) { // TODO: Display multiple graphs if server returns > 100 instances - window.apiResponseError = apiResponseError; - window.apiResponseData = apiResponseData; - console.log("For debugging: window.apiResponseData, window.apiResponseError"); + window.apiResponseError = apiResponseError; + window.apiResponseData = apiResponseData; + console.log("For debugging: window.apiResponseData, window.apiResponseError"); - renderGraph(chartDivId, legendDivId, cluster, resourceTypeText, onCompletion, onError); - }); + renderGraph(chartDivId, legendDivId, cluster, resourceTypeText, onCompletion, onError); + }); } catch (e) { - handleError("ERROR. Uncaught Exception: " + e, graph, onError); + handleError("ERROR. Uncaught Exception: " + e, null, onError); } } From 15f89818b34b17064d3c8b5d46c52c02b7b99a30 Mon Sep 17 00:00:00 2001 From: Matt Callanan Date: Mon, 2 Oct 2017 22:09:55 +1000 Subject: [PATCH 04/16] Add README instructions for debug logging. --- README.md | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/README.md b/README.md index ea1d6e4..d9647ab 100644 --- a/README.md +++ b/README.md @@ -113,6 +113,20 @@ From the browser, use a ```"?static=true"``` query parameter to have the server Browse to `http://localhost:3000/?static=true`. +### Debug Logging + +To see all debug log entries: + +``` +DEBUG=* npm start +``` + +To see just API debug log entries: + +``` +DEBUG=api npm start +``` + ### Debugging Add the following line to server-side Javascript code to add a breakpoint: From e76c2b9c011901f64e779d5cfcb4081686d06d07 Mon Sep 17 00:00:00 2001 From: mcallanan Date: Fri, 6 Apr 2018 21:43:41 +1000 Subject: [PATCH 05/16] Introduce short polling approach to avoid client timeouts: - Server responds immediately with 'fetching' state and populates cluster store asynchronously - Client polls until cluster is in 'fetched' state --- package.json | 2 +- routes/index.js | 204 ++++++++++++++++++++++++++++++------------------ 2 files changed, 127 insertions(+), 79 deletions(-) diff --git a/package.json b/package.json index 836e989..b86b3ff 100644 --- a/package.json +++ b/package.json @@ -4,7 +4,7 @@ "private": true, "scripts": { "prestart": "npm install", - "start": "node --harmony ./bin/www # --harmony required for extra ES6 functionality such as Array.find()" + "start": "node ./bin/www" }, "dependencies": { "body-parser": "1.12.4", diff --git a/routes/index.js b/routes/index.js index 91fd5ef..7bfd271 100644 --- a/routes/index.js +++ b/routes/index.js @@ -19,6 +19,8 @@ const utils = require('./utils'); const taskDefinitionCache = require('memory-cache'); +var clusterStore = {}; + const ecs = new AWS.ECS(); const ec2 = new AWS.EC2(); const maxSize = 100; @@ -38,101 +40,147 @@ router.get('/', function (req, res, next) { * Endpoints take a "?static=true" query param to enable testing with static data when AWS credentials aren't available */ +const STATUS_INITIAL = "initial"; +const STATUS_FETCHING = "fetching"; +const STATUS_FETCHED = "fetched"; + router.get('/api/instance_summaries_with_tasks', function (req, res, next) { console.log(`/api/instance_summaries_with_tasks: cluster='${req.query.cluster}', live=${!req.query.static})`); debugLog(`Headers: ${JSON.stringify(req.headers, null, 4)}`); + if (!req.query.cluster) { send400Response("Please provide a 'cluster' parameter", res); } else { - const cluster = req.query.cluster; + const clusterName = req.query.cluster; const live = req.query.static !== 'true'; if (live) { - let tasksArray = []; - getTasksWithTaskDefinitions(cluster) - .then(function (tasksResult) { - tasksArray = tasksResult; - return listAllContainerInstances(cluster); - }) - .then(function (listAllContainerInstanceArns) { - debugLog(`\tFound ${listAllContainerInstanceArns.length} ContainerInstanceARNs...`); - if (listAllContainerInstanceArns.length === 0) { - return new Promise(function (resolve, reject) { - resolve(null); - }); - } else { - const containerInstanceBatches = listAllContainerInstanceArns.map(function (instances, index) { - return index % maxSize === 0 ? listAllContainerInstanceArns.slice(index, index + maxSize) : null; - }).filter(function (instances) { - return instances; - }); - return batchPromises(1, containerInstanceBatches, containerInstanceBatch => new Promise((resolve, reject) => { - // The containerInstanceBatch iteratee will fire after each batch - debugLog(`\tCalling ecs.describeContainerInstances for Container Instance batch: ${containerInstanceBatch}`); - resolve(ecs.describeContainerInstances({ - cluster: cluster, - containerInstances: containerInstanceBatch - }).promise().then(delayPromise(AWS_API_DELAY))); - })); - } - }) - .then(function (describeContainerInstancesResponses) { - if (!describeContainerInstancesResponses || describeContainerInstancesResponses.length === 0) { - return new Promise(function (resolve, reject) { - console.warn("No Container Instances found"); - res.json([]); - }); - } else { - const containerInstances = describeContainerInstancesResponses.reduce(function (acc, current) { - return acc.concat(current.data.containerInstances); - }, []); - const ec2instanceIds = containerInstances.map(function (i) { - return i.ec2InstanceId; - }); - console.log(`Found ${ec2instanceIds.length} ec2InstanceIds: ${ec2instanceIds}`); - return ec2.describeInstances({ - InstanceIds: ec2instanceIds - }).promise() - .then(function (ec2Instances) { - const instances = [].concat.apply([], ec2Instances.data.Reservations.map(function (r) { - return r.Instances - })); - const privateIpAddresses = instances.map(function (i) { - return i.PrivateIpAddress - }); - console.log(`\twith ${privateIpAddresses.length} matching Private IP addresses: ${privateIpAddresses}`); - const instanceSummaries = containerInstances.map(function (instance) { - const ec2IpAddress = instances.find(function (i) { - return i.InstanceId === instance.ec2InstanceId - }).PrivateIpAddress; - return { - "ec2IpAddress": ec2IpAddress, - "ec2InstanceId": instance.ec2InstanceId, - "ec2InstanceConsoleUrl": "https://console.aws.amazon.com/ec2/v2/home?region=" + AWS.config.region + "#Instances:instanceId=" + instance.ec2InstanceId, - "ecsInstanceConsoleUrl": "https://console.aws.amazon.com/ecs/home?region=" + AWS.config.region + "#/clusters/" + cluster + "/containerInstances/" + instance["containerInstanceArn"].substring(instance["containerInstanceArn"].lastIndexOf("/") + 1), - "registeredCpu": utils.registeredCpu(instance), - "registeredMemory": utils.registeredMemory(instance), - "remainingCpu": utils.remainingCpu(instance), - "remainingMemory": utils.remainingMemory(instance), - "tasks": tasksArray.filter(function (t) { - return t.containerInstanceArn === instance.containerInstanceArn; - }) - } - }); - res.json(instanceSummaries); - }); - } + if (!getClusterStore(clusterName)) { + new Promise(function (resolve) { + initialiseClusterStore(clusterName); + resolve(); }) - .catch(function (err) { - sendErrorResponse(err, res); + .then(function () { + // Return to client + res.json(getClusterStore(clusterName)); }); + // Continue populating cluster store while client polls asynchronously + populateClusterStore(clusterName) + } else { + console.log("After populateState... responding with ", getClusterStore(clusterName)); + // Return currently fetched cluster store to client + res.json(getClusterStore(clusterName)); + } } else { // Return some static instance details with task details - const instanceSummaries = JSON.parse(fs.readFileSync("public/test_data/ecs_instance_summaries_with_tasks-" + cluster + ".json", "utf8")); + const instanceSummaries = JSON.parse(fs.readFileSync("public/test_data/ecs_instance_summaries_with_tasks-" + clusterName + ".json", "utf8")); res.json(instanceSummaries); } } }); +function setClusterStore(clusterName, status, instanceSummaries) { + console.log(`Setting fetch status to "${status}" for cluster "${clusterName}"`); + clusterStore[clusterName] = { + clusterName: clusterName, + fetchStatus: status, + instanceSummaries: instanceSummaries + }; + console.log(`clusterStore[${clusterName}] = ${getClusterStore(clusterName)}`) +} + +function initialiseClusterStore(clusterName) { + console.log("initialiseClusterStore"); + setClusterStore(clusterName, STATUS_INITIAL, {}); + console.log("clusterStore[" + clusterName + "]"); +} + +function getClusterStore(clusterName) { + return clusterStore[clusterName]; +} + +function populateClusterStore(clusterName) { + console.log(`populateClusterStore(${clusterName})`); + setClusterStore(clusterName, STATUS_FETCHING, {}); + + let tasksArray = []; + getTasksWithTaskDefinitions(cluster) + .then(function (tasksResult) { + tasksArray = tasksResult; + return listAllContainerInstances(cluster); + }) + .then(function (listAllContainerInstanceArns) { + debugLog(`\tFound ${listAllContainerInstanceArns.length} ContainerInstanceARNs...`); + if (listAllContainerInstanceArns.length === 0) { + return new Promise(function (resolve, reject) { + resolve(null); + }); + } else { + const containerInstanceBatches = listAllContainerInstanceArns.map(function (instances, index) { + return index % maxSize === 0 ? listAllContainerInstanceArns.slice(index, index + maxSize) : null; + }).filter(function (instances) { + return instances; + }); + return batchPromises(1, containerInstanceBatches, containerInstanceBatch => new Promise((resolve, reject) => { + // The containerInstanceBatch iteratee will fire after each batch + debugLog(`\tCalling ecs.describeContainerInstances for Container Instance batch: ${containerInstanceBatch}`); + resolve(ecs.describeContainerInstances({ + cluster: cluster, + containerInstances: containerInstanceBatch + }).promise().then(delayPromise(AWS_API_DELAY))); + })); + } + }) + .then(function (describeContainerInstancesResponses) { + if (!describeContainerInstancesResponses || describeContainerInstancesResponses.length === 0) { + return new Promise(function (resolve, reject) { + console.warn("No Container Instances found"); + res.json([]); + }); + } else { + const containerInstances = describeContainerInstancesResponses.reduce(function (acc, current) { + return acc.concat(current.data.containerInstances); + }, []); + const ec2instanceIds = containerInstances.map(function (i) { + return i.ec2InstanceId; + }); + console.log(`Found ${ec2instanceIds.length} ec2InstanceIds: ${ec2instanceIds}`); + return ec2.describeInstances({InstanceIds: ec2instanceIds}).promise() + .then(function (ec2Instances) { + const instances = [].concat.apply([], ec2Instances.data.Reservations.map(function (r) { + return r.Instances + })); + const privateIpAddresses = instances.map(function (i) { + return i.PrivateIpAddress + }); + console.log(`\twith ${privateIpAddresses.length} matching Private IP addresses: ${privateIpAddresses}`); + const instanceSummaries = containerInstances.map(function (instance) { + const ec2IpAddress = instances.find(function (i) { + return i.InstanceId === instance.ec2InstanceId + }).PrivateIpAddress; + return { + "ec2IpAddress": ec2IpAddress, + "ec2InstanceId": instance.ec2InstanceId, + "ec2InstanceConsoleUrl": "https://console.aws.amazon.com/ec2/v2/home?region=" + AWS.config.region + "#Instances:instanceId=" + instance.ec2InstanceId, + "ecsInstanceConsoleUrl": "https://console.aws.amazon.com/ecs/home?region=" + AWS.config.region + "#/clusters/" + cluster + "/containerInstances/" + instance["containerInstanceArn"].substring(instance["containerInstanceArn"].lastIndexOf("/") + 1), + "registeredCpu": utils.registeredCpu(instance), + "registeredMemory": utils.registeredMemory(instance), + "remainingCpu": utils.remainingCpu(instance), + "remainingMemory": utils.remainingMemory(instance), + "tasks": tasksArray.filter(function (t) { + return t.containerInstanceArn === instance.containerInstanceArn; + }) + } + }); + setClusterStore(clusterName, STATUS_FETCHED, instanceSummaries); + res.json(getClusterStore(clusterName)); + }); + } + }) + .catch(function (err) { + sendErrorResponse(err, res); + }); +} + router.get('/api/cluster_names', function (req, res, next) { const live = req.query.static !== 'true'; if (live) { From 2cf58857faa0a2b4a8461a9b3ae85ee0b7095fc2 Mon Sep 17 00:00:00 2001 From: mcallanan Date: Fri, 11 May 2018 22:18:34 +1000 Subject: [PATCH 06/16] Fix Y axis units to show 'k' instead of 'G' when showing CPU resource --- public/javascripts/graph.js | 26 +++++++++++++++++--------- 1 file changed, 17 insertions(+), 9 deletions(-) diff --git a/public/javascripts/graph.js b/public/javascripts/graph.js index e109cd7..afa8ac1 100644 --- a/public/javascripts/graph.js +++ b/public/javascripts/graph.js @@ -4,8 +4,16 @@ function taskFamilyAndRevision(t) { return t.taskDefinitionArn.substring(t.taskDefinitionArn.lastIndexOf('/') + 1) } -function toBytes(megabytes) { - return megabytes * 1000000; +// ECS API returns memory as MBs and CPU as CPU Units +// For memory we want to convert from MBs (e.g. 4096 MBs) to bytes (4096000) to show correct units on Y axis +function translateResourceAmountForYAxis(resourceAmount, resourceType) { + if (resourceType == ResourceEnum.MEMORY) { + return resourceAmount * 1000000; + } else if (resourceType == ResourceEnum.CPU) { + return resourceAmount; + } else { + throw "Unknown resource type: " + resourceType; + } } function showInfo(graph, message) { @@ -181,7 +189,7 @@ function renderGraph(chartDivId, legendDivId, cluster, resourceTypeText, onCompl return registeredResource(d, resourceType); }); // Set Y axis linear domain range from 0 to maximum memory/cpu in bytes - yRange.domain([0, toBytes(maxResource)]); + yRange.domain([0, translateResourceAmountForYAxis(maxResource, resourceType)]); // Draw X axis const xAxisLabels = graph.append("g") @@ -261,10 +269,10 @@ function renderGraph(chartDivId, legendDivId, cluster, resourceTypeText, onCompl .attr("class", "instance-block") .attr("width", xRange.rangeBand()) .attr("y", function (d) { - return yRange(toBytes(registeredResource(d, resourceType))) + return yRange(translateResourceAmountForYAxis(registeredResource(d, resourceType), resourceType)) }) .attr("height", function (d) { - return yRange(toBytes(maxResource - (registeredResource(d, resourceType)))); + return yRange(translateResourceAmountForYAxis(maxResource - (registeredResource(d, resourceType)), resourceType)); }); if (showTaskBreakdown) { @@ -277,10 +285,10 @@ function renderGraph(chartDivId, legendDivId, cluster, resourceTypeText, onCompl .attr("class", "task-block") .attr("width", xRange.rangeBand()) .attr("y", function (d) { - return yRange(toBytes(d.d3Data.y1)); + return yRange(translateResourceAmountForYAxis(d.d3Data.y1, resourceType)); }) .attr("height", function (d) { - return yRange(toBytes(d.d3Data.y0)) - yRange(toBytes(d.d3Data.y1)); + return yRange(translateResourceAmountForYAxis(d.d3Data.y0, resourceType)) - yRange(translateResourceAmountForYAxis(d.d3Data.y1, resourceType)); }) .style("fill", function (d) { return colorRange(d.d3Data.name); @@ -345,11 +353,11 @@ function renderGraph(chartDivId, legendDivId, cluster, resourceTypeText, onCompl instance.append("rect") .attr("width", xRange.rangeBand()) .attr("y", function (d) { - const usedCpu = toBytes(registeredResource(d, resourceType)) - toBytes(remainingResource(d, resourceType)); + const usedCpu = translateResourceAmountForYAxis(registeredResource(d, resourceType), resourceType) - translateResourceAmountForYAxis(remainingResource(d, resourceType), resourceType); return yRange(usedCpu) }) .attr("height", function (d) { - return yRange(toBytes(remainingResource(d, resourceType))) - yRange(toBytes(registeredResource(d, resourceType))); + return yRange(translateResourceAmountForYAxis(remainingResource(d, resourceType), resourceType)) - yRange(translateResourceAmountForYAxis(registeredResource(d, resourceType), resourceType)); }) .style("fill", "orange") .style("stroke", "grey"); From a239748daf5516447b866c2d34064433ecd5c70f Mon Sep 17 00:00:00 2001 From: mcallanan Date: Mon, 21 May 2018 21:33:44 +1000 Subject: [PATCH 07/16] Fix registeredCpu values in static demo cluster files. --- ...summaries_with_tasks-demo-cluster-100.json | 200 +++++++++--------- ..._summaries_with_tasks-demo-cluster-50.json | 100 ++++----- ..._summaries_with_tasks-demo-cluster-75.json | 150 ++++++------- ...e_summaries_with_tasks-demo-cluster-8.json | 16 +- 4 files changed, 233 insertions(+), 233 deletions(-) diff --git a/public/test_data/ecs_instance_summaries_with_tasks-demo-cluster-100.json b/public/test_data/ecs_instance_summaries_with_tasks-demo-cluster-100.json index 2fdaded..03afccb 100644 --- a/public/test_data/ecs_instance_summaries_with_tasks-demo-cluster-100.json +++ b/public/test_data/ecs_instance_summaries_with_tasks-demo-cluster-100.json @@ -2,7 +2,7 @@ { "ec2InstanceId": "i-aaaaaa00", "ec2IpAddress": "10.0.1.0", - "registeredCpu": 2048, + "registeredCpu": 4096, "registeredMemory": 16000, "remainingCpu": 2048, "remainingMemory": 16000, @@ -11,7 +11,7 @@ { "ec2InstanceId": "i-bbbbbb00", "ec2IpAddress": "10.0.2.0", - "registeredCpu": 2048, + "registeredCpu": 4096, "registeredMemory": 16000, "remainingCpu": 960, "remainingMemory": 11712, @@ -117,7 +117,7 @@ { "ec2InstanceId": "i-cccccc00", "ec2IpAddress": "10.0.3.0", - "registeredCpu": 2048, + "registeredCpu": 4096, "registeredMemory": 16000, "remainingCpu": 2048, "remainingMemory": 14880, @@ -175,7 +175,7 @@ { "ec2InstanceId": "i-dddddd00", "ec2IpAddress": "10.0.4.0", - "registeredCpu": 2048, + "registeredCpu": 4096, "registeredMemory": 16000, "remainingCpu": 2048, "remainingMemory": 16000, @@ -184,7 +184,7 @@ { "ec2InstanceId": "i-eeeeee00", "ec2IpAddress": "10.0.5.0", - "registeredCpu": 2048, + "registeredCpu": 4096, "registeredMemory": 16000, "remainingCpu": 536, "remainingMemory": 10592, @@ -338,7 +338,7 @@ { "ec2InstanceId": "i-aaaaaa01", "ec2IpAddress": "10.0.1.1", - "registeredCpu": 2048, + "registeredCpu": 4096, "registeredMemory": 16000, "remainingCpu": 2048, "remainingMemory": 16000, @@ -347,7 +347,7 @@ { "ec2InstanceId": "i-bbbbbb01", "ec2IpAddress": "10.0.2.1", - "registeredCpu": 2048, + "registeredCpu": 4096, "registeredMemory": 16000, "remainingCpu": 960, "remainingMemory": 11712, @@ -453,7 +453,7 @@ { "ec2InstanceId": "i-cccccc01", "ec2IpAddress": "10.0.3.1", - "registeredCpu": 2048, + "registeredCpu": 4096, "registeredMemory": 16000, "remainingCpu": 2048, "remainingMemory": 14880, @@ -511,7 +511,7 @@ { "ec2InstanceId": "i-dddddd01", "ec2IpAddress": "10.0.4.1", - "registeredCpu": 2048, + "registeredCpu": 4096, "registeredMemory": 16000, "remainingCpu": 2048, "remainingMemory": 16000, @@ -520,7 +520,7 @@ { "ec2InstanceId": "i-eeeeee01", "ec2IpAddress": "10.0.5.1", - "registeredCpu": 2048, + "registeredCpu": 4096, "registeredMemory": 16000, "remainingCpu": 536, "remainingMemory": 10592, @@ -674,7 +674,7 @@ { "ec2InstanceId": "i-aaaaaa02", "ec2IpAddress": "10.0.1.2", - "registeredCpu": 2048, + "registeredCpu": 4096, "registeredMemory": 16000, "remainingCpu": 2048, "remainingMemory": 16000, @@ -683,7 +683,7 @@ { "ec2InstanceId": "i-bbbbbb02", "ec2IpAddress": "10.0.2.2", - "registeredCpu": 2048, + "registeredCpu": 4096, "registeredMemory": 16000, "remainingCpu": 960, "remainingMemory": 11712, @@ -789,7 +789,7 @@ { "ec2InstanceId": "i-cccccc02", "ec2IpAddress": "10.0.3.2", - "registeredCpu": 2048, + "registeredCpu": 4096, "registeredMemory": 16000, "remainingCpu": 2048, "remainingMemory": 14880, @@ -847,7 +847,7 @@ { "ec2InstanceId": "i-dddddd02", "ec2IpAddress": "10.0.4.2", - "registeredCpu": 2048, + "registeredCpu": 4096, "registeredMemory": 16000, "remainingCpu": 2048, "remainingMemory": 16000, @@ -856,7 +856,7 @@ { "ec2InstanceId": "i-eeeeee02", "ec2IpAddress": "10.0.5.2", - "registeredCpu": 2048, + "registeredCpu": 4096, "registeredMemory": 16000, "remainingCpu": 536, "remainingMemory": 10592, @@ -1010,7 +1010,7 @@ { "ec2InstanceId": "i-aaaaaa03", "ec2IpAddress": "10.0.1.3", - "registeredCpu": 2048, + "registeredCpu": 4096, "registeredMemory": 16000, "remainingCpu": 2048, "remainingMemory": 16000, @@ -1019,7 +1019,7 @@ { "ec2InstanceId": "i-bbbbbb03", "ec2IpAddress": "10.0.2.3", - "registeredCpu": 2048, + "registeredCpu": 4096, "registeredMemory": 16000, "remainingCpu": 960, "remainingMemory": 11712, @@ -1125,7 +1125,7 @@ { "ec2InstanceId": "i-cccccc03", "ec2IpAddress": "10.0.3.3", - "registeredCpu": 2048, + "registeredCpu": 4096, "registeredMemory": 16000, "remainingCpu": 2048, "remainingMemory": 14880, @@ -1183,7 +1183,7 @@ { "ec2InstanceId": "i-dddddd03", "ec2IpAddress": "10.0.4.3", - "registeredCpu": 2048, + "registeredCpu": 4096, "registeredMemory": 16000, "remainingCpu": 2048, "remainingMemory": 16000, @@ -1192,7 +1192,7 @@ { "ec2InstanceId": "i-eeeeee03", "ec2IpAddress": "10.0.5.3", - "registeredCpu": 2048, + "registeredCpu": 4096, "registeredMemory": 16000, "remainingCpu": 536, "remainingMemory": 10592, @@ -1346,7 +1346,7 @@ { "ec2InstanceId": "i-aaaaaa04", "ec2IpAddress": "10.0.1.4", - "registeredCpu": 2048, + "registeredCpu": 4096, "registeredMemory": 16000, "remainingCpu": 2048, "remainingMemory": 16000, @@ -1355,7 +1355,7 @@ { "ec2InstanceId": "i-bbbbbb04", "ec2IpAddress": "10.0.2.4", - "registeredCpu": 2048, + "registeredCpu": 4096, "registeredMemory": 16000, "remainingCpu": 960, "remainingMemory": 11712, @@ -1461,7 +1461,7 @@ { "ec2InstanceId": "i-cccccc04", "ec2IpAddress": "10.0.3.4", - "registeredCpu": 2048, + "registeredCpu": 4096, "registeredMemory": 16000, "remainingCpu": 2048, "remainingMemory": 14880, @@ -1519,7 +1519,7 @@ { "ec2InstanceId": "i-dddddd04", "ec2IpAddress": "10.0.4.4", - "registeredCpu": 2048, + "registeredCpu": 4096, "registeredMemory": 16000, "remainingCpu": 2048, "remainingMemory": 16000, @@ -1528,7 +1528,7 @@ { "ec2InstanceId": "i-eeeeee04", "ec2IpAddress": "10.0.5.4", - "registeredCpu": 2048, + "registeredCpu": 4096, "registeredMemory": 16000, "remainingCpu": 536, "remainingMemory": 10592, @@ -1682,7 +1682,7 @@ { "ec2InstanceId": "i-aaaaaa05", "ec2IpAddress": "10.0.1.5", - "registeredCpu": 2048, + "registeredCpu": 4096, "registeredMemory": 16000, "remainingCpu": 2048, "remainingMemory": 16000, @@ -1691,7 +1691,7 @@ { "ec2InstanceId": "i-bbbbbb05", "ec2IpAddress": "10.0.2.5", - "registeredCpu": 2048, + "registeredCpu": 4096, "registeredMemory": 16000, "remainingCpu": 960, "remainingMemory": 11712, @@ -1797,7 +1797,7 @@ { "ec2InstanceId": "i-cccccc05", "ec2IpAddress": "10.0.3.5", - "registeredCpu": 2048, + "registeredCpu": 4096, "registeredMemory": 16000, "remainingCpu": 2048, "remainingMemory": 14880, @@ -1855,7 +1855,7 @@ { "ec2InstanceId": "i-dddddd05", "ec2IpAddress": "10.0.4.5", - "registeredCpu": 2048, + "registeredCpu": 4096, "registeredMemory": 16000, "remainingCpu": 2048, "remainingMemory": 16000, @@ -1864,7 +1864,7 @@ { "ec2InstanceId": "i-eeeeee05", "ec2IpAddress": "10.0.5.5", - "registeredCpu": 2048, + "registeredCpu": 4096, "registeredMemory": 16000, "remainingCpu": 536, "remainingMemory": 10592, @@ -2018,7 +2018,7 @@ { "ec2InstanceId": "i-aaaaaa06", "ec2IpAddress": "10.0.1.6", - "registeredCpu": 2048, + "registeredCpu": 4096, "registeredMemory": 16000, "remainingCpu": 2048, "remainingMemory": 16000, @@ -2027,7 +2027,7 @@ { "ec2InstanceId": "i-bbbbbb06", "ec2IpAddress": "10.0.2.6", - "registeredCpu": 2048, + "registeredCpu": 4096, "registeredMemory": 16000, "remainingCpu": 960, "remainingMemory": 11712, @@ -2133,7 +2133,7 @@ { "ec2InstanceId": "i-cccccc06", "ec2IpAddress": "10.0.3.6", - "registeredCpu": 2048, + "registeredCpu": 4096, "registeredMemory": 16000, "remainingCpu": 2048, "remainingMemory": 14880, @@ -2191,7 +2191,7 @@ { "ec2InstanceId": "i-dddddd06", "ec2IpAddress": "10.0.4.6", - "registeredCpu": 2048, + "registeredCpu": 4096, "registeredMemory": 16000, "remainingCpu": 2048, "remainingMemory": 16000, @@ -2200,7 +2200,7 @@ { "ec2InstanceId": "i-eeeeee06", "ec2IpAddress": "10.0.5.6", - "registeredCpu": 2048, + "registeredCpu": 4096, "registeredMemory": 16000, "remainingCpu": 536, "remainingMemory": 10592, @@ -2354,7 +2354,7 @@ { "ec2InstanceId": "i-aaaaaa07", "ec2IpAddress": "10.0.1.7", - "registeredCpu": 2048, + "registeredCpu": 4096, "registeredMemory": 16000, "remainingCpu": 2048, "remainingMemory": 16000, @@ -2363,7 +2363,7 @@ { "ec2InstanceId": "i-bbbbbb07", "ec2IpAddress": "10.0.2.7", - "registeredCpu": 2048, + "registeredCpu": 4096, "registeredMemory": 16000, "remainingCpu": 960, "remainingMemory": 11712, @@ -2469,7 +2469,7 @@ { "ec2InstanceId": "i-cccccc07", "ec2IpAddress": "10.0.3.7", - "registeredCpu": 2048, + "registeredCpu": 4096, "registeredMemory": 16000, "remainingCpu": 2048, "remainingMemory": 14880, @@ -2527,7 +2527,7 @@ { "ec2InstanceId": "i-dddddd07", "ec2IpAddress": "10.0.4.7", - "registeredCpu": 2048, + "registeredCpu": 4096, "registeredMemory": 16000, "remainingCpu": 2048, "remainingMemory": 16000, @@ -2536,7 +2536,7 @@ { "ec2InstanceId": "i-eeeeee07", "ec2IpAddress": "10.0.5.7", - "registeredCpu": 2048, + "registeredCpu": 4096, "registeredMemory": 16000, "remainingCpu": 536, "remainingMemory": 10592, @@ -2690,7 +2690,7 @@ { "ec2InstanceId": "i-aaaaaa08", "ec2IpAddress": "10.0.1.8", - "registeredCpu": 2048, + "registeredCpu": 4096, "registeredMemory": 16000, "remainingCpu": 2048, "remainingMemory": 16000, @@ -2699,7 +2699,7 @@ { "ec2InstanceId": "i-bbbbbb08", "ec2IpAddress": "10.0.2.8", - "registeredCpu": 2048, + "registeredCpu": 4096, "registeredMemory": 16000, "remainingCpu": 960, "remainingMemory": 11712, @@ -2805,7 +2805,7 @@ { "ec2InstanceId": "i-cccccc08", "ec2IpAddress": "10.0.3.8", - "registeredCpu": 2048, + "registeredCpu": 4096, "registeredMemory": 16000, "remainingCpu": 2048, "remainingMemory": 14880, @@ -2863,7 +2863,7 @@ { "ec2InstanceId": "i-dddddd08", "ec2IpAddress": "10.0.4.8", - "registeredCpu": 2048, + "registeredCpu": 4096, "registeredMemory": 16000, "remainingCpu": 2048, "remainingMemory": 16000, @@ -2872,7 +2872,7 @@ { "ec2InstanceId": "i-eeeeee08", "ec2IpAddress": "10.0.5.8", - "registeredCpu": 2048, + "registeredCpu": 4096, "registeredMemory": 16000, "remainingCpu": 536, "remainingMemory": 10592, @@ -3026,7 +3026,7 @@ { "ec2InstanceId": "i-aaaaaa09", "ec2IpAddress": "10.0.1.9", - "registeredCpu": 2048, + "registeredCpu": 4096, "registeredMemory": 16000, "remainingCpu": 2048, "remainingMemory": 16000, @@ -3035,7 +3035,7 @@ { "ec2InstanceId": "i-bbbbbb09", "ec2IpAddress": "10.0.2.9", - "registeredCpu": 2048, + "registeredCpu": 4096, "registeredMemory": 16000, "remainingCpu": 960, "remainingMemory": 11712, @@ -3141,7 +3141,7 @@ { "ec2InstanceId": "i-cccccc09", "ec2IpAddress": "10.0.3.9", - "registeredCpu": 2048, + "registeredCpu": 4096, "registeredMemory": 16000, "remainingCpu": 2048, "remainingMemory": 14880, @@ -3199,7 +3199,7 @@ { "ec2InstanceId": "i-dddddd09", "ec2IpAddress": "10.0.4.9", - "registeredCpu": 2048, + "registeredCpu": 4096, "registeredMemory": 16000, "remainingCpu": 2048, "remainingMemory": 16000, @@ -3208,7 +3208,7 @@ { "ec2InstanceId": "i-eeeeee09", "ec2IpAddress": "10.0.5.9", - "registeredCpu": 2048, + "registeredCpu": 4096, "registeredMemory": 16000, "remainingCpu": 536, "remainingMemory": 10592, @@ -3362,7 +3362,7 @@ { "ec2InstanceId": "i-aaaaaa10", "ec2IpAddress": "10.0.1.10", - "registeredCpu": 2048, + "registeredCpu": 4096, "registeredMemory": 16000, "remainingCpu": 2048, "remainingMemory": 16000, @@ -3371,7 +3371,7 @@ { "ec2InstanceId": "i-bbbbbb10", "ec2IpAddress": "10.0.2.10", - "registeredCpu": 2048, + "registeredCpu": 4096, "registeredMemory": 16000, "remainingCpu": 960, "remainingMemory": 11712, @@ -3477,7 +3477,7 @@ { "ec2InstanceId": "i-cccccc10", "ec2IpAddress": "10.0.3.10", - "registeredCpu": 2048, + "registeredCpu": 4096, "registeredMemory": 16000, "remainingCpu": 2048, "remainingMemory": 14880, @@ -3535,7 +3535,7 @@ { "ec2InstanceId": "i-dddddd10", "ec2IpAddress": "10.0.4.10", - "registeredCpu": 2048, + "registeredCpu": 4096, "registeredMemory": 16000, "remainingCpu": 2048, "remainingMemory": 16000, @@ -3544,7 +3544,7 @@ { "ec2InstanceId": "i-eeeeee10", "ec2IpAddress": "10.0.5.10", - "registeredCpu": 2048, + "registeredCpu": 4096, "registeredMemory": 16000, "remainingCpu": 536, "remainingMemory": 10592, @@ -3698,7 +3698,7 @@ { "ec2InstanceId": "i-aaaaaa11", "ec2IpAddress": "10.0.1.11", - "registeredCpu": 2048, + "registeredCpu": 4096, "registeredMemory": 16000, "remainingCpu": 2048, "remainingMemory": 16000, @@ -3707,7 +3707,7 @@ { "ec2InstanceId": "i-bbbbbb11", "ec2IpAddress": "10.0.2.11", - "registeredCpu": 2048, + "registeredCpu": 4096, "registeredMemory": 16000, "remainingCpu": 960, "remainingMemory": 11712, @@ -3813,7 +3813,7 @@ { "ec2InstanceId": "i-cccccc11", "ec2IpAddress": "10.0.3.11", - "registeredCpu": 2048, + "registeredCpu": 4096, "registeredMemory": 16000, "remainingCpu": 2048, "remainingMemory": 14880, @@ -3871,7 +3871,7 @@ { "ec2InstanceId": "i-dddddd11", "ec2IpAddress": "10.0.4.11", - "registeredCpu": 2048, + "registeredCpu": 4096, "registeredMemory": 16000, "remainingCpu": 2048, "remainingMemory": 16000, @@ -3880,7 +3880,7 @@ { "ec2InstanceId": "i-eeeeee11", "ec2IpAddress": "10.0.5.11", - "registeredCpu": 2048, + "registeredCpu": 4096, "registeredMemory": 16000, "remainingCpu": 536, "remainingMemory": 10592, @@ -4034,7 +4034,7 @@ { "ec2InstanceId": "i-aaaaaa12", "ec2IpAddress": "10.0.1.12", - "registeredCpu": 2048, + "registeredCpu": 4096, "registeredMemory": 16000, "remainingCpu": 2048, "remainingMemory": 16000, @@ -4043,7 +4043,7 @@ { "ec2InstanceId": "i-bbbbbb12", "ec2IpAddress": "10.0.2.12", - "registeredCpu": 2048, + "registeredCpu": 4096, "registeredMemory": 16000, "remainingCpu": 960, "remainingMemory": 11712, @@ -4149,7 +4149,7 @@ { "ec2InstanceId": "i-cccccc12", "ec2IpAddress": "10.0.3.12", - "registeredCpu": 2048, + "registeredCpu": 4096, "registeredMemory": 16000, "remainingCpu": 2048, "remainingMemory": 14880, @@ -4207,7 +4207,7 @@ { "ec2InstanceId": "i-dddddd12", "ec2IpAddress": "10.0.4.12", - "registeredCpu": 2048, + "registeredCpu": 4096, "registeredMemory": 16000, "remainingCpu": 2048, "remainingMemory": 16000, @@ -4216,7 +4216,7 @@ { "ec2InstanceId": "i-eeeeee12", "ec2IpAddress": "10.0.5.12", - "registeredCpu": 2048, + "registeredCpu": 4096, "registeredMemory": 16000, "remainingCpu": 536, "remainingMemory": 10592, @@ -4370,7 +4370,7 @@ { "ec2InstanceId": "i-aaaaaa13", "ec2IpAddress": "10.0.1.13", - "registeredCpu": 2048, + "registeredCpu": 4096, "registeredMemory": 16000, "remainingCpu": 2048, "remainingMemory": 16000, @@ -4379,7 +4379,7 @@ { "ec2InstanceId": "i-bbbbbb13", "ec2IpAddress": "10.0.2.13", - "registeredCpu": 2048, + "registeredCpu": 4096, "registeredMemory": 16000, "remainingCpu": 960, "remainingMemory": 11712, @@ -4485,7 +4485,7 @@ { "ec2InstanceId": "i-cccccc13", "ec2IpAddress": "10.0.3.13", - "registeredCpu": 2048, + "registeredCpu": 4096, "registeredMemory": 16000, "remainingCpu": 2048, "remainingMemory": 14880, @@ -4543,7 +4543,7 @@ { "ec2InstanceId": "i-dddddd13", "ec2IpAddress": "10.0.4.13", - "registeredCpu": 2048, + "registeredCpu": 4096, "registeredMemory": 16000, "remainingCpu": 2048, "remainingMemory": 16000, @@ -4552,7 +4552,7 @@ { "ec2InstanceId": "i-eeeeee13", "ec2IpAddress": "10.0.5.13", - "registeredCpu": 2048, + "registeredCpu": 4096, "registeredMemory": 16000, "remainingCpu": 536, "remainingMemory": 10592, @@ -4706,7 +4706,7 @@ { "ec2InstanceId": "i-aaaaaa14", "ec2IpAddress": "10.0.1.14", - "registeredCpu": 2048, + "registeredCpu": 4096, "registeredMemory": 16000, "remainingCpu": 2048, "remainingMemory": 16000, @@ -4715,7 +4715,7 @@ { "ec2InstanceId": "i-bbbbbb14", "ec2IpAddress": "10.0.2.14", - "registeredCpu": 2048, + "registeredCpu": 4096, "registeredMemory": 16000, "remainingCpu": 960, "remainingMemory": 11712, @@ -4821,7 +4821,7 @@ { "ec2InstanceId": "i-cccccc14", "ec2IpAddress": "10.0.3.14", - "registeredCpu": 2048, + "registeredCpu": 4096, "registeredMemory": 16000, "remainingCpu": 2048, "remainingMemory": 14880, @@ -4879,7 +4879,7 @@ { "ec2InstanceId": "i-dddddd14", "ec2IpAddress": "10.0.4.14", - "registeredCpu": 2048, + "registeredCpu": 4096, "registeredMemory": 16000, "remainingCpu": 2048, "remainingMemory": 16000, @@ -4888,7 +4888,7 @@ { "ec2InstanceId": "i-eeeeee14", "ec2IpAddress": "10.0.5.14", - "registeredCpu": 2048, + "registeredCpu": 4096, "registeredMemory": 16000, "remainingCpu": 536, "remainingMemory": 10592, @@ -5042,7 +5042,7 @@ { "ec2InstanceId": "i-aaaaaa15", "ec2IpAddress": "10.0.1.15", - "registeredCpu": 2048, + "registeredCpu": 4096, "registeredMemory": 16000, "remainingCpu": 2048, "remainingMemory": 16000, @@ -5051,7 +5051,7 @@ { "ec2InstanceId": "i-bbbbbb15", "ec2IpAddress": "10.0.2.15", - "registeredCpu": 2048, + "registeredCpu": 4096, "registeredMemory": 16000, "remainingCpu": 960, "remainingMemory": 11712, @@ -5157,7 +5157,7 @@ { "ec2InstanceId": "i-cccccc15", "ec2IpAddress": "10.0.3.15", - "registeredCpu": 2048, + "registeredCpu": 4096, "registeredMemory": 16000, "remainingCpu": 2048, "remainingMemory": 14880, @@ -5215,7 +5215,7 @@ { "ec2InstanceId": "i-dddddd15", "ec2IpAddress": "10.0.4.15", - "registeredCpu": 2048, + "registeredCpu": 4096, "registeredMemory": 16000, "remainingCpu": 2048, "remainingMemory": 16000, @@ -5224,7 +5224,7 @@ { "ec2InstanceId": "i-eeeeee15", "ec2IpAddress": "10.0.5.15", - "registeredCpu": 2048, + "registeredCpu": 4096, "registeredMemory": 16000, "remainingCpu": 536, "remainingMemory": 10592, @@ -5378,7 +5378,7 @@ { "ec2InstanceId": "i-aaaaaa16", "ec2IpAddress": "10.0.1.16", - "registeredCpu": 2048, + "registeredCpu": 4096, "registeredMemory": 16000, "remainingCpu": 2048, "remainingMemory": 16000, @@ -5387,7 +5387,7 @@ { "ec2InstanceId": "i-bbbbbb16", "ec2IpAddress": "10.0.2.16", - "registeredCpu": 2048, + "registeredCpu": 4096, "registeredMemory": 16000, "remainingCpu": 960, "remainingMemory": 11712, @@ -5493,7 +5493,7 @@ { "ec2InstanceId": "i-cccccc16", "ec2IpAddress": "10.0.3.16", - "registeredCpu": 2048, + "registeredCpu": 4096, "registeredMemory": 16000, "remainingCpu": 2048, "remainingMemory": 14880, @@ -5551,7 +5551,7 @@ { "ec2InstanceId": "i-dddddd16", "ec2IpAddress": "10.0.4.16", - "registeredCpu": 2048, + "registeredCpu": 4096, "registeredMemory": 16000, "remainingCpu": 2048, "remainingMemory": 16000, @@ -5560,7 +5560,7 @@ { "ec2InstanceId": "i-eeeeee16", "ec2IpAddress": "10.0.5.16", - "registeredCpu": 2048, + "registeredCpu": 4096, "registeredMemory": 16000, "remainingCpu": 536, "remainingMemory": 10592, @@ -5714,7 +5714,7 @@ { "ec2InstanceId": "i-aaaaaa17", "ec2IpAddress": "10.0.1.17", - "registeredCpu": 2048, + "registeredCpu": 4096, "registeredMemory": 16000, "remainingCpu": 2048, "remainingMemory": 16000, @@ -5723,7 +5723,7 @@ { "ec2InstanceId": "i-bbbbbb17", "ec2IpAddress": "10.0.2.17", - "registeredCpu": 2048, + "registeredCpu": 4096, "registeredMemory": 16000, "remainingCpu": 960, "remainingMemory": 11712, @@ -5829,7 +5829,7 @@ { "ec2InstanceId": "i-cccccc17", "ec2IpAddress": "10.0.3.17", - "registeredCpu": 2048, + "registeredCpu": 4096, "registeredMemory": 16000, "remainingCpu": 2048, "remainingMemory": 14880, @@ -5887,7 +5887,7 @@ { "ec2InstanceId": "i-dddddd17", "ec2IpAddress": "10.0.4.17", - "registeredCpu": 2048, + "registeredCpu": 4096, "registeredMemory": 16000, "remainingCpu": 2048, "remainingMemory": 16000, @@ -5896,7 +5896,7 @@ { "ec2InstanceId": "i-eeeeee17", "ec2IpAddress": "10.0.5.17", - "registeredCpu": 2048, + "registeredCpu": 4096, "registeredMemory": 16000, "remainingCpu": 536, "remainingMemory": 10592, @@ -6050,7 +6050,7 @@ { "ec2InstanceId": "i-aaaaaa18", "ec2IpAddress": "10.0.1.18", - "registeredCpu": 2048, + "registeredCpu": 4096, "registeredMemory": 16000, "remainingCpu": 2048, "remainingMemory": 16000, @@ -6059,7 +6059,7 @@ { "ec2InstanceId": "i-bbbbbb18", "ec2IpAddress": "10.0.2.18", - "registeredCpu": 2048, + "registeredCpu": 4096, "registeredMemory": 16000, "remainingCpu": 960, "remainingMemory": 11712, @@ -6165,7 +6165,7 @@ { "ec2InstanceId": "i-cccccc18", "ec2IpAddress": "10.0.3.18", - "registeredCpu": 2048, + "registeredCpu": 4096, "registeredMemory": 16000, "remainingCpu": 2048, "remainingMemory": 14880, @@ -6223,7 +6223,7 @@ { "ec2InstanceId": "i-dddddd18", "ec2IpAddress": "10.0.4.18", - "registeredCpu": 2048, + "registeredCpu": 4096, "registeredMemory": 16000, "remainingCpu": 2048, "remainingMemory": 16000, @@ -6232,7 +6232,7 @@ { "ec2InstanceId": "i-eeeeee18", "ec2IpAddress": "10.0.5.18", - "registeredCpu": 2048, + "registeredCpu": 4096, "registeredMemory": 16000, "remainingCpu": 536, "remainingMemory": 10592, @@ -6386,7 +6386,7 @@ { "ec2InstanceId": "i-aaaaaa19", "ec2IpAddress": "10.0.1.19", - "registeredCpu": 2048, + "registeredCpu": 4096, "registeredMemory": 16000, "remainingCpu": 2048, "remainingMemory": 16000, @@ -6395,7 +6395,7 @@ { "ec2InstanceId": "i-bbbbbb19", "ec2IpAddress": "10.0.2.19", - "registeredCpu": 2048, + "registeredCpu": 4096, "registeredMemory": 16000, "remainingCpu": 960, "remainingMemory": 11712, @@ -6501,7 +6501,7 @@ { "ec2InstanceId": "i-cccccc19", "ec2IpAddress": "10.0.3.19", - "registeredCpu": 2048, + "registeredCpu": 4096, "registeredMemory": 16000, "remainingCpu": 2048, "remainingMemory": 14880, @@ -6559,7 +6559,7 @@ { "ec2InstanceId": "i-dddddd19", "ec2IpAddress": "10.0.4.19", - "registeredCpu": 2048, + "registeredCpu": 4096, "registeredMemory": 16000, "remainingCpu": 2048, "remainingMemory": 16000, @@ -6568,7 +6568,7 @@ { "ec2InstanceId": "i-eeeeee19", "ec2IpAddress": "10.0.5.19", - "registeredCpu": 2048, + "registeredCpu": 4096, "registeredMemory": 16000, "remainingCpu": 536, "remainingMemory": 10592, diff --git a/public/test_data/ecs_instance_summaries_with_tasks-demo-cluster-50.json b/public/test_data/ecs_instance_summaries_with_tasks-demo-cluster-50.json index 3b31f2a..a6a4050 100644 --- a/public/test_data/ecs_instance_summaries_with_tasks-demo-cluster-50.json +++ b/public/test_data/ecs_instance_summaries_with_tasks-demo-cluster-50.json @@ -2,7 +2,7 @@ { "ec2InstanceId": "i-aaaaaa00", "ec2IpAddress": "10.0.1.0", - "registeredCpu": 2048, + "registeredCpu": 4096, "registeredMemory": 16000, "remainingCpu": 2048, "remainingMemory": 16000, @@ -11,7 +11,7 @@ { "ec2InstanceId": "i-bbbbbb00", "ec2IpAddress": "10.0.2.0", - "registeredCpu": 2048, + "registeredCpu": 4096, "registeredMemory": 16000, "remainingCpu": 960, "remainingMemory": 11712, @@ -117,7 +117,7 @@ { "ec2InstanceId": "i-cccccc00", "ec2IpAddress": "10.0.3.0", - "registeredCpu": 2048, + "registeredCpu": 4096, "registeredMemory": 16000, "remainingCpu": 2048, "remainingMemory": 14880, @@ -175,7 +175,7 @@ { "ec2InstanceId": "i-dddddd00", "ec2IpAddress": "10.0.4.0", - "registeredCpu": 2048, + "registeredCpu": 4096, "registeredMemory": 16000, "remainingCpu": 2048, "remainingMemory": 16000, @@ -184,7 +184,7 @@ { "ec2InstanceId": "i-eeeeee00", "ec2IpAddress": "10.0.5.0", - "registeredCpu": 2048, + "registeredCpu": 4096, "registeredMemory": 16000, "remainingCpu": 536, "remainingMemory": 10592, @@ -338,7 +338,7 @@ { "ec2InstanceId": "i-aaaaaa01", "ec2IpAddress": "10.0.1.1", - "registeredCpu": 2048, + "registeredCpu": 4096, "registeredMemory": 16000, "remainingCpu": 2048, "remainingMemory": 16000, @@ -347,7 +347,7 @@ { "ec2InstanceId": "i-bbbbbb01", "ec2IpAddress": "10.0.2.1", - "registeredCpu": 2048, + "registeredCpu": 4096, "registeredMemory": 16000, "remainingCpu": 960, "remainingMemory": 11712, @@ -453,7 +453,7 @@ { "ec2InstanceId": "i-cccccc01", "ec2IpAddress": "10.0.3.1", - "registeredCpu": 2048, + "registeredCpu": 4096, "registeredMemory": 16000, "remainingCpu": 2048, "remainingMemory": 14880, @@ -511,7 +511,7 @@ { "ec2InstanceId": "i-dddddd01", "ec2IpAddress": "10.0.4.1", - "registeredCpu": 2048, + "registeredCpu": 4096, "registeredMemory": 16000, "remainingCpu": 2048, "remainingMemory": 16000, @@ -520,7 +520,7 @@ { "ec2InstanceId": "i-eeeeee01", "ec2IpAddress": "10.0.5.1", - "registeredCpu": 2048, + "registeredCpu": 4096, "registeredMemory": 16000, "remainingCpu": 536, "remainingMemory": 10592, @@ -674,7 +674,7 @@ { "ec2InstanceId": "i-aaaaaa02", "ec2IpAddress": "10.0.1.2", - "registeredCpu": 2048, + "registeredCpu": 4096, "registeredMemory": 16000, "remainingCpu": 2048, "remainingMemory": 16000, @@ -683,7 +683,7 @@ { "ec2InstanceId": "i-bbbbbb02", "ec2IpAddress": "10.0.2.2", - "registeredCpu": 2048, + "registeredCpu": 4096, "registeredMemory": 16000, "remainingCpu": 960, "remainingMemory": 11712, @@ -789,7 +789,7 @@ { "ec2InstanceId": "i-cccccc02", "ec2IpAddress": "10.0.3.2", - "registeredCpu": 2048, + "registeredCpu": 4096, "registeredMemory": 16000, "remainingCpu": 2048, "remainingMemory": 14880, @@ -847,7 +847,7 @@ { "ec2InstanceId": "i-dddddd02", "ec2IpAddress": "10.0.4.2", - "registeredCpu": 2048, + "registeredCpu": 4096, "registeredMemory": 16000, "remainingCpu": 2048, "remainingMemory": 16000, @@ -856,7 +856,7 @@ { "ec2InstanceId": "i-eeeeee02", "ec2IpAddress": "10.0.5.2", - "registeredCpu": 2048, + "registeredCpu": 4096, "registeredMemory": 16000, "remainingCpu": 536, "remainingMemory": 10592, @@ -1010,7 +1010,7 @@ { "ec2InstanceId": "i-aaaaaa03", "ec2IpAddress": "10.0.1.3", - "registeredCpu": 2048, + "registeredCpu": 4096, "registeredMemory": 16000, "remainingCpu": 2048, "remainingMemory": 16000, @@ -1019,7 +1019,7 @@ { "ec2InstanceId": "i-bbbbbb03", "ec2IpAddress": "10.0.2.3", - "registeredCpu": 2048, + "registeredCpu": 4096, "registeredMemory": 16000, "remainingCpu": 960, "remainingMemory": 11712, @@ -1125,7 +1125,7 @@ { "ec2InstanceId": "i-cccccc03", "ec2IpAddress": "10.0.3.3", - "registeredCpu": 2048, + "registeredCpu": 4096, "registeredMemory": 16000, "remainingCpu": 2048, "remainingMemory": 14880, @@ -1183,7 +1183,7 @@ { "ec2InstanceId": "i-dddddd03", "ec2IpAddress": "10.0.4.3", - "registeredCpu": 2048, + "registeredCpu": 4096, "registeredMemory": 16000, "remainingCpu": 2048, "remainingMemory": 16000, @@ -1192,7 +1192,7 @@ { "ec2InstanceId": "i-eeeeee03", "ec2IpAddress": "10.0.5.3", - "registeredCpu": 2048, + "registeredCpu": 4096, "registeredMemory": 16000, "remainingCpu": 536, "remainingMemory": 10592, @@ -1346,7 +1346,7 @@ { "ec2InstanceId": "i-aaaaaa04", "ec2IpAddress": "10.0.1.4", - "registeredCpu": 2048, + "registeredCpu": 4096, "registeredMemory": 16000, "remainingCpu": 2048, "remainingMemory": 16000, @@ -1355,7 +1355,7 @@ { "ec2InstanceId": "i-bbbbbb04", "ec2IpAddress": "10.0.2.4", - "registeredCpu": 2048, + "registeredCpu": 4096, "registeredMemory": 16000, "remainingCpu": 960, "remainingMemory": 11712, @@ -1461,7 +1461,7 @@ { "ec2InstanceId": "i-cccccc04", "ec2IpAddress": "10.0.3.4", - "registeredCpu": 2048, + "registeredCpu": 4096, "registeredMemory": 16000, "remainingCpu": 2048, "remainingMemory": 14880, @@ -1519,7 +1519,7 @@ { "ec2InstanceId": "i-dddddd04", "ec2IpAddress": "10.0.4.4", - "registeredCpu": 2048, + "registeredCpu": 4096, "registeredMemory": 16000, "remainingCpu": 2048, "remainingMemory": 16000, @@ -1528,7 +1528,7 @@ { "ec2InstanceId": "i-eeeeee04", "ec2IpAddress": "10.0.5.4", - "registeredCpu": 2048, + "registeredCpu": 4096, "registeredMemory": 16000, "remainingCpu": 536, "remainingMemory": 10592, @@ -1682,7 +1682,7 @@ { "ec2InstanceId": "i-aaaaaa05", "ec2IpAddress": "10.0.1.5", - "registeredCpu": 2048, + "registeredCpu": 4096, "registeredMemory": 16000, "remainingCpu": 2048, "remainingMemory": 16000, @@ -1691,7 +1691,7 @@ { "ec2InstanceId": "i-bbbbbb05", "ec2IpAddress": "10.0.2.5", - "registeredCpu": 2048, + "registeredCpu": 4096, "registeredMemory": 16000, "remainingCpu": 960, "remainingMemory": 11712, @@ -1797,7 +1797,7 @@ { "ec2InstanceId": "i-cccccc05", "ec2IpAddress": "10.0.3.5", - "registeredCpu": 2048, + "registeredCpu": 4096, "registeredMemory": 16000, "remainingCpu": 2048, "remainingMemory": 14880, @@ -1855,7 +1855,7 @@ { "ec2InstanceId": "i-dddddd05", "ec2IpAddress": "10.0.4.5", - "registeredCpu": 2048, + "registeredCpu": 4096, "registeredMemory": 16000, "remainingCpu": 2048, "remainingMemory": 16000, @@ -1864,7 +1864,7 @@ { "ec2InstanceId": "i-eeeeee05", "ec2IpAddress": "10.0.5.5", - "registeredCpu": 2048, + "registeredCpu": 4096, "registeredMemory": 16000, "remainingCpu": 536, "remainingMemory": 10592, @@ -2018,7 +2018,7 @@ { "ec2InstanceId": "i-aaaaaa06", "ec2IpAddress": "10.0.1.6", - "registeredCpu": 2048, + "registeredCpu": 4096, "registeredMemory": 16000, "remainingCpu": 2048, "remainingMemory": 16000, @@ -2027,7 +2027,7 @@ { "ec2InstanceId": "i-bbbbbb06", "ec2IpAddress": "10.0.2.6", - "registeredCpu": 2048, + "registeredCpu": 4096, "registeredMemory": 16000, "remainingCpu": 960, "remainingMemory": 11712, @@ -2133,7 +2133,7 @@ { "ec2InstanceId": "i-cccccc06", "ec2IpAddress": "10.0.3.6", - "registeredCpu": 2048, + "registeredCpu": 4096, "registeredMemory": 16000, "remainingCpu": 2048, "remainingMemory": 14880, @@ -2191,7 +2191,7 @@ { "ec2InstanceId": "i-dddddd06", "ec2IpAddress": "10.0.4.6", - "registeredCpu": 2048, + "registeredCpu": 4096, "registeredMemory": 16000, "remainingCpu": 2048, "remainingMemory": 16000, @@ -2200,7 +2200,7 @@ { "ec2InstanceId": "i-eeeeee06", "ec2IpAddress": "10.0.5.6", - "registeredCpu": 2048, + "registeredCpu": 4096, "registeredMemory": 16000, "remainingCpu": 536, "remainingMemory": 10592, @@ -2354,7 +2354,7 @@ { "ec2InstanceId": "i-aaaaaa07", "ec2IpAddress": "10.0.1.7", - "registeredCpu": 2048, + "registeredCpu": 4096, "registeredMemory": 16000, "remainingCpu": 2048, "remainingMemory": 16000, @@ -2363,7 +2363,7 @@ { "ec2InstanceId": "i-bbbbbb07", "ec2IpAddress": "10.0.2.7", - "registeredCpu": 2048, + "registeredCpu": 4096, "registeredMemory": 16000, "remainingCpu": 960, "remainingMemory": 11712, @@ -2469,7 +2469,7 @@ { "ec2InstanceId": "i-cccccc07", "ec2IpAddress": "10.0.3.7", - "registeredCpu": 2048, + "registeredCpu": 4096, "registeredMemory": 16000, "remainingCpu": 2048, "remainingMemory": 14880, @@ -2527,7 +2527,7 @@ { "ec2InstanceId": "i-dddddd07", "ec2IpAddress": "10.0.4.7", - "registeredCpu": 2048, + "registeredCpu": 4096, "registeredMemory": 16000, "remainingCpu": 2048, "remainingMemory": 16000, @@ -2536,7 +2536,7 @@ { "ec2InstanceId": "i-eeeeee07", "ec2IpAddress": "10.0.5.7", - "registeredCpu": 2048, + "registeredCpu": 4096, "registeredMemory": 16000, "remainingCpu": 536, "remainingMemory": 10592, @@ -2690,7 +2690,7 @@ { "ec2InstanceId": "i-aaaaaa08", "ec2IpAddress": "10.0.1.8", - "registeredCpu": 2048, + "registeredCpu": 4096, "registeredMemory": 16000, "remainingCpu": 2048, "remainingMemory": 16000, @@ -2699,7 +2699,7 @@ { "ec2InstanceId": "i-bbbbbb08", "ec2IpAddress": "10.0.2.8", - "registeredCpu": 2048, + "registeredCpu": 4096, "registeredMemory": 16000, "remainingCpu": 960, "remainingMemory": 11712, @@ -2805,7 +2805,7 @@ { "ec2InstanceId": "i-cccccc08", "ec2IpAddress": "10.0.3.8", - "registeredCpu": 2048, + "registeredCpu": 4096, "registeredMemory": 16000, "remainingCpu": 2048, "remainingMemory": 14880, @@ -2863,7 +2863,7 @@ { "ec2InstanceId": "i-dddddd08", "ec2IpAddress": "10.0.4.8", - "registeredCpu": 2048, + "registeredCpu": 4096, "registeredMemory": 16000, "remainingCpu": 2048, "remainingMemory": 16000, @@ -2872,7 +2872,7 @@ { "ec2InstanceId": "i-eeeeee08", "ec2IpAddress": "10.0.5.8", - "registeredCpu": 2048, + "registeredCpu": 4096, "registeredMemory": 16000, "remainingCpu": 536, "remainingMemory": 10592, @@ -3026,7 +3026,7 @@ { "ec2InstanceId": "i-aaaaaa09", "ec2IpAddress": "10.0.1.9", - "registeredCpu": 2048, + "registeredCpu": 4096, "registeredMemory": 16000, "remainingCpu": 2048, "remainingMemory": 16000, @@ -3035,7 +3035,7 @@ { "ec2InstanceId": "i-bbbbbb09", "ec2IpAddress": "10.0.2.9", - "registeredCpu": 2048, + "registeredCpu": 4096, "registeredMemory": 16000, "remainingCpu": 960, "remainingMemory": 11712, @@ -3141,7 +3141,7 @@ { "ec2InstanceId": "i-cccccc09", "ec2IpAddress": "10.0.3.9", - "registeredCpu": 2048, + "registeredCpu": 4096, "registeredMemory": 16000, "remainingCpu": 2048, "remainingMemory": 14880, @@ -3199,7 +3199,7 @@ { "ec2InstanceId": "i-dddddd09", "ec2IpAddress": "10.0.4.9", - "registeredCpu": 2048, + "registeredCpu": 4096, "registeredMemory": 16000, "remainingCpu": 2048, "remainingMemory": 16000, @@ -3208,7 +3208,7 @@ { "ec2InstanceId": "i-eeeeee09", "ec2IpAddress": "10.0.5.9", - "registeredCpu": 2048, + "registeredCpu": 4096, "registeredMemory": 16000, "remainingCpu": 536, "remainingMemory": 10592, diff --git a/public/test_data/ecs_instance_summaries_with_tasks-demo-cluster-75.json b/public/test_data/ecs_instance_summaries_with_tasks-demo-cluster-75.json index 2b1f157..edac7cf 100644 --- a/public/test_data/ecs_instance_summaries_with_tasks-demo-cluster-75.json +++ b/public/test_data/ecs_instance_summaries_with_tasks-demo-cluster-75.json @@ -2,7 +2,7 @@ { "ec2InstanceId": "i-aaaaaa00", "ec2IpAddress": "10.0.1.0", - "registeredCpu": 2048, + "registeredCpu": 4096, "registeredMemory": 16000, "remainingCpu": 2048, "remainingMemory": 16000, @@ -11,7 +11,7 @@ { "ec2InstanceId": "i-bbbbbb00", "ec2IpAddress": "10.0.2.0", - "registeredCpu": 2048, + "registeredCpu": 4096, "registeredMemory": 16000, "remainingCpu": 960, "remainingMemory": 11712, @@ -117,7 +117,7 @@ { "ec2InstanceId": "i-cccccc00", "ec2IpAddress": "10.0.3.0", - "registeredCpu": 2048, + "registeredCpu": 4096, "registeredMemory": 16000, "remainingCpu": 2048, "remainingMemory": 14880, @@ -175,7 +175,7 @@ { "ec2InstanceId": "i-dddddd00", "ec2IpAddress": "10.0.4.0", - "registeredCpu": 2048, + "registeredCpu": 4096, "registeredMemory": 16000, "remainingCpu": 2048, "remainingMemory": 16000, @@ -184,7 +184,7 @@ { "ec2InstanceId": "i-eeeeee00", "ec2IpAddress": "10.0.5.0", - "registeredCpu": 2048, + "registeredCpu": 4096, "registeredMemory": 16000, "remainingCpu": 536, "remainingMemory": 10592, @@ -338,7 +338,7 @@ { "ec2InstanceId": "i-aaaaaa01", "ec2IpAddress": "10.0.1.1", - "registeredCpu": 2048, + "registeredCpu": 4096, "registeredMemory": 16000, "remainingCpu": 2048, "remainingMemory": 16000, @@ -347,7 +347,7 @@ { "ec2InstanceId": "i-bbbbbb01", "ec2IpAddress": "10.0.2.1", - "registeredCpu": 2048, + "registeredCpu": 4096, "registeredMemory": 16000, "remainingCpu": 960, "remainingMemory": 11712, @@ -453,7 +453,7 @@ { "ec2InstanceId": "i-cccccc01", "ec2IpAddress": "10.0.3.1", - "registeredCpu": 2048, + "registeredCpu": 4096, "registeredMemory": 16000, "remainingCpu": 2048, "remainingMemory": 14880, @@ -511,7 +511,7 @@ { "ec2InstanceId": "i-dddddd01", "ec2IpAddress": "10.0.4.1", - "registeredCpu": 2048, + "registeredCpu": 4096, "registeredMemory": 16000, "remainingCpu": 2048, "remainingMemory": 16000, @@ -520,7 +520,7 @@ { "ec2InstanceId": "i-eeeeee01", "ec2IpAddress": "10.0.5.1", - "registeredCpu": 2048, + "registeredCpu": 4096, "registeredMemory": 16000, "remainingCpu": 536, "remainingMemory": 10592, @@ -674,7 +674,7 @@ { "ec2InstanceId": "i-aaaaaa02", "ec2IpAddress": "10.0.1.2", - "registeredCpu": 2048, + "registeredCpu": 4096, "registeredMemory": 16000, "remainingCpu": 2048, "remainingMemory": 16000, @@ -683,7 +683,7 @@ { "ec2InstanceId": "i-bbbbbb02", "ec2IpAddress": "10.0.2.2", - "registeredCpu": 2048, + "registeredCpu": 4096, "registeredMemory": 16000, "remainingCpu": 960, "remainingMemory": 11712, @@ -789,7 +789,7 @@ { "ec2InstanceId": "i-cccccc02", "ec2IpAddress": "10.0.3.2", - "registeredCpu": 2048, + "registeredCpu": 4096, "registeredMemory": 16000, "remainingCpu": 2048, "remainingMemory": 14880, @@ -847,7 +847,7 @@ { "ec2InstanceId": "i-dddddd02", "ec2IpAddress": "10.0.4.2", - "registeredCpu": 2048, + "registeredCpu": 4096, "registeredMemory": 16000, "remainingCpu": 2048, "remainingMemory": 16000, @@ -856,7 +856,7 @@ { "ec2InstanceId": "i-eeeeee02", "ec2IpAddress": "10.0.5.2", - "registeredCpu": 2048, + "registeredCpu": 4096, "registeredMemory": 16000, "remainingCpu": 536, "remainingMemory": 10592, @@ -1010,7 +1010,7 @@ { "ec2InstanceId": "i-aaaaaa03", "ec2IpAddress": "10.0.1.3", - "registeredCpu": 2048, + "registeredCpu": 4096, "registeredMemory": 16000, "remainingCpu": 2048, "remainingMemory": 16000, @@ -1019,7 +1019,7 @@ { "ec2InstanceId": "i-bbbbbb03", "ec2IpAddress": "10.0.2.3", - "registeredCpu": 2048, + "registeredCpu": 4096, "registeredMemory": 16000, "remainingCpu": 960, "remainingMemory": 11712, @@ -1125,7 +1125,7 @@ { "ec2InstanceId": "i-cccccc03", "ec2IpAddress": "10.0.3.3", - "registeredCpu": 2048, + "registeredCpu": 4096, "registeredMemory": 16000, "remainingCpu": 2048, "remainingMemory": 14880, @@ -1183,7 +1183,7 @@ { "ec2InstanceId": "i-dddddd03", "ec2IpAddress": "10.0.4.3", - "registeredCpu": 2048, + "registeredCpu": 4096, "registeredMemory": 16000, "remainingCpu": 2048, "remainingMemory": 16000, @@ -1192,7 +1192,7 @@ { "ec2InstanceId": "i-eeeeee03", "ec2IpAddress": "10.0.5.3", - "registeredCpu": 2048, + "registeredCpu": 4096, "registeredMemory": 16000, "remainingCpu": 536, "remainingMemory": 10592, @@ -1346,7 +1346,7 @@ { "ec2InstanceId": "i-aaaaaa04", "ec2IpAddress": "10.0.1.4", - "registeredCpu": 2048, + "registeredCpu": 4096, "registeredMemory": 16000, "remainingCpu": 2048, "remainingMemory": 16000, @@ -1355,7 +1355,7 @@ { "ec2InstanceId": "i-bbbbbb04", "ec2IpAddress": "10.0.2.4", - "registeredCpu": 2048, + "registeredCpu": 4096, "registeredMemory": 16000, "remainingCpu": 960, "remainingMemory": 11712, @@ -1461,7 +1461,7 @@ { "ec2InstanceId": "i-cccccc04", "ec2IpAddress": "10.0.3.4", - "registeredCpu": 2048, + "registeredCpu": 4096, "registeredMemory": 16000, "remainingCpu": 2048, "remainingMemory": 14880, @@ -1519,7 +1519,7 @@ { "ec2InstanceId": "i-dddddd04", "ec2IpAddress": "10.0.4.4", - "registeredCpu": 2048, + "registeredCpu": 4096, "registeredMemory": 16000, "remainingCpu": 2048, "remainingMemory": 16000, @@ -1528,7 +1528,7 @@ { "ec2InstanceId": "i-eeeeee04", "ec2IpAddress": "10.0.5.4", - "registeredCpu": 2048, + "registeredCpu": 4096, "registeredMemory": 16000, "remainingCpu": 536, "remainingMemory": 10592, @@ -1682,7 +1682,7 @@ { "ec2InstanceId": "i-aaaaaa05", "ec2IpAddress": "10.0.1.5", - "registeredCpu": 2048, + "registeredCpu": 4096, "registeredMemory": 16000, "remainingCpu": 2048, "remainingMemory": 16000, @@ -1691,7 +1691,7 @@ { "ec2InstanceId": "i-bbbbbb05", "ec2IpAddress": "10.0.2.5", - "registeredCpu": 2048, + "registeredCpu": 4096, "registeredMemory": 16000, "remainingCpu": 960, "remainingMemory": 11712, @@ -1797,7 +1797,7 @@ { "ec2InstanceId": "i-cccccc05", "ec2IpAddress": "10.0.3.5", - "registeredCpu": 2048, + "registeredCpu": 4096, "registeredMemory": 16000, "remainingCpu": 2048, "remainingMemory": 14880, @@ -1855,7 +1855,7 @@ { "ec2InstanceId": "i-dddddd05", "ec2IpAddress": "10.0.4.5", - "registeredCpu": 2048, + "registeredCpu": 4096, "registeredMemory": 16000, "remainingCpu": 2048, "remainingMemory": 16000, @@ -1864,7 +1864,7 @@ { "ec2InstanceId": "i-eeeeee05", "ec2IpAddress": "10.0.5.5", - "registeredCpu": 2048, + "registeredCpu": 4096, "registeredMemory": 16000, "remainingCpu": 536, "remainingMemory": 10592, @@ -2018,7 +2018,7 @@ { "ec2InstanceId": "i-aaaaaa06", "ec2IpAddress": "10.0.1.6", - "registeredCpu": 2048, + "registeredCpu": 4096, "registeredMemory": 16000, "remainingCpu": 2048, "remainingMemory": 16000, @@ -2027,7 +2027,7 @@ { "ec2InstanceId": "i-bbbbbb06", "ec2IpAddress": "10.0.2.6", - "registeredCpu": 2048, + "registeredCpu": 4096, "registeredMemory": 16000, "remainingCpu": 960, "remainingMemory": 11712, @@ -2133,7 +2133,7 @@ { "ec2InstanceId": "i-cccccc06", "ec2IpAddress": "10.0.3.6", - "registeredCpu": 2048, + "registeredCpu": 4096, "registeredMemory": 16000, "remainingCpu": 2048, "remainingMemory": 14880, @@ -2191,7 +2191,7 @@ { "ec2InstanceId": "i-dddddd06", "ec2IpAddress": "10.0.4.6", - "registeredCpu": 2048, + "registeredCpu": 4096, "registeredMemory": 16000, "remainingCpu": 2048, "remainingMemory": 16000, @@ -2200,7 +2200,7 @@ { "ec2InstanceId": "i-eeeeee06", "ec2IpAddress": "10.0.5.6", - "registeredCpu": 2048, + "registeredCpu": 4096, "registeredMemory": 16000, "remainingCpu": 536, "remainingMemory": 10592, @@ -2354,7 +2354,7 @@ { "ec2InstanceId": "i-aaaaaa07", "ec2IpAddress": "10.0.1.7", - "registeredCpu": 2048, + "registeredCpu": 4096, "registeredMemory": 16000, "remainingCpu": 2048, "remainingMemory": 16000, @@ -2363,7 +2363,7 @@ { "ec2InstanceId": "i-bbbbbb07", "ec2IpAddress": "10.0.2.7", - "registeredCpu": 2048, + "registeredCpu": 4096, "registeredMemory": 16000, "remainingCpu": 960, "remainingMemory": 11712, @@ -2469,7 +2469,7 @@ { "ec2InstanceId": "i-cccccc07", "ec2IpAddress": "10.0.3.7", - "registeredCpu": 2048, + "registeredCpu": 4096, "registeredMemory": 16000, "remainingCpu": 2048, "remainingMemory": 14880, @@ -2527,7 +2527,7 @@ { "ec2InstanceId": "i-dddddd07", "ec2IpAddress": "10.0.4.7", - "registeredCpu": 2048, + "registeredCpu": 4096, "registeredMemory": 16000, "remainingCpu": 2048, "remainingMemory": 16000, @@ -2536,7 +2536,7 @@ { "ec2InstanceId": "i-eeeeee07", "ec2IpAddress": "10.0.5.7", - "registeredCpu": 2048, + "registeredCpu": 4096, "registeredMemory": 16000, "remainingCpu": 536, "remainingMemory": 10592, @@ -2690,7 +2690,7 @@ { "ec2InstanceId": "i-aaaaaa08", "ec2IpAddress": "10.0.1.8", - "registeredCpu": 2048, + "registeredCpu": 4096, "registeredMemory": 16000, "remainingCpu": 2048, "remainingMemory": 16000, @@ -2699,7 +2699,7 @@ { "ec2InstanceId": "i-bbbbbb08", "ec2IpAddress": "10.0.2.8", - "registeredCpu": 2048, + "registeredCpu": 4096, "registeredMemory": 16000, "remainingCpu": 960, "remainingMemory": 11712, @@ -2805,7 +2805,7 @@ { "ec2InstanceId": "i-cccccc08", "ec2IpAddress": "10.0.3.8", - "registeredCpu": 2048, + "registeredCpu": 4096, "registeredMemory": 16000, "remainingCpu": 2048, "remainingMemory": 14880, @@ -2863,7 +2863,7 @@ { "ec2InstanceId": "i-dddddd08", "ec2IpAddress": "10.0.4.8", - "registeredCpu": 2048, + "registeredCpu": 4096, "registeredMemory": 16000, "remainingCpu": 2048, "remainingMemory": 16000, @@ -2872,7 +2872,7 @@ { "ec2InstanceId": "i-eeeeee08", "ec2IpAddress": "10.0.5.8", - "registeredCpu": 2048, + "registeredCpu": 4096, "registeredMemory": 16000, "remainingCpu": 536, "remainingMemory": 10592, @@ -3026,7 +3026,7 @@ { "ec2InstanceId": "i-aaaaaa09", "ec2IpAddress": "10.0.1.9", - "registeredCpu": 2048, + "registeredCpu": 4096, "registeredMemory": 16000, "remainingCpu": 2048, "remainingMemory": 16000, @@ -3035,7 +3035,7 @@ { "ec2InstanceId": "i-bbbbbb09", "ec2IpAddress": "10.0.2.9", - "registeredCpu": 2048, + "registeredCpu": 4096, "registeredMemory": 16000, "remainingCpu": 960, "remainingMemory": 11712, @@ -3141,7 +3141,7 @@ { "ec2InstanceId": "i-cccccc09", "ec2IpAddress": "10.0.3.9", - "registeredCpu": 2048, + "registeredCpu": 4096, "registeredMemory": 16000, "remainingCpu": 2048, "remainingMemory": 14880, @@ -3199,7 +3199,7 @@ { "ec2InstanceId": "i-dddddd09", "ec2IpAddress": "10.0.4.9", - "registeredCpu": 2048, + "registeredCpu": 4096, "registeredMemory": 16000, "remainingCpu": 2048, "remainingMemory": 16000, @@ -3208,7 +3208,7 @@ { "ec2InstanceId": "i-eeeeee09", "ec2IpAddress": "10.0.5.9", - "registeredCpu": 2048, + "registeredCpu": 4096, "registeredMemory": 16000, "remainingCpu": 536, "remainingMemory": 10592, @@ -3362,7 +3362,7 @@ { "ec2InstanceId": "i-aaaaaa10", "ec2IpAddress": "10.0.1.10", - "registeredCpu": 2048, + "registeredCpu": 4096, "registeredMemory": 16000, "remainingCpu": 2048, "remainingMemory": 16000, @@ -3371,7 +3371,7 @@ { "ec2InstanceId": "i-bbbbbb10", "ec2IpAddress": "10.0.2.10", - "registeredCpu": 2048, + "registeredCpu": 4096, "registeredMemory": 16000, "remainingCpu": 960, "remainingMemory": 11712, @@ -3477,7 +3477,7 @@ { "ec2InstanceId": "i-cccccc10", "ec2IpAddress": "10.0.3.10", - "registeredCpu": 2048, + "registeredCpu": 4096, "registeredMemory": 16000, "remainingCpu": 2048, "remainingMemory": 14880, @@ -3535,7 +3535,7 @@ { "ec2InstanceId": "i-dddddd10", "ec2IpAddress": "10.0.4.10", - "registeredCpu": 2048, + "registeredCpu": 4096, "registeredMemory": 16000, "remainingCpu": 2048, "remainingMemory": 16000, @@ -3544,7 +3544,7 @@ { "ec2InstanceId": "i-eeeeee10", "ec2IpAddress": "10.0.5.10", - "registeredCpu": 2048, + "registeredCpu": 4096, "registeredMemory": 16000, "remainingCpu": 536, "remainingMemory": 10592, @@ -3698,7 +3698,7 @@ { "ec2InstanceId": "i-aaaaaa11", "ec2IpAddress": "10.0.1.11", - "registeredCpu": 2048, + "registeredCpu": 4096, "registeredMemory": 16000, "remainingCpu": 2048, "remainingMemory": 16000, @@ -3707,7 +3707,7 @@ { "ec2InstanceId": "i-bbbbbb11", "ec2IpAddress": "10.0.2.11", - "registeredCpu": 2048, + "registeredCpu": 4096, "registeredMemory": 16000, "remainingCpu": 960, "remainingMemory": 11712, @@ -3813,7 +3813,7 @@ { "ec2InstanceId": "i-cccccc11", "ec2IpAddress": "10.0.3.11", - "registeredCpu": 2048, + "registeredCpu": 4096, "registeredMemory": 16000, "remainingCpu": 2048, "remainingMemory": 14880, @@ -3871,7 +3871,7 @@ { "ec2InstanceId": "i-dddddd11", "ec2IpAddress": "10.0.4.11", - "registeredCpu": 2048, + "registeredCpu": 4096, "registeredMemory": 16000, "remainingCpu": 2048, "remainingMemory": 16000, @@ -3880,7 +3880,7 @@ { "ec2InstanceId": "i-eeeeee11", "ec2IpAddress": "10.0.5.11", - "registeredCpu": 2048, + "registeredCpu": 4096, "registeredMemory": 16000, "remainingCpu": 536, "remainingMemory": 10592, @@ -4034,7 +4034,7 @@ { "ec2InstanceId": "i-aaaaaa12", "ec2IpAddress": "10.0.1.12", - "registeredCpu": 2048, + "registeredCpu": 4096, "registeredMemory": 16000, "remainingCpu": 2048, "remainingMemory": 16000, @@ -4043,7 +4043,7 @@ { "ec2InstanceId": "i-bbbbbb12", "ec2IpAddress": "10.0.2.12", - "registeredCpu": 2048, + "registeredCpu": 4096, "registeredMemory": 16000, "remainingCpu": 960, "remainingMemory": 11712, @@ -4149,7 +4149,7 @@ { "ec2InstanceId": "i-cccccc12", "ec2IpAddress": "10.0.3.12", - "registeredCpu": 2048, + "registeredCpu": 4096, "registeredMemory": 16000, "remainingCpu": 2048, "remainingMemory": 14880, @@ -4207,7 +4207,7 @@ { "ec2InstanceId": "i-dddddd12", "ec2IpAddress": "10.0.4.12", - "registeredCpu": 2048, + "registeredCpu": 4096, "registeredMemory": 16000, "remainingCpu": 2048, "remainingMemory": 16000, @@ -4216,7 +4216,7 @@ { "ec2InstanceId": "i-eeeeee12", "ec2IpAddress": "10.0.5.12", - "registeredCpu": 2048, + "registeredCpu": 4096, "registeredMemory": 16000, "remainingCpu": 536, "remainingMemory": 10592, @@ -4370,7 +4370,7 @@ { "ec2InstanceId": "i-aaaaaa13", "ec2IpAddress": "10.0.1.13", - "registeredCpu": 2048, + "registeredCpu": 4096, "registeredMemory": 16000, "remainingCpu": 2048, "remainingMemory": 16000, @@ -4379,7 +4379,7 @@ { "ec2InstanceId": "i-bbbbbb13", "ec2IpAddress": "10.0.2.13", - "registeredCpu": 2048, + "registeredCpu": 4096, "registeredMemory": 16000, "remainingCpu": 960, "remainingMemory": 11712, @@ -4485,7 +4485,7 @@ { "ec2InstanceId": "i-cccccc13", "ec2IpAddress": "10.0.3.13", - "registeredCpu": 2048, + "registeredCpu": 4096, "registeredMemory": 16000, "remainingCpu": 2048, "remainingMemory": 14880, @@ -4543,7 +4543,7 @@ { "ec2InstanceId": "i-dddddd13", "ec2IpAddress": "10.0.4.13", - "registeredCpu": 2048, + "registeredCpu": 4096, "registeredMemory": 16000, "remainingCpu": 2048, "remainingMemory": 16000, @@ -4552,7 +4552,7 @@ { "ec2InstanceId": "i-eeeeee13", "ec2IpAddress": "10.0.5.13", - "registeredCpu": 2048, + "registeredCpu": 4096, "registeredMemory": 16000, "remainingCpu": 536, "remainingMemory": 10592, @@ -4706,7 +4706,7 @@ { "ec2InstanceId": "i-aaaaaa14", "ec2IpAddress": "10.0.1.14", - "registeredCpu": 2048, + "registeredCpu": 4096, "registeredMemory": 16000, "remainingCpu": 2048, "remainingMemory": 16000, @@ -4715,7 +4715,7 @@ { "ec2InstanceId": "i-bbbbbb14", "ec2IpAddress": "10.0.2.14", - "registeredCpu": 2048, + "registeredCpu": 4096, "registeredMemory": 16000, "remainingCpu": 960, "remainingMemory": 11712, @@ -4821,7 +4821,7 @@ { "ec2InstanceId": "i-cccccc14", "ec2IpAddress": "10.0.3.14", - "registeredCpu": 2048, + "registeredCpu": 4096, "registeredMemory": 16000, "remainingCpu": 2048, "remainingMemory": 14880, @@ -4879,7 +4879,7 @@ { "ec2InstanceId": "i-dddddd14", "ec2IpAddress": "10.0.4.14", - "registeredCpu": 2048, + "registeredCpu": 4096, "registeredMemory": 16000, "remainingCpu": 2048, "remainingMemory": 16000, @@ -4888,7 +4888,7 @@ { "ec2InstanceId": "i-eeeeee14", "ec2IpAddress": "10.0.5.14", - "registeredCpu": 2048, + "registeredCpu": 4096, "registeredMemory": 16000, "remainingCpu": 536, "remainingMemory": 10592, diff --git a/public/test_data/ecs_instance_summaries_with_tasks-demo-cluster-8.json b/public/test_data/ecs_instance_summaries_with_tasks-demo-cluster-8.json index ff7c2f7..03a0be4 100644 --- a/public/test_data/ecs_instance_summaries_with_tasks-demo-cluster-8.json +++ b/public/test_data/ecs_instance_summaries_with_tasks-demo-cluster-8.json @@ -2,7 +2,7 @@ { "ec2InstanceId": "i-aaaaaaaa", "ec2IpAddress": "10.0.0.1", - "registeredCpu": 2048, + "registeredCpu": 4096, "registeredMemory": 8000, "remainingCpu": 960, "remainingMemory": 3712, @@ -108,7 +108,7 @@ { "ec2InstanceId": "i-bbbbbbbb", "ec2IpAddress": "10.0.0.2", - "registeredCpu": 2048, + "registeredCpu": 4096, "registeredMemory": 8000, "remainingCpu": 960, "remainingMemory": 3712, @@ -214,7 +214,7 @@ { "ec2InstanceId": "i-cccccccc", "ec2IpAddress": "10.0.0.3", - "registeredCpu": 2048, + "registeredCpu": 4096, "registeredMemory": 8000, "remainingCpu": 2048, "remainingMemory": 6880, @@ -272,7 +272,7 @@ { "ec2InstanceId": "i-dddddddd", "ec2IpAddress": "10.0.0.4", - "registeredCpu": 2048, + "registeredCpu": 4096, "registeredMemory": 8000, "remainingCpu": 536, "remainingMemory": 2592, @@ -426,7 +426,7 @@ { "ec2InstanceId": "i-eeeeeeee", "ec2IpAddress": "10.0.0.5", - "registeredCpu": 2048, + "registeredCpu": 4096, "registeredMemory": 8000, "remainingCpu": 960, "remainingMemory": 3712, @@ -532,7 +532,7 @@ { "ec2InstanceId": "i-ffffffff", "ec2IpAddress": "10.0.0.6", - "registeredCpu": 2048, + "registeredCpu": 4096, "registeredMemory": 8000, "remainingCpu": 2048, "remainingMemory": 6880, @@ -590,7 +590,7 @@ { "ec2InstanceId": "i-gggggggg", "ec2IpAddress": "10.0.0.7", - "registeredCpu": 2048, + "registeredCpu": 4096, "registeredMemory": 8000, "remainingCpu": 2048, "remainingMemory": 8000, @@ -599,7 +599,7 @@ { "ec2InstanceId": "i-hhhhhhhh", "ec2IpAddress": "10.0.0.8", - "registeredCpu": 2048, + "registeredCpu": 4096, "registeredMemory": 8000, "remainingCpu": 536, "remainingMemory": 2592, From 4ec2c72a62d16c1c369a6987f66b9c6dfd2a4ea2 Mon Sep 17 00:00:00 2001 From: mcallanan Date: Mon, 21 May 2018 23:09:38 +1000 Subject: [PATCH 08/16] Improved short polling. Cached cluster data for 30mins with "forceRefresh" option. Externalised server-side defaults into environment-based configuration files. UI: - Implemented short polling logic - query the server up to 120 times with 5s intervals - Added "Reload Server Cache" button to UI to force server to refresh cluster data. - Upgraded bootstrap from v3.3.5 to v3.3.7 for glyphicons including "glyphicons-halflings-regular" fonts. - Improved error handling. Server: - Externalised server-side defaults into environment-based configuration files. - Added ClusterState & ClusterStateCache to cache cluster data. - Added FetchStatus to represent current status of fetching cluster data. - Improved error handling. - Added test suite - run tests with "npm run test" --- ClientServerInteraction-SequenceDiagram.png | Bin 0 -> 35817 bytes ClientServerInteraction-SequenceDiagram.puml | 59 ++ FetchStatus-StateDiagram.puml | 38 + FetchStatus-StateDiagram1.png | Bin 0 -> 11780 bytes FetchStatus-StateDiagram2.png | Bin 0 -> 19303 bytes bin/www | 3 +- config/config.js | 11 + config/defaults.js | 14 + config/env/dev.js | 3 + config/env/prod.js | 3 + config/env/test.js | 3 + package-lock.json | 870 ++++++++++++++++++ package.json | 17 +- public/javascripts/graph.js | 127 ++- .../thirdparty/bootstrap-3.3.7.min.js | 7 + public/javascripts/types.js | 2 +- .../fonts/glyphicons-halflings-regular.woff | Bin 0 -> 23424 bytes .../fonts/glyphicons-halflings-regular.woff2 | Bin 0 -> 18028 bytes .../thirdparty/bootstrap-3.3.7.min.css | 6 + .../thirdparty/bootstrap-theme.min.css | 5 - .../stylesheets/thirdparty/bootstrap.min.css | 5 - routes/clusterState.js | 13 + routes/clusterStateCache.js | 22 + routes/fetchStatus.js | 6 + routes/index.js | 201 ++-- test/testClusterState.js | 16 + test/testClusterStateCache.js | 23 + test/testConfig.js | 17 + views/index.ejs | 42 +- 29 files changed, 1372 insertions(+), 141 deletions(-) create mode 100644 ClientServerInteraction-SequenceDiagram.png create mode 100644 ClientServerInteraction-SequenceDiagram.puml create mode 100644 FetchStatus-StateDiagram.puml create mode 100644 FetchStatus-StateDiagram1.png create mode 100644 FetchStatus-StateDiagram2.png create mode 100644 config/config.js create mode 100644 config/defaults.js create mode 100644 config/env/dev.js create mode 100644 config/env/prod.js create mode 100644 config/env/test.js create mode 100644 package-lock.json create mode 100644 public/javascripts/thirdparty/bootstrap-3.3.7.min.js create mode 100644 public/stylesheets/fonts/glyphicons-halflings-regular.woff create mode 100644 public/stylesheets/fonts/glyphicons-halflings-regular.woff2 create mode 100644 public/stylesheets/thirdparty/bootstrap-3.3.7.min.css delete mode 100644 public/stylesheets/thirdparty/bootstrap-theme.min.css delete mode 100644 public/stylesheets/thirdparty/bootstrap.min.css create mode 100644 routes/clusterState.js create mode 100644 routes/clusterStateCache.js create mode 100644 routes/fetchStatus.js create mode 100644 test/testClusterState.js create mode 100644 test/testClusterStateCache.js create mode 100644 test/testConfig.js diff --git a/ClientServerInteraction-SequenceDiagram.png b/ClientServerInteraction-SequenceDiagram.png new file mode 100644 index 0000000000000000000000000000000000000000..ba7bc7ece7187208fe22a1c53546f562c62fb6a5 GIT binary patch literal 35817 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 A: Select Cluster +activate A + +A -> B: "/api/instance_summaries_with_tasks" +activate B + +B -> C: Get State +activate C +C -> D: Create +activate D +C -> D: INITIAL +C --> B: INITIAL + +B --> A: INITIAL +deactivate B + +C -> D: FETCHING + +A -> B: "/api/instance_summaries_with_tasks" +activate B +B -> C: Get State +C --> B: FETCHING +B --> A: FETCHING +deactivate B + +C -> D: FETCHED + +A -> B: "/api/instance_summaries_with_tasks" +activate B +B -> C: Get State +C --> B: FETCHED +B --> A: FETCHED +deactivate B + +D -> D : Expire +deactivate D + +A --> User: Render Graph +deactivate A + + +deactivate C +deactivate D + +@enduml \ No newline at end of file diff --git a/FetchStatus-StateDiagram.puml b/FetchStatus-StateDiagram.puml new file mode 100644 index 0000000..69203ac --- /dev/null +++ b/FetchStatus-StateDiagram.puml @@ -0,0 +1,38 @@ +@startuml + +'[*] --> null +[*] --> INITIAL +INITIAL --> FETCHING +FETCHING --> FETCHED +'INITIAL --> null : Expired +'FETCHING --> null : Expired +'FETCHED --> null : Expired +'ERROR --> null : Expired +INITIAL --> ERROR : Failed +FETCHING --> ERROR : Failed +FETCHED --> ERROR : Failed + +'FETCHED --> [*] : Success +'ERROR --> [*] : Error + +@enduml + +@startuml + +[*] --> null +null --> INITIAL +INITIAL --> FETCHING +FETCHING --> FETCHED +INITIAL --> null : Expired +FETCHING --> null : Expired +FETCHED --> null : Expired +ERROR --> null : Expired +INITIAL --> ERROR +FETCHING --> ERROR +FETCHED --> ERROR + +'FETCHED --> [*] : Success +'ERROR --> [*] : Error + +@enduml + diff --git a/FetchStatus-StateDiagram1.png b/FetchStatus-StateDiagram1.png new file mode 100644 index 0000000000000000000000000000000000000000..2c8b9444a744b42bdd195f7efa16565ed42e1f8c GIT binary patch literal 11780 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 W&5S$=_qTt_OoO|FK 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 literal 0 HcmV?d00001 diff --git a/bin/www b/bin/www index 0bf43cf..adbb622 100755 --- a/bin/www +++ b/bin/www @@ -7,12 +7,13 @@ var app = require('../app'); var debug = require('debug')('c3vis:server'); var http = require('http'); +var config = require('../config/config'); /** * Get port from environment and store in Express. */ -var port = normalizePort(process.env.PORT || '3000'); +var port = normalizePort(config.port); app.set('port', port); /** diff --git a/config/config.js b/config/config.js new file mode 100644 index 0000000..ddf21b7 --- /dev/null +++ b/config/config.js @@ -0,0 +1,11 @@ +const _ = require('lodash'); + +// Assume running in 'dev' environment if NODE_ENV environment variable not defined +const targetEnvironment = (process.env.NODE_ENV || 'dev'); + +// Load default configuration from defaults.js +// and extend with environment-specific configuration +module.exports = _.merge( + require('./defaults.js'), + require(`./env/${targetEnvironment}.js`) || {} +); diff --git a/config/defaults.js b/config/defaults.js new file mode 100644 index 0000000..4cfe495 --- /dev/null +++ b/config/defaults.js @@ -0,0 +1,14 @@ +module.exports = { + port: process.env.PORT || 3000, + clusterStateCacheTtl: 30 * 60 * 1000, // Invalidate clusters in cache after 30 minutes + aws: { + configFile: './aws_config.json', + apiDelay: 100, // milliseconds to pause between AWS API calls to prevent API rate limiting + listInstancesPageSize: 100, // max 100 + describeInstancesPageSize: 100, // max 100 + listTasksPageSize: 100, // max 100 + describeTasksPageSize: 100, // max 100 + maxSimultaneousDescribeTasksCalls: 2, + maxSimultaneousDescribeTaskDefinitionCalls: 1, + } +}; diff --git a/config/env/dev.js b/config/env/dev.js new file mode 100644 index 0000000..0b11063 --- /dev/null +++ b/config/env/dev.js @@ -0,0 +1,3 @@ +module.exports = { + // add dev environment config overrides here and enable at startup with NODE_ENV=dev environment variable +}; diff --git a/config/env/prod.js b/config/env/prod.js new file mode 100644 index 0000000..da0b4f1 --- /dev/null +++ b/config/env/prod.js @@ -0,0 +1,3 @@ +module.exports = { + // add production environment config overrides here and enable at startup with NODE_ENV=prod environment variable +}; diff --git a/config/env/test.js b/config/env/test.js new file mode 100644 index 0000000..d6b7894 --- /dev/null +++ b/config/env/test.js @@ -0,0 +1,3 @@ +module.exports = { + // add test environment config overrides here and enable at startup with NODE_ENV=test environment variable +}; diff --git a/package-lock.json b/package-lock.json new file mode 100644 index 0000000..3777034 --- /dev/null +++ b/package-lock.json @@ -0,0 +1,870 @@ +{ + "name": "c3vis", + "version": "0.3.2", + "lockfileVersion": 1, + "requires": true, + "dependencies": { + "asap": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/asap/-/asap-1.0.0.tgz", + "integrity": "sha1-sqRdpf36ILBJb8N2jMJ8EvqRan0=" + }, + "assertion-error": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/assertion-error/-/assertion-error-1.1.0.tgz", + "integrity": "sha512-jgsaNduz+ndvGyFt3uSuWqvy4lCnIJiovtouQN5JZHOKCS2QuhEdbcQHFhVksz2N2U9hXJo8odG7ETyWlEeuDw==" + }, + "aws-sdk": { + "version": "2.77.0", + "resolved": "https://registry.npmjs.org/aws-sdk/-/aws-sdk-2.77.0.tgz", + "integrity": "sha1-gJCQu4dNj0//ysUxZilYdjjnhlw=", + "requires": { + "buffer": "5.0.6", + "crypto-browserify": "1.0.9", + "jmespath": "0.15.0", + "querystring": "0.2.0", + "sax": "1.2.1", + "url": "0.10.3", + "uuid": "3.0.1", + "xml2js": "0.4.17", + "xmlbuilder": "4.2.1" + } + }, + "aws-sdk-promise": { + "version": "0.0.2", + "resolved": "https://registry.npmjs.org/aws-sdk-promise/-/aws-sdk-promise-0.0.2.tgz", + "integrity": "sha1-KIa9H8TEfry5r91sWXz0Jg+55eg=", + "requires": { + "promise": "6.1.0" + } + }, + "balanced-match": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.0.tgz", + "integrity": "sha1-ibTRmasr7kneFk6gK4nORi1xt2c=" + }, + "base64-js": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/base64-js/-/base64-js-1.2.1.tgz", + "integrity": "sha1-qRlH2h9KUW6jjltOwOw3c2deCIY=" + }, + "batch-promises": { + "version": "0.0.3", + "resolved": "https://registry.npmjs.org/batch-promises/-/batch-promises-0.0.3.tgz", + "integrity": "sha1-BLhQ/mhHx6/wXzzKQQRZMkMLHDI=" + }, + "body-parser": { + "version": "1.12.4", + "resolved": "https://registry.npmjs.org/body-parser/-/body-parser-1.12.4.tgz", + "integrity": "sha1-CQcAxLoohiqFIO83g5X97l9hwik=", + "requires": { + "bytes": "1.0.0", + "content-type": "1.0.1", + "debug": "2.2.0", + "depd": "1.0.1", + "iconv-lite": "0.4.8", + "on-finished": "2.2.1", + "qs": "2.4.2", + "raw-body": "2.0.2", + "type-is": "1.6.8" + }, + "dependencies": { + "bytes": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/bytes/-/bytes-1.0.0.tgz", + "integrity": "sha1-NWnt6Lo0MV+rmcPpLLBMciDeH6g=" + }, + "content-type": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/content-type/-/content-type-1.0.1.tgz", + "integrity": "sha1-oZ0iRzJ9wDgFDOYit6FU7FnF5gA=" + }, + "depd": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/depd/-/depd-1.0.1.tgz", + "integrity": "sha1-gK7GTJ1tl+ZcwqnKqTwKpqv3Oqo=" + }, + "iconv-lite": { + "version": "0.4.8", + "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.8.tgz", + "integrity": "sha1-xgGadZXyzvynAuq2lKAQvNkpjSA=" + }, + "on-finished": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/on-finished/-/on-finished-2.2.1.tgz", + "integrity": "sha1-XIXBzDYpn3gCllP2Z/J7a5nrwCk=", + "requires": { + "ee-first": "1.1.0" + }, + "dependencies": { + "ee-first": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/ee-first/-/ee-first-1.1.0.tgz", + "integrity": "sha1-ag18YiHkkP7v2S7D9EHJzozQl/Q=" + } + } + }, + "qs": { + "version": "2.4.2", + "resolved": "https://registry.npmjs.org/qs/-/qs-2.4.2.tgz", + "integrity": "sha1-9854jld33wtQENp/fE5zujJHD1o=" + }, + "raw-body": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/raw-body/-/raw-body-2.0.2.tgz", + "integrity": "sha1-osL5jIUxzumcY9jSOLfel7tln8o=", + "requires": { + "bytes": "2.1.0", + "iconv-lite": "0.4.8" + }, + "dependencies": { + "bytes": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/bytes/-/bytes-2.1.0.tgz", + "integrity": "sha1-rJPEEOL/ycx89LRks4KJBn9eR7Q=" + } + } + }, + "type-is": { + "version": "1.6.8", + "resolved": "https://registry.npmjs.org/type-is/-/type-is-1.6.8.tgz", + "integrity": "sha1-O6yMDIUnVMhVFD4gbUoW6Qi/AxU=", + "requires": { + "media-typer": "0.3.0", + "mime-types": "2.1.7" + }, + "dependencies": { + "media-typer": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/media-typer/-/media-typer-0.3.0.tgz", + "integrity": "sha1-hxDXrwqmJvj/+hzgAWhUUmMlV0g=" + }, + "mime-types": { + "version": "2.1.7", + "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.7.tgz", + "integrity": "sha1-/2A5cOPHMe9vf03zyaD0Y6E8J1U=", + "requires": { + "mime-db": "1.19.0" + }, + "dependencies": { + "mime-db": { + "version": "1.19.0", + "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.19.0.tgz", + "integrity": "sha1-SWoYGYp86CRFNOJbsQK3T7Qg/VY=" + } + } + } + } + } + } + }, + "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" + } + }, + "browser-stdout": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/browser-stdout/-/browser-stdout-1.3.1.tgz", + "integrity": "sha512-qhAVI1+Av2X7qelOfAIYwXONood6XlZE/fXaBSmW/T5SzLAmCgzi+eiWE7fUvbHaeNBQH13UftjpXxsfLkMpgw==" + }, + "buffer": { + "version": "5.0.6", + "resolved": "https://registry.npmjs.org/buffer/-/buffer-5.0.6.tgz", + "integrity": "sha1-LqZp9+7Atu2gWwj4tf9mGyhXNYg=", + "requires": { + "base64-js": "1.2.1", + "ieee754": "1.1.8" + } + }, + "chai": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chai/-/chai-4.1.2.tgz", + "integrity": "sha1-D2RYS6ZC8PKs4oBiefTwbKI61zw=", + "requires": { + "assertion-error": "1.1.0", + "check-error": "1.0.2", + "deep-eql": "3.0.1", + "get-func-name": "2.0.0", + "pathval": "1.1.0", + "type-detect": "4.0.8" + } + }, + "check-error": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/check-error/-/check-error-1.0.2.tgz", + "integrity": "sha1-V00xLt2Iu13YkS6Sht1sCu1KrII=" + }, + "commander": { + "version": "2.11.0", + "resolved": "https://registry.npmjs.org/commander/-/commander-2.11.0.tgz", + "integrity": "sha512-b0553uYA5YAEGgyYIGYROzKQ7X5RAqedkfjiZxwi0kL1g3bOaBNNZfYkzt/CL0umgD5wc9Jec2FbB98CjkMRvQ==" + }, + "concat-map": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", + "integrity": "sha1-2Klr13/Wjfd5OnMDajug1UBdR3s=" + }, + "cookie-parser": { + "version": "1.3.5", + "resolved": "https://registry.npmjs.org/cookie-parser/-/cookie-parser-1.3.5.tgz", + "integrity": "sha1-nXVVcPtdF4kHcSJ6AjFNm+fPg1Y=", + "requires": { + "cookie": "0.1.3", + "cookie-signature": "1.0.6" + }, + "dependencies": { + "cookie": { + "version": "0.1.3", + "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.1.3.tgz", + "integrity": "sha1-5zSlwUF/zkctWu+Cw4HKu2TRpDU=" + }, + "cookie-signature": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/cookie-signature/-/cookie-signature-1.0.6.tgz", + "integrity": "sha1-4wOogrNCzD7oylE6eZmXNNqzriw=" + } + } + }, + "crypto-browserify": { + "version": "1.0.9", + "resolved": "https://registry.npmjs.org/crypto-browserify/-/crypto-browserify-1.0.9.tgz", + "integrity": "sha1-zFRJaF37hesRyYKKzHy4erW7/MA=" + }, + "debug": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.2.0.tgz", + "integrity": "sha1-+HBX6ZWxofauaklgZkE3vFbwOdo=", + "requires": { + "ms": "0.7.1" + }, + "dependencies": { + "ms": { + "version": "0.7.1", + "resolved": "https://registry.npmjs.org/ms/-/ms-0.7.1.tgz", + "integrity": "sha1-nNE8A62/8ltl7/3nzoZO6VIBcJg=" + } + } + }, + "deep-eql": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/deep-eql/-/deep-eql-3.0.1.tgz", + "integrity": "sha512-+QeIQyN5ZuO+3Uk5DYh6/1eKO0m0YmJFGNmFHGACpf1ClL1nmlV/p4gNgbl2pJGxgXb4faqo6UE+M5ACEMyVcw==", + "requires": { + "type-detect": "4.0.8" + } + }, + "diff": { + "version": "3.5.0", + "resolved": "https://registry.npmjs.org/diff/-/diff-3.5.0.tgz", + "integrity": "sha512-A46qtFgd+g7pDZinpnwiRJtxbC1hpgf0uzP3iG89scHk0AUC7A1TGxf5OiiOUv/JMZR8GOt8hL900hV0bOy5xA==" + }, + "ejs": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/ejs/-/ejs-2.3.1.tgz", + "integrity": "sha1-ppfZisQB4yqZw97tksYMGbYZmn8=" + }, + "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=" + }, + "express": { + "version": "4.12.4", + "resolved": "https://registry.npmjs.org/express/-/express-4.12.4.tgz", + "integrity": "sha1-j+wlECVbxrLlgQfEgjnA+jB8GqI=", + "requires": { + "accepts": "1.2.13", + "content-disposition": "0.5.0", + "content-type": "1.0.1", + "cookie": "0.1.2", + "cookie-signature": "1.0.6", + "debug": "2.2.0", + "depd": "1.0.1", + "escape-html": "1.0.1", + "etag": "1.6.0", + "finalhandler": "0.3.6", + "fresh": "0.2.4", + "merge-descriptors": "1.0.0", + "methods": "1.1.1", + "on-finished": "2.2.1", + "parseurl": "1.3.0", + "path-to-regexp": "0.1.3", + "proxy-addr": "1.0.8", + "qs": "2.4.2", + "range-parser": "1.0.2", + "send": "0.12.3", + "serve-static": "1.9.3", + "type-is": "1.6.8", + "utils-merge": "1.0.0", + "vary": "1.0.1" + }, + "dependencies": { + "accepts": { + "version": "1.2.13", + "resolved": "https://registry.npmjs.org/accepts/-/accepts-1.2.13.tgz", + "integrity": "sha1-5fHzkoxtlf2WVYw27D2dDeSm7Oo=", + "requires": { + "mime-types": "2.1.7", + "negotiator": "0.5.3" + }, + "dependencies": { + "mime-types": { + "version": "2.1.7", + "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.7.tgz", + "integrity": "sha1-/2A5cOPHMe9vf03zyaD0Y6E8J1U=", + "requires": { + "mime-db": "1.19.0" + }, + "dependencies": { + "mime-db": { + "version": "1.19.0", + "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.19.0.tgz", + "integrity": "sha1-SWoYGYp86CRFNOJbsQK3T7Qg/VY=" + } + } + }, + "negotiator": { + "version": "0.5.3", + "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-0.5.3.tgz", + "integrity": "sha1-Jp1cR2gQ7JLtvntsLygxY4T5p+g=" + } + } + }, + "content-disposition": { + "version": "0.5.0", + "resolved": "https://registry.npmjs.org/content-disposition/-/content-disposition-0.5.0.tgz", + "integrity": "sha1-QoT+auBjCHRjnkToCkGMKTQTXp4=" + }, + "content-type": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/content-type/-/content-type-1.0.1.tgz", + "integrity": "sha1-oZ0iRzJ9wDgFDOYit6FU7FnF5gA=" + }, + "cookie": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.1.2.tgz", + "integrity": "sha1-cv7D0k5Io0Mgc9kMEmQgBQYQBLE=" + }, + "cookie-signature": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/cookie-signature/-/cookie-signature-1.0.6.tgz", + "integrity": "sha1-4wOogrNCzD7oylE6eZmXNNqzriw=" + }, + "depd": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/depd/-/depd-1.0.1.tgz", + "integrity": "sha1-gK7GTJ1tl+ZcwqnKqTwKpqv3Oqo=" + }, + "escape-html": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/escape-html/-/escape-html-1.0.1.tgz", + "integrity": "sha1-GBoobq05ejmpKFfPsdQwUuNWv/A=" + }, + "etag": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/etag/-/etag-1.6.0.tgz", + "integrity": "sha1-i8ssavElTEgd/IuZfJBu9ORCwgc=", + "requires": { + "crc": "3.2.1" + }, + "dependencies": { + "crc": { + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/crc/-/crc-3.2.1.tgz", + "integrity": "sha1-XZyPt3okXNXsopHl0tAFM0urAII=" + } + } + }, + "finalhandler": { + "version": "0.3.6", + "resolved": "https://registry.npmjs.org/finalhandler/-/finalhandler-0.3.6.tgz", + "integrity": "sha1-2vnEFhsbBuABRmsUEd/baXO+E4s=", + "requires": { + "debug": "2.2.0", + "escape-html": "1.0.1", + "on-finished": "2.2.1" + } + }, + "fresh": { + "version": "0.2.4", + "resolved": "https://registry.npmjs.org/fresh/-/fresh-0.2.4.tgz", + "integrity": "sha1-NYJJkgbJcjcUGQ7ddLRgT+tKYUw=" + }, + "merge-descriptors": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/merge-descriptors/-/merge-descriptors-1.0.0.tgz", + "integrity": "sha1-IWnPdTjhsMyH+4jhUC2EdLv3mGQ=" + }, + "methods": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/methods/-/methods-1.1.1.tgz", + "integrity": "sha1-F+pjZgZtAMWON1uOx9/QRTyJgio=" + }, + "on-finished": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/on-finished/-/on-finished-2.2.1.tgz", + "integrity": "sha1-XIXBzDYpn3gCllP2Z/J7a5nrwCk=", + "requires": { + "ee-first": "1.1.0" + }, + "dependencies": { + "ee-first": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/ee-first/-/ee-first-1.1.0.tgz", + "integrity": "sha1-ag18YiHkkP7v2S7D9EHJzozQl/Q=" + } + } + }, + "parseurl": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/parseurl/-/parseurl-1.3.0.tgz", + "integrity": "sha1-tYBG20Ij4UWvp2AJ5hush8wigbM=" + }, + "path-to-regexp": { + "version": "0.1.3", + "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-0.1.3.tgz", + "integrity": "sha1-IbmrgidCed4lsVbqCP0SylG4rss=" + }, + "proxy-addr": { + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/proxy-addr/-/proxy-addr-1.0.8.tgz", + "integrity": "sha1-21Tsh4vMEFPVdkZgkhmzcVZ4uv4=", + "requires": { + "forwarded": "0.1.0", + "ipaddr.js": "1.0.1" + }, + "dependencies": { + "forwarded": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/forwarded/-/forwarded-0.1.0.tgz", + "integrity": "sha1-Ge+YdMSuHCl7zweP3mOgm2aoQ2M=" + }, + "ipaddr.js": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-1.0.1.tgz", + "integrity": "sha1-XziAHcc+BAD8cHY4b27VIV+9j5U=" + } + } + }, + "qs": { + "version": "2.4.2", + "resolved": "https://registry.npmjs.org/qs/-/qs-2.4.2.tgz", + "integrity": "sha1-9854jld33wtQENp/fE5zujJHD1o=" + }, + "range-parser": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/range-parser/-/range-parser-1.0.2.tgz", + "integrity": "sha1-BqEqQuUTG6jkV82JIESGfyNE5Uk=" + }, + "send": { + "version": "0.12.3", + "resolved": "https://registry.npmjs.org/send/-/send-0.12.3.tgz", + "integrity": "sha1-zRLcWP3iHk+RkCs5sv2gWnptm9w=", + "requires": { + "debug": "2.2.0", + "depd": "1.0.1", + "destroy": "1.0.3", + "escape-html": "1.0.1", + "etag": "1.6.0", + "fresh": "0.2.4", + "mime": "1.3.4", + "ms": "0.7.1", + "on-finished": "2.2.1", + "range-parser": "1.0.2" + }, + "dependencies": { + "destroy": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/destroy/-/destroy-1.0.3.tgz", + "integrity": "sha1-tDO0ck5x/YVR2YhRdIUcX8N34sk=" + }, + "mime": { + "version": "1.3.4", + "resolved": "https://registry.npmjs.org/mime/-/mime-1.3.4.tgz", + "integrity": "sha1-EV+eO2s9rylZmDyzjxSaLUDrXVM=" + }, + "ms": { + "version": "0.7.1", + "resolved": "https://registry.npmjs.org/ms/-/ms-0.7.1.tgz", + "integrity": "sha1-nNE8A62/8ltl7/3nzoZO6VIBcJg=" + } + } + }, + "serve-static": { + "version": "1.9.3", + "resolved": "https://registry.npmjs.org/serve-static/-/serve-static-1.9.3.tgz", + "integrity": "sha1-X42gcyOtOF/z3FQfGnkXsuQ261c=", + "requires": { + "escape-html": "1.0.1", + "parseurl": "1.3.0", + "send": "0.12.3", + "utils-merge": "1.0.0" + } + }, + "type-is": { + "version": "1.6.8", + "resolved": "https://registry.npmjs.org/type-is/-/type-is-1.6.8.tgz", + "integrity": "sha1-O6yMDIUnVMhVFD4gbUoW6Qi/AxU=", + "requires": { + "media-typer": "0.3.0", + "mime-types": "2.1.7" + }, + "dependencies": { + "media-typer": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/media-typer/-/media-typer-0.3.0.tgz", + "integrity": "sha1-hxDXrwqmJvj/+hzgAWhUUmMlV0g=" + }, + "mime-types": { + "version": "2.1.7", + "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.7.tgz", + "integrity": "sha1-/2A5cOPHMe9vf03zyaD0Y6E8J1U=", + "requires": { + "mime-db": "1.19.0" + }, + "dependencies": { + "mime-db": { + "version": "1.19.0", + "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.19.0.tgz", + "integrity": "sha1-SWoYGYp86CRFNOJbsQK3T7Qg/VY=" + } + } + } + } + }, + "utils-merge": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/utils-merge/-/utils-merge-1.0.0.tgz", + "integrity": "sha1-ApT7kiu5N1FTVBxPcJYjHyh8ivg=" + }, + "vary": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/vary/-/vary-1.0.1.tgz", + "integrity": "sha1-meSYFWaihhGN+yuBc1ffeZM3bRA=" + } + } + }, + "fs.realpath": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", + "integrity": "sha1-FQStJSMVjKpA20onh8sBQRmU6k8=" + }, + "get-func-name": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/get-func-name/-/get-func-name-2.0.0.tgz", + "integrity": "sha1-6td0q+5y4gQJQzoGY2YCPdaIekE=" + }, + "glob": { + "version": "7.1.2", + "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.2.tgz", + "integrity": "sha512-MJTUg1kjuLeQCJ+ccE4Vpa6kKVXkPYJ2mOCQyUuKLcLQsdrMCpBPUi8qVE6+YuaJkozeA9NusTAw3hLr8Xe5EQ==", + "requires": { + "fs.realpath": "1.0.0", + "inflight": "1.0.6", + "inherits": "2.0.3", + "minimatch": "3.0.4", + "once": "1.4.0", + "path-is-absolute": "1.0.1" + } + }, + "growl": { + "version": "1.10.3", + "resolved": "https://registry.npmjs.org/growl/-/growl-1.10.3.tgz", + "integrity": "sha512-hKlsbA5Vu3xsh1Cg3J7jSmX/WaW6A5oBeqzM88oNbCRQFz+zUaXm6yxS4RVytp1scBoJzSYl4YAEOQIt6O8V1Q==" + }, + "has-flag": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-2.0.0.tgz", + "integrity": "sha1-6CB68cx7MNRGzHC3NLXovhj4jVE=" + }, + "he": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/he/-/he-1.1.1.tgz", + "integrity": "sha1-k0EP0hsAlzUVH4howvJx80J+I/0=" + }, + "ieee754": { + "version": "1.1.8", + "resolved": "https://registry.npmjs.org/ieee754/-/ieee754-1.1.8.tgz", + "integrity": "sha1-vjPUCsEO8ZJnAfbwii2G+/0a0+Q=" + }, + "inflight": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", + "integrity": "sha1-Sb1jMdfQLQwJvJEKEHW6gWW1bfk=", + "requires": { + "once": "1.4.0", + "wrappy": "1.0.2" + } + }, + "inherits": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.3.tgz", + "integrity": "sha1-Yzwsg+PaQqUC9SRmAiSA9CCCYd4=" + }, + "jmespath": { + "version": "0.15.0", + "resolved": "https://registry.npmjs.org/jmespath/-/jmespath-0.15.0.tgz", + "integrity": "sha1-o/Iiqarp+Wb10nx5ZRDigJF2Qhc=" + }, + "lodash": { + "version": "4.17.4", + "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.4.tgz", + "integrity": "sha1-eCA6TRwyiuHYbcpkYONptX9AVa4=" + }, + "memory-cache": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/memory-cache/-/memory-cache-0.2.0.tgz", + "integrity": "sha1-eJCwHVLADI68nVM+H46xfjA0hxo=" + }, + "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.11" + } + }, + "minimist": { + "version": "0.0.8", + "resolved": "https://registry.npmjs.org/minimist/-/minimist-0.0.8.tgz", + "integrity": "sha1-hX/Kv8M5fSYluCKCYuhqp6ARsF0=" + }, + "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" + } + }, + "mocha": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/mocha/-/mocha-5.1.1.tgz", + "integrity": "sha512-kKKs/H1KrMMQIEsWNxGmb4/BGsmj0dkeyotEvbrAuQ01FcWRLssUNXCEUZk6SZtyJBi6EE7SL0zDDtItw1rGhw==", + "requires": { + "browser-stdout": "1.3.1", + "commander": "2.11.0", + "debug": "3.1.0", + "diff": "3.5.0", + "escape-string-regexp": "1.0.5", + "glob": "7.1.2", + "growl": "1.10.3", + "he": "1.1.1", + "minimatch": "3.0.4", + "mkdirp": "0.5.1", + "supports-color": "4.4.0" + }, + "dependencies": { + "debug": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/debug/-/debug-3.1.0.tgz", + "integrity": "sha512-OX8XqP7/1a9cqkxYw2yXss15f26NKWBpDXQd0/uK/KPqdQhxbPa994hnzjcE2VqQpDslf55723cKPUOGSmMY3g==", + "requires": { + "ms": "2.0.0" + } + } + } + }, + "moment": { + "version": "2.22.1", + "resolved": "https://registry.npmjs.org/moment/-/moment-2.22.1.tgz", + "integrity": "sha512-shJkRTSebXvsVqk56I+lkb2latjBs8I+pc2TzWc545y2iFnSjm7Wg0QMh+ZWcdSLQyGEau5jI8ocnmkyTgr9YQ==" + }, + "morgan": { + "version": "1.5.3", + "resolved": "https://registry.npmjs.org/morgan/-/morgan-1.5.3.tgz", + "integrity": "sha1-ittOcvnlxUNuXZP0KRCDX3nan98=", + "requires": { + "basic-auth": "1.0.3", + "debug": "2.2.0", + "depd": "1.0.1", + "on-finished": "2.2.1" + }, + "dependencies": { + "basic-auth": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/basic-auth/-/basic-auth-1.0.3.tgz", + "integrity": "sha1-QfVVI+WJQFA47jVnlYxipe1wVRo=" + }, + "depd": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/depd/-/depd-1.0.1.tgz", + "integrity": "sha1-gK7GTJ1tl+ZcwqnKqTwKpqv3Oqo=" + }, + "on-finished": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/on-finished/-/on-finished-2.2.1.tgz", + "integrity": "sha1-XIXBzDYpn3gCllP2Z/J7a5nrwCk=", + "requires": { + "ee-first": "1.1.0" + }, + "dependencies": { + "ee-first": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/ee-first/-/ee-first-1.1.0.tgz", + "integrity": "sha1-ag18YiHkkP7v2S7D9EHJzozQl/Q=" + } + } + } + } + }, + "ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=" + }, + "once": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", + "integrity": "sha1-WDsap3WWHUsROsF9nFC6753Xa9E=", + "requires": { + "wrappy": "1.0.2" + } + }, + "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=" + }, + "pathval": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/pathval/-/pathval-1.1.0.tgz", + "integrity": "sha1-uULm1L3mUwBe9rcTYd74cn0GReA=" + }, + "promise": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/promise/-/promise-6.1.0.tgz", + "integrity": "sha1-LOcp9rlLRcJoka0GAsXJDgTG7vY=", + "requires": { + "asap": "1.0.0" + } + }, + "punycode": { + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/punycode/-/punycode-1.3.2.tgz", + "integrity": "sha1-llOgNvt8HuQjQvIyXM7v6jkmxI0=" + }, + "querystring": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/querystring/-/querystring-0.2.0.tgz", + "integrity": "sha1-sgmEkgO7Jd+CDadW50cAWHhSFiA=" + }, + "sax": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/sax/-/sax-1.2.1.tgz", + "integrity": "sha1-e45lYZCyKOgaZq6nSEgNgozS03o=" + }, + "serve-favicon": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/serve-favicon/-/serve-favicon-2.2.1.tgz", + "integrity": "sha1-2XuswVD2b+DlzEx4qEuhW65aWEo=", + "requires": { + "etag": "1.6.0", + "fresh": "0.2.4", + "ms": "0.7.1", + "parseurl": "1.3.0" + }, + "dependencies": { + "etag": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/etag/-/etag-1.6.0.tgz", + "integrity": "sha1-i8ssavElTEgd/IuZfJBu9ORCwgc=", + "requires": { + "crc": "3.2.1" + }, + "dependencies": { + "crc": { + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/crc/-/crc-3.2.1.tgz", + "integrity": "sha1-XZyPt3okXNXsopHl0tAFM0urAII=" + } + } + }, + "fresh": { + "version": "0.2.4", + "resolved": "https://registry.npmjs.org/fresh/-/fresh-0.2.4.tgz", + "integrity": "sha1-NYJJkgbJcjcUGQ7ddLRgT+tKYUw=" + }, + "ms": { + "version": "0.7.1", + "resolved": "https://registry.npmjs.org/ms/-/ms-0.7.1.tgz", + "integrity": "sha1-nNE8A62/8ltl7/3nzoZO6VIBcJg=" + }, + "parseurl": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/parseurl/-/parseurl-1.3.0.tgz", + "integrity": "sha1-tYBG20Ij4UWvp2AJ5hush8wigbM=" + } + } + }, + "sleep": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/sleep/-/sleep-3.0.1.tgz", + "integrity": "sha1-vk0XxXk2DgfgTtgXK6KxCmkFTfM=", + "requires": { + "nan": "2.4.0" + }, + "dependencies": { + "nan": { + "version": "2.4.0", + "resolved": "https://registry.npmjs.org/nan/-/nan-2.4.0.tgz", + "integrity": "sha1-+zxZ1F/k7/4hXwuJD4rfbrMtIjI=" + } + } + }, + "supports-color": { + "version": "4.4.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-4.4.0.tgz", + "integrity": "sha512-rKC3+DyXWgK0ZLKwmRsrkyHVZAjNkfzeehuFWdGGcqGDTZFH73+RH6S/RDAAxl9GusSjZSUWYLmT9N5pzXFOXQ==", + "requires": { + "has-flag": "2.0.0" + } + }, + "type-detect": { + "version": "4.0.8", + "resolved": "https://registry.npmjs.org/type-detect/-/type-detect-4.0.8.tgz", + "integrity": "sha512-0fr/mIH1dlO+x7TlcMy+bIDqKPsw/70tVyeHW787goQjhmqaZe10uwLujubK9q9Lg6Fiho1KUKDYz0Z7k7g5/g==" + }, + "url": { + "version": "0.10.3", + "resolved": "https://registry.npmjs.org/url/-/url-0.10.3.tgz", + "integrity": "sha1-Ah5NnHcF8hu/N9A861h2dAJ3TGQ=", + "requires": { + "punycode": "1.3.2", + "querystring": "0.2.0" + } + }, + "uuid": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-3.0.1.tgz", + "integrity": "sha1-ZUS7ot/ajBzxfmKaOjBeK7H+5sE=" + }, + "wrappy": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", + "integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8=" + }, + "xml2js": { + "version": "0.4.17", + "resolved": "https://registry.npmjs.org/xml2js/-/xml2js-0.4.17.tgz", + "integrity": "sha1-F76T6q4/O3eTWceVtBlwWogX6Gg=", + "requires": { + "sax": "1.2.1", + "xmlbuilder": "4.2.1" + } + }, + "xmlbuilder": { + "version": "4.2.1", + "resolved": "https://registry.npmjs.org/xmlbuilder/-/xmlbuilder-4.2.1.tgz", + "integrity": "sha1-qlijBBoGb5DqoWwvU4n/GfP0YaU=", + "requires": { + "lodash": "4.17.4" + } + } + } +} diff --git a/package.json b/package.json index b86b3ff..0707649 100644 --- a/package.json +++ b/package.json @@ -4,23 +4,28 @@ "private": true, "scripts": { "prestart": "npm install", - "start": "node ./bin/www" + "start": "node ./bin/www", + "test": "mocha test/" }, "dependencies": { + "aws-sdk": "2.77.0", + "aws-sdk-promise": "0.0.2", + "batch-promises": "0.0.3", "body-parser": "1.12.4", "cookie-parser": "1.3.5", "debug": "2.2.0", "ejs": "2.3.1", "express": "4.12.4", + "lodash": "4.17.10", + "memory-cache": "0.2.0", + "moment": "2.22.1", "morgan": "1.5.3", "serve-favicon": "2.2.1", - "aws-sdk": "2.77.0", - "aws-sdk-promise": "0.0.2", - "sleep": "3.0.1", - "batch-promises": "0.0.3", - "memory-cache": "0.2.0" + "sleep": "3.0.1" }, "dev-dependencies": { + "chai": "4.1.2", + "mocha": "5.1.1", "nodemon": "1.3.7" } } diff --git a/public/javascripts/graph.js b/public/javascripts/graph.js index afa8ac1..69d76f8 100644 --- a/public/javascripts/graph.js +++ b/public/javascripts/graph.js @@ -131,19 +131,38 @@ const TOTAL_HEIGHT = 400; const DEFAULT_GRAPH_WIDTH = 1000; const EXPANDED_GRAPH_WIDTH = 1300; -function renderGraph(chartDivId, legendDivId, cluster, resourceTypeText, onCompletion, onError) { +function renderErrorGraph(chartDivId, errorMsg, onError) { + const graph = recreateMainGraphElement(chartDivId, DEFAULT_GRAPH_WIDTH, LEFT_MARGIN, RIGHT_MARGIN, TOTAL_HEIGHT, GRAPH_TOP_MARGIN, GRAPH_BOTTOM_MARGIN); + handleError(errorMsg, graph, onError); + return graph; +} + +function errorResponseText(apiResponseError) { + const errorMsg = apiResponseError instanceof XMLHttpRequest + ? apiResponseError.responseText + : JSON.stringify(apiResponseError); + return `Server Error: ${errorMsg}`; +} + +function renderGraph(timestampDivId, chartDivId, legendDivId, cluster, resourceTypeText, onCompletion, onError) { if (window.apiResponseError) { - const errorMsg = "Server Error: " + (window.apiResponseError instanceof XMLHttpRequest ? window.apiResponseError.responseText : JSON.stringify(window.apiResponseError)); - const graph = recreateMainGraphElement(chartDivId, DEFAULT_GRAPH_WIDTH, LEFT_MARGIN, RIGHT_MARGIN, TOTAL_HEIGHT, GRAPH_TOP_MARGIN, GRAPH_BOTTOM_MARGIN); - handleError(errorMsg, graph, onError); - return graph; + const apiResponseError = window.apiResponseError; + const errorMsg = `Server Error: ${errorResponseText(apiResponseError)}`; + return renderErrorGraph(chartDivId, errorMsg, onError); + } else if (window.apiResponseData == null || window.apiResponseData.instanceSummaries === null) { + const errorMsg = "Response from server contains no data."; + return renderErrorGraph(chartDivId, errorMsg, onError); } const showTaskBreakdown = true; // TODO: Parameterise + const instanceSummaries = window.apiResponseData.instanceSummaries; + const createTimestamp = window.apiResponseData.createTimestamp; + const localizedClusterCacheTimestamp = new Date(Date.parse(createTimestamp)); + try { const resourceType = parseResourceType(resourceTypeText, ResourceEnum.MEMORY); - const graphWidth = window.apiResponseError ? DEFAULT_GRAPH_WIDTH : (window.apiResponseData.length > 50 ? EXPANDED_GRAPH_WIDTH : DEFAULT_GRAPH_WIDTH) - LEFT_MARGIN - RIGHT_MARGIN; //establishes width based on data set size + const graphWidth = window.apiResponseError ? DEFAULT_GRAPH_WIDTH : (instanceSummaries.length > 50 ? EXPANDED_GRAPH_WIDTH : DEFAULT_GRAPH_WIDTH) - LEFT_MARGIN - RIGHT_MARGIN; //establishes width based on data set size const colorRange = d3.scale.ordinal().range(colorbrewer.Pastel1[9].concat(colorbrewer.Pastel2[8]).concat(colorbrewer.Set1[9]).concat(colorbrewer.Set2[8]).concat(colorbrewer.Set3[12])); const xRange = d3.scale.ordinal().rangeRoundBands([10, graphWidth], .1); const yRange = d3.scale.linear().rangeRound([GRAPH_HEIGHT, 0]); @@ -153,13 +172,19 @@ function renderGraph(chartDivId, legendDivId, cluster, resourceTypeText, onCompl // Main graph area const graph = recreateMainGraphElement(chartDivId, graphWidth, LEFT_MARGIN, RIGHT_MARGIN, TOTAL_HEIGHT, GRAPH_TOP_MARGIN, GRAPH_BOTTOM_MARGIN); - if (window.apiResponseData.length == 0) { + if (instanceSummaries.length == 0) { showInfo(graph, "No instances are registered for the '" + cluster + "' cluster."); onCompletion(); return graph; } - let uniqueTaskDefs = window.apiResponseData.reduce(function (acc, current) { + // TODO: Move this to footer + graph.append("g") + .attr("class", "fetch-timestamp") + .append("text") + .text(localizedClusterCacheTimestamp ? `Fetched: ${localizedClusterCacheTimestamp}` : "No fetch timestamp available"); + + let uniqueTaskDefs = instanceSummaries.reduce(function (acc, current) { return acc.concat(current.tasks.map(function (t) { return taskFamilyAndRevision(t); })) @@ -171,7 +196,7 @@ function renderGraph(chartDivId, legendDivId, cluster, resourceTypeText, onCompl colorRange.domain(uniqueTaskDefs); - window.apiResponseData.forEach(function (instance) { + instanceSummaries.forEach(function (instance) { // Add d3Data to each task for later display let y0 = 0; instance.tasks.forEach(function (task) { @@ -180,12 +205,12 @@ function renderGraph(chartDivId, legendDivId, cluster, resourceTypeText, onCompl }); // Set X axis ordinal domain range to be list of server names - xRange.domain(window.apiResponseData.map(function (d) { + xRange.domain(instanceSummaries.map(function (d) { return d.ec2IpAddress; })); // Calculate maximum resource (memory/cpu) across all servers - const maxResource = d3.max(window.apiResponseData, function (d) { + const maxResource = d3.max(instanceSummaries, function (d) { return registeredResource(d, resourceType); }); // Set Y axis linear domain range from 0 to maximum memory/cpu in bytes @@ -207,13 +232,13 @@ function renderGraph(chartDivId, legendDivId, cluster, resourceTypeText, onCompl { title: 'Open ECS Container Instance Console', action: function (elm, d, i) { - window.open(ecsInstanceConsoleUrl(window.apiResponseData, d), '_blank'); + window.open(ecsInstanceConsoleUrl(instanceSummaries, d), '_blank'); } }, { title: 'Open EC2 Instance Console', action: function (elm, d, i) { - window.open(ec2InstanceConsoleUrl(window.apiResponseData, d), '_blank'); + window.open(ec2InstanceConsoleUrl(instanceSummaries, d), '_blank'); } } ]; @@ -257,7 +282,7 @@ function renderGraph(chartDivId, legendDivId, cluster, resourceTypeText, onCompl // TODO: Request task data in parallel with instance data. Draw instance outline first then draw task boxes // Create svg elements for each server const instance = graph.selectAll(".instance") - .data(window.apiResponseData) + .data(instanceSummaries) .enter().append("g") .attr("class", "g") .attr("transform", function (d) { @@ -372,7 +397,58 @@ function renderGraph(chartDivId, legendDivId, cluster, resourceTypeText, onCompl } } -function populateGraph(useStaticData, chartDivId, legendDivId, cluster, resourceTypeText, onCompletion, onError) { +function calculateInterval(attemptIndex, defaultInterval) { + // For first 4 attempts, take shorter but progressively longer intervals. + // E.g. if defaultInterval = 5000 then take 1s,2s,3s,4s for first 4 attempts respectively + return attemptIndex < 5 ? attemptIndex * (defaultInterval / 5) : defaultInterval; +} + +function pollUntilFetched(c3visApiUrl, forceRefresh, attemptIndex, onFetched, onError) { + const interval = 5000; + const maxAttempts = 120; + + if (attemptIndex >= maxAttempts) { + const errorMsg = `Could not successfully retrieve cluster details from '${c3visApiUrl}' after ${maxAttempts} polling attempts.`; + onError(errorMsg); + return; + } + + const optionalForceRefreshParam = (forceRefresh ? "&forceRefresh=true" : ""); + const updatedC3visApiUrl = c3visApiUrl + optionalForceRefreshParam; + + console.log(`Polling '${updatedC3visApiUrl}' until found in 'fetched' state. Attempt #${attemptIndex}/${maxAttempts}`); + + // TODO: Upgrade to D3 v5, convert to use promises + + d3.json(updatedC3visApiUrl, function (apiResponseError, apiResponseData) { + // TODO: Display multiple graphs if server returns > 100 instances + if (apiResponseError != null) { + window.apiResponseError = apiResponseError; + console.debug(` window.apiResponseError set to '${window.apiResponseError}'`); + onError(errorResponseText(apiResponseError)); + } + if (apiResponseData != null) { + window.apiResponseData = apiResponseData; + console.debug(` window.apiResponseData contains response data for cluster '${apiResponseData.clusterName}'.`); + if (apiResponseData.errorDetails != null) { + onError(`Server Error: ${apiResponseData.errorDetails}`); + } else { + console.debug(` Found '${apiResponseData.fetchStatus}' status in response from '${c3visApiUrl}'`); + if (apiResponseData.fetchStatus === 'fetched') { + onFetched(); + } else { + console.debug(` Not yet fetched, trying again after ${calculateInterval(attemptIndex, interval)}ms`); + setTimeout(function () { + // Use forceRefresh=false to ensure server is instructed to refresh only once at start of polling loop + pollUntilFetched(c3visApiUrl, false, attemptIndex + 1, onFetched, onError) + }, calculateInterval(attemptIndex, interval)); + } + } + } + }); +} + +function populateGraph(useStaticData, forceRefresh, timestampDivId, chartDivId, legendDivId, cluster, resourceTypeText, onCompletion, onError) { try { if (!cluster && !useStaticData) { handleError("Please select a cluster.", null, onError); @@ -383,17 +459,16 @@ function populateGraph(useStaticData, chartDivId, legendDivId, cluster, resource const optionalStaticParam = (useStaticData ? "&static=true" : ""); const c3visApiUrl = "/api/instance_summaries_with_tasks?" + clusterParam + optionalStaticParam; - // after a GET all data from API begin drawing graph - d3.json(c3visApiUrl, function (apiResponseError, apiResponseData) { - // TODO: Display multiple graphs if server returns > 100 instances - - window.apiResponseError = apiResponseError; - window.apiResponseData = apiResponseData; - console.log("For debugging: window.apiResponseData, window.apiResponseError"); - - renderGraph(chartDivId, legendDivId, cluster, resourceTypeText, onCompletion, onError); - }); + window.fetchStatus = ''; + console.debug(`Requesting '${c3visApiUrl}'...`); + // TODO: Timeout after 10mins + pollUntilFetched(c3visApiUrl, forceRefresh, 1, function() { + renderGraph(timestampDivId, chartDivId, legendDivId, cluster, resourceTypeText, onCompletion, onError); + }, function(e) { + renderErrorGraph(chartDivId, `${e.message || JSON.stringify(e)}`, onError); + }); } catch (e) { - handleError("ERROR. Uncaught Exception: " + e, null, onError); + console.error(e.stack); + renderErrorGraph(chartDivId, `ERROR. Uncaught Exception: ${e}`, onError); } } diff --git a/public/javascripts/thirdparty/bootstrap-3.3.7.min.js b/public/javascripts/thirdparty/bootstrap-3.3.7.min.js new file mode 100644 index 0000000..9bcd2fc --- /dev/null +++ b/public/javascripts/thirdparty/bootstrap-3.3.7.min.js @@ -0,0 +1,7 @@ +/*! + * Bootstrap v3.3.7 (http://getbootstrap.com) + * Copyright 2011-2016 Twitter, Inc. + * Licensed under the MIT license + */ +if("undefined"==typeof jQuery)throw new Error("Bootstrap's JavaScript requires jQuery");+function(a){"use strict";var b=a.fn.jquery.split(" ")[0].split(".");if(b[0]<2&&b[1]<9||1==b[0]&&9==b[1]&&b[2]<1||b[0]>3)throw new Error("Bootstrap's JavaScript requires jQuery version 1.9.1 or higher, but lower than version 4")}(jQuery),+function(a){"use strict";function b(){var a=document.createElement("bootstrap"),b={WebkitTransition:"webkitTransitionEnd",MozTransition:"transitionend",OTransition:"oTransitionEnd otransitionend",transition:"transitionend"};for(var c in b)if(void 0!==a.style[c])return{end:b[c]};return!1}a.fn.emulateTransitionEnd=function(b){var c=!1,d=this;a(this).one("bsTransitionEnd",function(){c=!0});var e=function(){c||a(d).trigger(a.support.transition.end)};return setTimeout(e,b),this},a(function(){a.support.transition=b(),a.support.transition&&(a.event.special.bsTransitionEnd={bindType:a.support.transition.end,delegateType:a.support.transition.end,handle:function(b){if(a(b.target).is(this))return b.handleObj.handler.apply(this,arguments)}})})}(jQuery),+function(a){"use strict";function b(b){return this.each(function(){var c=a(this),e=c.data("bs.alert");e||c.data("bs.alert",e=new d(this)),"string"==typeof b&&e[b].call(c)})}var c='[data-dismiss="alert"]',d=function(b){a(b).on("click",c,this.close)};d.VERSION="3.3.7",d.TRANSITION_DURATION=150,d.prototype.close=function(b){function c(){g.detach().trigger("closed.bs.alert").remove()}var e=a(this),f=e.attr("data-target");f||(f=e.attr("href"),f=f&&f.replace(/.*(?=#[^\s]*$)/,""));var g=a("#"===f?[]:f);b&&b.preventDefault(),g.length||(g=e.closest(".alert")),g.trigger(b=a.Event("close.bs.alert")),b.isDefaultPrevented()||(g.removeClass("in"),a.support.transition&&g.hasClass("fade")?g.one("bsTransitionEnd",c).emulateTransitionEnd(d.TRANSITION_DURATION):c())};var e=a.fn.alert;a.fn.alert=b,a.fn.alert.Constructor=d,a.fn.alert.noConflict=function(){return a.fn.alert=e,this},a(document).on("click.bs.alert.data-api",c,d.prototype.close)}(jQuery),+function(a){"use strict";function b(b){return this.each(function(){var d=a(this),e=d.data("bs.button"),f="object"==typeof b&&b;e||d.data("bs.button",e=new c(this,f)),"toggle"==b?e.toggle():b&&e.setState(b)})}var c=function(b,d){this.$element=a(b),this.options=a.extend({},c.DEFAULTS,d),this.isLoading=!1};c.VERSION="3.3.7",c.DEFAULTS={loadingText:"loading..."},c.prototype.setState=function(b){var c="disabled",d=this.$element,e=d.is("input")?"val":"html",f=d.data();b+="Text",null==f.resetText&&d.data("resetText",d[e]()),setTimeout(a.proxy(function(){d[e](null==f[b]?this.options[b]:f[b]),"loadingText"==b?(this.isLoading=!0,d.addClass(c).attr(c,c).prop(c,!0)):this.isLoading&&(this.isLoading=!1,d.removeClass(c).removeAttr(c).prop(c,!1))},this),0)},c.prototype.toggle=function(){var a=!0,b=this.$element.closest('[data-toggle="buttons"]');if(b.length){var c=this.$element.find("input");"radio"==c.prop("type")?(c.prop("checked")&&(a=!1),b.find(".active").removeClass("active"),this.$element.addClass("active")):"checkbox"==c.prop("type")&&(c.prop("checked")!==this.$element.hasClass("active")&&(a=!1),this.$element.toggleClass("active")),c.prop("checked",this.$element.hasClass("active")),a&&c.trigger("change")}else this.$element.attr("aria-pressed",!this.$element.hasClass("active")),this.$element.toggleClass("active")};var d=a.fn.button;a.fn.button=b,a.fn.button.Constructor=c,a.fn.button.noConflict=function(){return a.fn.button=d,this},a(document).on("click.bs.button.data-api",'[data-toggle^="button"]',function(c){var d=a(c.target).closest(".btn");b.call(d,"toggle"),a(c.target).is('input[type="radio"], input[type="checkbox"]')||(c.preventDefault(),d.is("input,button")?d.trigger("focus"):d.find("input:visible,button:visible").first().trigger("focus"))}).on("focus.bs.button.data-api blur.bs.button.data-api",'[data-toggle^="button"]',function(b){a(b.target).closest(".btn").toggleClass("focus",/^focus(in)?$/.test(b.type))})}(jQuery),+function(a){"use strict";function b(b){return this.each(function(){var d=a(this),e=d.data("bs.carousel"),f=a.extend({},c.DEFAULTS,d.data(),"object"==typeof b&&b),g="string"==typeof b?b:f.slide;e||d.data("bs.carousel",e=new c(this,f)),"number"==typeof b?e.to(b):g?e[g]():f.interval&&e.pause().cycle()})}var c=function(b,c){this.$element=a(b),this.$indicators=this.$element.find(".carousel-indicators"),this.options=c,this.paused=null,this.sliding=null,this.interval=null,this.$active=null,this.$items=null,this.options.keyboard&&this.$element.on("keydown.bs.carousel",a.proxy(this.keydown,this)),"hover"==this.options.pause&&!("ontouchstart"in document.documentElement)&&this.$element.on("mouseenter.bs.carousel",a.proxy(this.pause,this)).on("mouseleave.bs.carousel",a.proxy(this.cycle,this))};c.VERSION="3.3.7",c.TRANSITION_DURATION=600,c.DEFAULTS={interval:5e3,pause:"hover",wrap:!0,keyboard:!0},c.prototype.keydown=function(a){if(!/input|textarea/i.test(a.target.tagName)){switch(a.which){case 37:this.prev();break;case 39:this.next();break;default:return}a.preventDefault()}},c.prototype.cycle=function(b){return b||(this.paused=!1),this.interval&&clearInterval(this.interval),this.options.interval&&!this.paused&&(this.interval=setInterval(a.proxy(this.next,this),this.options.interval)),this},c.prototype.getItemIndex=function(a){return this.$items=a.parent().children(".item"),this.$items.index(a||this.$active)},c.prototype.getItemForDirection=function(a,b){var c=this.getItemIndex(b),d="prev"==a&&0===c||"next"==a&&c==this.$items.length-1;if(d&&!this.options.wrap)return b;var e="prev"==a?-1:1,f=(c+e)%this.$items.length;return this.$items.eq(f)},c.prototype.to=function(a){var b=this,c=this.getItemIndex(this.$active=this.$element.find(".item.active"));if(!(a>this.$items.length-1||a<0))return this.sliding?this.$element.one("slid.bs.carousel",function(){b.to(a)}):c==a?this.pause().cycle():this.slide(a>c?"next":"prev",this.$items.eq(a))},c.prototype.pause=function(b){return b||(this.paused=!0),this.$element.find(".next, .prev").length&&a.support.transition&&(this.$element.trigger(a.support.transition.end),this.cycle(!0)),this.interval=clearInterval(this.interval),this},c.prototype.next=function(){if(!this.sliding)return this.slide("next")},c.prototype.prev=function(){if(!this.sliding)return this.slide("prev")},c.prototype.slide=function(b,d){var e=this.$element.find(".item.active"),f=d||this.getItemForDirection(b,e),g=this.interval,h="next"==b?"left":"right",i=this;if(f.hasClass("active"))return this.sliding=!1;var j=f[0],k=a.Event("slide.bs.carousel",{relatedTarget:j,direction:h});if(this.$element.trigger(k),!k.isDefaultPrevented()){if(this.sliding=!0,g&&this.pause(),this.$indicators.length){this.$indicators.find(".active").removeClass("active");var l=a(this.$indicators.children()[this.getItemIndex(f)]);l&&l.addClass("active")}var m=a.Event("slid.bs.carousel",{relatedTarget:j,direction:h});return a.support.transition&&this.$element.hasClass("slide")?(f.addClass(b),f[0].offsetWidth,e.addClass(h),f.addClass(h),e.one("bsTransitionEnd",function(){f.removeClass([b,h].join(" ")).addClass("active"),e.removeClass(["active",h].join(" ")),i.sliding=!1,setTimeout(function(){i.$element.trigger(m)},0)}).emulateTransitionEnd(c.TRANSITION_DURATION)):(e.removeClass("active"),f.addClass("active"),this.sliding=!1,this.$element.trigger(m)),g&&this.cycle(),this}};var d=a.fn.carousel;a.fn.carousel=b,a.fn.carousel.Constructor=c,a.fn.carousel.noConflict=function(){return a.fn.carousel=d,this};var e=function(c){var d,e=a(this),f=a(e.attr("data-target")||(d=e.attr("href"))&&d.replace(/.*(?=#[^\s]+$)/,""));if(f.hasClass("carousel")){var g=a.extend({},f.data(),e.data()),h=e.attr("data-slide-to");h&&(g.interval=!1),b.call(f,g),h&&f.data("bs.carousel").to(h),c.preventDefault()}};a(document).on("click.bs.carousel.data-api","[data-slide]",e).on("click.bs.carousel.data-api","[data-slide-to]",e),a(window).on("load",function(){a('[data-ride="carousel"]').each(function(){var c=a(this);b.call(c,c.data())})})}(jQuery),+function(a){"use strict";function b(b){var c,d=b.attr("data-target")||(c=b.attr("href"))&&c.replace(/.*(?=#[^\s]+$)/,"");return a(d)}function c(b){return this.each(function(){var c=a(this),e=c.data("bs.collapse"),f=a.extend({},d.DEFAULTS,c.data(),"object"==typeof b&&b);!e&&f.toggle&&/show|hide/.test(b)&&(f.toggle=!1),e||c.data("bs.collapse",e=new d(this,f)),"string"==typeof b&&e[b]()})}var d=function(b,c){this.$element=a(b),this.options=a.extend({},d.DEFAULTS,c),this.$trigger=a('[data-toggle="collapse"][href="#'+b.id+'"],[data-toggle="collapse"][data-target="#'+b.id+'"]'),this.transitioning=null,this.options.parent?this.$parent=this.getParent():this.addAriaAndCollapsedClass(this.$element,this.$trigger),this.options.toggle&&this.toggle()};d.VERSION="3.3.7",d.TRANSITION_DURATION=350,d.DEFAULTS={toggle:!0},d.prototype.dimension=function(){var a=this.$element.hasClass("width");return a?"width":"height"},d.prototype.show=function(){if(!this.transitioning&&!this.$element.hasClass("in")){var b,e=this.$parent&&this.$parent.children(".panel").children(".in, .collapsing");if(!(e&&e.length&&(b=e.data("bs.collapse"),b&&b.transitioning))){var f=a.Event("show.bs.collapse");if(this.$element.trigger(f),!f.isDefaultPrevented()){e&&e.length&&(c.call(e,"hide"),b||e.data("bs.collapse",null));var g=this.dimension();this.$element.removeClass("collapse").addClass("collapsing")[g](0).attr("aria-expanded",!0),this.$trigger.removeClass("collapsed").attr("aria-expanded",!0),this.transitioning=1;var h=function(){this.$element.removeClass("collapsing").addClass("collapse in")[g](""),this.transitioning=0,this.$element.trigger("shown.bs.collapse")};if(!a.support.transition)return h.call(this);var i=a.camelCase(["scroll",g].join("-"));this.$element.one("bsTransitionEnd",a.proxy(h,this)).emulateTransitionEnd(d.TRANSITION_DURATION)[g](this.$element[0][i])}}}},d.prototype.hide=function(){if(!this.transitioning&&this.$element.hasClass("in")){var b=a.Event("hide.bs.collapse");if(this.$element.trigger(b),!b.isDefaultPrevented()){var c=this.dimension();this.$element[c](this.$element[c]())[0].offsetHeight,this.$element.addClass("collapsing").removeClass("collapse in").attr("aria-expanded",!1),this.$trigger.addClass("collapsed").attr("aria-expanded",!1),this.transitioning=1;var e=function(){this.transitioning=0,this.$element.removeClass("collapsing").addClass("collapse").trigger("hidden.bs.collapse")};return a.support.transition?void this.$element[c](0).one("bsTransitionEnd",a.proxy(e,this)).emulateTransitionEnd(d.TRANSITION_DURATION):e.call(this)}}},d.prototype.toggle=function(){this[this.$element.hasClass("in")?"hide":"show"]()},d.prototype.getParent=function(){return a(this.options.parent).find('[data-toggle="collapse"][data-parent="'+this.options.parent+'"]').each(a.proxy(function(c,d){var e=a(d);this.addAriaAndCollapsedClass(b(e),e)},this)).end()},d.prototype.addAriaAndCollapsedClass=function(a,b){var c=a.hasClass("in");a.attr("aria-expanded",c),b.toggleClass("collapsed",!c).attr("aria-expanded",c)};var e=a.fn.collapse;a.fn.collapse=c,a.fn.collapse.Constructor=d,a.fn.collapse.noConflict=function(){return a.fn.collapse=e,this},a(document).on("click.bs.collapse.data-api",'[data-toggle="collapse"]',function(d){var e=a(this);e.attr("data-target")||d.preventDefault();var f=b(e),g=f.data("bs.collapse"),h=g?"toggle":e.data();c.call(f,h)})}(jQuery),+function(a){"use strict";function b(b){var c=b.attr("data-target");c||(c=b.attr("href"),c=c&&/#[A-Za-z]/.test(c)&&c.replace(/.*(?=#[^\s]*$)/,""));var d=c&&a(c);return d&&d.length?d:b.parent()}function c(c){c&&3===c.which||(a(e).remove(),a(f).each(function(){var d=a(this),e=b(d),f={relatedTarget:this};e.hasClass("open")&&(c&&"click"==c.type&&/input|textarea/i.test(c.target.tagName)&&a.contains(e[0],c.target)||(e.trigger(c=a.Event("hide.bs.dropdown",f)),c.isDefaultPrevented()||(d.attr("aria-expanded","false"),e.removeClass("open").trigger(a.Event("hidden.bs.dropdown",f)))))}))}function d(b){return this.each(function(){var c=a(this),d=c.data("bs.dropdown");d||c.data("bs.dropdown",d=new g(this)),"string"==typeof b&&d[b].call(c)})}var e=".dropdown-backdrop",f='[data-toggle="dropdown"]',g=function(b){a(b).on("click.bs.dropdown",this.toggle)};g.VERSION="3.3.7",g.prototype.toggle=function(d){var e=a(this);if(!e.is(".disabled, :disabled")){var f=b(e),g=f.hasClass("open");if(c(),!g){"ontouchstart"in document.documentElement&&!f.closest(".navbar-nav").length&&a(document.createElement("div")).addClass("dropdown-backdrop").insertAfter(a(this)).on("click",c);var h={relatedTarget:this};if(f.trigger(d=a.Event("show.bs.dropdown",h)),d.isDefaultPrevented())return;e.trigger("focus").attr("aria-expanded","true"),f.toggleClass("open").trigger(a.Event("shown.bs.dropdown",h))}return!1}},g.prototype.keydown=function(c){if(/(38|40|27|32)/.test(c.which)&&!/input|textarea/i.test(c.target.tagName)){var d=a(this);if(c.preventDefault(),c.stopPropagation(),!d.is(".disabled, :disabled")){var e=b(d),g=e.hasClass("open");if(!g&&27!=c.which||g&&27==c.which)return 27==c.which&&e.find(f).trigger("focus"),d.trigger("click");var h=" li:not(.disabled):visible a",i=e.find(".dropdown-menu"+h);if(i.length){var j=i.index(c.target);38==c.which&&j>0&&j--,40==c.which&&jdocument.documentElement.clientHeight;this.$element.css({paddingLeft:!this.bodyIsOverflowing&&a?this.scrollbarWidth:"",paddingRight:this.bodyIsOverflowing&&!a?this.scrollbarWidth:""})},c.prototype.resetAdjustments=function(){this.$element.css({paddingLeft:"",paddingRight:""})},c.prototype.checkScrollbar=function(){var a=window.innerWidth;if(!a){var b=document.documentElement.getBoundingClientRect();a=b.right-Math.abs(b.left)}this.bodyIsOverflowing=document.body.clientWidth
',trigger:"hover focus",title:"",delay:0,html:!1,container:!1,viewport:{selector:"body",padding:0}},c.prototype.init=function(b,c,d){if(this.enabled=!0,this.type=b,this.$element=a(c),this.options=this.getOptions(d),this.$viewport=this.options.viewport&&a(a.isFunction(this.options.viewport)?this.options.viewport.call(this,this.$element):this.options.viewport.selector||this.options.viewport),this.inState={click:!1,hover:!1,focus:!1},this.$element[0]instanceof document.constructor&&!this.options.selector)throw new Error("`selector` option must be specified when initializing "+this.type+" on the window.document object!");for(var e=this.options.trigger.split(" "),f=e.length;f--;){var g=e[f];if("click"==g)this.$element.on("click."+this.type,this.options.selector,a.proxy(this.toggle,this));else if("manual"!=g){var h="hover"==g?"mouseenter":"focusin",i="hover"==g?"mouseleave":"focusout";this.$element.on(h+"."+this.type,this.options.selector,a.proxy(this.enter,this)),this.$element.on(i+"."+this.type,this.options.selector,a.proxy(this.leave,this))}}this.options.selector?this._options=a.extend({},this.options,{trigger:"manual",selector:""}):this.fixTitle()},c.prototype.getDefaults=function(){return c.DEFAULTS},c.prototype.getOptions=function(b){return b=a.extend({},this.getDefaults(),this.$element.data(),b),b.delay&&"number"==typeof b.delay&&(b.delay={show:b.delay,hide:b.delay}),b},c.prototype.getDelegateOptions=function(){var b={},c=this.getDefaults();return this._options&&a.each(this._options,function(a,d){c[a]!=d&&(b[a]=d)}),b},c.prototype.enter=function(b){var c=b instanceof this.constructor?b:a(b.currentTarget).data("bs."+this.type);return c||(c=new this.constructor(b.currentTarget,this.getDelegateOptions()),a(b.currentTarget).data("bs."+this.type,c)),b instanceof a.Event&&(c.inState["focusin"==b.type?"focus":"hover"]=!0),c.tip().hasClass("in")||"in"==c.hoverState?void(c.hoverState="in"):(clearTimeout(c.timeout),c.hoverState="in",c.options.delay&&c.options.delay.show?void(c.timeout=setTimeout(function(){"in"==c.hoverState&&c.show()},c.options.delay.show)):c.show())},c.prototype.isInStateTrue=function(){for(var a in this.inState)if(this.inState[a])return!0;return!1},c.prototype.leave=function(b){var c=b instanceof this.constructor?b:a(b.currentTarget).data("bs."+this.type);if(c||(c=new this.constructor(b.currentTarget,this.getDelegateOptions()),a(b.currentTarget).data("bs."+this.type,c)),b instanceof a.Event&&(c.inState["focusout"==b.type?"focus":"hover"]=!1),!c.isInStateTrue())return clearTimeout(c.timeout),c.hoverState="out",c.options.delay&&c.options.delay.hide?void(c.timeout=setTimeout(function(){"out"==c.hoverState&&c.hide()},c.options.delay.hide)):c.hide()},c.prototype.show=function(){var b=a.Event("show.bs."+this.type);if(this.hasContent()&&this.enabled){this.$element.trigger(b);var d=a.contains(this.$element[0].ownerDocument.documentElement,this.$element[0]);if(b.isDefaultPrevented()||!d)return;var e=this,f=this.tip(),g=this.getUID(this.type);this.setContent(),f.attr("id",g),this.$element.attr("aria-describedby",g),this.options.animation&&f.addClass("fade");var h="function"==typeof this.options.placement?this.options.placement.call(this,f[0],this.$element[0]):this.options.placement,i=/\s?auto?\s?/i,j=i.test(h);j&&(h=h.replace(i,"")||"top"),f.detach().css({top:0,left:0,display:"block"}).addClass(h).data("bs."+this.type,this),this.options.container?f.appendTo(this.options.container):f.insertAfter(this.$element),this.$element.trigger("inserted.bs."+this.type);var k=this.getPosition(),l=f[0].offsetWidth,m=f[0].offsetHeight;if(j){var n=h,o=this.getPosition(this.$viewport);h="bottom"==h&&k.bottom+m>o.bottom?"top":"top"==h&&k.top-mo.width?"left":"left"==h&&k.left-lg.top+g.height&&(e.top=g.top+g.height-i)}else{var j=b.left-f,k=b.left+f+c;jg.right&&(e.left=g.left+g.width-k)}return e},c.prototype.getTitle=function(){var a,b=this.$element,c=this.options;return a=b.attr("data-original-title")||("function"==typeof c.title?c.title.call(b[0]):c.title)},c.prototype.getUID=function(a){do a+=~~(1e6*Math.random());while(document.getElementById(a));return a},c.prototype.tip=function(){if(!this.$tip&&(this.$tip=a(this.options.template),1!=this.$tip.length))throw new Error(this.type+" `template` option must consist of exactly 1 top-level element!");return this.$tip},c.prototype.arrow=function(){return this.$arrow=this.$arrow||this.tip().find(".tooltip-arrow")},c.prototype.enable=function(){this.enabled=!0},c.prototype.disable=function(){this.enabled=!1},c.prototype.toggleEnabled=function(){this.enabled=!this.enabled},c.prototype.toggle=function(b){var c=this;b&&(c=a(b.currentTarget).data("bs."+this.type),c||(c=new this.constructor(b.currentTarget,this.getDelegateOptions()),a(b.currentTarget).data("bs."+this.type,c))),b?(c.inState.click=!c.inState.click,c.isInStateTrue()?c.enter(c):c.leave(c)):c.tip().hasClass("in")?c.leave(c):c.enter(c)},c.prototype.destroy=function(){var a=this;clearTimeout(this.timeout),this.hide(function(){a.$element.off("."+a.type).removeData("bs."+a.type),a.$tip&&a.$tip.detach(),a.$tip=null,a.$arrow=null,a.$viewport=null,a.$element=null})};var d=a.fn.tooltip;a.fn.tooltip=b,a.fn.tooltip.Constructor=c,a.fn.tooltip.noConflict=function(){return a.fn.tooltip=d,this}}(jQuery),+function(a){"use strict";function b(b){return this.each(function(){var d=a(this),e=d.data("bs.popover"),f="object"==typeof b&&b;!e&&/destroy|hide/.test(b)||(e||d.data("bs.popover",e=new c(this,f)),"string"==typeof b&&e[b]())})}var c=function(a,b){this.init("popover",a,b)};if(!a.fn.tooltip)throw new Error("Popover requires tooltip.js");c.VERSION="3.3.7",c.DEFAULTS=a.extend({},a.fn.tooltip.Constructor.DEFAULTS,{placement:"right",trigger:"click",content:"",template:''}),c.prototype=a.extend({},a.fn.tooltip.Constructor.prototype),c.prototype.constructor=c,c.prototype.getDefaults=function(){return c.DEFAULTS},c.prototype.setContent=function(){var a=this.tip(),b=this.getTitle(),c=this.getContent();a.find(".popover-title")[this.options.html?"html":"text"](b),a.find(".popover-content").children().detach().end()[this.options.html?"string"==typeof c?"html":"append":"text"](c),a.removeClass("fade top bottom left right in"),a.find(".popover-title").html()||a.find(".popover-title").hide()},c.prototype.hasContent=function(){return this.getTitle()||this.getContent()},c.prototype.getContent=function(){var a=this.$element,b=this.options;return a.attr("data-content")||("function"==typeof b.content?b.content.call(a[0]):b.content)},c.prototype.arrow=function(){return this.$arrow=this.$arrow||this.tip().find(".arrow")};var d=a.fn.popover;a.fn.popover=b,a.fn.popover.Constructor=c,a.fn.popover.noConflict=function(){return a.fn.popover=d,this}}(jQuery),+function(a){"use strict";function b(c,d){this.$body=a(document.body),this.$scrollElement=a(a(c).is(document.body)?window:c),this.options=a.extend({},b.DEFAULTS,d),this.selector=(this.options.target||"")+" .nav li > a",this.offsets=[],this.targets=[],this.activeTarget=null,this.scrollHeight=0,this.$scrollElement.on("scroll.bs.scrollspy",a.proxy(this.process,this)),this.refresh(),this.process()}function c(c){return this.each(function(){var d=a(this),e=d.data("bs.scrollspy"),f="object"==typeof c&&c;e||d.data("bs.scrollspy",e=new b(this,f)),"string"==typeof c&&e[c]()})}b.VERSION="3.3.7",b.DEFAULTS={offset:10},b.prototype.getScrollHeight=function(){return this.$scrollElement[0].scrollHeight||Math.max(this.$body[0].scrollHeight,document.documentElement.scrollHeight)},b.prototype.refresh=function(){var b=this,c="offset",d=0;this.offsets=[],this.targets=[],this.scrollHeight=this.getScrollHeight(),a.isWindow(this.$scrollElement[0])||(c="position",d=this.$scrollElement.scrollTop()),this.$body.find(this.selector).map(function(){var b=a(this),e=b.data("target")||b.attr("href"),f=/^#./.test(e)&&a(e);return f&&f.length&&f.is(":visible")&&[[f[c]().top+d,e]]||null}).sort(function(a,b){return a[0]-b[0]}).each(function(){b.offsets.push(this[0]),b.targets.push(this[1])})},b.prototype.process=function(){var a,b=this.$scrollElement.scrollTop()+this.options.offset,c=this.getScrollHeight(),d=this.options.offset+c-this.$scrollElement.height(),e=this.offsets,f=this.targets,g=this.activeTarget;if(this.scrollHeight!=c&&this.refresh(),b>=d)return g!=(a=f[f.length-1])&&this.activate(a);if(g&&b=e[a]&&(void 0===e[a+1]||b .dropdown-menu > .active").removeClass("active").end().find('[data-toggle="tab"]').attr("aria-expanded",!1),b.addClass("active").find('[data-toggle="tab"]').attr("aria-expanded",!0),h?(b[0].offsetWidth,b.addClass("in")):b.removeClass("fade"),b.parent(".dropdown-menu").length&&b.closest("li.dropdown").addClass("active").end().find('[data-toggle="tab"]').attr("aria-expanded",!0),e&&e()}var g=d.find("> .active"),h=e&&a.support.transition&&(g.length&&g.hasClass("fade")||!!d.find("> .fade").length);g.length&&h?g.one("bsTransitionEnd",f).emulateTransitionEnd(c.TRANSITION_DURATION):f(),g.removeClass("in")};var d=a.fn.tab;a.fn.tab=b,a.fn.tab.Constructor=c,a.fn.tab.noConflict=function(){return a.fn.tab=d,this};var e=function(c){c.preventDefault(),b.call(a(this),"show")};a(document).on("click.bs.tab.data-api",'[data-toggle="tab"]',e).on("click.bs.tab.data-api",'[data-toggle="pill"]',e)}(jQuery),+function(a){"use strict";function b(b){return this.each(function(){var d=a(this),e=d.data("bs.affix"),f="object"==typeof b&&b;e||d.data("bs.affix",e=new c(this,f)),"string"==typeof b&&e[b]()})}var c=function(b,d){this.options=a.extend({},c.DEFAULTS,d),this.$target=a(this.options.target).on("scroll.bs.affix.data-api",a.proxy(this.checkPosition,this)).on("click.bs.affix.data-api",a.proxy(this.checkPositionWithEventLoop,this)),this.$element=a(b),this.affixed=null,this.unpin=null,this.pinnedOffset=null,this.checkPosition()};c.VERSION="3.3.7",c.RESET="affix affix-top affix-bottom",c.DEFAULTS={offset:0,target:window},c.prototype.getState=function(a,b,c,d){var e=this.$target.scrollTop(),f=this.$element.offset(),g=this.$target.height();if(null!=c&&"top"==this.affixed)return e=a-d&&"bottom"},c.prototype.getPinnedOffset=function(){if(this.pinnedOffset)return this.pinnedOffset;this.$element.removeClass(c.RESET).addClass("affix");var a=this.$target.scrollTop(),b=this.$element.offset();return this.pinnedOffset=b.top-a},c.prototype.checkPositionWithEventLoop=function(){setTimeout(a.proxy(this.checkPosition,this),1)},c.prototype.checkPosition=function(){if(this.$element.is(":visible")){var b=this.$element.height(),d=this.options.offset,e=d.top,f=d.bottom,g=Math.max(a(document).height(),a(document.body).height());"object"!=typeof d&&(f=e=d),"function"==typeof e&&(e=d.top(this.$element)),"function"==typeof f&&(f=d.bottom(this.$element));var h=this.getState(g,b,e,f);if(this.affixed!=h){null!=this.unpin&&this.$element.css("top","");var i="affix"+(h?"-"+h:""),j=a.Event(i+".bs.affix");if(this.$element.trigger(j),j.isDefaultPrevented())return;this.affixed=h,this.unpin="bottom"==h?this.getPinnedOffset():null,this.$element.removeClass(c.RESET).addClass(i).trigger(i.replace("affix","affixed")+".bs.affix")}"bottom"==h&&this.$element.offset({top:g-b-f})}};var d=a.fn.affix;a.fn.affix=b,a.fn.affix.Constructor=c,a.fn.affix.noConflict=function(){return a.fn.affix=d,this},a(window).on("load",function(){a('[data-spy="affix"]').each(function(){var c=a(this),d=c.data();d.offset=d.offset||{},null!=d.offsetBottom&&(d.offset.bottom=d.offsetBottom),null!=d.offsetTop&&(d.offset.top=d.offsetTop),b.call(c,d)})})}(jQuery); \ No newline at end of file diff --git a/public/javascripts/types.js b/public/javascripts/types.js index 407a8c2..59efa5c 100644 --- a/public/javascripts/types.js +++ b/public/javascripts/types.js @@ -3,7 +3,7 @@ var ResourceEnum = { CPU: 1, properties: { 0: {name: "memory", value: 0, label: "Memory"}, - 1: {name: "cpu", value: 1, label: "CPU"} + 1: {name: "cpu", value: 1, label: "CPU Units"} } }; diff --git a/public/stylesheets/fonts/glyphicons-halflings-regular.woff b/public/stylesheets/fonts/glyphicons-halflings-regular.woff new file mode 100644 index 0000000000000000000000000000000000000000..9e612858f802245ddcbf59788a0db942224bab35 GIT binary patch literal 23424 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 G`u_lkUM?>H literal 0 HcmV?d00001 diff --git a/public/stylesheets/fonts/glyphicons-halflings-regular.woff2 b/public/stylesheets/fonts/glyphicons-halflings-regular.woff2 new file mode 100644 index 0000000000000000000000000000000000000000..64539b54c3751a6d9adb44c8e3a45ba5a73b77f0 GIT binary patch literal 18028 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 literal 0 HcmV?d00001 diff --git a/public/stylesheets/thirdparty/bootstrap-3.3.7.min.css b/public/stylesheets/thirdparty/bootstrap-3.3.7.min.css new file mode 100644 index 0000000..ed3905e --- /dev/null +++ b/public/stylesheets/thirdparty/bootstrap-3.3.7.min.css @@ -0,0 +1,6 @@ +/*! + * Bootstrap v3.3.7 (http://getbootstrap.com) + * Copyright 2011-2016 Twitter, Inc. + * Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE) + *//*! normalize.css v3.0.3 | MIT License | github.com/necolas/normalize.css */html{font-family:sans-serif;-webkit-text-size-adjust:100%;-ms-text-size-adjust:100%}body{margin:0}article,aside,details,figcaption,figure,footer,header,hgroup,main,menu,nav,section,summary{display:block}audio,canvas,progress,video{display:inline-block;vertical-align:baseline}audio:not([controls]){display:none;height:0}[hidden],template{display:none}a{background-color:transparent}a:active,a:hover{outline:0}abbr[title]{border-bottom:1px dotted}b,strong{font-weight:700}dfn{font-style:italic}h1{margin:.67em 0;font-size:2em}mark{color:#000;background:#ff0}small{font-size:80%}sub,sup{position:relative;font-size:75%;line-height:0;vertical-align:baseline}sup{top:-.5em}sub{bottom:-.25em}img{border:0}svg:not(:root){overflow:hidden}figure{margin:1em 40px}hr{height:0;-webkit-box-sizing:content-box;-moz-box-sizing:content-box;box-sizing:content-box}pre{overflow:auto}code,kbd,pre,samp{font-family:monospace,monospace;font-size:1em}button,input,optgroup,select,textarea{margin:0;font:inherit;color:inherit}button{overflow:visible}button,select{text-transform:none}button,html input[type=button],input[type=reset],input[type=submit]{-webkit-appearance:button;cursor:pointer}button[disabled],html input[disabled]{cursor:default}button::-moz-focus-inner,input::-moz-focus-inner{padding:0;border:0}input{line-height:normal}input[type=checkbox],input[type=radio]{-webkit-box-sizing:border-box;-moz-box-sizing:border-box;box-sizing:border-box;padding:0}input[type=number]::-webkit-inner-spin-button,input[type=number]::-webkit-outer-spin-button{height:auto}input[type=search]{-webkit-box-sizing:content-box;-moz-box-sizing:content-box;box-sizing:content-box;-webkit-appearance:textfield}input[type=search]::-webkit-search-cancel-button,input[type=search]::-webkit-search-decoration{-webkit-appearance:none}fieldset{padding:.35em .625em .75em;margin:0 2px;border:1px solid silver}legend{padding:0;border:0}textarea{overflow:auto}optgroup{font-weight:700}table{border-spacing:0;border-collapse:collapse}td,th{padding:0}/*! Source: https://github.com/h5bp/html5-boilerplate/blob/master/src/css/main.css */@media print{*,:after,:before{color:#000!important;text-shadow:none!important;background:0 0!important;-webkit-box-shadow:none!important;box-shadow:none!important}a,a:visited{text-decoration:underline}a[href]:after{content:" (" attr(href) ")"}abbr[title]:after{content:" (" attr(title) ")"}a[href^="javascript:"]:after,a[href^="#"]:after{content:""}blockquote,pre{border:1px solid #999;page-break-inside:avoid}thead{display:table-header-group}img,tr{page-break-inside:avoid}img{max-width:100%!important}h2,h3,p{orphans:3;widows:3}h2,h3{page-break-after:avoid}.navbar{display:none}.btn>.caret,.dropup>.btn>.caret{border-top-color:#000!important}.label{border:1px solid #000}.table{border-collapse:collapse!important}.table td,.table th{background-color:#fff!important}.table-bordered td,.table-bordered th{border:1px solid #ddd!important}}@font-face{font-family:'Glyphicons Halflings';src:url(../fonts/glyphicons-halflings-regular.eot);src:url(../fonts/glyphicons-halflings-regular.eot?#iefix) format('embedded-opentype'),url(../fonts/glyphicons-halflings-regular.woff2) format('woff2'),url(../fonts/glyphicons-halflings-regular.woff) format('woff'),url(../fonts/glyphicons-halflings-regular.ttf) format('truetype'),url(../fonts/glyphicons-halflings-regular.svg#glyphicons_halflingsregular) format('svg')}.glyphicon{position:relative;top:1px;display:inline-block;font-family:'Glyphicons Halflings';font-style:normal;font-weight:400;line-height:1;-webkit-font-smoothing:antialiased;-moz-osx-font-smoothing:grayscale}.glyphicon-asterisk:before{content:"\002a"}.glyphicon-plus:before{content:"\002b"}.glyphicon-eur:before,.glyphicon-euro:before{content:"\20ac"}.glyphicon-minus:before{content:"\2212"}.glyphicon-cloud:before{content:"\2601"}.glyphicon-envelope:before{content:"\2709"}.glyphicon-pencil:before{content:"\270f"}.glyphicon-glass:before{content:"\e001"}.glyphicon-music:before{content:"\e002"}.glyphicon-search:before{content:"\e003"}.glyphicon-heart:before{content:"\e005"}.glyphicon-star:before{content:"\e006"}.glyphicon-star-empty:before{content:"\e007"}.glyphicon-user:before{content:"\e008"}.glyphicon-film:before{content:"\e009"}.glyphicon-th-large:before{content:"\e010"}.glyphicon-th:before{content:"\e011"}.glyphicon-th-list:before{content:"\e012"}.glyphicon-ok:before{content:"\e013"}.glyphicon-remove:before{content:"\e014"}.glyphicon-zoom-in:before{content:"\e015"}.glyphicon-zoom-out:before{content:"\e016"}.glyphicon-off:before{content:"\e017"}.glyphicon-signal:before{content:"\e018"}.glyphicon-cog:before{content:"\e019"}.glyphicon-trash:before{content:"\e020"}.glyphicon-home:before{content:"\e021"}.glyphicon-file:before{content:"\e022"}.glyphicon-time:before{content:"\e023"}.glyphicon-road:before{content:"\e024"}.glyphicon-download-alt:before{content:"\e025"}.glyphicon-download:before{content:"\e026"}.glyphicon-upload:before{content:"\e027"}.glyphicon-inbox:before{content:"\e028"}.glyphicon-play-circle:before{content:"\e029"}.glyphicon-repeat:before{content:"\e030"}.glyphicon-refresh:before{content:"\e031"}.glyphicon-list-alt:before{content:"\e032"}.glyphicon-lock:before{content:"\e033"}.glyphicon-flag:before{content:"\e034"}.glyphicon-headphones:before{content:"\e035"}.glyphicon-volume-off:before{content:"\e036"}.glyphicon-volume-down:before{content:"\e037"}.glyphicon-volume-up:before{content:"\e038"}.glyphicon-qrcode:before{content:"\e039"}.glyphicon-barcode:before{content:"\e040"}.glyphicon-tag:before{content:"\e041"}.glyphicon-tags:before{content:"\e042"}.glyphicon-book:before{content:"\e043"}.glyphicon-bookmark:before{content:"\e044"}.glyphicon-print:before{content:"\e045"}.glyphicon-camera:before{content:"\e046"}.glyphicon-font:before{content:"\e047"}.glyphicon-bold:before{content:"\e048"}.glyphicon-italic:before{content:"\e049"}.glyphicon-text-height:before{content:"\e050"}.glyphicon-text-width:before{content:"\e051"}.glyphicon-align-left:before{content:"\e052"}.glyphicon-align-center:before{content:"\e053"}.glyphicon-align-right:before{content:"\e054"}.glyphicon-align-justify:before{content:"\e055"}.glyphicon-list:before{content:"\e056"}.glyphicon-indent-left:before{content:"\e057"}.glyphicon-indent-right:before{content:"\e058"}.glyphicon-facetime-video:before{content:"\e059"}.glyphicon-picture:before{content:"\e060"}.glyphicon-map-marker:before{content:"\e062"}.glyphicon-adjust:before{content:"\e063"}.glyphicon-tint:before{content:"\e064"}.glyphicon-edit:before{content:"\e065"}.glyphicon-share:before{content:"\e066"}.glyphicon-check:before{content:"\e067"}.glyphicon-move:before{content:"\e068"}.glyphicon-step-backward:before{content:"\e069"}.glyphicon-fast-backward:before{content:"\e070"}.glyphicon-backward:before{content:"\e071"}.glyphicon-play:before{content:"\e072"}.glyphicon-pause:before{content:"\e073"}.glyphicon-stop:before{content:"\e074"}.glyphicon-forward:before{content:"\e075"}.glyphicon-fast-forward:before{content:"\e076"}.glyphicon-step-forward:before{content:"\e077"}.glyphicon-eject:before{content:"\e078"}.glyphicon-chevron-left:before{content:"\e079"}.glyphicon-chevron-right:before{content:"\e080"}.glyphicon-plus-sign:before{content:"\e081"}.glyphicon-minus-sign:before{content:"\e082"}.glyphicon-remove-sign:before{content:"\e083"}.glyphicon-ok-sign:before{content:"\e084"}.glyphicon-question-sign:before{content:"\e085"}.glyphicon-info-sign:before{content:"\e086"}.glyphicon-screenshot:before{content:"\e087"}.glyphicon-remove-circle:before{content:"\e088"}.glyphicon-ok-circle:before{content:"\e089"}.glyphicon-ban-circle:before{content:"\e090"}.glyphicon-arrow-left:before{content:"\e091"}.glyphicon-arrow-right:before{content:"\e092"}.glyphicon-arrow-up:before{content:"\e093"}.glyphicon-arrow-down:before{content:"\e094"}.glyphicon-share-alt:before{content:"\e095"}.glyphicon-resize-full:before{content:"\e096"}.glyphicon-resize-small:before{content:"\e097"}.glyphicon-exclamation-sign:before{content:"\e101"}.glyphicon-gift:before{content:"\e102"}.glyphicon-leaf:before{content:"\e103"}.glyphicon-fire:before{content:"\e104"}.glyphicon-eye-open:before{content:"\e105"}.glyphicon-eye-close:before{content:"\e106"}.glyphicon-warning-sign:before{content:"\e107"}.glyphicon-plane:before{content:"\e108"}.glyphicon-calendar:before{content:"\e109"}.glyphicon-random:before{content:"\e110"}.glyphicon-comment:before{content:"\e111"}.glyphicon-magnet:before{content:"\e112"}.glyphicon-chevron-up:before{content:"\e113"}.glyphicon-chevron-down:before{content:"\e114"}.glyphicon-retweet:before{content:"\e115"}.glyphicon-shopping-cart:before{content:"\e116"}.glyphicon-folder-close:before{content:"\e117"}.glyphicon-folder-open:before{content:"\e118"}.glyphicon-resize-vertical:before{content:"\e119"}.glyphicon-resize-horizontal:before{content:"\e120"}.glyphicon-hdd:before{content:"\e121"}.glyphicon-bullhorn:before{content:"\e122"}.glyphicon-bell:before{content:"\e123"}.glyphicon-certificate:before{content:"\e124"}.glyphicon-thumbs-up:before{content:"\e125"}.glyphicon-thumbs-down:before{content:"\e126"}.glyphicon-hand-right:before{content:"\e127"}.glyphicon-hand-left:before{content:"\e128"}.glyphicon-hand-up:before{content:"\e129"}.glyphicon-hand-down:before{content:"\e130"}.glyphicon-circle-arrow-right:before{content:"\e131"}.glyphicon-circle-arrow-left:before{content:"\e132"}.glyphicon-circle-arrow-up:before{content:"\e133"}.glyphicon-circle-arrow-down:before{content:"\e134"}.glyphicon-globe:before{content:"\e135"}.glyphicon-wrench:before{content:"\e136"}.glyphicon-tasks:before{content:"\e137"}.glyphicon-filter:before{content:"\e138"}.glyphicon-briefcase:before{content:"\e139"}.glyphicon-fullscreen:before{content:"\e140"}.glyphicon-dashboard:before{content:"\e141"}.glyphicon-paperclip:before{content:"\e142"}.glyphicon-heart-empty:before{content:"\e143"}.glyphicon-link:before{content:"\e144"}.glyphicon-phone:before{content:"\e145"}.glyphicon-pushpin:before{content:"\e146"}.glyphicon-usd:before{content:"\e148"}.glyphicon-gbp:before{content:"\e149"}.glyphicon-sort:before{content:"\e150"}.glyphicon-sort-by-alphabet:before{content:"\e151"}.glyphicon-sort-by-alphabet-alt:before{content:"\e152"}.glyphicon-sort-by-order:before{content:"\e153"}.glyphicon-sort-by-order-alt:before{content:"\e154"}.glyphicon-sort-by-attributes:before{content:"\e155"}.glyphicon-sort-by-attributes-alt:before{content:"\e156"}.glyphicon-unchecked:before{content:"\e157"}.glyphicon-expand:before{content:"\e158"}.glyphicon-collapse-down:before{content:"\e159"}.glyphicon-collapse-up:before{content:"\e160"}.glyphicon-log-in:before{content:"\e161"}.glyphicon-flash:before{content:"\e162"}.glyphicon-log-out:before{content:"\e163"}.glyphicon-new-window:before{content:"\e164"}.glyphicon-record:before{content:"\e165"}.glyphicon-save:before{content:"\e166"}.glyphicon-open:before{content:"\e167"}.glyphicon-saved:before{content:"\e168"}.glyphicon-import:before{content:"\e169"}.glyphicon-export:before{content:"\e170"}.glyphicon-send:before{content:"\e171"}.glyphicon-floppy-disk:before{content:"\e172"}.glyphicon-floppy-saved:before{content:"\e173"}.glyphicon-floppy-remove:before{content:"\e174"}.glyphicon-floppy-save:before{content:"\e175"}.glyphicon-floppy-open:before{content:"\e176"}.glyphicon-credit-card:before{content:"\e177"}.glyphicon-transfer:before{content:"\e178"}.glyphicon-cutlery:before{content:"\e179"}.glyphicon-header:before{content:"\e180"}.glyphicon-compressed:before{content:"\e181"}.glyphicon-earphone:before{content:"\e182"}.glyphicon-phone-alt:before{content:"\e183"}.glyphicon-tower:before{content:"\e184"}.glyphicon-stats:before{content:"\e185"}.glyphicon-sd-video:before{content:"\e186"}.glyphicon-hd-video:before{content:"\e187"}.glyphicon-subtitles:before{content:"\e188"}.glyphicon-sound-stereo:before{content:"\e189"}.glyphicon-sound-dolby:before{content:"\e190"}.glyphicon-sound-5-1:before{content:"\e191"}.glyphicon-sound-6-1:before{content:"\e192"}.glyphicon-sound-7-1:before{content:"\e193"}.glyphicon-copyright-mark:before{content:"\e194"}.glyphicon-registration-mark:before{content:"\e195"}.glyphicon-cloud-download:before{content:"\e197"}.glyphicon-cloud-upload:before{content:"\e198"}.glyphicon-tree-conifer:before{content:"\e199"}.glyphicon-tree-deciduous:before{content:"\e200"}.glyphicon-cd:before{content:"\e201"}.glyphicon-save-file:before{content:"\e202"}.glyphicon-open-file:before{content:"\e203"}.glyphicon-level-up:before{content:"\e204"}.glyphicon-copy:before{content:"\e205"}.glyphicon-paste:before{content:"\e206"}.glyphicon-alert:before{content:"\e209"}.glyphicon-equalizer:before{content:"\e210"}.glyphicon-king:before{content:"\e211"}.glyphicon-queen:before{content:"\e212"}.glyphicon-pawn:before{content:"\e213"}.glyphicon-bishop:before{content:"\e214"}.glyphicon-knight:before{content:"\e215"}.glyphicon-baby-formula:before{content:"\e216"}.glyphicon-tent:before{content:"\26fa"}.glyphicon-blackboard:before{content:"\e218"}.glyphicon-bed:before{content:"\e219"}.glyphicon-apple:before{content:"\f8ff"}.glyphicon-erase:before{content:"\e221"}.glyphicon-hourglass:before{content:"\231b"}.glyphicon-lamp:before{content:"\e223"}.glyphicon-duplicate:before{content:"\e224"}.glyphicon-piggy-bank:before{content:"\e225"}.glyphicon-scissors:before{content:"\e226"}.glyphicon-bitcoin:before{content:"\e227"}.glyphicon-btc:before{content:"\e227"}.glyphicon-xbt:before{content:"\e227"}.glyphicon-yen:before{content:"\00a5"}.glyphicon-jpy:before{content:"\00a5"}.glyphicon-ruble:before{content:"\20bd"}.glyphicon-rub:before{content:"\20bd"}.glyphicon-scale:before{content:"\e230"}.glyphicon-ice-lolly:before{content:"\e231"}.glyphicon-ice-lolly-tasted:before{content:"\e232"}.glyphicon-education:before{content:"\e233"}.glyphicon-option-horizontal:before{content:"\e234"}.glyphicon-option-vertical:before{content:"\e235"}.glyphicon-menu-hamburger:before{content:"\e236"}.glyphicon-modal-window:before{content:"\e237"}.glyphicon-oil:before{content:"\e238"}.glyphicon-grain:before{content:"\e239"}.glyphicon-sunglasses:before{content:"\e240"}.glyphicon-text-size:before{content:"\e241"}.glyphicon-text-color:before{content:"\e242"}.glyphicon-text-background:before{content:"\e243"}.glyphicon-object-align-top:before{content:"\e244"}.glyphicon-object-align-bottom:before{content:"\e245"}.glyphicon-object-align-horizontal:before{content:"\e246"}.glyphicon-object-align-left:before{content:"\e247"}.glyphicon-object-align-vertical:before{content:"\e248"}.glyphicon-object-align-right:before{content:"\e249"}.glyphicon-triangle-right:before{content:"\e250"}.glyphicon-triangle-left:before{content:"\e251"}.glyphicon-triangle-bottom:before{content:"\e252"}.glyphicon-triangle-top:before{content:"\e253"}.glyphicon-console:before{content:"\e254"}.glyphicon-superscript:before{content:"\e255"}.glyphicon-subscript:before{content:"\e256"}.glyphicon-menu-left:before{content:"\e257"}.glyphicon-menu-right:before{content:"\e258"}.glyphicon-menu-down:before{content:"\e259"}.glyphicon-menu-up:before{content:"\e260"}*{-webkit-box-sizing:border-box;-moz-box-sizing:border-box;box-sizing:border-box}:after,:before{-webkit-box-sizing:border-box;-moz-box-sizing:border-box;box-sizing:border-box}html{font-size:10px;-webkit-tap-highlight-color:rgba(0,0,0,0)}body{font-family:"Helvetica Neue",Helvetica,Arial,sans-serif;font-size:14px;line-height:1.42857143;color:#333;background-color:#fff}button,input,select,textarea{font-family:inherit;font-size:inherit;line-height:inherit}a{color:#337ab7;text-decoration:none}a:focus,a:hover{color:#23527c;text-decoration:underline}a:focus{outline:5px auto -webkit-focus-ring-color;outline-offset:-2px}figure{margin:0}img{vertical-align:middle}.carousel-inner>.item>a>img,.carousel-inner>.item>img,.img-responsive,.thumbnail a>img,.thumbnail>img{display:block;max-width:100%;height:auto}.img-rounded{border-radius:6px}.img-thumbnail{display:inline-block;max-width:100%;height:auto;padding:4px;line-height:1.42857143;background-color:#fff;border:1px solid #ddd;border-radius:4px;-webkit-transition:all .2s ease-in-out;-o-transition:all .2s ease-in-out;transition:all .2s ease-in-out}.img-circle{border-radius:50%}hr{margin-top:20px;margin-bottom:20px;border:0;border-top:1px solid #eee}.sr-only{position:absolute;width:1px;height:1px;padding:0;margin:-1px;overflow:hidden;clip:rect(0,0,0,0);border:0}.sr-only-focusable:active,.sr-only-focusable:focus{position:static;width:auto;height:auto;margin:0;overflow:visible;clip:auto}[role=button]{cursor:pointer}.h1,.h2,.h3,.h4,.h5,.h6,h1,h2,h3,h4,h5,h6{font-family:inherit;font-weight:500;line-height:1.1;color:inherit}.h1 .small,.h1 small,.h2 .small,.h2 small,.h3 .small,.h3 small,.h4 .small,.h4 small,.h5 .small,.h5 small,.h6 .small,.h6 small,h1 .small,h1 small,h2 .small,h2 small,h3 .small,h3 small,h4 .small,h4 small,h5 .small,h5 small,h6 .small,h6 small{font-weight:400;line-height:1;color:#777}.h1,.h2,.h3,h1,h2,h3{margin-top:20px;margin-bottom:10px}.h1 .small,.h1 small,.h2 .small,.h2 small,.h3 .small,.h3 small,h1 .small,h1 small,h2 .small,h2 small,h3 .small,h3 small{font-size:65%}.h4,.h5,.h6,h4,h5,h6{margin-top:10px;margin-bottom:10px}.h4 .small,.h4 small,.h5 .small,.h5 small,.h6 .small,.h6 small,h4 .small,h4 small,h5 .small,h5 small,h6 .small,h6 small{font-size:75%}.h1,h1{font-size:36px}.h2,h2{font-size:30px}.h3,h3{font-size:24px}.h4,h4{font-size:18px}.h5,h5{font-size:14px}.h6,h6{font-size:12px}p{margin:0 0 10px}.lead{margin-bottom:20px;font-size:16px;font-weight:300;line-height:1.4}@media (min-width:768px){.lead{font-size:21px}}.small,small{font-size:85%}.mark,mark{padding:.2em;background-color:#fcf8e3}.text-left{text-align:left}.text-right{text-align:right}.text-center{text-align:center}.text-justify{text-align:justify}.text-nowrap{white-space:nowrap}.text-lowercase{text-transform:lowercase}.text-uppercase{text-transform:uppercase}.text-capitalize{text-transform:capitalize}.text-muted{color:#777}.text-primary{color:#337ab7}a.text-primary:focus,a.text-primary:hover{color:#286090}.text-success{color:#3c763d}a.text-success:focus,a.text-success:hover{color:#2b542c}.text-info{color:#31708f}a.text-info:focus,a.text-info:hover{color:#245269}.text-warning{color:#8a6d3b}a.text-warning:focus,a.text-warning:hover{color:#66512c}.text-danger{color:#a94442}a.text-danger:focus,a.text-danger:hover{color:#843534}.bg-primary{color:#fff;background-color:#337ab7}a.bg-primary:focus,a.bg-primary:hover{background-color:#286090}.bg-success{background-color:#dff0d8}a.bg-success:focus,a.bg-success:hover{background-color:#c1e2b3}.bg-info{background-color:#d9edf7}a.bg-info:focus,a.bg-info:hover{background-color:#afd9ee}.bg-warning{background-color:#fcf8e3}a.bg-warning:focus,a.bg-warning:hover{background-color:#f7ecb5}.bg-danger{background-color:#f2dede}a.bg-danger:focus,a.bg-danger:hover{background-color:#e4b9b9}.page-header{padding-bottom:9px;margin:40px 0 20px;border-bottom:1px solid #eee}ol,ul{margin-top:0;margin-bottom:10px}ol ol,ol ul,ul ol,ul ul{margin-bottom:0}.list-unstyled{padding-left:0;list-style:none}.list-inline{padding-left:0;margin-left:-5px;list-style:none}.list-inline>li{display:inline-block;padding-right:5px;padding-left:5px}dl{margin-top:0;margin-bottom:20px}dd,dt{line-height:1.42857143}dt{font-weight:700}dd{margin-left:0}@media (min-width:768px){.dl-horizontal dt{float:left;width:160px;overflow:hidden;clear:left;text-align:right;text-overflow:ellipsis;white-space:nowrap}.dl-horizontal dd{margin-left:180px}}abbr[data-original-title],abbr[title]{cursor:help;border-bottom:1px dotted #777}.initialism{font-size:90%;text-transform:uppercase}blockquote{padding:10px 20px;margin:0 0 20px;font-size:17.5px;border-left:5px solid #eee}blockquote ol:last-child,blockquote p:last-child,blockquote ul:last-child{margin-bottom:0}blockquote .small,blockquote footer,blockquote small{display:block;font-size:80%;line-height:1.42857143;color:#777}blockquote .small:before,blockquote footer:before,blockquote small:before{content:'\2014 \00A0'}.blockquote-reverse,blockquote.pull-right{padding-right:15px;padding-left:0;text-align:right;border-right:5px solid #eee;border-left:0}.blockquote-reverse .small:before,.blockquote-reverse footer:before,.blockquote-reverse small:before,blockquote.pull-right .small:before,blockquote.pull-right footer:before,blockquote.pull-right small:before{content:''}.blockquote-reverse .small:after,.blockquote-reverse footer:after,.blockquote-reverse small:after,blockquote.pull-right .small:after,blockquote.pull-right footer:after,blockquote.pull-right small:after{content:'\00A0 \2014'}address{margin-bottom:20px;font-style:normal;line-height:1.42857143}code,kbd,pre,samp{font-family:Menlo,Monaco,Consolas,"Courier New",monospace}code{padding:2px 4px;font-size:90%;color:#c7254e;background-color:#f9f2f4;border-radius:4px}kbd{padding:2px 4px;font-size:90%;color:#fff;background-color:#333;border-radius:3px;-webkit-box-shadow:inset 0 -1px 0 rgba(0,0,0,.25);box-shadow:inset 0 -1px 0 rgba(0,0,0,.25)}kbd kbd{padding:0;font-size:100%;font-weight:700;-webkit-box-shadow:none;box-shadow:none}pre{display:block;padding:9.5px;margin:0 0 10px;font-size:13px;line-height:1.42857143;color:#333;word-break:break-all;word-wrap:break-word;background-color:#f5f5f5;border:1px solid #ccc;border-radius:4px}pre code{padding:0;font-size:inherit;color:inherit;white-space:pre-wrap;background-color:transparent;border-radius:0}.pre-scrollable{max-height:340px;overflow-y:scroll}.container{padding-right:15px;padding-left:15px;margin-right:auto;margin-left:auto}@media (min-width:768px){.container{width:750px}}@media (min-width:992px){.container{width:970px}}@media (min-width:1200px){.container{width:1170px}}.container-fluid{padding-right:15px;padding-left:15px;margin-right:auto;margin-left:auto}.row{margin-right:-15px;margin-left:-15px}.col-lg-1,.col-lg-10,.col-lg-11,.col-lg-12,.col-lg-2,.col-lg-3,.col-lg-4,.col-lg-5,.col-lg-6,.col-lg-7,.col-lg-8,.col-lg-9,.col-md-1,.col-md-10,.col-md-11,.col-md-12,.col-md-2,.col-md-3,.col-md-4,.col-md-5,.col-md-6,.col-md-7,.col-md-8,.col-md-9,.col-sm-1,.col-sm-10,.col-sm-11,.col-sm-12,.col-sm-2,.col-sm-3,.col-sm-4,.col-sm-5,.col-sm-6,.col-sm-7,.col-sm-8,.col-sm-9,.col-xs-1,.col-xs-10,.col-xs-11,.col-xs-12,.col-xs-2,.col-xs-3,.col-xs-4,.col-xs-5,.col-xs-6,.col-xs-7,.col-xs-8,.col-xs-9{position:relative;min-height:1px;padding-right:15px;padding-left:15px}.col-xs-1,.col-xs-10,.col-xs-11,.col-xs-12,.col-xs-2,.col-xs-3,.col-xs-4,.col-xs-5,.col-xs-6,.col-xs-7,.col-xs-8,.col-xs-9{float:left}.col-xs-12{width:100%}.col-xs-11{width:91.66666667%}.col-xs-10{width:83.33333333%}.col-xs-9{width:75%}.col-xs-8{width:66.66666667%}.col-xs-7{width:58.33333333%}.col-xs-6{width:50%}.col-xs-5{width:41.66666667%}.col-xs-4{width:33.33333333%}.col-xs-3{width:25%}.col-xs-2{width:16.66666667%}.col-xs-1{width:8.33333333%}.col-xs-pull-12{right:100%}.col-xs-pull-11{right:91.66666667%}.col-xs-pull-10{right:83.33333333%}.col-xs-pull-9{right:75%}.col-xs-pull-8{right:66.66666667%}.col-xs-pull-7{right:58.33333333%}.col-xs-pull-6{right:50%}.col-xs-pull-5{right:41.66666667%}.col-xs-pull-4{right:33.33333333%}.col-xs-pull-3{right:25%}.col-xs-pull-2{right:16.66666667%}.col-xs-pull-1{right:8.33333333%}.col-xs-pull-0{right:auto}.col-xs-push-12{left:100%}.col-xs-push-11{left:91.66666667%}.col-xs-push-10{left:83.33333333%}.col-xs-push-9{left:75%}.col-xs-push-8{left:66.66666667%}.col-xs-push-7{left:58.33333333%}.col-xs-push-6{left:50%}.col-xs-push-5{left:41.66666667%}.col-xs-push-4{left:33.33333333%}.col-xs-push-3{left:25%}.col-xs-push-2{left:16.66666667%}.col-xs-push-1{left:8.33333333%}.col-xs-push-0{left:auto}.col-xs-offset-12{margin-left:100%}.col-xs-offset-11{margin-left:91.66666667%}.col-xs-offset-10{margin-left:83.33333333%}.col-xs-offset-9{margin-left:75%}.col-xs-offset-8{margin-left:66.66666667%}.col-xs-offset-7{margin-left:58.33333333%}.col-xs-offset-6{margin-left:50%}.col-xs-offset-5{margin-left:41.66666667%}.col-xs-offset-4{margin-left:33.33333333%}.col-xs-offset-3{margin-left:25%}.col-xs-offset-2{margin-left:16.66666667%}.col-xs-offset-1{margin-left:8.33333333%}.col-xs-offset-0{margin-left:0}@media (min-width:768px){.col-sm-1,.col-sm-10,.col-sm-11,.col-sm-12,.col-sm-2,.col-sm-3,.col-sm-4,.col-sm-5,.col-sm-6,.col-sm-7,.col-sm-8,.col-sm-9{float:left}.col-sm-12{width:100%}.col-sm-11{width:91.66666667%}.col-sm-10{width:83.33333333%}.col-sm-9{width:75%}.col-sm-8{width:66.66666667%}.col-sm-7{width:58.33333333%}.col-sm-6{width:50%}.col-sm-5{width:41.66666667%}.col-sm-4{width:33.33333333%}.col-sm-3{width:25%}.col-sm-2{width:16.66666667%}.col-sm-1{width:8.33333333%}.col-sm-pull-12{right:100%}.col-sm-pull-11{right:91.66666667%}.col-sm-pull-10{right:83.33333333%}.col-sm-pull-9{right:75%}.col-sm-pull-8{right:66.66666667%}.col-sm-pull-7{right:58.33333333%}.col-sm-pull-6{right:50%}.col-sm-pull-5{right:41.66666667%}.col-sm-pull-4{right:33.33333333%}.col-sm-pull-3{right:25%}.col-sm-pull-2{right:16.66666667%}.col-sm-pull-1{right:8.33333333%}.col-sm-pull-0{right:auto}.col-sm-push-12{left:100%}.col-sm-push-11{left:91.66666667%}.col-sm-push-10{left:83.33333333%}.col-sm-push-9{left:75%}.col-sm-push-8{left:66.66666667%}.col-sm-push-7{left:58.33333333%}.col-sm-push-6{left:50%}.col-sm-push-5{left:41.66666667%}.col-sm-push-4{left:33.33333333%}.col-sm-push-3{left:25%}.col-sm-push-2{left:16.66666667%}.col-sm-push-1{left:8.33333333%}.col-sm-push-0{left:auto}.col-sm-offset-12{margin-left:100%}.col-sm-offset-11{margin-left:91.66666667%}.col-sm-offset-10{margin-left:83.33333333%}.col-sm-offset-9{margin-left:75%}.col-sm-offset-8{margin-left:66.66666667%}.col-sm-offset-7{margin-left:58.33333333%}.col-sm-offset-6{margin-left:50%}.col-sm-offset-5{margin-left:41.66666667%}.col-sm-offset-4{margin-left:33.33333333%}.col-sm-offset-3{margin-left:25%}.col-sm-offset-2{margin-left:16.66666667%}.col-sm-offset-1{margin-left:8.33333333%}.col-sm-offset-0{margin-left:0}}@media (min-width:992px){.col-md-1,.col-md-10,.col-md-11,.col-md-12,.col-md-2,.col-md-3,.col-md-4,.col-md-5,.col-md-6,.col-md-7,.col-md-8,.col-md-9{float:left}.col-md-12{width:100%}.col-md-11{width:91.66666667%}.col-md-10{width:83.33333333%}.col-md-9{width:75%}.col-md-8{width:66.66666667%}.col-md-7{width:58.33333333%}.col-md-6{width:50%}.col-md-5{width:41.66666667%}.col-md-4{width:33.33333333%}.col-md-3{width:25%}.col-md-2{width:16.66666667%}.col-md-1{width:8.33333333%}.col-md-pull-12{right:100%}.col-md-pull-11{right:91.66666667%}.col-md-pull-10{right:83.33333333%}.col-md-pull-9{right:75%}.col-md-pull-8{right:66.66666667%}.col-md-pull-7{right:58.33333333%}.col-md-pull-6{right:50%}.col-md-pull-5{right:41.66666667%}.col-md-pull-4{right:33.33333333%}.col-md-pull-3{right:25%}.col-md-pull-2{right:16.66666667%}.col-md-pull-1{right:8.33333333%}.col-md-pull-0{right:auto}.col-md-push-12{left:100%}.col-md-push-11{left:91.66666667%}.col-md-push-10{left:83.33333333%}.col-md-push-9{left:75%}.col-md-push-8{left:66.66666667%}.col-md-push-7{left:58.33333333%}.col-md-push-6{left:50%}.col-md-push-5{left:41.66666667%}.col-md-push-4{left:33.33333333%}.col-md-push-3{left:25%}.col-md-push-2{left:16.66666667%}.col-md-push-1{left:8.33333333%}.col-md-push-0{left:auto}.col-md-offset-12{margin-left:100%}.col-md-offset-11{margin-left:91.66666667%}.col-md-offset-10{margin-left:83.33333333%}.col-md-offset-9{margin-left:75%}.col-md-offset-8{margin-left:66.66666667%}.col-md-offset-7{margin-left:58.33333333%}.col-md-offset-6{margin-left:50%}.col-md-offset-5{margin-left:41.66666667%}.col-md-offset-4{margin-left:33.33333333%}.col-md-offset-3{margin-left:25%}.col-md-offset-2{margin-left:16.66666667%}.col-md-offset-1{margin-left:8.33333333%}.col-md-offset-0{margin-left:0}}@media (min-width:1200px){.col-lg-1,.col-lg-10,.col-lg-11,.col-lg-12,.col-lg-2,.col-lg-3,.col-lg-4,.col-lg-5,.col-lg-6,.col-lg-7,.col-lg-8,.col-lg-9{float:left}.col-lg-12{width:100%}.col-lg-11{width:91.66666667%}.col-lg-10{width:83.33333333%}.col-lg-9{width:75%}.col-lg-8{width:66.66666667%}.col-lg-7{width:58.33333333%}.col-lg-6{width:50%}.col-lg-5{width:41.66666667%}.col-lg-4{width:33.33333333%}.col-lg-3{width:25%}.col-lg-2{width:16.66666667%}.col-lg-1{width:8.33333333%}.col-lg-pull-12{right:100%}.col-lg-pull-11{right:91.66666667%}.col-lg-pull-10{right:83.33333333%}.col-lg-pull-9{right:75%}.col-lg-pull-8{right:66.66666667%}.col-lg-pull-7{right:58.33333333%}.col-lg-pull-6{right:50%}.col-lg-pull-5{right:41.66666667%}.col-lg-pull-4{right:33.33333333%}.col-lg-pull-3{right:25%}.col-lg-pull-2{right:16.66666667%}.col-lg-pull-1{right:8.33333333%}.col-lg-pull-0{right:auto}.col-lg-push-12{left:100%}.col-lg-push-11{left:91.66666667%}.col-lg-push-10{left:83.33333333%}.col-lg-push-9{left:75%}.col-lg-push-8{left:66.66666667%}.col-lg-push-7{left:58.33333333%}.col-lg-push-6{left:50%}.col-lg-push-5{left:41.66666667%}.col-lg-push-4{left:33.33333333%}.col-lg-push-3{left:25%}.col-lg-push-2{left:16.66666667%}.col-lg-push-1{left:8.33333333%}.col-lg-push-0{left:auto}.col-lg-offset-12{margin-left:100%}.col-lg-offset-11{margin-left:91.66666667%}.col-lg-offset-10{margin-left:83.33333333%}.col-lg-offset-9{margin-left:75%}.col-lg-offset-8{margin-left:66.66666667%}.col-lg-offset-7{margin-left:58.33333333%}.col-lg-offset-6{margin-left:50%}.col-lg-offset-5{margin-left:41.66666667%}.col-lg-offset-4{margin-left:33.33333333%}.col-lg-offset-3{margin-left:25%}.col-lg-offset-2{margin-left:16.66666667%}.col-lg-offset-1{margin-left:8.33333333%}.col-lg-offset-0{margin-left:0}}table{background-color:transparent}caption{padding-top:8px;padding-bottom:8px;color:#777;text-align:left}th{text-align:left}.table{width:100%;max-width:100%;margin-bottom:20px}.table>tbody>tr>td,.table>tbody>tr>th,.table>tfoot>tr>td,.table>tfoot>tr>th,.table>thead>tr>td,.table>thead>tr>th{padding:8px;line-height:1.42857143;vertical-align:top;border-top:1px solid #ddd}.table>thead>tr>th{vertical-align:bottom;border-bottom:2px solid #ddd}.table>caption+thead>tr:first-child>td,.table>caption+thead>tr:first-child>th,.table>colgroup+thead>tr:first-child>td,.table>colgroup+thead>tr:first-child>th,.table>thead:first-child>tr:first-child>td,.table>thead:first-child>tr:first-child>th{border-top:0}.table>tbody+tbody{border-top:2px solid #ddd}.table .table{background-color:#fff}.table-condensed>tbody>tr>td,.table-condensed>tbody>tr>th,.table-condensed>tfoot>tr>td,.table-condensed>tfoot>tr>th,.table-condensed>thead>tr>td,.table-condensed>thead>tr>th{padding:5px}.table-bordered{border:1px solid #ddd}.table-bordered>tbody>tr>td,.table-bordered>tbody>tr>th,.table-bordered>tfoot>tr>td,.table-bordered>tfoot>tr>th,.table-bordered>thead>tr>td,.table-bordered>thead>tr>th{border:1px solid #ddd}.table-bordered>thead>tr>td,.table-bordered>thead>tr>th{border-bottom-width:2px}.table-striped>tbody>tr:nth-of-type(odd){background-color:#f9f9f9}.table-hover>tbody>tr:hover{background-color:#f5f5f5}table col[class*=col-]{position:static;display:table-column;float:none}table td[class*=col-],table th[class*=col-]{position:static;display:table-cell;float:none}.table>tbody>tr.active>td,.table>tbody>tr.active>th,.table>tbody>tr>td.active,.table>tbody>tr>th.active,.table>tfoot>tr.active>td,.table>tfoot>tr.active>th,.table>tfoot>tr>td.active,.table>tfoot>tr>th.active,.table>thead>tr.active>td,.table>thead>tr.active>th,.table>thead>tr>td.active,.table>thead>tr>th.active{background-color:#f5f5f5}.table-hover>tbody>tr.active:hover>td,.table-hover>tbody>tr.active:hover>th,.table-hover>tbody>tr:hover>.active,.table-hover>tbody>tr>td.active:hover,.table-hover>tbody>tr>th.active:hover{background-color:#e8e8e8}.table>tbody>tr.success>td,.table>tbody>tr.success>th,.table>tbody>tr>td.success,.table>tbody>tr>th.success,.table>tfoot>tr.success>td,.table>tfoot>tr.success>th,.table>tfoot>tr>td.success,.table>tfoot>tr>th.success,.table>thead>tr.success>td,.table>thead>tr.success>th,.table>thead>tr>td.success,.table>thead>tr>th.success{background-color:#dff0d8}.table-hover>tbody>tr.success:hover>td,.table-hover>tbody>tr.success:hover>th,.table-hover>tbody>tr:hover>.success,.table-hover>tbody>tr>td.success:hover,.table-hover>tbody>tr>th.success:hover{background-color:#d0e9c6}.table>tbody>tr.info>td,.table>tbody>tr.info>th,.table>tbody>tr>td.info,.table>tbody>tr>th.info,.table>tfoot>tr.info>td,.table>tfoot>tr.info>th,.table>tfoot>tr>td.info,.table>tfoot>tr>th.info,.table>thead>tr.info>td,.table>thead>tr.info>th,.table>thead>tr>td.info,.table>thead>tr>th.info{background-color:#d9edf7}.table-hover>tbody>tr.info:hover>td,.table-hover>tbody>tr.info:hover>th,.table-hover>tbody>tr:hover>.info,.table-hover>tbody>tr>td.info:hover,.table-hover>tbody>tr>th.info:hover{background-color:#c4e3f3}.table>tbody>tr.warning>td,.table>tbody>tr.warning>th,.table>tbody>tr>td.warning,.table>tbody>tr>th.warning,.table>tfoot>tr.warning>td,.table>tfoot>tr.warning>th,.table>tfoot>tr>td.warning,.table>tfoot>tr>th.warning,.table>thead>tr.warning>td,.table>thead>tr.warning>th,.table>thead>tr>td.warning,.table>thead>tr>th.warning{background-color:#fcf8e3}.table-hover>tbody>tr.warning:hover>td,.table-hover>tbody>tr.warning:hover>th,.table-hover>tbody>tr:hover>.warning,.table-hover>tbody>tr>td.warning:hover,.table-hover>tbody>tr>th.warning:hover{background-color:#faf2cc}.table>tbody>tr.danger>td,.table>tbody>tr.danger>th,.table>tbody>tr>td.danger,.table>tbody>tr>th.danger,.table>tfoot>tr.danger>td,.table>tfoot>tr.danger>th,.table>tfoot>tr>td.danger,.table>tfoot>tr>th.danger,.table>thead>tr.danger>td,.table>thead>tr.danger>th,.table>thead>tr>td.danger,.table>thead>tr>th.danger{background-color:#f2dede}.table-hover>tbody>tr.danger:hover>td,.table-hover>tbody>tr.danger:hover>th,.table-hover>tbody>tr:hover>.danger,.table-hover>tbody>tr>td.danger:hover,.table-hover>tbody>tr>th.danger:hover{background-color:#ebcccc}.table-responsive{min-height:.01%;overflow-x:auto}@media screen and (max-width:767px){.table-responsive{width:100%;margin-bottom:15px;overflow-y:hidden;-ms-overflow-style:-ms-autohiding-scrollbar;border:1px solid #ddd}.table-responsive>.table{margin-bottom:0}.table-responsive>.table>tbody>tr>td,.table-responsive>.table>tbody>tr>th,.table-responsive>.table>tfoot>tr>td,.table-responsive>.table>tfoot>tr>th,.table-responsive>.table>thead>tr>td,.table-responsive>.table>thead>tr>th{white-space:nowrap}.table-responsive>.table-bordered{border:0}.table-responsive>.table-bordered>tbody>tr>td:first-child,.table-responsive>.table-bordered>tbody>tr>th:first-child,.table-responsive>.table-bordered>tfoot>tr>td:first-child,.table-responsive>.table-bordered>tfoot>tr>th:first-child,.table-responsive>.table-bordered>thead>tr>td:first-child,.table-responsive>.table-bordered>thead>tr>th:first-child{border-left:0}.table-responsive>.table-bordered>tbody>tr>td:last-child,.table-responsive>.table-bordered>tbody>tr>th:last-child,.table-responsive>.table-bordered>tfoot>tr>td:last-child,.table-responsive>.table-bordered>tfoot>tr>th:last-child,.table-responsive>.table-bordered>thead>tr>td:last-child,.table-responsive>.table-bordered>thead>tr>th:last-child{border-right:0}.table-responsive>.table-bordered>tbody>tr:last-child>td,.table-responsive>.table-bordered>tbody>tr:last-child>th,.table-responsive>.table-bordered>tfoot>tr:last-child>td,.table-responsive>.table-bordered>tfoot>tr:last-child>th{border-bottom:0}}fieldset{min-width:0;padding:0;margin:0;border:0}legend{display:block;width:100%;padding:0;margin-bottom:20px;font-size:21px;line-height:inherit;color:#333;border:0;border-bottom:1px solid #e5e5e5}label{display:inline-block;max-width:100%;margin-bottom:5px;font-weight:700}input[type=search]{-webkit-box-sizing:border-box;-moz-box-sizing:border-box;box-sizing:border-box}input[type=checkbox],input[type=radio]{margin:4px 0 0;margin-top:1px\9;line-height:normal}input[type=file]{display:block}input[type=range]{display:block;width:100%}select[multiple],select[size]{height:auto}input[type=file]:focus,input[type=checkbox]:focus,input[type=radio]:focus{outline:5px auto -webkit-focus-ring-color;outline-offset:-2px}output{display:block;padding-top:7px;font-size:14px;line-height:1.42857143;color:#555}.form-control{display:block;width:100%;height:34px;padding:6px 12px;font-size:14px;line-height:1.42857143;color:#555;background-color:#fff;background-image:none;border:1px solid #ccc;border-radius:4px;-webkit-box-shadow:inset 0 1px 1px rgba(0,0,0,.075);box-shadow:inset 0 1px 1px rgba(0,0,0,.075);-webkit-transition:border-color ease-in-out .15s,-webkit-box-shadow ease-in-out .15s;-o-transition:border-color ease-in-out .15s,box-shadow ease-in-out .15s;transition:border-color ease-in-out .15s,box-shadow ease-in-out .15s}.form-control:focus{border-color:#66afe9;outline:0;-webkit-box-shadow:inset 0 1px 1px rgba(0,0,0,.075),0 0 8px rgba(102,175,233,.6);box-shadow:inset 0 1px 1px rgba(0,0,0,.075),0 0 8px rgba(102,175,233,.6)}.form-control::-moz-placeholder{color:#999;opacity:1}.form-control:-ms-input-placeholder{color:#999}.form-control::-webkit-input-placeholder{color:#999}.form-control::-ms-expand{background-color:transparent;border:0}.form-control[disabled],.form-control[readonly],fieldset[disabled] .form-control{background-color:#eee;opacity:1}.form-control[disabled],fieldset[disabled] .form-control{cursor:not-allowed}textarea.form-control{height:auto}input[type=search]{-webkit-appearance:none}@media screen and (-webkit-min-device-pixel-ratio:0){input[type=date].form-control,input[type=time].form-control,input[type=datetime-local].form-control,input[type=month].form-control{line-height:34px}.input-group-sm input[type=date],.input-group-sm input[type=time],.input-group-sm input[type=datetime-local],.input-group-sm input[type=month],input[type=date].input-sm,input[type=time].input-sm,input[type=datetime-local].input-sm,input[type=month].input-sm{line-height:30px}.input-group-lg input[type=date],.input-group-lg input[type=time],.input-group-lg input[type=datetime-local],.input-group-lg input[type=month],input[type=date].input-lg,input[type=time].input-lg,input[type=datetime-local].input-lg,input[type=month].input-lg{line-height:46px}}.form-group{margin-bottom:15px}.checkbox,.radio{position:relative;display:block;margin-top:10px;margin-bottom:10px}.checkbox label,.radio label{min-height:20px;padding-left:20px;margin-bottom:0;font-weight:400;cursor:pointer}.checkbox input[type=checkbox],.checkbox-inline input[type=checkbox],.radio input[type=radio],.radio-inline input[type=radio]{position:absolute;margin-top:4px\9;margin-left:-20px}.checkbox+.checkbox,.radio+.radio{margin-top:-5px}.checkbox-inline,.radio-inline{position:relative;display:inline-block;padding-left:20px;margin-bottom:0;font-weight:400;vertical-align:middle;cursor:pointer}.checkbox-inline+.checkbox-inline,.radio-inline+.radio-inline{margin-top:0;margin-left:10px}fieldset[disabled] input[type=checkbox],fieldset[disabled] input[type=radio],input[type=checkbox].disabled,input[type=checkbox][disabled],input[type=radio].disabled,input[type=radio][disabled]{cursor:not-allowed}.checkbox-inline.disabled,.radio-inline.disabled,fieldset[disabled] .checkbox-inline,fieldset[disabled] .radio-inline{cursor:not-allowed}.checkbox.disabled label,.radio.disabled label,fieldset[disabled] .checkbox label,fieldset[disabled] .radio label{cursor:not-allowed}.form-control-static{min-height:34px;padding-top:7px;padding-bottom:7px;margin-bottom:0}.form-control-static.input-lg,.form-control-static.input-sm{padding-right:0;padding-left:0}.input-sm{height:30px;padding:5px 10px;font-size:12px;line-height:1.5;border-radius:3px}select.input-sm{height:30px;line-height:30px}select[multiple].input-sm,textarea.input-sm{height:auto}.form-group-sm .form-control{height:30px;padding:5px 10px;font-size:12px;line-height:1.5;border-radius:3px}.form-group-sm select.form-control{height:30px;line-height:30px}.form-group-sm select[multiple].form-control,.form-group-sm textarea.form-control{height:auto}.form-group-sm .form-control-static{height:30px;min-height:32px;padding:6px 10px;font-size:12px;line-height:1.5}.input-lg{height:46px;padding:10px 16px;font-size:18px;line-height:1.3333333;border-radius:6px}select.input-lg{height:46px;line-height:46px}select[multiple].input-lg,textarea.input-lg{height:auto}.form-group-lg .form-control{height:46px;padding:10px 16px;font-size:18px;line-height:1.3333333;border-radius:6px}.form-group-lg select.form-control{height:46px;line-height:46px}.form-group-lg select[multiple].form-control,.form-group-lg textarea.form-control{height:auto}.form-group-lg .form-control-static{height:46px;min-height:38px;padding:11px 16px;font-size:18px;line-height:1.3333333}.has-feedback{position:relative}.has-feedback .form-control{padding-right:42.5px}.form-control-feedback{position:absolute;top:0;right:0;z-index:2;display:block;width:34px;height:34px;line-height:34px;text-align:center;pointer-events:none}.form-group-lg .form-control+.form-control-feedback,.input-group-lg+.form-control-feedback,.input-lg+.form-control-feedback{width:46px;height:46px;line-height:46px}.form-group-sm .form-control+.form-control-feedback,.input-group-sm+.form-control-feedback,.input-sm+.form-control-feedback{width:30px;height:30px;line-height:30px}.has-success .checkbox,.has-success .checkbox-inline,.has-success .control-label,.has-success .help-block,.has-success .radio,.has-success .radio-inline,.has-success.checkbox label,.has-success.checkbox-inline label,.has-success.radio label,.has-success.radio-inline label{color:#3c763d}.has-success .form-control{border-color:#3c763d;-webkit-box-shadow:inset 0 1px 1px rgba(0,0,0,.075);box-shadow:inset 0 1px 1px rgba(0,0,0,.075)}.has-success .form-control:focus{border-color:#2b542c;-webkit-box-shadow:inset 0 1px 1px rgba(0,0,0,.075),0 0 6px #67b168;box-shadow:inset 0 1px 1px rgba(0,0,0,.075),0 0 6px #67b168}.has-success .input-group-addon{color:#3c763d;background-color:#dff0d8;border-color:#3c763d}.has-success .form-control-feedback{color:#3c763d}.has-warning .checkbox,.has-warning .checkbox-inline,.has-warning .control-label,.has-warning .help-block,.has-warning .radio,.has-warning .radio-inline,.has-warning.checkbox label,.has-warning.checkbox-inline label,.has-warning.radio label,.has-warning.radio-inline label{color:#8a6d3b}.has-warning .form-control{border-color:#8a6d3b;-webkit-box-shadow:inset 0 1px 1px rgba(0,0,0,.075);box-shadow:inset 0 1px 1px rgba(0,0,0,.075)}.has-warning .form-control:focus{border-color:#66512c;-webkit-box-shadow:inset 0 1px 1px rgba(0,0,0,.075),0 0 6px #c0a16b;box-shadow:inset 0 1px 1px rgba(0,0,0,.075),0 0 6px #c0a16b}.has-warning .input-group-addon{color:#8a6d3b;background-color:#fcf8e3;border-color:#8a6d3b}.has-warning .form-control-feedback{color:#8a6d3b}.has-error .checkbox,.has-error .checkbox-inline,.has-error .control-label,.has-error .help-block,.has-error .radio,.has-error .radio-inline,.has-error.checkbox label,.has-error.checkbox-inline label,.has-error.radio label,.has-error.radio-inline label{color:#a94442}.has-error .form-control{border-color:#a94442;-webkit-box-shadow:inset 0 1px 1px rgba(0,0,0,.075);box-shadow:inset 0 1px 1px rgba(0,0,0,.075)}.has-error .form-control:focus{border-color:#843534;-webkit-box-shadow:inset 0 1px 1px rgba(0,0,0,.075),0 0 6px #ce8483;box-shadow:inset 0 1px 1px rgba(0,0,0,.075),0 0 6px #ce8483}.has-error .input-group-addon{color:#a94442;background-color:#f2dede;border-color:#a94442}.has-error .form-control-feedback{color:#a94442}.has-feedback label~.form-control-feedback{top:25px}.has-feedback label.sr-only~.form-control-feedback{top:0}.help-block{display:block;margin-top:5px;margin-bottom:10px;color:#737373}@media (min-width:768px){.form-inline .form-group{display:inline-block;margin-bottom:0;vertical-align:middle}.form-inline .form-control{display:inline-block;width:auto;vertical-align:middle}.form-inline .form-control-static{display:inline-block}.form-inline .input-group{display:inline-table;vertical-align:middle}.form-inline .input-group .form-control,.form-inline .input-group .input-group-addon,.form-inline .input-group .input-group-btn{width:auto}.form-inline .input-group>.form-control{width:100%}.form-inline .control-label{margin-bottom:0;vertical-align:middle}.form-inline .checkbox,.form-inline .radio{display:inline-block;margin-top:0;margin-bottom:0;vertical-align:middle}.form-inline .checkbox label,.form-inline .radio label{padding-left:0}.form-inline .checkbox input[type=checkbox],.form-inline .radio input[type=radio]{position:relative;margin-left:0}.form-inline .has-feedback .form-control-feedback{top:0}}.form-horizontal .checkbox,.form-horizontal .checkbox-inline,.form-horizontal .radio,.form-horizontal .radio-inline{padding-top:7px;margin-top:0;margin-bottom:0}.form-horizontal .checkbox,.form-horizontal .radio{min-height:27px}.form-horizontal .form-group{margin-right:-15px;margin-left:-15px}@media (min-width:768px){.form-horizontal .control-label{padding-top:7px;margin-bottom:0;text-align:right}}.form-horizontal .has-feedback .form-control-feedback{right:15px}@media (min-width:768px){.form-horizontal .form-group-lg .control-label{padding-top:11px;font-size:18px}}@media (min-width:768px){.form-horizontal .form-group-sm .control-label{padding-top:6px;font-size:12px}}.btn{display:inline-block;padding:6px 12px;margin-bottom:0;font-size:14px;font-weight:400;line-height:1.42857143;text-align:center;white-space:nowrap;vertical-align:middle;-ms-touch-action:manipulation;touch-action:manipulation;cursor:pointer;-webkit-user-select:none;-moz-user-select:none;-ms-user-select:none;user-select:none;background-image:none;border:1px solid transparent;border-radius:4px}.btn.active.focus,.btn.active:focus,.btn.focus,.btn:active.focus,.btn:active:focus,.btn:focus{outline:5px auto -webkit-focus-ring-color;outline-offset:-2px}.btn.focus,.btn:focus,.btn:hover{color:#333;text-decoration:none}.btn.active,.btn:active{background-image:none;outline:0;-webkit-box-shadow:inset 0 3px 5px rgba(0,0,0,.125);box-shadow:inset 0 3px 5px rgba(0,0,0,.125)}.btn.disabled,.btn[disabled],fieldset[disabled] .btn{cursor:not-allowed;filter:alpha(opacity=65);-webkit-box-shadow:none;box-shadow:none;opacity:.65}a.btn.disabled,fieldset[disabled] a.btn{pointer-events:none}.btn-default{color:#333;background-color:#fff;border-color:#ccc}.btn-default.focus,.btn-default:focus{color:#333;background-color:#e6e6e6;border-color:#8c8c8c}.btn-default:hover{color:#333;background-color:#e6e6e6;border-color:#adadad}.btn-default.active,.btn-default:active,.open>.dropdown-toggle.btn-default{color:#333;background-color:#e6e6e6;border-color:#adadad}.btn-default.active.focus,.btn-default.active:focus,.btn-default.active:hover,.btn-default:active.focus,.btn-default:active:focus,.btn-default:active:hover,.open>.dropdown-toggle.btn-default.focus,.open>.dropdown-toggle.btn-default:focus,.open>.dropdown-toggle.btn-default:hover{color:#333;background-color:#d4d4d4;border-color:#8c8c8c}.btn-default.active,.btn-default:active,.open>.dropdown-toggle.btn-default{background-image:none}.btn-default.disabled.focus,.btn-default.disabled:focus,.btn-default.disabled:hover,.btn-default[disabled].focus,.btn-default[disabled]:focus,.btn-default[disabled]:hover,fieldset[disabled] .btn-default.focus,fieldset[disabled] .btn-default:focus,fieldset[disabled] .btn-default:hover{background-color:#fff;border-color:#ccc}.btn-default .badge{color:#fff;background-color:#333}.btn-primary{color:#fff;background-color:#337ab7;border-color:#2e6da4}.btn-primary.focus,.btn-primary:focus{color:#fff;background-color:#286090;border-color:#122b40}.btn-primary:hover{color:#fff;background-color:#286090;border-color:#204d74}.btn-primary.active,.btn-primary:active,.open>.dropdown-toggle.btn-primary{color:#fff;background-color:#286090;border-color:#204d74}.btn-primary.active.focus,.btn-primary.active:focus,.btn-primary.active:hover,.btn-primary:active.focus,.btn-primary:active:focus,.btn-primary:active:hover,.open>.dropdown-toggle.btn-primary.focus,.open>.dropdown-toggle.btn-primary:focus,.open>.dropdown-toggle.btn-primary:hover{color:#fff;background-color:#204d74;border-color:#122b40}.btn-primary.active,.btn-primary:active,.open>.dropdown-toggle.btn-primary{background-image:none}.btn-primary.disabled.focus,.btn-primary.disabled:focus,.btn-primary.disabled:hover,.btn-primary[disabled].focus,.btn-primary[disabled]:focus,.btn-primary[disabled]:hover,fieldset[disabled] .btn-primary.focus,fieldset[disabled] .btn-primary:focus,fieldset[disabled] .btn-primary:hover{background-color:#337ab7;border-color:#2e6da4}.btn-primary .badge{color:#337ab7;background-color:#fff}.btn-success{color:#fff;background-color:#5cb85c;border-color:#4cae4c}.btn-success.focus,.btn-success:focus{color:#fff;background-color:#449d44;border-color:#255625}.btn-success:hover{color:#fff;background-color:#449d44;border-color:#398439}.btn-success.active,.btn-success:active,.open>.dropdown-toggle.btn-success{color:#fff;background-color:#449d44;border-color:#398439}.btn-success.active.focus,.btn-success.active:focus,.btn-success.active:hover,.btn-success:active.focus,.btn-success:active:focus,.btn-success:active:hover,.open>.dropdown-toggle.btn-success.focus,.open>.dropdown-toggle.btn-success:focus,.open>.dropdown-toggle.btn-success:hover{color:#fff;background-color:#398439;border-color:#255625}.btn-success.active,.btn-success:active,.open>.dropdown-toggle.btn-success{background-image:none}.btn-success.disabled.focus,.btn-success.disabled:focus,.btn-success.disabled:hover,.btn-success[disabled].focus,.btn-success[disabled]:focus,.btn-success[disabled]:hover,fieldset[disabled] .btn-success.focus,fieldset[disabled] .btn-success:focus,fieldset[disabled] .btn-success:hover{background-color:#5cb85c;border-color:#4cae4c}.btn-success .badge{color:#5cb85c;background-color:#fff}.btn-info{color:#fff;background-color:#5bc0de;border-color:#46b8da}.btn-info.focus,.btn-info:focus{color:#fff;background-color:#31b0d5;border-color:#1b6d85}.btn-info:hover{color:#fff;background-color:#31b0d5;border-color:#269abc}.btn-info.active,.btn-info:active,.open>.dropdown-toggle.btn-info{color:#fff;background-color:#31b0d5;border-color:#269abc}.btn-info.active.focus,.btn-info.active:focus,.btn-info.active:hover,.btn-info:active.focus,.btn-info:active:focus,.btn-info:active:hover,.open>.dropdown-toggle.btn-info.focus,.open>.dropdown-toggle.btn-info:focus,.open>.dropdown-toggle.btn-info:hover{color:#fff;background-color:#269abc;border-color:#1b6d85}.btn-info.active,.btn-info:active,.open>.dropdown-toggle.btn-info{background-image:none}.btn-info.disabled.focus,.btn-info.disabled:focus,.btn-info.disabled:hover,.btn-info[disabled].focus,.btn-info[disabled]:focus,.btn-info[disabled]:hover,fieldset[disabled] .btn-info.focus,fieldset[disabled] .btn-info:focus,fieldset[disabled] .btn-info:hover{background-color:#5bc0de;border-color:#46b8da}.btn-info .badge{color:#5bc0de;background-color:#fff}.btn-warning{color:#fff;background-color:#f0ad4e;border-color:#eea236}.btn-warning.focus,.btn-warning:focus{color:#fff;background-color:#ec971f;border-color:#985f0d}.btn-warning:hover{color:#fff;background-color:#ec971f;border-color:#d58512}.btn-warning.active,.btn-warning:active,.open>.dropdown-toggle.btn-warning{color:#fff;background-color:#ec971f;border-color:#d58512}.btn-warning.active.focus,.btn-warning.active:focus,.btn-warning.active:hover,.btn-warning:active.focus,.btn-warning:active:focus,.btn-warning:active:hover,.open>.dropdown-toggle.btn-warning.focus,.open>.dropdown-toggle.btn-warning:focus,.open>.dropdown-toggle.btn-warning:hover{color:#fff;background-color:#d58512;border-color:#985f0d}.btn-warning.active,.btn-warning:active,.open>.dropdown-toggle.btn-warning{background-image:none}.btn-warning.disabled.focus,.btn-warning.disabled:focus,.btn-warning.disabled:hover,.btn-warning[disabled].focus,.btn-warning[disabled]:focus,.btn-warning[disabled]:hover,fieldset[disabled] .btn-warning.focus,fieldset[disabled] .btn-warning:focus,fieldset[disabled] .btn-warning:hover{background-color:#f0ad4e;border-color:#eea236}.btn-warning .badge{color:#f0ad4e;background-color:#fff}.btn-danger{color:#fff;background-color:#d9534f;border-color:#d43f3a}.btn-danger.focus,.btn-danger:focus{color:#fff;background-color:#c9302c;border-color:#761c19}.btn-danger:hover{color:#fff;background-color:#c9302c;border-color:#ac2925}.btn-danger.active,.btn-danger:active,.open>.dropdown-toggle.btn-danger{color:#fff;background-color:#c9302c;border-color:#ac2925}.btn-danger.active.focus,.btn-danger.active:focus,.btn-danger.active:hover,.btn-danger:active.focus,.btn-danger:active:focus,.btn-danger:active:hover,.open>.dropdown-toggle.btn-danger.focus,.open>.dropdown-toggle.btn-danger:focus,.open>.dropdown-toggle.btn-danger:hover{color:#fff;background-color:#ac2925;border-color:#761c19}.btn-danger.active,.btn-danger:active,.open>.dropdown-toggle.btn-danger{background-image:none}.btn-danger.disabled.focus,.btn-danger.disabled:focus,.btn-danger.disabled:hover,.btn-danger[disabled].focus,.btn-danger[disabled]:focus,.btn-danger[disabled]:hover,fieldset[disabled] .btn-danger.focus,fieldset[disabled] .btn-danger:focus,fieldset[disabled] .btn-danger:hover{background-color:#d9534f;border-color:#d43f3a}.btn-danger .badge{color:#d9534f;background-color:#fff}.btn-link{font-weight:400;color:#337ab7;border-radius:0}.btn-link,.btn-link.active,.btn-link:active,.btn-link[disabled],fieldset[disabled] .btn-link{background-color:transparent;-webkit-box-shadow:none;box-shadow:none}.btn-link,.btn-link:active,.btn-link:focus,.btn-link:hover{border-color:transparent}.btn-link:focus,.btn-link:hover{color:#23527c;text-decoration:underline;background-color:transparent}.btn-link[disabled]:focus,.btn-link[disabled]:hover,fieldset[disabled] .btn-link:focus,fieldset[disabled] .btn-link:hover{color:#777;text-decoration:none}.btn-group-lg>.btn,.btn-lg{padding:10px 16px;font-size:18px;line-height:1.3333333;border-radius:6px}.btn-group-sm>.btn,.btn-sm{padding:5px 10px;font-size:12px;line-height:1.5;border-radius:3px}.btn-group-xs>.btn,.btn-xs{padding:1px 5px;font-size:12px;line-height:1.5;border-radius:3px}.btn-block{display:block;width:100%}.btn-block+.btn-block{margin-top:5px}input[type=button].btn-block,input[type=reset].btn-block,input[type=submit].btn-block{width:100%}.fade{opacity:0;-webkit-transition:opacity .15s linear;-o-transition:opacity .15s linear;transition:opacity .15s linear}.fade.in{opacity:1}.collapse{display:none}.collapse.in{display:block}tr.collapse.in{display:table-row}tbody.collapse.in{display:table-row-group}.collapsing{position:relative;height:0;overflow:hidden;-webkit-transition-timing-function:ease;-o-transition-timing-function:ease;transition-timing-function:ease;-webkit-transition-duration:.35s;-o-transition-duration:.35s;transition-duration:.35s;-webkit-transition-property:height,visibility;-o-transition-property:height,visibility;transition-property:height,visibility}.caret{display:inline-block;width:0;height:0;margin-left:2px;vertical-align:middle;border-top:4px dashed;border-top:4px solid\9;border-right:4px solid transparent;border-left:4px solid transparent}.dropdown,.dropup{position:relative}.dropdown-toggle:focus{outline:0}.dropdown-menu{position:absolute;top:100%;left:0;z-index:1000;display:none;float:left;min-width:160px;padding:5px 0;margin:2px 0 0;font-size:14px;text-align:left;list-style:none;background-color:#fff;-webkit-background-clip:padding-box;background-clip:padding-box;border:1px solid #ccc;border:1px solid rgba(0,0,0,.15);border-radius:4px;-webkit-box-shadow:0 6px 12px rgba(0,0,0,.175);box-shadow:0 6px 12px rgba(0,0,0,.175)}.dropdown-menu.pull-right{right:0;left:auto}.dropdown-menu .divider{height:1px;margin:9px 0;overflow:hidden;background-color:#e5e5e5}.dropdown-menu>li>a{display:block;padding:3px 20px;clear:both;font-weight:400;line-height:1.42857143;color:#333;white-space:nowrap}.dropdown-menu>li>a:focus,.dropdown-menu>li>a:hover{color:#262626;text-decoration:none;background-color:#f5f5f5}.dropdown-menu>.active>a,.dropdown-menu>.active>a:focus,.dropdown-menu>.active>a:hover{color:#fff;text-decoration:none;background-color:#337ab7;outline:0}.dropdown-menu>.disabled>a,.dropdown-menu>.disabled>a:focus,.dropdown-menu>.disabled>a:hover{color:#777}.dropdown-menu>.disabled>a:focus,.dropdown-menu>.disabled>a:hover{text-decoration:none;cursor:not-allowed;background-color:transparent;background-image:none;filter:progid:DXImageTransform.Microsoft.gradient(enabled=false)}.open>.dropdown-menu{display:block}.open>a{outline:0}.dropdown-menu-right{right:0;left:auto}.dropdown-menu-left{right:auto;left:0}.dropdown-header{display:block;padding:3px 20px;font-size:12px;line-height:1.42857143;color:#777;white-space:nowrap}.dropdown-backdrop{position:fixed;top:0;right:0;bottom:0;left:0;z-index:990}.pull-right>.dropdown-menu{right:0;left:auto}.dropup .caret,.navbar-fixed-bottom .dropdown .caret{content:"";border-top:0;border-bottom:4px dashed;border-bottom:4px solid\9}.dropup .dropdown-menu,.navbar-fixed-bottom .dropdown .dropdown-menu{top:auto;bottom:100%;margin-bottom:2px}@media (min-width:768px){.navbar-right .dropdown-menu{right:0;left:auto}.navbar-right .dropdown-menu-left{right:auto;left:0}}.btn-group,.btn-group-vertical{position:relative;display:inline-block;vertical-align:middle}.btn-group-vertical>.btn,.btn-group>.btn{position:relative;float:left}.btn-group-vertical>.btn.active,.btn-group-vertical>.btn:active,.btn-group-vertical>.btn:focus,.btn-group-vertical>.btn:hover,.btn-group>.btn.active,.btn-group>.btn:active,.btn-group>.btn:focus,.btn-group>.btn:hover{z-index:2}.btn-group .btn+.btn,.btn-group .btn+.btn-group,.btn-group .btn-group+.btn,.btn-group .btn-group+.btn-group{margin-left:-1px}.btn-toolbar{margin-left:-5px}.btn-toolbar .btn,.btn-toolbar .btn-group,.btn-toolbar .input-group{float:left}.btn-toolbar>.btn,.btn-toolbar>.btn-group,.btn-toolbar>.input-group{margin-left:5px}.btn-group>.btn:not(:first-child):not(:last-child):not(.dropdown-toggle){border-radius:0}.btn-group>.btn:first-child{margin-left:0}.btn-group>.btn:first-child:not(:last-child):not(.dropdown-toggle){border-top-right-radius:0;border-bottom-right-radius:0}.btn-group>.btn:last-child:not(:first-child),.btn-group>.dropdown-toggle:not(:first-child){border-top-left-radius:0;border-bottom-left-radius:0}.btn-group>.btn-group{float:left}.btn-group>.btn-group:not(:first-child):not(:last-child)>.btn{border-radius:0}.btn-group>.btn-group:first-child:not(:last-child)>.btn:last-child,.btn-group>.btn-group:first-child:not(:last-child)>.dropdown-toggle{border-top-right-radius:0;border-bottom-right-radius:0}.btn-group>.btn-group:last-child:not(:first-child)>.btn:first-child{border-top-left-radius:0;border-bottom-left-radius:0}.btn-group .dropdown-toggle:active,.btn-group.open .dropdown-toggle{outline:0}.btn-group>.btn+.dropdown-toggle{padding-right:8px;padding-left:8px}.btn-group>.btn-lg+.dropdown-toggle{padding-right:12px;padding-left:12px}.btn-group.open .dropdown-toggle{-webkit-box-shadow:inset 0 3px 5px rgba(0,0,0,.125);box-shadow:inset 0 3px 5px rgba(0,0,0,.125)}.btn-group.open .dropdown-toggle.btn-link{-webkit-box-shadow:none;box-shadow:none}.btn .caret{margin-left:0}.btn-lg .caret{border-width:5px 5px 0;border-bottom-width:0}.dropup .btn-lg .caret{border-width:0 5px 5px}.btn-group-vertical>.btn,.btn-group-vertical>.btn-group,.btn-group-vertical>.btn-group>.btn{display:block;float:none;width:100%;max-width:100%}.btn-group-vertical>.btn-group>.btn{float:none}.btn-group-vertical>.btn+.btn,.btn-group-vertical>.btn+.btn-group,.btn-group-vertical>.btn-group+.btn,.btn-group-vertical>.btn-group+.btn-group{margin-top:-1px;margin-left:0}.btn-group-vertical>.btn:not(:first-child):not(:last-child){border-radius:0}.btn-group-vertical>.btn:first-child:not(:last-child){border-top-left-radius:4px;border-top-right-radius:4px;border-bottom-right-radius:0;border-bottom-left-radius:0}.btn-group-vertical>.btn:last-child:not(:first-child){border-top-left-radius:0;border-top-right-radius:0;border-bottom-right-radius:4px;border-bottom-left-radius:4px}.btn-group-vertical>.btn-group:not(:first-child):not(:last-child)>.btn{border-radius:0}.btn-group-vertical>.btn-group:first-child:not(:last-child)>.btn:last-child,.btn-group-vertical>.btn-group:first-child:not(:last-child)>.dropdown-toggle{border-bottom-right-radius:0;border-bottom-left-radius:0}.btn-group-vertical>.btn-group:last-child:not(:first-child)>.btn:first-child{border-top-left-radius:0;border-top-right-radius:0}.btn-group-justified{display:table;width:100%;table-layout:fixed;border-collapse:separate}.btn-group-justified>.btn,.btn-group-justified>.btn-group{display:table-cell;float:none;width:1%}.btn-group-justified>.btn-group .btn{width:100%}.btn-group-justified>.btn-group .dropdown-menu{left:auto}[data-toggle=buttons]>.btn input[type=checkbox],[data-toggle=buttons]>.btn input[type=radio],[data-toggle=buttons]>.btn-group>.btn input[type=checkbox],[data-toggle=buttons]>.btn-group>.btn input[type=radio]{position:absolute;clip:rect(0,0,0,0);pointer-events:none}.input-group{position:relative;display:table;border-collapse:separate}.input-group[class*=col-]{float:none;padding-right:0;padding-left:0}.input-group .form-control{position:relative;z-index:2;float:left;width:100%;margin-bottom:0}.input-group .form-control:focus{z-index:3}.input-group-lg>.form-control,.input-group-lg>.input-group-addon,.input-group-lg>.input-group-btn>.btn{height:46px;padding:10px 16px;font-size:18px;line-height:1.3333333;border-radius:6px}select.input-group-lg>.form-control,select.input-group-lg>.input-group-addon,select.input-group-lg>.input-group-btn>.btn{height:46px;line-height:46px}select[multiple].input-group-lg>.form-control,select[multiple].input-group-lg>.input-group-addon,select[multiple].input-group-lg>.input-group-btn>.btn,textarea.input-group-lg>.form-control,textarea.input-group-lg>.input-group-addon,textarea.input-group-lg>.input-group-btn>.btn{height:auto}.input-group-sm>.form-control,.input-group-sm>.input-group-addon,.input-group-sm>.input-group-btn>.btn{height:30px;padding:5px 10px;font-size:12px;line-height:1.5;border-radius:3px}select.input-group-sm>.form-control,select.input-group-sm>.input-group-addon,select.input-group-sm>.input-group-btn>.btn{height:30px;line-height:30px}select[multiple].input-group-sm>.form-control,select[multiple].input-group-sm>.input-group-addon,select[multiple].input-group-sm>.input-group-btn>.btn,textarea.input-group-sm>.form-control,textarea.input-group-sm>.input-group-addon,textarea.input-group-sm>.input-group-btn>.btn{height:auto}.input-group .form-control,.input-group-addon,.input-group-btn{display:table-cell}.input-group .form-control:not(:first-child):not(:last-child),.input-group-addon:not(:first-child):not(:last-child),.input-group-btn:not(:first-child):not(:last-child){border-radius:0}.input-group-addon,.input-group-btn{width:1%;white-space:nowrap;vertical-align:middle}.input-group-addon{padding:6px 12px;font-size:14px;font-weight:400;line-height:1;color:#555;text-align:center;background-color:#eee;border:1px solid #ccc;border-radius:4px}.input-group-addon.input-sm{padding:5px 10px;font-size:12px;border-radius:3px}.input-group-addon.input-lg{padding:10px 16px;font-size:18px;border-radius:6px}.input-group-addon input[type=checkbox],.input-group-addon input[type=radio]{margin-top:0}.input-group .form-control:first-child,.input-group-addon:first-child,.input-group-btn:first-child>.btn,.input-group-btn:first-child>.btn-group>.btn,.input-group-btn:first-child>.dropdown-toggle,.input-group-btn:last-child>.btn-group:not(:last-child)>.btn,.input-group-btn:last-child>.btn:not(:last-child):not(.dropdown-toggle){border-top-right-radius:0;border-bottom-right-radius:0}.input-group-addon:first-child{border-right:0}.input-group .form-control:last-child,.input-group-addon:last-child,.input-group-btn:first-child>.btn-group:not(:first-child)>.btn,.input-group-btn:first-child>.btn:not(:first-child),.input-group-btn:last-child>.btn,.input-group-btn:last-child>.btn-group>.btn,.input-group-btn:last-child>.dropdown-toggle{border-top-left-radius:0;border-bottom-left-radius:0}.input-group-addon:last-child{border-left:0}.input-group-btn{position:relative;font-size:0;white-space:nowrap}.input-group-btn>.btn{position:relative}.input-group-btn>.btn+.btn{margin-left:-1px}.input-group-btn>.btn:active,.input-group-btn>.btn:focus,.input-group-btn>.btn:hover{z-index:2}.input-group-btn:first-child>.btn,.input-group-btn:first-child>.btn-group{margin-right:-1px}.input-group-btn:last-child>.btn,.input-group-btn:last-child>.btn-group{z-index:2;margin-left:-1px}.nav{padding-left:0;margin-bottom:0;list-style:none}.nav>li{position:relative;display:block}.nav>li>a{position:relative;display:block;padding:10px 15px}.nav>li>a:focus,.nav>li>a:hover{text-decoration:none;background-color:#eee}.nav>li.disabled>a{color:#777}.nav>li.disabled>a:focus,.nav>li.disabled>a:hover{color:#777;text-decoration:none;cursor:not-allowed;background-color:transparent}.nav .open>a,.nav .open>a:focus,.nav .open>a:hover{background-color:#eee;border-color:#337ab7}.nav .nav-divider{height:1px;margin:9px 0;overflow:hidden;background-color:#e5e5e5}.nav>li>a>img{max-width:none}.nav-tabs{border-bottom:1px solid #ddd}.nav-tabs>li{float:left;margin-bottom:-1px}.nav-tabs>li>a{margin-right:2px;line-height:1.42857143;border:1px solid transparent;border-radius:4px 4px 0 0}.nav-tabs>li>a:hover{border-color:#eee #eee #ddd}.nav-tabs>li.active>a,.nav-tabs>li.active>a:focus,.nav-tabs>li.active>a:hover{color:#555;cursor:default;background-color:#fff;border:1px solid #ddd;border-bottom-color:transparent}.nav-tabs.nav-justified{width:100%;border-bottom:0}.nav-tabs.nav-justified>li{float:none}.nav-tabs.nav-justified>li>a{margin-bottom:5px;text-align:center}.nav-tabs.nav-justified>.dropdown .dropdown-menu{top:auto;left:auto}@media (min-width:768px){.nav-tabs.nav-justified>li{display:table-cell;width:1%}.nav-tabs.nav-justified>li>a{margin-bottom:0}}.nav-tabs.nav-justified>li>a{margin-right:0;border-radius:4px}.nav-tabs.nav-justified>.active>a,.nav-tabs.nav-justified>.active>a:focus,.nav-tabs.nav-justified>.active>a:hover{border:1px solid #ddd}@media (min-width:768px){.nav-tabs.nav-justified>li>a{border-bottom:1px solid #ddd;border-radius:4px 4px 0 0}.nav-tabs.nav-justified>.active>a,.nav-tabs.nav-justified>.active>a:focus,.nav-tabs.nav-justified>.active>a:hover{border-bottom-color:#fff}}.nav-pills>li{float:left}.nav-pills>li>a{border-radius:4px}.nav-pills>li+li{margin-left:2px}.nav-pills>li.active>a,.nav-pills>li.active>a:focus,.nav-pills>li.active>a:hover{color:#fff;background-color:#337ab7}.nav-stacked>li{float:none}.nav-stacked>li+li{margin-top:2px;margin-left:0}.nav-justified{width:100%}.nav-justified>li{float:none}.nav-justified>li>a{margin-bottom:5px;text-align:center}.nav-justified>.dropdown .dropdown-menu{top:auto;left:auto}@media (min-width:768px){.nav-justified>li{display:table-cell;width:1%}.nav-justified>li>a{margin-bottom:0}}.nav-tabs-justified{border-bottom:0}.nav-tabs-justified>li>a{margin-right:0;border-radius:4px}.nav-tabs-justified>.active>a,.nav-tabs-justified>.active>a:focus,.nav-tabs-justified>.active>a:hover{border:1px solid #ddd}@media (min-width:768px){.nav-tabs-justified>li>a{border-bottom:1px solid #ddd;border-radius:4px 4px 0 0}.nav-tabs-justified>.active>a,.nav-tabs-justified>.active>a:focus,.nav-tabs-justified>.active>a:hover{border-bottom-color:#fff}}.tab-content>.tab-pane{display:none}.tab-content>.active{display:block}.nav-tabs .dropdown-menu{margin-top:-1px;border-top-left-radius:0;border-top-right-radius:0}.navbar{position:relative;min-height:50px;margin-bottom:20px;border:1px solid transparent}@media (min-width:768px){.navbar{border-radius:4px}}@media (min-width:768px){.navbar-header{float:left}}.navbar-collapse{padding-right:15px;padding-left:15px;overflow-x:visible;-webkit-overflow-scrolling:touch;border-top:1px solid transparent;-webkit-box-shadow:inset 0 1px 0 rgba(255,255,255,.1);box-shadow:inset 0 1px 0 rgba(255,255,255,.1)}.navbar-collapse.in{overflow-y:auto}@media (min-width:768px){.navbar-collapse{width:auto;border-top:0;-webkit-box-shadow:none;box-shadow:none}.navbar-collapse.collapse{display:block!important;height:auto!important;padding-bottom:0;overflow:visible!important}.navbar-collapse.in{overflow-y:visible}.navbar-fixed-bottom .navbar-collapse,.navbar-fixed-top .navbar-collapse,.navbar-static-top .navbar-collapse{padding-right:0;padding-left:0}}.navbar-fixed-bottom .navbar-collapse,.navbar-fixed-top .navbar-collapse{max-height:340px}@media (max-device-width:480px) and (orientation:landscape){.navbar-fixed-bottom .navbar-collapse,.navbar-fixed-top .navbar-collapse{max-height:200px}}.container-fluid>.navbar-collapse,.container-fluid>.navbar-header,.container>.navbar-collapse,.container>.navbar-header{margin-right:-15px;margin-left:-15px}@media (min-width:768px){.container-fluid>.navbar-collapse,.container-fluid>.navbar-header,.container>.navbar-collapse,.container>.navbar-header{margin-right:0;margin-left:0}}.navbar-static-top{z-index:1000;border-width:0 0 1px}@media (min-width:768px){.navbar-static-top{border-radius:0}}.navbar-fixed-bottom,.navbar-fixed-top{position:fixed;right:0;left:0;z-index:1030}@media (min-width:768px){.navbar-fixed-bottom,.navbar-fixed-top{border-radius:0}}.navbar-fixed-top{top:0;border-width:0 0 1px}.navbar-fixed-bottom{bottom:0;margin-bottom:0;border-width:1px 0 0}.navbar-brand{float:left;height:50px;padding:15px 15px;font-size:18px;line-height:20px}.navbar-brand:focus,.navbar-brand:hover{text-decoration:none}.navbar-brand>img{display:block}@media (min-width:768px){.navbar>.container .navbar-brand,.navbar>.container-fluid .navbar-brand{margin-left:-15px}}.navbar-toggle{position:relative;float:right;padding:9px 10px;margin-top:8px;margin-right:15px;margin-bottom:8px;background-color:transparent;background-image:none;border:1px solid transparent;border-radius:4px}.navbar-toggle:focus{outline:0}.navbar-toggle .icon-bar{display:block;width:22px;height:2px;border-radius:1px}.navbar-toggle .icon-bar+.icon-bar{margin-top:4px}@media (min-width:768px){.navbar-toggle{display:none}}.navbar-nav{margin:7.5px -15px}.navbar-nav>li>a{padding-top:10px;padding-bottom:10px;line-height:20px}@media (max-width:767px){.navbar-nav .open .dropdown-menu{position:static;float:none;width:auto;margin-top:0;background-color:transparent;border:0;-webkit-box-shadow:none;box-shadow:none}.navbar-nav .open .dropdown-menu .dropdown-header,.navbar-nav .open .dropdown-menu>li>a{padding:5px 15px 5px 25px}.navbar-nav .open .dropdown-menu>li>a{line-height:20px}.navbar-nav .open .dropdown-menu>li>a:focus,.navbar-nav .open .dropdown-menu>li>a:hover{background-image:none}}@media (min-width:768px){.navbar-nav{float:left;margin:0}.navbar-nav>li{float:left}.navbar-nav>li>a{padding-top:15px;padding-bottom:15px}}.navbar-form{padding:10px 15px;margin-top:8px;margin-right:-15px;margin-bottom:8px;margin-left:-15px;border-top:1px solid transparent;border-bottom:1px solid transparent;-webkit-box-shadow:inset 0 1px 0 rgba(255,255,255,.1),0 1px 0 rgba(255,255,255,.1);box-shadow:inset 0 1px 0 rgba(255,255,255,.1),0 1px 0 rgba(255,255,255,.1)}@media (min-width:768px){.navbar-form .form-group{display:inline-block;margin-bottom:0;vertical-align:middle}.navbar-form .form-control{display:inline-block;width:auto;vertical-align:middle}.navbar-form .form-control-static{display:inline-block}.navbar-form .input-group{display:inline-table;vertical-align:middle}.navbar-form .input-group .form-control,.navbar-form .input-group .input-group-addon,.navbar-form .input-group .input-group-btn{width:auto}.navbar-form .input-group>.form-control{width:100%}.navbar-form .control-label{margin-bottom:0;vertical-align:middle}.navbar-form .checkbox,.navbar-form .radio{display:inline-block;margin-top:0;margin-bottom:0;vertical-align:middle}.navbar-form .checkbox label,.navbar-form .radio label{padding-left:0}.navbar-form .checkbox input[type=checkbox],.navbar-form .radio input[type=radio]{position:relative;margin-left:0}.navbar-form .has-feedback .form-control-feedback{top:0}}@media (max-width:767px){.navbar-form .form-group{margin-bottom:5px}.navbar-form .form-group:last-child{margin-bottom:0}}@media (min-width:768px){.navbar-form{width:auto;padding-top:0;padding-bottom:0;margin-right:0;margin-left:0;border:0;-webkit-box-shadow:none;box-shadow:none}}.navbar-nav>li>.dropdown-menu{margin-top:0;border-top-left-radius:0;border-top-right-radius:0}.navbar-fixed-bottom .navbar-nav>li>.dropdown-menu{margin-bottom:0;border-top-left-radius:4px;border-top-right-radius:4px;border-bottom-right-radius:0;border-bottom-left-radius:0}.navbar-btn{margin-top:8px;margin-bottom:8px}.navbar-btn.btn-sm{margin-top:10px;margin-bottom:10px}.navbar-btn.btn-xs{margin-top:14px;margin-bottom:14px}.navbar-text{margin-top:15px;margin-bottom:15px}@media (min-width:768px){.navbar-text{float:left;margin-right:15px;margin-left:15px}}@media (min-width:768px){.navbar-left{float:left!important}.navbar-right{float:right!important;margin-right:-15px}.navbar-right~.navbar-right{margin-right:0}}.navbar-default{background-color:#f8f8f8;border-color:#e7e7e7}.navbar-default .navbar-brand{color:#777}.navbar-default .navbar-brand:focus,.navbar-default .navbar-brand:hover{color:#5e5e5e;background-color:transparent}.navbar-default .navbar-text{color:#777}.navbar-default .navbar-nav>li>a{color:#777}.navbar-default .navbar-nav>li>a:focus,.navbar-default .navbar-nav>li>a:hover{color:#333;background-color:transparent}.navbar-default .navbar-nav>.active>a,.navbar-default .navbar-nav>.active>a:focus,.navbar-default .navbar-nav>.active>a:hover{color:#555;background-color:#e7e7e7}.navbar-default .navbar-nav>.disabled>a,.navbar-default .navbar-nav>.disabled>a:focus,.navbar-default .navbar-nav>.disabled>a:hover{color:#ccc;background-color:transparent}.navbar-default .navbar-toggle{border-color:#ddd}.navbar-default .navbar-toggle:focus,.navbar-default .navbar-toggle:hover{background-color:#ddd}.navbar-default .navbar-toggle .icon-bar{background-color:#888}.navbar-default .navbar-collapse,.navbar-default .navbar-form{border-color:#e7e7e7}.navbar-default .navbar-nav>.open>a,.navbar-default .navbar-nav>.open>a:focus,.navbar-default .navbar-nav>.open>a:hover{color:#555;background-color:#e7e7e7}@media (max-width:767px){.navbar-default .navbar-nav .open .dropdown-menu>li>a{color:#777}.navbar-default .navbar-nav .open .dropdown-menu>li>a:focus,.navbar-default .navbar-nav .open .dropdown-menu>li>a:hover{color:#333;background-color:transparent}.navbar-default .navbar-nav .open .dropdown-menu>.active>a,.navbar-default .navbar-nav .open .dropdown-menu>.active>a:focus,.navbar-default .navbar-nav .open .dropdown-menu>.active>a:hover{color:#555;background-color:#e7e7e7}.navbar-default .navbar-nav .open .dropdown-menu>.disabled>a,.navbar-default .navbar-nav .open .dropdown-menu>.disabled>a:focus,.navbar-default .navbar-nav .open .dropdown-menu>.disabled>a:hover{color:#ccc;background-color:transparent}}.navbar-default .navbar-link{color:#777}.navbar-default .navbar-link:hover{color:#333}.navbar-default .btn-link{color:#777}.navbar-default .btn-link:focus,.navbar-default .btn-link:hover{color:#333}.navbar-default .btn-link[disabled]:focus,.navbar-default .btn-link[disabled]:hover,fieldset[disabled] .navbar-default .btn-link:focus,fieldset[disabled] .navbar-default .btn-link:hover{color:#ccc}.navbar-inverse{background-color:#222;border-color:#080808}.navbar-inverse .navbar-brand{color:#9d9d9d}.navbar-inverse .navbar-brand:focus,.navbar-inverse .navbar-brand:hover{color:#fff;background-color:transparent}.navbar-inverse .navbar-text{color:#9d9d9d}.navbar-inverse .navbar-nav>li>a{color:#9d9d9d}.navbar-inverse .navbar-nav>li>a:focus,.navbar-inverse .navbar-nav>li>a:hover{color:#fff;background-color:transparent}.navbar-inverse .navbar-nav>.active>a,.navbar-inverse .navbar-nav>.active>a:focus,.navbar-inverse .navbar-nav>.active>a:hover{color:#fff;background-color:#080808}.navbar-inverse .navbar-nav>.disabled>a,.navbar-inverse .navbar-nav>.disabled>a:focus,.navbar-inverse .navbar-nav>.disabled>a:hover{color:#444;background-color:transparent}.navbar-inverse .navbar-toggle{border-color:#333}.navbar-inverse .navbar-toggle:focus,.navbar-inverse .navbar-toggle:hover{background-color:#333}.navbar-inverse .navbar-toggle .icon-bar{background-color:#fff}.navbar-inverse .navbar-collapse,.navbar-inverse .navbar-form{border-color:#101010}.navbar-inverse .navbar-nav>.open>a,.navbar-inverse .navbar-nav>.open>a:focus,.navbar-inverse .navbar-nav>.open>a:hover{color:#fff;background-color:#080808}@media (max-width:767px){.navbar-inverse .navbar-nav .open .dropdown-menu>.dropdown-header{border-color:#080808}.navbar-inverse .navbar-nav .open .dropdown-menu .divider{background-color:#080808}.navbar-inverse .navbar-nav .open .dropdown-menu>li>a{color:#9d9d9d}.navbar-inverse .navbar-nav .open .dropdown-menu>li>a:focus,.navbar-inverse .navbar-nav .open .dropdown-menu>li>a:hover{color:#fff;background-color:transparent}.navbar-inverse .navbar-nav .open .dropdown-menu>.active>a,.navbar-inverse .navbar-nav .open .dropdown-menu>.active>a:focus,.navbar-inverse .navbar-nav .open .dropdown-menu>.active>a:hover{color:#fff;background-color:#080808}.navbar-inverse .navbar-nav .open .dropdown-menu>.disabled>a,.navbar-inverse .navbar-nav .open .dropdown-menu>.disabled>a:focus,.navbar-inverse .navbar-nav .open .dropdown-menu>.disabled>a:hover{color:#444;background-color:transparent}}.navbar-inverse .navbar-link{color:#9d9d9d}.navbar-inverse .navbar-link:hover{color:#fff}.navbar-inverse .btn-link{color:#9d9d9d}.navbar-inverse .btn-link:focus,.navbar-inverse .btn-link:hover{color:#fff}.navbar-inverse .btn-link[disabled]:focus,.navbar-inverse .btn-link[disabled]:hover,fieldset[disabled] .navbar-inverse .btn-link:focus,fieldset[disabled] .navbar-inverse .btn-link:hover{color:#444}.breadcrumb{padding:8px 15px;margin-bottom:20px;list-style:none;background-color:#f5f5f5;border-radius:4px}.breadcrumb>li{display:inline-block}.breadcrumb>li+li:before{padding:0 5px;color:#ccc;content:"/\00a0"}.breadcrumb>.active{color:#777}.pagination{display:inline-block;padding-left:0;margin:20px 0;border-radius:4px}.pagination>li{display:inline}.pagination>li>a,.pagination>li>span{position:relative;float:left;padding:6px 12px;margin-left:-1px;line-height:1.42857143;color:#337ab7;text-decoration:none;background-color:#fff;border:1px solid #ddd}.pagination>li:first-child>a,.pagination>li:first-child>span{margin-left:0;border-top-left-radius:4px;border-bottom-left-radius:4px}.pagination>li:last-child>a,.pagination>li:last-child>span{border-top-right-radius:4px;border-bottom-right-radius:4px}.pagination>li>a:focus,.pagination>li>a:hover,.pagination>li>span:focus,.pagination>li>span:hover{z-index:2;color:#23527c;background-color:#eee;border-color:#ddd}.pagination>.active>a,.pagination>.active>a:focus,.pagination>.active>a:hover,.pagination>.active>span,.pagination>.active>span:focus,.pagination>.active>span:hover{z-index:3;color:#fff;cursor:default;background-color:#337ab7;border-color:#337ab7}.pagination>.disabled>a,.pagination>.disabled>a:focus,.pagination>.disabled>a:hover,.pagination>.disabled>span,.pagination>.disabled>span:focus,.pagination>.disabled>span:hover{color:#777;cursor:not-allowed;background-color:#fff;border-color:#ddd}.pagination-lg>li>a,.pagination-lg>li>span{padding:10px 16px;font-size:18px;line-height:1.3333333}.pagination-lg>li:first-child>a,.pagination-lg>li:first-child>span{border-top-left-radius:6px;border-bottom-left-radius:6px}.pagination-lg>li:last-child>a,.pagination-lg>li:last-child>span{border-top-right-radius:6px;border-bottom-right-radius:6px}.pagination-sm>li>a,.pagination-sm>li>span{padding:5px 10px;font-size:12px;line-height:1.5}.pagination-sm>li:first-child>a,.pagination-sm>li:first-child>span{border-top-left-radius:3px;border-bottom-left-radius:3px}.pagination-sm>li:last-child>a,.pagination-sm>li:last-child>span{border-top-right-radius:3px;border-bottom-right-radius:3px}.pager{padding-left:0;margin:20px 0;text-align:center;list-style:none}.pager li{display:inline}.pager li>a,.pager li>span{display:inline-block;padding:5px 14px;background-color:#fff;border:1px solid #ddd;border-radius:15px}.pager li>a:focus,.pager li>a:hover{text-decoration:none;background-color:#eee}.pager .next>a,.pager .next>span{float:right}.pager .previous>a,.pager .previous>span{float:left}.pager .disabled>a,.pager .disabled>a:focus,.pager .disabled>a:hover,.pager .disabled>span{color:#777;cursor:not-allowed;background-color:#fff}.label{display:inline;padding:.2em .6em .3em;font-size:75%;font-weight:700;line-height:1;color:#fff;text-align:center;white-space:nowrap;vertical-align:baseline;border-radius:.25em}a.label:focus,a.label:hover{color:#fff;text-decoration:none;cursor:pointer}.label:empty{display:none}.btn .label{position:relative;top:-1px}.label-default{background-color:#777}.label-default[href]:focus,.label-default[href]:hover{background-color:#5e5e5e}.label-primary{background-color:#337ab7}.label-primary[href]:focus,.label-primary[href]:hover{background-color:#286090}.label-success{background-color:#5cb85c}.label-success[href]:focus,.label-success[href]:hover{background-color:#449d44}.label-info{background-color:#5bc0de}.label-info[href]:focus,.label-info[href]:hover{background-color:#31b0d5}.label-warning{background-color:#f0ad4e}.label-warning[href]:focus,.label-warning[href]:hover{background-color:#ec971f}.label-danger{background-color:#d9534f}.label-danger[href]:focus,.label-danger[href]:hover{background-color:#c9302c}.badge{display:inline-block;min-width:10px;padding:3px 7px;font-size:12px;font-weight:700;line-height:1;color:#fff;text-align:center;white-space:nowrap;vertical-align:middle;background-color:#777;border-radius:10px}.badge:empty{display:none}.btn .badge{position:relative;top:-1px}.btn-group-xs>.btn .badge,.btn-xs .badge{top:0;padding:1px 5px}a.badge:focus,a.badge:hover{color:#fff;text-decoration:none;cursor:pointer}.list-group-item.active>.badge,.nav-pills>.active>a>.badge{color:#337ab7;background-color:#fff}.list-group-item>.badge{float:right}.list-group-item>.badge+.badge{margin-right:5px}.nav-pills>li>a>.badge{margin-left:3px}.jumbotron{padding-top:30px;padding-bottom:30px;margin-bottom:30px;color:inherit;background-color:#eee}.jumbotron .h1,.jumbotron h1{color:inherit}.jumbotron p{margin-bottom:15px;font-size:21px;font-weight:200}.jumbotron>hr{border-top-color:#d5d5d5}.container .jumbotron,.container-fluid .jumbotron{padding-right:15px;padding-left:15px;border-radius:6px}.jumbotron .container{max-width:100%}@media screen and (min-width:768px){.jumbotron{padding-top:48px;padding-bottom:48px}.container .jumbotron,.container-fluid .jumbotron{padding-right:60px;padding-left:60px}.jumbotron .h1,.jumbotron h1{font-size:63px}}.thumbnail{display:block;padding:4px;margin-bottom:20px;line-height:1.42857143;background-color:#fff;border:1px solid #ddd;border-radius:4px;-webkit-transition:border .2s ease-in-out;-o-transition:border .2s ease-in-out;transition:border .2s ease-in-out}.thumbnail a>img,.thumbnail>img{margin-right:auto;margin-left:auto}a.thumbnail.active,a.thumbnail:focus,a.thumbnail:hover{border-color:#337ab7}.thumbnail .caption{padding:9px;color:#333}.alert{padding:15px;margin-bottom:20px;border:1px solid transparent;border-radius:4px}.alert h4{margin-top:0;color:inherit}.alert .alert-link{font-weight:700}.alert>p,.alert>ul{margin-bottom:0}.alert>p+p{margin-top:5px}.alert-dismissable,.alert-dismissible{padding-right:35px}.alert-dismissable .close,.alert-dismissible .close{position:relative;top:-2px;right:-21px;color:inherit}.alert-success{color:#3c763d;background-color:#dff0d8;border-color:#d6e9c6}.alert-success hr{border-top-color:#c9e2b3}.alert-success .alert-link{color:#2b542c}.alert-info{color:#31708f;background-color:#d9edf7;border-color:#bce8f1}.alert-info hr{border-top-color:#a6e1ec}.alert-info .alert-link{color:#245269}.alert-warning{color:#8a6d3b;background-color:#fcf8e3;border-color:#faebcc}.alert-warning hr{border-top-color:#f7e1b5}.alert-warning .alert-link{color:#66512c}.alert-danger{color:#a94442;background-color:#f2dede;border-color:#ebccd1}.alert-danger hr{border-top-color:#e4b9c0}.alert-danger .alert-link{color:#843534}@-webkit-keyframes progress-bar-stripes{from{background-position:40px 0}to{background-position:0 0}}@-o-keyframes progress-bar-stripes{from{background-position:40px 0}to{background-position:0 0}}@keyframes progress-bar-stripes{from{background-position:40px 0}to{background-position:0 0}}.progress{height:20px;margin-bottom:20px;overflow:hidden;background-color:#f5f5f5;border-radius:4px;-webkit-box-shadow:inset 0 1px 2px rgba(0,0,0,.1);box-shadow:inset 0 1px 2px rgba(0,0,0,.1)}.progress-bar{float:left;width:0;height:100%;font-size:12px;line-height:20px;color:#fff;text-align:center;background-color:#337ab7;-webkit-box-shadow:inset 0 -1px 0 rgba(0,0,0,.15);box-shadow:inset 0 -1px 0 rgba(0,0,0,.15);-webkit-transition:width .6s ease;-o-transition:width .6s ease;transition:width .6s ease}.progress-bar-striped,.progress-striped .progress-bar{background-image:-webkit-linear-gradient(45deg,rgba(255,255,255,.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,.15) 50%,rgba(255,255,255,.15) 75%,transparent 75%,transparent);background-image:-o-linear-gradient(45deg,rgba(255,255,255,.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,.15) 50%,rgba(255,255,255,.15) 75%,transparent 75%,transparent);background-image:linear-gradient(45deg,rgba(255,255,255,.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,.15) 50%,rgba(255,255,255,.15) 75%,transparent 75%,transparent);-webkit-background-size:40px 40px;background-size:40px 40px}.progress-bar.active,.progress.active .progress-bar{-webkit-animation:progress-bar-stripes 2s linear infinite;-o-animation:progress-bar-stripes 2s linear infinite;animation:progress-bar-stripes 2s linear infinite}.progress-bar-success{background-color:#5cb85c}.progress-striped .progress-bar-success{background-image:-webkit-linear-gradient(45deg,rgba(255,255,255,.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,.15) 50%,rgba(255,255,255,.15) 75%,transparent 75%,transparent);background-image:-o-linear-gradient(45deg,rgba(255,255,255,.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,.15) 50%,rgba(255,255,255,.15) 75%,transparent 75%,transparent);background-image:linear-gradient(45deg,rgba(255,255,255,.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,.15) 50%,rgba(255,255,255,.15) 75%,transparent 75%,transparent)}.progress-bar-info{background-color:#5bc0de}.progress-striped .progress-bar-info{background-image:-webkit-linear-gradient(45deg,rgba(255,255,255,.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,.15) 50%,rgba(255,255,255,.15) 75%,transparent 75%,transparent);background-image:-o-linear-gradient(45deg,rgba(255,255,255,.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,.15) 50%,rgba(255,255,255,.15) 75%,transparent 75%,transparent);background-image:linear-gradient(45deg,rgba(255,255,255,.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,.15) 50%,rgba(255,255,255,.15) 75%,transparent 75%,transparent)}.progress-bar-warning{background-color:#f0ad4e}.progress-striped .progress-bar-warning{background-image:-webkit-linear-gradient(45deg,rgba(255,255,255,.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,.15) 50%,rgba(255,255,255,.15) 75%,transparent 75%,transparent);background-image:-o-linear-gradient(45deg,rgba(255,255,255,.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,.15) 50%,rgba(255,255,255,.15) 75%,transparent 75%,transparent);background-image:linear-gradient(45deg,rgba(255,255,255,.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,.15) 50%,rgba(255,255,255,.15) 75%,transparent 75%,transparent)}.progress-bar-danger{background-color:#d9534f}.progress-striped .progress-bar-danger{background-image:-webkit-linear-gradient(45deg,rgba(255,255,255,.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,.15) 50%,rgba(255,255,255,.15) 75%,transparent 75%,transparent);background-image:-o-linear-gradient(45deg,rgba(255,255,255,.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,.15) 50%,rgba(255,255,255,.15) 75%,transparent 75%,transparent);background-image:linear-gradient(45deg,rgba(255,255,255,.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,.15) 50%,rgba(255,255,255,.15) 75%,transparent 75%,transparent)}.media{margin-top:15px}.media:first-child{margin-top:0}.media,.media-body{overflow:hidden;zoom:1}.media-body{width:10000px}.media-object{display:block}.media-object.img-thumbnail{max-width:none}.media-right,.media>.pull-right{padding-left:10px}.media-left,.media>.pull-left{padding-right:10px}.media-body,.media-left,.media-right{display:table-cell;vertical-align:top}.media-middle{vertical-align:middle}.media-bottom{vertical-align:bottom}.media-heading{margin-top:0;margin-bottom:5px}.media-list{padding-left:0;list-style:none}.list-group{padding-left:0;margin-bottom:20px}.list-group-item{position:relative;display:block;padding:10px 15px;margin-bottom:-1px;background-color:#fff;border:1px solid #ddd}.list-group-item:first-child{border-top-left-radius:4px;border-top-right-radius:4px}.list-group-item:last-child{margin-bottom:0;border-bottom-right-radius:4px;border-bottom-left-radius:4px}a.list-group-item,button.list-group-item{color:#555}a.list-group-item .list-group-item-heading,button.list-group-item .list-group-item-heading{color:#333}a.list-group-item:focus,a.list-group-item:hover,button.list-group-item:focus,button.list-group-item:hover{color:#555;text-decoration:none;background-color:#f5f5f5}button.list-group-item{width:100%;text-align:left}.list-group-item.disabled,.list-group-item.disabled:focus,.list-group-item.disabled:hover{color:#777;cursor:not-allowed;background-color:#eee}.list-group-item.disabled .list-group-item-heading,.list-group-item.disabled:focus .list-group-item-heading,.list-group-item.disabled:hover .list-group-item-heading{color:inherit}.list-group-item.disabled .list-group-item-text,.list-group-item.disabled:focus .list-group-item-text,.list-group-item.disabled:hover .list-group-item-text{color:#777}.list-group-item.active,.list-group-item.active:focus,.list-group-item.active:hover{z-index:2;color:#fff;background-color:#337ab7;border-color:#337ab7}.list-group-item.active .list-group-item-heading,.list-group-item.active .list-group-item-heading>.small,.list-group-item.active .list-group-item-heading>small,.list-group-item.active:focus .list-group-item-heading,.list-group-item.active:focus .list-group-item-heading>.small,.list-group-item.active:focus .list-group-item-heading>small,.list-group-item.active:hover .list-group-item-heading,.list-group-item.active:hover .list-group-item-heading>.small,.list-group-item.active:hover .list-group-item-heading>small{color:inherit}.list-group-item.active .list-group-item-text,.list-group-item.active:focus .list-group-item-text,.list-group-item.active:hover .list-group-item-text{color:#c7ddef}.list-group-item-success{color:#3c763d;background-color:#dff0d8}a.list-group-item-success,button.list-group-item-success{color:#3c763d}a.list-group-item-success .list-group-item-heading,button.list-group-item-success .list-group-item-heading{color:inherit}a.list-group-item-success:focus,a.list-group-item-success:hover,button.list-group-item-success:focus,button.list-group-item-success:hover{color:#3c763d;background-color:#d0e9c6}a.list-group-item-success.active,a.list-group-item-success.active:focus,a.list-group-item-success.active:hover,button.list-group-item-success.active,button.list-group-item-success.active:focus,button.list-group-item-success.active:hover{color:#fff;background-color:#3c763d;border-color:#3c763d}.list-group-item-info{color:#31708f;background-color:#d9edf7}a.list-group-item-info,button.list-group-item-info{color:#31708f}a.list-group-item-info .list-group-item-heading,button.list-group-item-info .list-group-item-heading{color:inherit}a.list-group-item-info:focus,a.list-group-item-info:hover,button.list-group-item-info:focus,button.list-group-item-info:hover{color:#31708f;background-color:#c4e3f3}a.list-group-item-info.active,a.list-group-item-info.active:focus,a.list-group-item-info.active:hover,button.list-group-item-info.active,button.list-group-item-info.active:focus,button.list-group-item-info.active:hover{color:#fff;background-color:#31708f;border-color:#31708f}.list-group-item-warning{color:#8a6d3b;background-color:#fcf8e3}a.list-group-item-warning,button.list-group-item-warning{color:#8a6d3b}a.list-group-item-warning .list-group-item-heading,button.list-group-item-warning .list-group-item-heading{color:inherit}a.list-group-item-warning:focus,a.list-group-item-warning:hover,button.list-group-item-warning:focus,button.list-group-item-warning:hover{color:#8a6d3b;background-color:#faf2cc}a.list-group-item-warning.active,a.list-group-item-warning.active:focus,a.list-group-item-warning.active:hover,button.list-group-item-warning.active,button.list-group-item-warning.active:focus,button.list-group-item-warning.active:hover{color:#fff;background-color:#8a6d3b;border-color:#8a6d3b}.list-group-item-danger{color:#a94442;background-color:#f2dede}a.list-group-item-danger,button.list-group-item-danger{color:#a94442}a.list-group-item-danger .list-group-item-heading,button.list-group-item-danger .list-group-item-heading{color:inherit}a.list-group-item-danger:focus,a.list-group-item-danger:hover,button.list-group-item-danger:focus,button.list-group-item-danger:hover{color:#a94442;background-color:#ebcccc}a.list-group-item-danger.active,a.list-group-item-danger.active:focus,a.list-group-item-danger.active:hover,button.list-group-item-danger.active,button.list-group-item-danger.active:focus,button.list-group-item-danger.active:hover{color:#fff;background-color:#a94442;border-color:#a94442}.list-group-item-heading{margin-top:0;margin-bottom:5px}.list-group-item-text{margin-bottom:0;line-height:1.3}.panel{margin-bottom:20px;background-color:#fff;border:1px solid transparent;border-radius:4px;-webkit-box-shadow:0 1px 1px rgba(0,0,0,.05);box-shadow:0 1px 1px rgba(0,0,0,.05)}.panel-body{padding:15px}.panel-heading{padding:10px 15px;border-bottom:1px solid transparent;border-top-left-radius:3px;border-top-right-radius:3px}.panel-heading>.dropdown .dropdown-toggle{color:inherit}.panel-title{margin-top:0;margin-bottom:0;font-size:16px;color:inherit}.panel-title>.small,.panel-title>.small>a,.panel-title>a,.panel-title>small,.panel-title>small>a{color:inherit}.panel-footer{padding:10px 15px;background-color:#f5f5f5;border-top:1px solid #ddd;border-bottom-right-radius:3px;border-bottom-left-radius:3px}.panel>.list-group,.panel>.panel-collapse>.list-group{margin-bottom:0}.panel>.list-group .list-group-item,.panel>.panel-collapse>.list-group .list-group-item{border-width:1px 0;border-radius:0}.panel>.list-group:first-child .list-group-item:first-child,.panel>.panel-collapse>.list-group:first-child .list-group-item:first-child{border-top:0;border-top-left-radius:3px;border-top-right-radius:3px}.panel>.list-group:last-child .list-group-item:last-child,.panel>.panel-collapse>.list-group:last-child .list-group-item:last-child{border-bottom:0;border-bottom-right-radius:3px;border-bottom-left-radius:3px}.panel>.panel-heading+.panel-collapse>.list-group .list-group-item:first-child{border-top-left-radius:0;border-top-right-radius:0}.panel-heading+.list-group .list-group-item:first-child{border-top-width:0}.list-group+.panel-footer{border-top-width:0}.panel>.panel-collapse>.table,.panel>.table,.panel>.table-responsive>.table{margin-bottom:0}.panel>.panel-collapse>.table caption,.panel>.table caption,.panel>.table-responsive>.table caption{padding-right:15px;padding-left:15px}.panel>.table-responsive:first-child>.table:first-child,.panel>.table:first-child{border-top-left-radius:3px;border-top-right-radius:3px}.panel>.table-responsive:first-child>.table:first-child>tbody:first-child>tr:first-child,.panel>.table-responsive:first-child>.table:first-child>thead:first-child>tr:first-child,.panel>.table:first-child>tbody:first-child>tr:first-child,.panel>.table:first-child>thead:first-child>tr:first-child{border-top-left-radius:3px;border-top-right-radius:3px}.panel>.table-responsive:first-child>.table:first-child>tbody:first-child>tr:first-child td:first-child,.panel>.table-responsive:first-child>.table:first-child>tbody:first-child>tr:first-child th:first-child,.panel>.table-responsive:first-child>.table:first-child>thead:first-child>tr:first-child td:first-child,.panel>.table-responsive:first-child>.table:first-child>thead:first-child>tr:first-child th:first-child,.panel>.table:first-child>tbody:first-child>tr:first-child td:first-child,.panel>.table:first-child>tbody:first-child>tr:first-child th:first-child,.panel>.table:first-child>thead:first-child>tr:first-child td:first-child,.panel>.table:first-child>thead:first-child>tr:first-child th:first-child{border-top-left-radius:3px}.panel>.table-responsive:first-child>.table:first-child>tbody:first-child>tr:first-child td:last-child,.panel>.table-responsive:first-child>.table:first-child>tbody:first-child>tr:first-child th:last-child,.panel>.table-responsive:first-child>.table:first-child>thead:first-child>tr:first-child td:last-child,.panel>.table-responsive:first-child>.table:first-child>thead:first-child>tr:first-child th:last-child,.panel>.table:first-child>tbody:first-child>tr:first-child td:last-child,.panel>.table:first-child>tbody:first-child>tr:first-child th:last-child,.panel>.table:first-child>thead:first-child>tr:first-child td:last-child,.panel>.table:first-child>thead:first-child>tr:first-child th:last-child{border-top-right-radius:3px}.panel>.table-responsive:last-child>.table:last-child,.panel>.table:last-child{border-bottom-right-radius:3px;border-bottom-left-radius:3px}.panel>.table-responsive:last-child>.table:last-child>tbody:last-child>tr:last-child,.panel>.table-responsive:last-child>.table:last-child>tfoot:last-child>tr:last-child,.panel>.table:last-child>tbody:last-child>tr:last-child,.panel>.table:last-child>tfoot:last-child>tr:last-child{border-bottom-right-radius:3px;border-bottom-left-radius:3px}.panel>.table-responsive:last-child>.table:last-child>tbody:last-child>tr:last-child td:first-child,.panel>.table-responsive:last-child>.table:last-child>tbody:last-child>tr:last-child th:first-child,.panel>.table-responsive:last-child>.table:last-child>tfoot:last-child>tr:last-child td:first-child,.panel>.table-responsive:last-child>.table:last-child>tfoot:last-child>tr:last-child th:first-child,.panel>.table:last-child>tbody:last-child>tr:last-child td:first-child,.panel>.table:last-child>tbody:last-child>tr:last-child th:first-child,.panel>.table:last-child>tfoot:last-child>tr:last-child td:first-child,.panel>.table:last-child>tfoot:last-child>tr:last-child th:first-child{border-bottom-left-radius:3px}.panel>.table-responsive:last-child>.table:last-child>tbody:last-child>tr:last-child td:last-child,.panel>.table-responsive:last-child>.table:last-child>tbody:last-child>tr:last-child th:last-child,.panel>.table-responsive:last-child>.table:last-child>tfoot:last-child>tr:last-child td:last-child,.panel>.table-responsive:last-child>.table:last-child>tfoot:last-child>tr:last-child th:last-child,.panel>.table:last-child>tbody:last-child>tr:last-child td:last-child,.panel>.table:last-child>tbody:last-child>tr:last-child th:last-child,.panel>.table:last-child>tfoot:last-child>tr:last-child td:last-child,.panel>.table:last-child>tfoot:last-child>tr:last-child th:last-child{border-bottom-right-radius:3px}.panel>.panel-body+.table,.panel>.panel-body+.table-responsive,.panel>.table+.panel-body,.panel>.table-responsive+.panel-body{border-top:1px solid #ddd}.panel>.table>tbody:first-child>tr:first-child td,.panel>.table>tbody:first-child>tr:first-child th{border-top:0}.panel>.table-bordered,.panel>.table-responsive>.table-bordered{border:0}.panel>.table-bordered>tbody>tr>td:first-child,.panel>.table-bordered>tbody>tr>th:first-child,.panel>.table-bordered>tfoot>tr>td:first-child,.panel>.table-bordered>tfoot>tr>th:first-child,.panel>.table-bordered>thead>tr>td:first-child,.panel>.table-bordered>thead>tr>th:first-child,.panel>.table-responsive>.table-bordered>tbody>tr>td:first-child,.panel>.table-responsive>.table-bordered>tbody>tr>th:first-child,.panel>.table-responsive>.table-bordered>tfoot>tr>td:first-child,.panel>.table-responsive>.table-bordered>tfoot>tr>th:first-child,.panel>.table-responsive>.table-bordered>thead>tr>td:first-child,.panel>.table-responsive>.table-bordered>thead>tr>th:first-child{border-left:0}.panel>.table-bordered>tbody>tr>td:last-child,.panel>.table-bordered>tbody>tr>th:last-child,.panel>.table-bordered>tfoot>tr>td:last-child,.panel>.table-bordered>tfoot>tr>th:last-child,.panel>.table-bordered>thead>tr>td:last-child,.panel>.table-bordered>thead>tr>th:last-child,.panel>.table-responsive>.table-bordered>tbody>tr>td:last-child,.panel>.table-responsive>.table-bordered>tbody>tr>th:last-child,.panel>.table-responsive>.table-bordered>tfoot>tr>td:last-child,.panel>.table-responsive>.table-bordered>tfoot>tr>th:last-child,.panel>.table-responsive>.table-bordered>thead>tr>td:last-child,.panel>.table-responsive>.table-bordered>thead>tr>th:last-child{border-right:0}.panel>.table-bordered>tbody>tr:first-child>td,.panel>.table-bordered>tbody>tr:first-child>th,.panel>.table-bordered>thead>tr:first-child>td,.panel>.table-bordered>thead>tr:first-child>th,.panel>.table-responsive>.table-bordered>tbody>tr:first-child>td,.panel>.table-responsive>.table-bordered>tbody>tr:first-child>th,.panel>.table-responsive>.table-bordered>thead>tr:first-child>td,.panel>.table-responsive>.table-bordered>thead>tr:first-child>th{border-bottom:0}.panel>.table-bordered>tbody>tr:last-child>td,.panel>.table-bordered>tbody>tr:last-child>th,.panel>.table-bordered>tfoot>tr:last-child>td,.panel>.table-bordered>tfoot>tr:last-child>th,.panel>.table-responsive>.table-bordered>tbody>tr:last-child>td,.panel>.table-responsive>.table-bordered>tbody>tr:last-child>th,.panel>.table-responsive>.table-bordered>tfoot>tr:last-child>td,.panel>.table-responsive>.table-bordered>tfoot>tr:last-child>th{border-bottom:0}.panel>.table-responsive{margin-bottom:0;border:0}.panel-group{margin-bottom:20px}.panel-group .panel{margin-bottom:0;border-radius:4px}.panel-group .panel+.panel{margin-top:5px}.panel-group .panel-heading{border-bottom:0}.panel-group .panel-heading+.panel-collapse>.list-group,.panel-group .panel-heading+.panel-collapse>.panel-body{border-top:1px solid #ddd}.panel-group .panel-footer{border-top:0}.panel-group .panel-footer+.panel-collapse .panel-body{border-bottom:1px solid #ddd}.panel-default{border-color:#ddd}.panel-default>.panel-heading{color:#333;background-color:#f5f5f5;border-color:#ddd}.panel-default>.panel-heading+.panel-collapse>.panel-body{border-top-color:#ddd}.panel-default>.panel-heading .badge{color:#f5f5f5;background-color:#333}.panel-default>.panel-footer+.panel-collapse>.panel-body{border-bottom-color:#ddd}.panel-primary{border-color:#337ab7}.panel-primary>.panel-heading{color:#fff;background-color:#337ab7;border-color:#337ab7}.panel-primary>.panel-heading+.panel-collapse>.panel-body{border-top-color:#337ab7}.panel-primary>.panel-heading .badge{color:#337ab7;background-color:#fff}.panel-primary>.panel-footer+.panel-collapse>.panel-body{border-bottom-color:#337ab7}.panel-success{border-color:#d6e9c6}.panel-success>.panel-heading{color:#3c763d;background-color:#dff0d8;border-color:#d6e9c6}.panel-success>.panel-heading+.panel-collapse>.panel-body{border-top-color:#d6e9c6}.panel-success>.panel-heading .badge{color:#dff0d8;background-color:#3c763d}.panel-success>.panel-footer+.panel-collapse>.panel-body{border-bottom-color:#d6e9c6}.panel-info{border-color:#bce8f1}.panel-info>.panel-heading{color:#31708f;background-color:#d9edf7;border-color:#bce8f1}.panel-info>.panel-heading+.panel-collapse>.panel-body{border-top-color:#bce8f1}.panel-info>.panel-heading .badge{color:#d9edf7;background-color:#31708f}.panel-info>.panel-footer+.panel-collapse>.panel-body{border-bottom-color:#bce8f1}.panel-warning{border-color:#faebcc}.panel-warning>.panel-heading{color:#8a6d3b;background-color:#fcf8e3;border-color:#faebcc}.panel-warning>.panel-heading+.panel-collapse>.panel-body{border-top-color:#faebcc}.panel-warning>.panel-heading .badge{color:#fcf8e3;background-color:#8a6d3b}.panel-warning>.panel-footer+.panel-collapse>.panel-body{border-bottom-color:#faebcc}.panel-danger{border-color:#ebccd1}.panel-danger>.panel-heading{color:#a94442;background-color:#f2dede;border-color:#ebccd1}.panel-danger>.panel-heading+.panel-collapse>.panel-body{border-top-color:#ebccd1}.panel-danger>.panel-heading .badge{color:#f2dede;background-color:#a94442}.panel-danger>.panel-footer+.panel-collapse>.panel-body{border-bottom-color:#ebccd1}.embed-responsive{position:relative;display:block;height:0;padding:0;overflow:hidden}.embed-responsive .embed-responsive-item,.embed-responsive embed,.embed-responsive iframe,.embed-responsive object,.embed-responsive video{position:absolute;top:0;bottom:0;left:0;width:100%;height:100%;border:0}.embed-responsive-16by9{padding-bottom:56.25%}.embed-responsive-4by3{padding-bottom:75%}.well{min-height:20px;padding:19px;margin-bottom:20px;background-color:#f5f5f5;border:1px solid #e3e3e3;border-radius:4px;-webkit-box-shadow:inset 0 1px 1px rgba(0,0,0,.05);box-shadow:inset 0 1px 1px rgba(0,0,0,.05)}.well blockquote{border-color:#ddd;border-color:rgba(0,0,0,.15)}.well-lg{padding:24px;border-radius:6px}.well-sm{padding:9px;border-radius:3px}.close{float:right;font-size:21px;font-weight:700;line-height:1;color:#000;text-shadow:0 1px 0 #fff;filter:alpha(opacity=20);opacity:.2}.close:focus,.close:hover{color:#000;text-decoration:none;cursor:pointer;filter:alpha(opacity=50);opacity:.5}button.close{-webkit-appearance:none;padding:0;cursor:pointer;background:0 0;border:0}.modal-open{overflow:hidden}.modal{position:fixed;top:0;right:0;bottom:0;left:0;z-index:1050;display:none;overflow:hidden;-webkit-overflow-scrolling:touch;outline:0}.modal.fade .modal-dialog{-webkit-transition:-webkit-transform .3s ease-out;-o-transition:-o-transform .3s ease-out;transition:transform .3s ease-out;-webkit-transform:translate(0,-25%);-ms-transform:translate(0,-25%);-o-transform:translate(0,-25%);transform:translate(0,-25%)}.modal.in .modal-dialog{-webkit-transform:translate(0,0);-ms-transform:translate(0,0);-o-transform:translate(0,0);transform:translate(0,0)}.modal-open .modal{overflow-x:hidden;overflow-y:auto}.modal-dialog{position:relative;width:auto;margin:10px}.modal-content{position:relative;background-color:#fff;-webkit-background-clip:padding-box;background-clip:padding-box;border:1px solid #999;border:1px solid rgba(0,0,0,.2);border-radius:6px;outline:0;-webkit-box-shadow:0 3px 9px rgba(0,0,0,.5);box-shadow:0 3px 9px rgba(0,0,0,.5)}.modal-backdrop{position:fixed;top:0;right:0;bottom:0;left:0;z-index:1040;background-color:#000}.modal-backdrop.fade{filter:alpha(opacity=0);opacity:0}.modal-backdrop.in{filter:alpha(opacity=50);opacity:.5}.modal-header{padding:15px;border-bottom:1px solid #e5e5e5}.modal-header .close{margin-top:-2px}.modal-title{margin:0;line-height:1.42857143}.modal-body{position:relative;padding:15px}.modal-footer{padding:15px;text-align:right;border-top:1px solid #e5e5e5}.modal-footer .btn+.btn{margin-bottom:0;margin-left:5px}.modal-footer .btn-group .btn+.btn{margin-left:-1px}.modal-footer .btn-block+.btn-block{margin-left:0}.modal-scrollbar-measure{position:absolute;top:-9999px;width:50px;height:50px;overflow:scroll}@media (min-width:768px){.modal-dialog{width:600px;margin:30px auto}.modal-content{-webkit-box-shadow:0 5px 15px rgba(0,0,0,.5);box-shadow:0 5px 15px rgba(0,0,0,.5)}.modal-sm{width:300px}}@media (min-width:992px){.modal-lg{width:900px}}.tooltip{position:absolute;z-index:1070;display:block;font-family:"Helvetica Neue",Helvetica,Arial,sans-serif;font-size:12px;font-style:normal;font-weight:400;line-height:1.42857143;text-align:left;text-align:start;text-decoration:none;text-shadow:none;text-transform:none;letter-spacing:normal;word-break:normal;word-spacing:normal;word-wrap:normal;white-space:normal;filter:alpha(opacity=0);opacity:0;line-break:auto}.tooltip.in{filter:alpha(opacity=90);opacity:.9}.tooltip.top{padding:5px 0;margin-top:-3px}.tooltip.right{padding:0 5px;margin-left:3px}.tooltip.bottom{padding:5px 0;margin-top:3px}.tooltip.left{padding:0 5px;margin-left:-3px}.tooltip-inner{max-width:200px;padding:3px 8px;color:#fff;text-align:center;background-color:#000;border-radius:4px}.tooltip-arrow{position:absolute;width:0;height:0;border-color:transparent;border-style:solid}.tooltip.top .tooltip-arrow{bottom:0;left:50%;margin-left:-5px;border-width:5px 5px 0;border-top-color:#000}.tooltip.top-left .tooltip-arrow{right:5px;bottom:0;margin-bottom:-5px;border-width:5px 5px 0;border-top-color:#000}.tooltip.top-right .tooltip-arrow{bottom:0;left:5px;margin-bottom:-5px;border-width:5px 5px 0;border-top-color:#000}.tooltip.right .tooltip-arrow{top:50%;left:0;margin-top:-5px;border-width:5px 5px 5px 0;border-right-color:#000}.tooltip.left .tooltip-arrow{top:50%;right:0;margin-top:-5px;border-width:5px 0 5px 5px;border-left-color:#000}.tooltip.bottom .tooltip-arrow{top:0;left:50%;margin-left:-5px;border-width:0 5px 5px;border-bottom-color:#000}.tooltip.bottom-left .tooltip-arrow{top:0;right:5px;margin-top:-5px;border-width:0 5px 5px;border-bottom-color:#000}.tooltip.bottom-right .tooltip-arrow{top:0;left:5px;margin-top:-5px;border-width:0 5px 5px;border-bottom-color:#000}.popover{position:absolute;top:0;left:0;z-index:1060;display:none;max-width:276px;padding:1px;font-family:"Helvetica Neue",Helvetica,Arial,sans-serif;font-size:14px;font-style:normal;font-weight:400;line-height:1.42857143;text-align:left;text-align:start;text-decoration:none;text-shadow:none;text-transform:none;letter-spacing:normal;word-break:normal;word-spacing:normal;word-wrap:normal;white-space:normal;background-color:#fff;-webkit-background-clip:padding-box;background-clip:padding-box;border:1px solid #ccc;border:1px solid rgba(0,0,0,.2);border-radius:6px;-webkit-box-shadow:0 5px 10px rgba(0,0,0,.2);box-shadow:0 5px 10px rgba(0,0,0,.2);line-break:auto}.popover.top{margin-top:-10px}.popover.right{margin-left:10px}.popover.bottom{margin-top:10px}.popover.left{margin-left:-10px}.popover-title{padding:8px 14px;margin:0;font-size:14px;background-color:#f7f7f7;border-bottom:1px solid #ebebeb;border-radius:5px 5px 0 0}.popover-content{padding:9px 14px}.popover>.arrow,.popover>.arrow:after{position:absolute;display:block;width:0;height:0;border-color:transparent;border-style:solid}.popover>.arrow{border-width:11px}.popover>.arrow:after{content:"";border-width:10px}.popover.top>.arrow{bottom:-11px;left:50%;margin-left:-11px;border-top-color:#999;border-top-color:rgba(0,0,0,.25);border-bottom-width:0}.popover.top>.arrow:after{bottom:1px;margin-left:-10px;content:" ";border-top-color:#fff;border-bottom-width:0}.popover.right>.arrow{top:50%;left:-11px;margin-top:-11px;border-right-color:#999;border-right-color:rgba(0,0,0,.25);border-left-width:0}.popover.right>.arrow:after{bottom:-10px;left:1px;content:" ";border-right-color:#fff;border-left-width:0}.popover.bottom>.arrow{top:-11px;left:50%;margin-left:-11px;border-top-width:0;border-bottom-color:#999;border-bottom-color:rgba(0,0,0,.25)}.popover.bottom>.arrow:after{top:1px;margin-left:-10px;content:" ";border-top-width:0;border-bottom-color:#fff}.popover.left>.arrow{top:50%;right:-11px;margin-top:-11px;border-right-width:0;border-left-color:#999;border-left-color:rgba(0,0,0,.25)}.popover.left>.arrow:after{right:1px;bottom:-10px;content:" ";border-right-width:0;border-left-color:#fff}.carousel{position:relative}.carousel-inner{position:relative;width:100%;overflow:hidden}.carousel-inner>.item{position:relative;display:none;-webkit-transition:.6s ease-in-out left;-o-transition:.6s ease-in-out left;transition:.6s ease-in-out left}.carousel-inner>.item>a>img,.carousel-inner>.item>img{line-height:1}@media all and (transform-3d),(-webkit-transform-3d){.carousel-inner>.item{-webkit-transition:-webkit-transform .6s ease-in-out;-o-transition:-o-transform .6s ease-in-out;transition:transform .6s ease-in-out;-webkit-backface-visibility:hidden;backface-visibility:hidden;-webkit-perspective:1000px;perspective:1000px}.carousel-inner>.item.active.right,.carousel-inner>.item.next{left:0;-webkit-transform:translate3d(100%,0,0);transform:translate3d(100%,0,0)}.carousel-inner>.item.active.left,.carousel-inner>.item.prev{left:0;-webkit-transform:translate3d(-100%,0,0);transform:translate3d(-100%,0,0)}.carousel-inner>.item.active,.carousel-inner>.item.next.left,.carousel-inner>.item.prev.right{left:0;-webkit-transform:translate3d(0,0,0);transform:translate3d(0,0,0)}}.carousel-inner>.active,.carousel-inner>.next,.carousel-inner>.prev{display:block}.carousel-inner>.active{left:0}.carousel-inner>.next,.carousel-inner>.prev{position:absolute;top:0;width:100%}.carousel-inner>.next{left:100%}.carousel-inner>.prev{left:-100%}.carousel-inner>.next.left,.carousel-inner>.prev.right{left:0}.carousel-inner>.active.left{left:-100%}.carousel-inner>.active.right{left:100%}.carousel-control{position:absolute;top:0;bottom:0;left:0;width:15%;font-size:20px;color:#fff;text-align:center;text-shadow:0 1px 2px rgba(0,0,0,.6);background-color:rgba(0,0,0,0);filter:alpha(opacity=50);opacity:.5}.carousel-control.left{background-image:-webkit-linear-gradient(left,rgba(0,0,0,.5) 0,rgba(0,0,0,.0001) 100%);background-image:-o-linear-gradient(left,rgba(0,0,0,.5) 0,rgba(0,0,0,.0001) 100%);background-image:-webkit-gradient(linear,left top,right top,from(rgba(0,0,0,.5)),to(rgba(0,0,0,.0001)));background-image:linear-gradient(to right,rgba(0,0,0,.5) 0,rgba(0,0,0,.0001) 100%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#80000000', endColorstr='#00000000', GradientType=1);background-repeat:repeat-x}.carousel-control.right{right:0;left:auto;background-image:-webkit-linear-gradient(left,rgba(0,0,0,.0001) 0,rgba(0,0,0,.5) 100%);background-image:-o-linear-gradient(left,rgba(0,0,0,.0001) 0,rgba(0,0,0,.5) 100%);background-image:-webkit-gradient(linear,left top,right top,from(rgba(0,0,0,.0001)),to(rgba(0,0,0,.5)));background-image:linear-gradient(to right,rgba(0,0,0,.0001) 0,rgba(0,0,0,.5) 100%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#00000000', endColorstr='#80000000', GradientType=1);background-repeat:repeat-x}.carousel-control:focus,.carousel-control:hover{color:#fff;text-decoration:none;filter:alpha(opacity=90);outline:0;opacity:.9}.carousel-control .glyphicon-chevron-left,.carousel-control .glyphicon-chevron-right,.carousel-control .icon-next,.carousel-control .icon-prev{position:absolute;top:50%;z-index:5;display:inline-block;margin-top:-10px}.carousel-control .glyphicon-chevron-left,.carousel-control .icon-prev{left:50%;margin-left:-10px}.carousel-control .glyphicon-chevron-right,.carousel-control .icon-next{right:50%;margin-right:-10px}.carousel-control .icon-next,.carousel-control .icon-prev{width:20px;height:20px;font-family:serif;line-height:1}.carousel-control .icon-prev:before{content:'\2039'}.carousel-control .icon-next:before{content:'\203a'}.carousel-indicators{position:absolute;bottom:10px;left:50%;z-index:15;width:60%;padding-left:0;margin-left:-30%;text-align:center;list-style:none}.carousel-indicators li{display:inline-block;width:10px;height:10px;margin:1px;text-indent:-999px;cursor:pointer;background-color:#000\9;background-color:rgba(0,0,0,0);border:1px solid #fff;border-radius:10px}.carousel-indicators .active{width:12px;height:12px;margin:0;background-color:#fff}.carousel-caption{position:absolute;right:15%;bottom:20px;left:15%;z-index:10;padding-top:20px;padding-bottom:20px;color:#fff;text-align:center;text-shadow:0 1px 2px rgba(0,0,0,.6)}.carousel-caption .btn{text-shadow:none}@media screen and (min-width:768px){.carousel-control .glyphicon-chevron-left,.carousel-control .glyphicon-chevron-right,.carousel-control .icon-next,.carousel-control .icon-prev{width:30px;height:30px;margin-top:-10px;font-size:30px}.carousel-control .glyphicon-chevron-left,.carousel-control .icon-prev{margin-left:-10px}.carousel-control .glyphicon-chevron-right,.carousel-control .icon-next{margin-right:-10px}.carousel-caption{right:20%;left:20%;padding-bottom:30px}.carousel-indicators{bottom:20px}}.btn-group-vertical>.btn-group:after,.btn-group-vertical>.btn-group:before,.btn-toolbar:after,.btn-toolbar:before,.clearfix:after,.clearfix:before,.container-fluid:after,.container-fluid:before,.container:after,.container:before,.dl-horizontal dd:after,.dl-horizontal dd:before,.form-horizontal .form-group:after,.form-horizontal .form-group:before,.modal-footer:after,.modal-footer:before,.modal-header:after,.modal-header:before,.nav:after,.nav:before,.navbar-collapse:after,.navbar-collapse:before,.navbar-header:after,.navbar-header:before,.navbar:after,.navbar:before,.pager:after,.pager:before,.panel-body:after,.panel-body:before,.row:after,.row:before{display:table;content:" "}.btn-group-vertical>.btn-group:after,.btn-toolbar:after,.clearfix:after,.container-fluid:after,.container:after,.dl-horizontal dd:after,.form-horizontal .form-group:after,.modal-footer:after,.modal-header:after,.nav:after,.navbar-collapse:after,.navbar-header:after,.navbar:after,.pager:after,.panel-body:after,.row:after{clear:both}.center-block{display:block;margin-right:auto;margin-left:auto}.pull-right{float:right!important}.pull-left{float:left!important}.hide{display:none!important}.show{display:block!important}.invisible{visibility:hidden}.text-hide{font:0/0 a;color:transparent;text-shadow:none;background-color:transparent;border:0}.hidden{display:none!important}.affix{position:fixed}@-ms-viewport{width:device-width}.visible-lg,.visible-md,.visible-sm,.visible-xs{display:none!important}.visible-lg-block,.visible-lg-inline,.visible-lg-inline-block,.visible-md-block,.visible-md-inline,.visible-md-inline-block,.visible-sm-block,.visible-sm-inline,.visible-sm-inline-block,.visible-xs-block,.visible-xs-inline,.visible-xs-inline-block{display:none!important}@media (max-width:767px){.visible-xs{display:block!important}table.visible-xs{display:table!important}tr.visible-xs{display:table-row!important}td.visible-xs,th.visible-xs{display:table-cell!important}}@media (max-width:767px){.visible-xs-block{display:block!important}}@media (max-width:767px){.visible-xs-inline{display:inline!important}}@media (max-width:767px){.visible-xs-inline-block{display:inline-block!important}}@media (min-width:768px) and (max-width:991px){.visible-sm{display:block!important}table.visible-sm{display:table!important}tr.visible-sm{display:table-row!important}td.visible-sm,th.visible-sm{display:table-cell!important}}@media (min-width:768px) and (max-width:991px){.visible-sm-block{display:block!important}}@media (min-width:768px) and (max-width:991px){.visible-sm-inline{display:inline!important}}@media (min-width:768px) and (max-width:991px){.visible-sm-inline-block{display:inline-block!important}}@media (min-width:992px) and (max-width:1199px){.visible-md{display:block!important}table.visible-md{display:table!important}tr.visible-md{display:table-row!important}td.visible-md,th.visible-md{display:table-cell!important}}@media (min-width:992px) and (max-width:1199px){.visible-md-block{display:block!important}}@media (min-width:992px) and (max-width:1199px){.visible-md-inline{display:inline!important}}@media (min-width:992px) and (max-width:1199px){.visible-md-inline-block{display:inline-block!important}}@media (min-width:1200px){.visible-lg{display:block!important}table.visible-lg{display:table!important}tr.visible-lg{display:table-row!important}td.visible-lg,th.visible-lg{display:table-cell!important}}@media (min-width:1200px){.visible-lg-block{display:block!important}}@media (min-width:1200px){.visible-lg-inline{display:inline!important}}@media (min-width:1200px){.visible-lg-inline-block{display:inline-block!important}}@media (max-width:767px){.hidden-xs{display:none!important}}@media (min-width:768px) and (max-width:991px){.hidden-sm{display:none!important}}@media (min-width:992px) and (max-width:1199px){.hidden-md{display:none!important}}@media (min-width:1200px){.hidden-lg{display:none!important}}.visible-print{display:none!important}@media print{.visible-print{display:block!important}table.visible-print{display:table!important}tr.visible-print{display:table-row!important}td.visible-print,th.visible-print{display:table-cell!important}}.visible-print-block{display:none!important}@media print{.visible-print-block{display:block!important}}.visible-print-inline{display:none!important}@media print{.visible-print-inline{display:inline!important}}.visible-print-inline-block{display:none!important}@media print{.visible-print-inline-block{display:inline-block!important}}@media print{.hidden-print{display:none!important}} +/*# sourceMappingURL=bootstrap.min.css.map */ \ No newline at end of file diff --git a/public/stylesheets/thirdparty/bootstrap-theme.min.css b/public/stylesheets/thirdparty/bootstrap-theme.min.css deleted file mode 100644 index 61358b1..0000000 --- a/public/stylesheets/thirdparty/bootstrap-theme.min.css +++ /dev/null @@ -1,5 +0,0 @@ -/*! - * Bootstrap v3.3.5 (http://getbootstrap.com) - * Copyright 2011-2015 Twitter, Inc. - * Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE) - */.btn-danger,.btn-default,.btn-info,.btn-primary,.btn-success,.btn-warning{text-shadow:0 -1px 0 rgba(0,0,0,.2);-webkit-box-shadow:inset 0 1px 0 rgba(255,255,255,.15),0 1px 1px rgba(0,0,0,.075);box-shadow:inset 0 1px 0 rgba(255,255,255,.15),0 1px 1px rgba(0,0,0,.075)}.btn-danger.active,.btn-danger:active,.btn-default.active,.btn-default:active,.btn-info.active,.btn-info:active,.btn-primary.active,.btn-primary:active,.btn-success.active,.btn-success:active,.btn-warning.active,.btn-warning:active{-webkit-box-shadow:inset 0 3px 5px rgba(0,0,0,.125);box-shadow:inset 0 3px 5px rgba(0,0,0,.125)}.btn-danger.disabled,.btn-danger[disabled],.btn-default.disabled,.btn-default[disabled],.btn-info.disabled,.btn-info[disabled],.btn-primary.disabled,.btn-primary[disabled],.btn-success.disabled,.btn-success[disabled],.btn-warning.disabled,.btn-warning[disabled],fieldset[disabled] .btn-danger,fieldset[disabled] .btn-default,fieldset[disabled] .btn-info,fieldset[disabled] .btn-primary,fieldset[disabled] .btn-success,fieldset[disabled] .btn-warning{-webkit-box-shadow:none;box-shadow:none}.btn-danger .badge,.btn-default .badge,.btn-info .badge,.btn-primary .badge,.btn-success .badge,.btn-warning .badge{text-shadow:none}.btn.active,.btn:active{background-image:none}.btn-default{text-shadow:0 1px 0 #fff;background-image:-webkit-linear-gradient(top,#fff 0,#e0e0e0 100%);background-image:-o-linear-gradient(top,#fff 0,#e0e0e0 100%);background-image:-webkit-gradient(linear,left top,left bottom,from(#fff),to(#e0e0e0));background-image:linear-gradient(to bottom,#fff 0,#e0e0e0 100%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffffffff', endColorstr='#ffe0e0e0', GradientType=0);filter:progid:DXImageTransform.Microsoft.gradient(enabled=false);background-repeat:repeat-x;border-color:#dbdbdb;border-color:#ccc}.btn-default:focus,.btn-default:hover{background-color:#e0e0e0;background-position:0 -15px}.btn-default.active,.btn-default:active{background-color:#e0e0e0;border-color:#dbdbdb}.btn-default.disabled,.btn-default.disabled.active,.btn-default.disabled.focus,.btn-default.disabled:active,.btn-default.disabled:focus,.btn-default.disabled:hover,.btn-default[disabled],.btn-default[disabled].active,.btn-default[disabled].focus,.btn-default[disabled]:active,.btn-default[disabled]:focus,.btn-default[disabled]:hover,fieldset[disabled] .btn-default,fieldset[disabled] .btn-default.active,fieldset[disabled] .btn-default.focus,fieldset[disabled] .btn-default:active,fieldset[disabled] .btn-default:focus,fieldset[disabled] .btn-default:hover{background-color:#e0e0e0;background-image:none}.btn-primary{background-image:-webkit-linear-gradient(top,#337ab7 0,#265a88 100%);background-image:-o-linear-gradient(top,#337ab7 0,#265a88 100%);background-image:-webkit-gradient(linear,left top,left bottom,from(#337ab7),to(#265a88));background-image:linear-gradient(to bottom,#337ab7 0,#265a88 100%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff337ab7', endColorstr='#ff265a88', GradientType=0);filter:progid:DXImageTransform.Microsoft.gradient(enabled=false);background-repeat:repeat-x;border-color:#245580}.btn-primary:focus,.btn-primary:hover{background-color:#265a88;background-position:0 -15px}.btn-primary.active,.btn-primary:active{background-color:#265a88;border-color:#245580}.btn-primary.disabled,.btn-primary.disabled.active,.btn-primary.disabled.focus,.btn-primary.disabled:active,.btn-primary.disabled:focus,.btn-primary.disabled:hover,.btn-primary[disabled],.btn-primary[disabled].active,.btn-primary[disabled].focus,.btn-primary[disabled]:active,.btn-primary[disabled]:focus,.btn-primary[disabled]:hover,fieldset[disabled] .btn-primary,fieldset[disabled] .btn-primary.active,fieldset[disabled] .btn-primary.focus,fieldset[disabled] .btn-primary:active,fieldset[disabled] .btn-primary:focus,fieldset[disabled] .btn-primary:hover{background-color:#265a88;background-image:none}.btn-success{background-image:-webkit-linear-gradient(top,#5cb85c 0,#419641 100%);background-image:-o-linear-gradient(top,#5cb85c 0,#419641 100%);background-image:-webkit-gradient(linear,left top,left bottom,from(#5cb85c),to(#419641));background-image:linear-gradient(to bottom,#5cb85c 0,#419641 100%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff5cb85c', endColorstr='#ff419641', GradientType=0);filter:progid:DXImageTransform.Microsoft.gradient(enabled=false);background-repeat:repeat-x;border-color:#3e8f3e}.btn-success:focus,.btn-success:hover{background-color:#419641;background-position:0 -15px}.btn-success.active,.btn-success:active{background-color:#419641;border-color:#3e8f3e}.btn-success.disabled,.btn-success.disabled.active,.btn-success.disabled.focus,.btn-success.disabled:active,.btn-success.disabled:focus,.btn-success.disabled:hover,.btn-success[disabled],.btn-success[disabled].active,.btn-success[disabled].focus,.btn-success[disabled]:active,.btn-success[disabled]:focus,.btn-success[disabled]:hover,fieldset[disabled] .btn-success,fieldset[disabled] .btn-success.active,fieldset[disabled] .btn-success.focus,fieldset[disabled] .btn-success:active,fieldset[disabled] .btn-success:focus,fieldset[disabled] .btn-success:hover{background-color:#419641;background-image:none}.btn-info{background-image:-webkit-linear-gradient(top,#5bc0de 0,#2aabd2 100%);background-image:-o-linear-gradient(top,#5bc0de 0,#2aabd2 100%);background-image:-webkit-gradient(linear,left top,left bottom,from(#5bc0de),to(#2aabd2));background-image:linear-gradient(to bottom,#5bc0de 0,#2aabd2 100%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff5bc0de', endColorstr='#ff2aabd2', GradientType=0);filter:progid:DXImageTransform.Microsoft.gradient(enabled=false);background-repeat:repeat-x;border-color:#28a4c9}.btn-info:focus,.btn-info:hover{background-color:#2aabd2;background-position:0 -15px}.btn-info.active,.btn-info:active{background-color:#2aabd2;border-color:#28a4c9}.btn-info.disabled,.btn-info.disabled.active,.btn-info.disabled.focus,.btn-info.disabled:active,.btn-info.disabled:focus,.btn-info.disabled:hover,.btn-info[disabled],.btn-info[disabled].active,.btn-info[disabled].focus,.btn-info[disabled]:active,.btn-info[disabled]:focus,.btn-info[disabled]:hover,fieldset[disabled] .btn-info,fieldset[disabled] .btn-info.active,fieldset[disabled] .btn-info.focus,fieldset[disabled] .btn-info:active,fieldset[disabled] .btn-info:focus,fieldset[disabled] .btn-info:hover{background-color:#2aabd2;background-image:none}.btn-warning{background-image:-webkit-linear-gradient(top,#f0ad4e 0,#eb9316 100%);background-image:-o-linear-gradient(top,#f0ad4e 0,#eb9316 100%);background-image:-webkit-gradient(linear,left top,left bottom,from(#f0ad4e),to(#eb9316));background-image:linear-gradient(to bottom,#f0ad4e 0,#eb9316 100%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#fff0ad4e', endColorstr='#ffeb9316', GradientType=0);filter:progid:DXImageTransform.Microsoft.gradient(enabled=false);background-repeat:repeat-x;border-color:#e38d13}.btn-warning:focus,.btn-warning:hover{background-color:#eb9316;background-position:0 -15px}.btn-warning.active,.btn-warning:active{background-color:#eb9316;border-color:#e38d13}.btn-warning.disabled,.btn-warning.disabled.active,.btn-warning.disabled.focus,.btn-warning.disabled:active,.btn-warning.disabled:focus,.btn-warning.disabled:hover,.btn-warning[disabled],.btn-warning[disabled].active,.btn-warning[disabled].focus,.btn-warning[disabled]:active,.btn-warning[disabled]:focus,.btn-warning[disabled]:hover,fieldset[disabled] .btn-warning,fieldset[disabled] .btn-warning.active,fieldset[disabled] .btn-warning.focus,fieldset[disabled] .btn-warning:active,fieldset[disabled] .btn-warning:focus,fieldset[disabled] .btn-warning:hover{background-color:#eb9316;background-image:none}.btn-danger{background-image:-webkit-linear-gradient(top,#d9534f 0,#c12e2a 100%);background-image:-o-linear-gradient(top,#d9534f 0,#c12e2a 100%);background-image:-webkit-gradient(linear,left top,left bottom,from(#d9534f),to(#c12e2a));background-image:linear-gradient(to bottom,#d9534f 0,#c12e2a 100%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffd9534f', endColorstr='#ffc12e2a', GradientType=0);filter:progid:DXImageTransform.Microsoft.gradient(enabled=false);background-repeat:repeat-x;border-color:#b92c28}.btn-danger:focus,.btn-danger:hover{background-color:#c12e2a;background-position:0 -15px}.btn-danger.active,.btn-danger:active{background-color:#c12e2a;border-color:#b92c28}.btn-danger.disabled,.btn-danger.disabled.active,.btn-danger.disabled.focus,.btn-danger.disabled:active,.btn-danger.disabled:focus,.btn-danger.disabled:hover,.btn-danger[disabled],.btn-danger[disabled].active,.btn-danger[disabled].focus,.btn-danger[disabled]:active,.btn-danger[disabled]:focus,.btn-danger[disabled]:hover,fieldset[disabled] .btn-danger,fieldset[disabled] .btn-danger.active,fieldset[disabled] .btn-danger.focus,fieldset[disabled] .btn-danger:active,fieldset[disabled] .btn-danger:focus,fieldset[disabled] .btn-danger:hover{background-color:#c12e2a;background-image:none}.img-thumbnail,.thumbnail{-webkit-box-shadow:0 1px 2px rgba(0,0,0,.075);box-shadow:0 1px 2px rgba(0,0,0,.075)}.dropdown-menu>li>a:focus,.dropdown-menu>li>a:hover{background-color:#e8e8e8;background-image:-webkit-linear-gradient(top,#f5f5f5 0,#e8e8e8 100%);background-image:-o-linear-gradient(top,#f5f5f5 0,#e8e8e8 100%);background-image:-webkit-gradient(linear,left top,left bottom,from(#f5f5f5),to(#e8e8e8));background-image:linear-gradient(to bottom,#f5f5f5 0,#e8e8e8 100%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#fff5f5f5', endColorstr='#ffe8e8e8', GradientType=0);background-repeat:repeat-x}.dropdown-menu>.active>a,.dropdown-menu>.active>a:focus,.dropdown-menu>.active>a:hover{background-color:#2e6da4;background-image:-webkit-linear-gradient(top,#337ab7 0,#2e6da4 100%);background-image:-o-linear-gradient(top,#337ab7 0,#2e6da4 100%);background-image:-webkit-gradient(linear,left top,left bottom,from(#337ab7),to(#2e6da4));background-image:linear-gradient(to bottom,#337ab7 0,#2e6da4 100%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff337ab7', endColorstr='#ff2e6da4', GradientType=0);background-repeat:repeat-x}.navbar-default{background-image:-webkit-linear-gradient(top,#fff 0,#f8f8f8 100%);background-image:-o-linear-gradient(top,#fff 0,#f8f8f8 100%);background-image:-webkit-gradient(linear,left top,left bottom,from(#fff),to(#f8f8f8));background-image:linear-gradient(to bottom,#fff 0,#f8f8f8 100%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffffffff', endColorstr='#fff8f8f8', GradientType=0);filter:progid:DXImageTransform.Microsoft.gradient(enabled=false);background-repeat:repeat-x;border-radius:4px;-webkit-box-shadow:inset 0 1px 0 rgba(255,255,255,.15),0 1px 5px rgba(0,0,0,.075);box-shadow:inset 0 1px 0 rgba(255,255,255,.15),0 1px 5px rgba(0,0,0,.075)}.navbar-default .navbar-nav>.active>a,.navbar-default .navbar-nav>.open>a{background-image:-webkit-linear-gradient(top,#dbdbdb 0,#e2e2e2 100%);background-image:-o-linear-gradient(top,#dbdbdb 0,#e2e2e2 100%);background-image:-webkit-gradient(linear,left top,left bottom,from(#dbdbdb),to(#e2e2e2));background-image:linear-gradient(to bottom,#dbdbdb 0,#e2e2e2 100%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffdbdbdb', endColorstr='#ffe2e2e2', GradientType=0);background-repeat:repeat-x;-webkit-box-shadow:inset 0 3px 9px rgba(0,0,0,.075);box-shadow:inset 0 3px 9px rgba(0,0,0,.075)}.navbar-brand,.navbar-nav>li>a{text-shadow:0 1px 0 rgba(255,255,255,.25)}.navbar-inverse{background-image:-webkit-linear-gradient(top,#3c3c3c 0,#222 100%);background-image:-o-linear-gradient(top,#3c3c3c 0,#222 100%);background-image:-webkit-gradient(linear,left top,left bottom,from(#3c3c3c),to(#222));background-image:linear-gradient(to bottom,#3c3c3c 0,#222 100%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff3c3c3c', endColorstr='#ff222222', GradientType=0);filter:progid:DXImageTransform.Microsoft.gradient(enabled=false);background-repeat:repeat-x;border-radius:4px}.navbar-inverse .navbar-nav>.active>a,.navbar-inverse .navbar-nav>.open>a{background-image:-webkit-linear-gradient(top,#080808 0,#0f0f0f 100%);background-image:-o-linear-gradient(top,#080808 0,#0f0f0f 100%);background-image:-webkit-gradient(linear,left top,left bottom,from(#080808),to(#0f0f0f));background-image:linear-gradient(to bottom,#080808 0,#0f0f0f 100%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff080808', endColorstr='#ff0f0f0f', GradientType=0);background-repeat:repeat-x;-webkit-box-shadow:inset 0 3px 9px rgba(0,0,0,.25);box-shadow:inset 0 3px 9px rgba(0,0,0,.25)}.navbar-inverse .navbar-brand,.navbar-inverse .navbar-nav>li>a{text-shadow:0 -1px 0 rgba(0,0,0,.25)}.navbar-fixed-bottom,.navbar-fixed-top,.navbar-static-top{border-radius:0}@media (max-width:767px){.navbar .navbar-nav .open .dropdown-menu>.active>a,.navbar .navbar-nav .open .dropdown-menu>.active>a:focus,.navbar .navbar-nav .open .dropdown-menu>.active>a:hover{color:#fff;background-image:-webkit-linear-gradient(top,#337ab7 0,#2e6da4 100%);background-image:-o-linear-gradient(top,#337ab7 0,#2e6da4 100%);background-image:-webkit-gradient(linear,left top,left bottom,from(#337ab7),to(#2e6da4));background-image:linear-gradient(to bottom,#337ab7 0,#2e6da4 100%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff337ab7', endColorstr='#ff2e6da4', GradientType=0);background-repeat:repeat-x}}.alert{text-shadow:0 1px 0 rgba(255,255,255,.2);-webkit-box-shadow:inset 0 1px 0 rgba(255,255,255,.25),0 1px 2px rgba(0,0,0,.05);box-shadow:inset 0 1px 0 rgba(255,255,255,.25),0 1px 2px rgba(0,0,0,.05)}.alert-success{background-image:-webkit-linear-gradient(top,#dff0d8 0,#c8e5bc 100%);background-image:-o-linear-gradient(top,#dff0d8 0,#c8e5bc 100%);background-image:-webkit-gradient(linear,left top,left bottom,from(#dff0d8),to(#c8e5bc));background-image:linear-gradient(to bottom,#dff0d8 0,#c8e5bc 100%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffdff0d8', endColorstr='#ffc8e5bc', GradientType=0);background-repeat:repeat-x;border-color:#b2dba1}.alert-info{background-image:-webkit-linear-gradient(top,#d9edf7 0,#b9def0 100%);background-image:-o-linear-gradient(top,#d9edf7 0,#b9def0 100%);background-image:-webkit-gradient(linear,left top,left bottom,from(#d9edf7),to(#b9def0));background-image:linear-gradient(to bottom,#d9edf7 0,#b9def0 100%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffd9edf7', endColorstr='#ffb9def0', GradientType=0);background-repeat:repeat-x;border-color:#9acfea}.alert-warning{background-image:-webkit-linear-gradient(top,#fcf8e3 0,#f8efc0 100%);background-image:-o-linear-gradient(top,#fcf8e3 0,#f8efc0 100%);background-image:-webkit-gradient(linear,left top,left bottom,from(#fcf8e3),to(#f8efc0));background-image:linear-gradient(to bottom,#fcf8e3 0,#f8efc0 100%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#fffcf8e3', endColorstr='#fff8efc0', GradientType=0);background-repeat:repeat-x;border-color:#f5e79e}.alert-danger{background-image:-webkit-linear-gradient(top,#f2dede 0,#e7c3c3 100%);background-image:-o-linear-gradient(top,#f2dede 0,#e7c3c3 100%);background-image:-webkit-gradient(linear,left top,left bottom,from(#f2dede),to(#e7c3c3));background-image:linear-gradient(to bottom,#f2dede 0,#e7c3c3 100%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#fff2dede', endColorstr='#ffe7c3c3', GradientType=0);background-repeat:repeat-x;border-color:#dca7a7}.progress{background-image:-webkit-linear-gradient(top,#ebebeb 0,#f5f5f5 100%);background-image:-o-linear-gradient(top,#ebebeb 0,#f5f5f5 100%);background-image:-webkit-gradient(linear,left top,left bottom,from(#ebebeb),to(#f5f5f5));background-image:linear-gradient(to bottom,#ebebeb 0,#f5f5f5 100%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffebebeb', endColorstr='#fff5f5f5', GradientType=0);background-repeat:repeat-x}.progress-bar{background-image:-webkit-linear-gradient(top,#337ab7 0,#286090 100%);background-image:-o-linear-gradient(top,#337ab7 0,#286090 100%);background-image:-webkit-gradient(linear,left top,left bottom,from(#337ab7),to(#286090));background-image:linear-gradient(to bottom,#337ab7 0,#286090 100%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff337ab7', endColorstr='#ff286090', GradientType=0);background-repeat:repeat-x}.progress-bar-success{background-image:-webkit-linear-gradient(top,#5cb85c 0,#449d44 100%);background-image:-o-linear-gradient(top,#5cb85c 0,#449d44 100%);background-image:-webkit-gradient(linear,left top,left bottom,from(#5cb85c),to(#449d44));background-image:linear-gradient(to bottom,#5cb85c 0,#449d44 100%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff5cb85c', endColorstr='#ff449d44', GradientType=0);background-repeat:repeat-x}.progress-bar-info{background-image:-webkit-linear-gradient(top,#5bc0de 0,#31b0d5 100%);background-image:-o-linear-gradient(top,#5bc0de 0,#31b0d5 100%);background-image:-webkit-gradient(linear,left top,left bottom,from(#5bc0de),to(#31b0d5));background-image:linear-gradient(to bottom,#5bc0de 0,#31b0d5 100%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff5bc0de', endColorstr='#ff31b0d5', GradientType=0);background-repeat:repeat-x}.progress-bar-warning{background-image:-webkit-linear-gradient(top,#f0ad4e 0,#ec971f 100%);background-image:-o-linear-gradient(top,#f0ad4e 0,#ec971f 100%);background-image:-webkit-gradient(linear,left top,left bottom,from(#f0ad4e),to(#ec971f));background-image:linear-gradient(to bottom,#f0ad4e 0,#ec971f 100%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#fff0ad4e', endColorstr='#ffec971f', GradientType=0);background-repeat:repeat-x}.progress-bar-danger{background-image:-webkit-linear-gradient(top,#d9534f 0,#c9302c 100%);background-image:-o-linear-gradient(top,#d9534f 0,#c9302c 100%);background-image:-webkit-gradient(linear,left top,left bottom,from(#d9534f),to(#c9302c));background-image:linear-gradient(to bottom,#d9534f 0,#c9302c 100%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffd9534f', endColorstr='#ffc9302c', GradientType=0);background-repeat:repeat-x}.progress-bar-striped{background-image:-webkit-linear-gradient(45deg,rgba(255,255,255,.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,.15) 50%,rgba(255,255,255,.15) 75%,transparent 75%,transparent);background-image:-o-linear-gradient(45deg,rgba(255,255,255,.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,.15) 50%,rgba(255,255,255,.15) 75%,transparent 75%,transparent);background-image:linear-gradient(45deg,rgba(255,255,255,.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,.15) 50%,rgba(255,255,255,.15) 75%,transparent 75%,transparent)}.list-group{border-radius:4px;-webkit-box-shadow:0 1px 2px rgba(0,0,0,.075);box-shadow:0 1px 2px rgba(0,0,0,.075)}.list-group-item.active,.list-group-item.active:focus,.list-group-item.active:hover{text-shadow:0 -1px 0 #286090;background-image:-webkit-linear-gradient(top,#337ab7 0,#2b669a 100%);background-image:-o-linear-gradient(top,#337ab7 0,#2b669a 100%);background-image:-webkit-gradient(linear,left top,left bottom,from(#337ab7),to(#2b669a));background-image:linear-gradient(to bottom,#337ab7 0,#2b669a 100%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff337ab7', endColorstr='#ff2b669a', GradientType=0);background-repeat:repeat-x;border-color:#2b669a}.list-group-item.active .badge,.list-group-item.active:focus .badge,.list-group-item.active:hover .badge{text-shadow:none}.panel{-webkit-box-shadow:0 1px 2px rgba(0,0,0,.05);box-shadow:0 1px 2px rgba(0,0,0,.05)}.panel-default>.panel-heading{background-image:-webkit-linear-gradient(top,#f5f5f5 0,#e8e8e8 100%);background-image:-o-linear-gradient(top,#f5f5f5 0,#e8e8e8 100%);background-image:-webkit-gradient(linear,left top,left bottom,from(#f5f5f5),to(#e8e8e8));background-image:linear-gradient(to bottom,#f5f5f5 0,#e8e8e8 100%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#fff5f5f5', endColorstr='#ffe8e8e8', GradientType=0);background-repeat:repeat-x}.panel-primary>.panel-heading{background-image:-webkit-linear-gradient(top,#337ab7 0,#2e6da4 100%);background-image:-o-linear-gradient(top,#337ab7 0,#2e6da4 100%);background-image:-webkit-gradient(linear,left top,left bottom,from(#337ab7),to(#2e6da4));background-image:linear-gradient(to bottom,#337ab7 0,#2e6da4 100%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff337ab7', endColorstr='#ff2e6da4', GradientType=0);background-repeat:repeat-x}.panel-success>.panel-heading{background-image:-webkit-linear-gradient(top,#dff0d8 0,#d0e9c6 100%);background-image:-o-linear-gradient(top,#dff0d8 0,#d0e9c6 100%);background-image:-webkit-gradient(linear,left top,left bottom,from(#dff0d8),to(#d0e9c6));background-image:linear-gradient(to bottom,#dff0d8 0,#d0e9c6 100%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffdff0d8', endColorstr='#ffd0e9c6', GradientType=0);background-repeat:repeat-x}.panel-info>.panel-heading{background-image:-webkit-linear-gradient(top,#d9edf7 0,#c4e3f3 100%);background-image:-o-linear-gradient(top,#d9edf7 0,#c4e3f3 100%);background-image:-webkit-gradient(linear,left top,left bottom,from(#d9edf7),to(#c4e3f3));background-image:linear-gradient(to bottom,#d9edf7 0,#c4e3f3 100%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffd9edf7', endColorstr='#ffc4e3f3', GradientType=0);background-repeat:repeat-x}.panel-warning>.panel-heading{background-image:-webkit-linear-gradient(top,#fcf8e3 0,#faf2cc 100%);background-image:-o-linear-gradient(top,#fcf8e3 0,#faf2cc 100%);background-image:-webkit-gradient(linear,left top,left bottom,from(#fcf8e3),to(#faf2cc));background-image:linear-gradient(to bottom,#fcf8e3 0,#faf2cc 100%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#fffcf8e3', endColorstr='#fffaf2cc', GradientType=0);background-repeat:repeat-x}.panel-danger>.panel-heading{background-image:-webkit-linear-gradient(top,#f2dede 0,#ebcccc 100%);background-image:-o-linear-gradient(top,#f2dede 0,#ebcccc 100%);background-image:-webkit-gradient(linear,left top,left bottom,from(#f2dede),to(#ebcccc));background-image:linear-gradient(to bottom,#f2dede 0,#ebcccc 100%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#fff2dede', endColorstr='#ffebcccc', GradientType=0);background-repeat:repeat-x}.well{background-image:-webkit-linear-gradient(top,#e8e8e8 0,#f5f5f5 100%);background-image:-o-linear-gradient(top,#e8e8e8 0,#f5f5f5 100%);background-image:-webkit-gradient(linear,left top,left bottom,from(#e8e8e8),to(#f5f5f5));background-image:linear-gradient(to bottom,#e8e8e8 0,#f5f5f5 100%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffe8e8e8', endColorstr='#fff5f5f5', GradientType=0);background-repeat:repeat-x;border-color:#dcdcdc;-webkit-box-shadow:inset 0 1px 3px rgba(0,0,0,.05),0 1px 0 rgba(255,255,255,.1);box-shadow:inset 0 1px 3px rgba(0,0,0,.05),0 1px 0 rgba(255,255,255,.1)} \ No newline at end of file diff --git a/public/stylesheets/thirdparty/bootstrap.min.css b/public/stylesheets/thirdparty/bootstrap.min.css deleted file mode 100644 index d65c66b..0000000 --- a/public/stylesheets/thirdparty/bootstrap.min.css +++ /dev/null @@ -1,5 +0,0 @@ -/*! - * Bootstrap v3.3.5 (http://getbootstrap.com) - * Copyright 2011-2015 Twitter, Inc. - * Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE) - *//*! normalize.css v3.0.3 | MIT License | github.com/necolas/normalize.css */html{font-family:sans-serif;-webkit-text-size-adjust:100%;-ms-text-size-adjust:100%}body{margin:0}article,aside,details,figcaption,figure,footer,header,hgroup,main,menu,nav,section,summary{display:block}audio,canvas,progress,video{display:inline-block;vertical-align:baseline}audio:not([controls]){display:none;height:0}[hidden],template{display:none}a{background-color:transparent}a:active,a:hover{outline:0}abbr[title]{border-bottom:1px dotted}b,strong{font-weight:700}dfn{font-style:italic}h1{margin:.67em 0;font-size:2em}mark{color:#000;background:#ff0}small{font-size:80%}sub,sup{position:relative;font-size:75%;line-height:0;vertical-align:baseline}sup{top:-.5em}sub{bottom:-.25em}img{border:0}svg:not(:root){overflow:hidden}figure{margin:1em 40px}hr{height:0;-webkit-box-sizing:content-box;-moz-box-sizing:content-box;box-sizing:content-box}pre{overflow:auto}code,kbd,pre,samp{font-family:monospace,monospace;font-size:1em}button,input,optgroup,select,textarea{margin:0;font:inherit;color:inherit}button{overflow:visible}button,select{text-transform:none}button,html input[type=button],input[type=reset],input[type=submit]{-webkit-appearance:button;cursor:pointer}button[disabled],html input[disabled]{cursor:default}button::-moz-focus-inner,input::-moz-focus-inner{padding:0;border:0}input{line-height:normal}input[type=checkbox],input[type=radio]{-webkit-box-sizing:border-box;-moz-box-sizing:border-box;box-sizing:border-box;padding:0}input[type=number]::-webkit-inner-spin-button,input[type=number]::-webkit-outer-spin-button{height:auto}input[type=search]{-webkit-box-sizing:content-box;-moz-box-sizing:content-box;box-sizing:content-box;-webkit-appearance:textfield}input[type=search]::-webkit-search-cancel-button,input[type=search]::-webkit-search-decoration{-webkit-appearance:none}fieldset{padding:.35em .625em .75em;margin:0 2px;border:1px solid silver}legend{padding:0;border:0}textarea{overflow:auto}optgroup{font-weight:700}table{border-spacing:0;border-collapse:collapse}td,th{padding:0}/*! Source: https://github.com/h5bp/html5-boilerplate/blob/master/src/css/main.css */@media print{*,:after,:before{color:#000!important;text-shadow:none!important;background:0 0!important;-webkit-box-shadow:none!important;box-shadow:none!important}a,a:visited{text-decoration:underline}a[href]:after{content:" (" attr(href) ")"}abbr[title]:after{content:" (" attr(title) ")"}a[href^="javascript:"]:after,a[href^="#"]:after{content:""}blockquote,pre{border:1px solid #999;page-break-inside:avoid}thead{display:table-header-group}img,tr{page-break-inside:avoid}img{max-width:100%!important}h2,h3,p{orphans:3;widows:3}h2,h3{page-break-after:avoid}.navbar{display:none}.btn>.caret,.dropup>.btn>.caret{border-top-color:#000!important}.label{border:1px solid #000}.table{border-collapse:collapse!important}.table td,.table th{background-color:#fff!important}.table-bordered td,.table-bordered th{border:1px solid #ddd!important}}@font-face{font-family:'Glyphicons Halflings';src:url(../fonts/glyphicons-halflings-regular.eot);src:url(../fonts/glyphicons-halflings-regular.eot?#iefix) format('embedded-opentype'),url(../fonts/glyphicons-halflings-regular.woff2) format('woff2'),url(../fonts/glyphicons-halflings-regular.woff) format('woff'),url(../fonts/glyphicons-halflings-regular.ttf) format('truetype'),url(../fonts/glyphicons-halflings-regular.svg#glyphicons_halflingsregular) format('svg')}.glyphicon{position:relative;top:1px;display:inline-block;font-family:'Glyphicons Halflings';font-style:normal;font-weight:400;line-height:1;-webkit-font-smoothing:antialiased;-moz-osx-font-smoothing:grayscale}.glyphicon-asterisk:before{content:"\2a"}.glyphicon-plus:before{content:"\2b"}.glyphicon-eur:before,.glyphicon-euro:before{content:"\20ac"}.glyphicon-minus:before{content:"\2212"}.glyphicon-cloud:before{content:"\2601"}.glyphicon-envelope:before{content:"\2709"}.glyphicon-pencil:before{content:"\270f"}.glyphicon-glass:before{content:"\e001"}.glyphicon-music:before{content:"\e002"}.glyphicon-search:before{content:"\e003"}.glyphicon-heart:before{content:"\e005"}.glyphicon-star:before{content:"\e006"}.glyphicon-star-empty:before{content:"\e007"}.glyphicon-user:before{content:"\e008"}.glyphicon-film:before{content:"\e009"}.glyphicon-th-large:before{content:"\e010"}.glyphicon-th:before{content:"\e011"}.glyphicon-th-list:before{content:"\e012"}.glyphicon-ok:before{content:"\e013"}.glyphicon-remove:before{content:"\e014"}.glyphicon-zoom-in:before{content:"\e015"}.glyphicon-zoom-out:before{content:"\e016"}.glyphicon-off:before{content:"\e017"}.glyphicon-signal:before{content:"\e018"}.glyphicon-cog:before{content:"\e019"}.glyphicon-trash:before{content:"\e020"}.glyphicon-home:before{content:"\e021"}.glyphicon-file:before{content:"\e022"}.glyphicon-time:before{content:"\e023"}.glyphicon-road:before{content:"\e024"}.glyphicon-download-alt:before{content:"\e025"}.glyphicon-download:before{content:"\e026"}.glyphicon-upload:before{content:"\e027"}.glyphicon-inbox:before{content:"\e028"}.glyphicon-play-circle:before{content:"\e029"}.glyphicon-repeat:before{content:"\e030"}.glyphicon-refresh:before{content:"\e031"}.glyphicon-list-alt:before{content:"\e032"}.glyphicon-lock:before{content:"\e033"}.glyphicon-flag:before{content:"\e034"}.glyphicon-headphones:before{content:"\e035"}.glyphicon-volume-off:before{content:"\e036"}.glyphicon-volume-down:before{content:"\e037"}.glyphicon-volume-up:before{content:"\e038"}.glyphicon-qrcode:before{content:"\e039"}.glyphicon-barcode:before{content:"\e040"}.glyphicon-tag:before{content:"\e041"}.glyphicon-tags:before{content:"\e042"}.glyphicon-book:before{content:"\e043"}.glyphicon-bookmark:before{content:"\e044"}.glyphicon-print:before{content:"\e045"}.glyphicon-camera:before{content:"\e046"}.glyphicon-font:before{content:"\e047"}.glyphicon-bold:before{content:"\e048"}.glyphicon-italic:before{content:"\e049"}.glyphicon-text-height:before{content:"\e050"}.glyphicon-text-width:before{content:"\e051"}.glyphicon-align-left:before{content:"\e052"}.glyphicon-align-center:before{content:"\e053"}.glyphicon-align-right:before{content:"\e054"}.glyphicon-align-justify:before{content:"\e055"}.glyphicon-list:before{content:"\e056"}.glyphicon-indent-left:before{content:"\e057"}.glyphicon-indent-right:before{content:"\e058"}.glyphicon-facetime-video:before{content:"\e059"}.glyphicon-picture:before{content:"\e060"}.glyphicon-map-marker:before{content:"\e062"}.glyphicon-adjust:before{content:"\e063"}.glyphicon-tint:before{content:"\e064"}.glyphicon-edit:before{content:"\e065"}.glyphicon-share:before{content:"\e066"}.glyphicon-check:before{content:"\e067"}.glyphicon-move:before{content:"\e068"}.glyphicon-step-backward:before{content:"\e069"}.glyphicon-fast-backward:before{content:"\e070"}.glyphicon-backward:before{content:"\e071"}.glyphicon-play:before{content:"\e072"}.glyphicon-pause:before{content:"\e073"}.glyphicon-stop:before{content:"\e074"}.glyphicon-forward:before{content:"\e075"}.glyphicon-fast-forward:before{content:"\e076"}.glyphicon-step-forward:before{content:"\e077"}.glyphicon-eject:before{content:"\e078"}.glyphicon-chevron-left:before{content:"\e079"}.glyphicon-chevron-right:before{content:"\e080"}.glyphicon-plus-sign:before{content:"\e081"}.glyphicon-minus-sign:before{content:"\e082"}.glyphicon-remove-sign:before{content:"\e083"}.glyphicon-ok-sign:before{content:"\e084"}.glyphicon-question-sign:before{content:"\e085"}.glyphicon-info-sign:before{content:"\e086"}.glyphicon-screenshot:before{content:"\e087"}.glyphicon-remove-circle:before{content:"\e088"}.glyphicon-ok-circle:before{content:"\e089"}.glyphicon-ban-circle:before{content:"\e090"}.glyphicon-arrow-left:before{content:"\e091"}.glyphicon-arrow-right:before{content:"\e092"}.glyphicon-arrow-up:before{content:"\e093"}.glyphicon-arrow-down:before{content:"\e094"}.glyphicon-share-alt:before{content:"\e095"}.glyphicon-resize-full:before{content:"\e096"}.glyphicon-resize-small:before{content:"\e097"}.glyphicon-exclamation-sign:before{content:"\e101"}.glyphicon-gift:before{content:"\e102"}.glyphicon-leaf:before{content:"\e103"}.glyphicon-fire:before{content:"\e104"}.glyphicon-eye-open:before{content:"\e105"}.glyphicon-eye-close:before{content:"\e106"}.glyphicon-warning-sign:before{content:"\e107"}.glyphicon-plane:before{content:"\e108"}.glyphicon-calendar:before{content:"\e109"}.glyphicon-random:before{content:"\e110"}.glyphicon-comment:before{content:"\e111"}.glyphicon-magnet:before{content:"\e112"}.glyphicon-chevron-up:before{content:"\e113"}.glyphicon-chevron-down:before{content:"\e114"}.glyphicon-retweet:before{content:"\e115"}.glyphicon-shopping-cart:before{content:"\e116"}.glyphicon-folder-close:before{content:"\e117"}.glyphicon-folder-open:before{content:"\e118"}.glyphicon-resize-vertical:before{content:"\e119"}.glyphicon-resize-horizontal:before{content:"\e120"}.glyphicon-hdd:before{content:"\e121"}.glyphicon-bullhorn:before{content:"\e122"}.glyphicon-bell:before{content:"\e123"}.glyphicon-certificate:before{content:"\e124"}.glyphicon-thumbs-up:before{content:"\e125"}.glyphicon-thumbs-down:before{content:"\e126"}.glyphicon-hand-right:before{content:"\e127"}.glyphicon-hand-left:before{content:"\e128"}.glyphicon-hand-up:before{content:"\e129"}.glyphicon-hand-down:before{content:"\e130"}.glyphicon-circle-arrow-right:before{content:"\e131"}.glyphicon-circle-arrow-left:before{content:"\e132"}.glyphicon-circle-arrow-up:before{content:"\e133"}.glyphicon-circle-arrow-down:before{content:"\e134"}.glyphicon-globe:before{content:"\e135"}.glyphicon-wrench:before{content:"\e136"}.glyphicon-tasks:before{content:"\e137"}.glyphicon-filter:before{content:"\e138"}.glyphicon-briefcase:before{content:"\e139"}.glyphicon-fullscreen:before{content:"\e140"}.glyphicon-dashboard:before{content:"\e141"}.glyphicon-paperclip:before{content:"\e142"}.glyphicon-heart-empty:before{content:"\e143"}.glyphicon-link:before{content:"\e144"}.glyphicon-phone:before{content:"\e145"}.glyphicon-pushpin:before{content:"\e146"}.glyphicon-usd:before{content:"\e148"}.glyphicon-gbp:before{content:"\e149"}.glyphicon-sort:before{content:"\e150"}.glyphicon-sort-by-alphabet:before{content:"\e151"}.glyphicon-sort-by-alphabet-alt:before{content:"\e152"}.glyphicon-sort-by-order:before{content:"\e153"}.glyphicon-sort-by-order-alt:before{content:"\e154"}.glyphicon-sort-by-attributes:before{content:"\e155"}.glyphicon-sort-by-attributes-alt:before{content:"\e156"}.glyphicon-unchecked:before{content:"\e157"}.glyphicon-expand:before{content:"\e158"}.glyphicon-collapse-down:before{content:"\e159"}.glyphicon-collapse-up:before{content:"\e160"}.glyphicon-log-in:before{content:"\e161"}.glyphicon-flash:before{content:"\e162"}.glyphicon-log-out:before{content:"\e163"}.glyphicon-new-window:before{content:"\e164"}.glyphicon-record:before{content:"\e165"}.glyphicon-save:before{content:"\e166"}.glyphicon-open:before{content:"\e167"}.glyphicon-saved:before{content:"\e168"}.glyphicon-import:before{content:"\e169"}.glyphicon-export:before{content:"\e170"}.glyphicon-send:before{content:"\e171"}.glyphicon-floppy-disk:before{content:"\e172"}.glyphicon-floppy-saved:before{content:"\e173"}.glyphicon-floppy-remove:before{content:"\e174"}.glyphicon-floppy-save:before{content:"\e175"}.glyphicon-floppy-open:before{content:"\e176"}.glyphicon-credit-card:before{content:"\e177"}.glyphicon-transfer:before{content:"\e178"}.glyphicon-cutlery:before{content:"\e179"}.glyphicon-header:before{content:"\e180"}.glyphicon-compressed:before{content:"\e181"}.glyphicon-earphone:before{content:"\e182"}.glyphicon-phone-alt:before{content:"\e183"}.glyphicon-tower:before{content:"\e184"}.glyphicon-stats:before{content:"\e185"}.glyphicon-sd-video:before{content:"\e186"}.glyphicon-hd-video:before{content:"\e187"}.glyphicon-subtitles:before{content:"\e188"}.glyphicon-sound-stereo:before{content:"\e189"}.glyphicon-sound-dolby:before{content:"\e190"}.glyphicon-sound-5-1:before{content:"\e191"}.glyphicon-sound-6-1:before{content:"\e192"}.glyphicon-sound-7-1:before{content:"\e193"}.glyphicon-copyright-mark:before{content:"\e194"}.glyphicon-registration-mark:before{content:"\e195"}.glyphicon-cloud-download:before{content:"\e197"}.glyphicon-cloud-upload:before{content:"\e198"}.glyphicon-tree-conifer:before{content:"\e199"}.glyphicon-tree-deciduous:before{content:"\e200"}.glyphicon-cd:before{content:"\e201"}.glyphicon-save-file:before{content:"\e202"}.glyphicon-open-file:before{content:"\e203"}.glyphicon-level-up:before{content:"\e204"}.glyphicon-copy:before{content:"\e205"}.glyphicon-paste:before{content:"\e206"}.glyphicon-alert:before{content:"\e209"}.glyphicon-equalizer:before{content:"\e210"}.glyphicon-king:before{content:"\e211"}.glyphicon-queen:before{content:"\e212"}.glyphicon-pawn:before{content:"\e213"}.glyphicon-bishop:before{content:"\e214"}.glyphicon-knight:before{content:"\e215"}.glyphicon-baby-formula:before{content:"\e216"}.glyphicon-tent:before{content:"\26fa"}.glyphicon-blackboard:before{content:"\e218"}.glyphicon-bed:before{content:"\e219"}.glyphicon-apple:before{content:"\f8ff"}.glyphicon-erase:before{content:"\e221"}.glyphicon-hourglass:before{content:"\231b"}.glyphicon-lamp:before{content:"\e223"}.glyphicon-duplicate:before{content:"\e224"}.glyphicon-piggy-bank:before{content:"\e225"}.glyphicon-scissors:before{content:"\e226"}.glyphicon-bitcoin:before{content:"\e227"}.glyphicon-btc:before{content:"\e227"}.glyphicon-xbt:before{content:"\e227"}.glyphicon-yen:before{content:"\00a5"}.glyphicon-jpy:before{content:"\00a5"}.glyphicon-ruble:before{content:"\20bd"}.glyphicon-rub:before{content:"\20bd"}.glyphicon-scale:before{content:"\e230"}.glyphicon-ice-lolly:before{content:"\e231"}.glyphicon-ice-lolly-tasted:before{content:"\e232"}.glyphicon-education:before{content:"\e233"}.glyphicon-option-horizontal:before{content:"\e234"}.glyphicon-option-vertical:before{content:"\e235"}.glyphicon-menu-hamburger:before{content:"\e236"}.glyphicon-modal-window:before{content:"\e237"}.glyphicon-oil:before{content:"\e238"}.glyphicon-grain:before{content:"\e239"}.glyphicon-sunglasses:before{content:"\e240"}.glyphicon-text-size:before{content:"\e241"}.glyphicon-text-color:before{content:"\e242"}.glyphicon-text-background:before{content:"\e243"}.glyphicon-object-align-top:before{content:"\e244"}.glyphicon-object-align-bottom:before{content:"\e245"}.glyphicon-object-align-horizontal:before{content:"\e246"}.glyphicon-object-align-left:before{content:"\e247"}.glyphicon-object-align-vertical:before{content:"\e248"}.glyphicon-object-align-right:before{content:"\e249"}.glyphicon-triangle-right:before{content:"\e250"}.glyphicon-triangle-left:before{content:"\e251"}.glyphicon-triangle-bottom:before{content:"\e252"}.glyphicon-triangle-top:before{content:"\e253"}.glyphicon-console:before{content:"\e254"}.glyphicon-superscript:before{content:"\e255"}.glyphicon-subscript:before{content:"\e256"}.glyphicon-menu-left:before{content:"\e257"}.glyphicon-menu-right:before{content:"\e258"}.glyphicon-menu-down:before{content:"\e259"}.glyphicon-menu-up:before{content:"\e260"}*{-webkit-box-sizing:border-box;-moz-box-sizing:border-box;box-sizing:border-box}:after,:before{-webkit-box-sizing:border-box;-moz-box-sizing:border-box;box-sizing:border-box}html{font-size:10px;-webkit-tap-highlight-color:rgba(0,0,0,0)}body{font-family:"Helvetica Neue",Helvetica,Arial,sans-serif;font-size:14px;line-height:1.42857143;color:#333;background-color:#fff}button,input,select,textarea{font-family:inherit;font-size:inherit;line-height:inherit}a{color:#337ab7;text-decoration:none}a:focus,a:hover{color:#23527c;text-decoration:underline}a:focus{outline:thin dotted;outline:5px auto -webkit-focus-ring-color;outline-offset:-2px}figure{margin:0}img{vertical-align:middle}.carousel-inner>.item>a>img,.carousel-inner>.item>img,.img-responsive,.thumbnail a>img,.thumbnail>img{display:block;max-width:100%;height:auto}.img-rounded{border-radius:6px}.img-thumbnail{display:inline-block;max-width:100%;height:auto;padding:4px;line-height:1.42857143;background-color:#fff;border:1px solid #ddd;border-radius:4px;-webkit-transition:all .2s ease-in-out;-o-transition:all .2s ease-in-out;transition:all .2s ease-in-out}.img-circle{border-radius:50%}hr{margin-top:20px;margin-bottom:20px;border:0;border-top:1px solid #eee}.sr-only{position:absolute;width:1px;height:1px;padding:0;margin:-1px;overflow:hidden;clip:rect(0,0,0,0);border:0}.sr-only-focusable:active,.sr-only-focusable:focus{position:static;width:auto;height:auto;margin:0;overflow:visible;clip:auto}[role=button]{cursor:pointer}.h1,.h2,.h3,.h4,.h5,.h6,h1,h2,h3,h4,h5,h6{font-family:inherit;font-weight:500;line-height:1.1;color:inherit}.h1 .small,.h1 small,.h2 .small,.h2 small,.h3 .small,.h3 small,.h4 .small,.h4 small,.h5 .small,.h5 small,.h6 .small,.h6 small,h1 .small,h1 small,h2 .small,h2 small,h3 .small,h3 small,h4 .small,h4 small,h5 .small,h5 small,h6 .small,h6 small{font-weight:400;line-height:1;color:#777}.h1,.h2,.h3,h1,h2,h3{margin-top:20px;margin-bottom:10px}.h1 .small,.h1 small,.h2 .small,.h2 small,.h3 .small,.h3 small,h1 .small,h1 small,h2 .small,h2 small,h3 .small,h3 small{font-size:65%}.h4,.h5,.h6,h4,h5,h6{margin-top:10px;margin-bottom:10px}.h4 .small,.h4 small,.h5 .small,.h5 small,.h6 .small,.h6 small,h4 .small,h4 small,h5 .small,h5 small,h6 .small,h6 small{font-size:75%}.h1,h1{font-size:36px}.h2,h2{font-size:30px}.h3,h3{font-size:24px}.h4,h4{font-size:18px}.h5,h5{font-size:14px}.h6,h6{font-size:12px}p{margin:0 0 10px}.lead{margin-bottom:20px;font-size:16px;font-weight:300;line-height:1.4}@media (min-width:768px){.lead{font-size:21px}}.small,small{font-size:85%}.mark,mark{padding:.2em;background-color:#fcf8e3}.text-left{text-align:left}.text-right{text-align:right}.text-center{text-align:center}.text-justify{text-align:justify}.text-nowrap{white-space:nowrap}.text-lowercase{text-transform:lowercase}.text-uppercase{text-transform:uppercase}.text-capitalize{text-transform:capitalize}.text-muted{color:#777}.text-primary{color:#337ab7}a.text-primary:focus,a.text-primary:hover{color:#286090}.text-success{color:#3c763d}a.text-success:focus,a.text-success:hover{color:#2b542c}.text-info{color:#31708f}a.text-info:focus,a.text-info:hover{color:#245269}.text-warning{color:#8a6d3b}a.text-warning:focus,a.text-warning:hover{color:#66512c}.text-danger{color:#a94442}a.text-danger:focus,a.text-danger:hover{color:#843534}.bg-primary{color:#fff;background-color:#337ab7}a.bg-primary:focus,a.bg-primary:hover{background-color:#286090}.bg-success{background-color:#dff0d8}a.bg-success:focus,a.bg-success:hover{background-color:#c1e2b3}.bg-info{background-color:#d9edf7}a.bg-info:focus,a.bg-info:hover{background-color:#afd9ee}.bg-warning{background-color:#fcf8e3}a.bg-warning:focus,a.bg-warning:hover{background-color:#f7ecb5}.bg-danger{background-color:#f2dede}a.bg-danger:focus,a.bg-danger:hover{background-color:#e4b9b9}.page-header{padding-bottom:9px;margin:40px 0 20px;border-bottom:1px solid #eee}ol,ul{margin-top:0;margin-bottom:10px}ol ol,ol ul,ul ol,ul ul{margin-bottom:0}.list-unstyled{padding-left:0;list-style:none}.list-inline{padding-left:0;margin-left:-5px;list-style:none}.list-inline>li{display:inline-block;padding-right:5px;padding-left:5px}dl{margin-top:0;margin-bottom:20px}dd,dt{line-height:1.42857143}dt{font-weight:700}dd{margin-left:0}@media (min-width:768px){.dl-horizontal dt{float:left;width:160px;overflow:hidden;clear:left;text-align:right;text-overflow:ellipsis;white-space:nowrap}.dl-horizontal dd{margin-left:180px}}abbr[data-original-title],abbr[title]{cursor:help;border-bottom:1px dotted #777}.initialism{font-size:90%;text-transform:uppercase}blockquote{padding:10px 20px;margin:0 0 20px;font-size:17.5px;border-left:5px solid #eee}blockquote ol:last-child,blockquote p:last-child,blockquote ul:last-child{margin-bottom:0}blockquote .small,blockquote footer,blockquote small{display:block;font-size:80%;line-height:1.42857143;color:#777}blockquote .small:before,blockquote footer:before,blockquote small:before{content:'\2014 \00A0'}.blockquote-reverse,blockquote.pull-right{padding-right:15px;padding-left:0;text-align:right;border-right:5px solid #eee;border-left:0}.blockquote-reverse .small:before,.blockquote-reverse footer:before,.blockquote-reverse small:before,blockquote.pull-right .small:before,blockquote.pull-right footer:before,blockquote.pull-right small:before{content:''}.blockquote-reverse .small:after,.blockquote-reverse footer:after,.blockquote-reverse small:after,blockquote.pull-right .small:after,blockquote.pull-right footer:after,blockquote.pull-right small:after{content:'\00A0 \2014'}address{margin-bottom:20px;font-style:normal;line-height:1.42857143}code,kbd,pre,samp{font-family:Menlo,Monaco,Consolas,"Courier New",monospace}code{padding:2px 4px;font-size:90%;color:#c7254e;background-color:#f9f2f4;border-radius:4px}kbd{padding:2px 4px;font-size:90%;color:#fff;background-color:#333;border-radius:3px;-webkit-box-shadow:inset 0 -1px 0 rgba(0,0,0,.25);box-shadow:inset 0 -1px 0 rgba(0,0,0,.25)}kbd kbd{padding:0;font-size:100%;font-weight:700;-webkit-box-shadow:none;box-shadow:none}pre{display:block;padding:9.5px;margin:0 0 10px;font-size:13px;line-height:1.42857143;color:#333;word-break:break-all;word-wrap:break-word;background-color:#f5f5f5;border:1px solid #ccc;border-radius:4px}pre code{padding:0;font-size:inherit;color:inherit;white-space:pre-wrap;background-color:transparent;border-radius:0}.pre-scrollable{max-height:340px;overflow-y:scroll}.container{padding-right:15px;padding-left:15px;margin-right:auto;margin-left:auto}@media (min-width:768px){.container{width:750px}}@media (min-width:992px){.container{width:970px}}@media (min-width:1200px){.container{width:1170px}}.container-fluid{padding-right:15px;padding-left:15px;margin-right:auto;margin-left:auto}.row{margin-right:-15px;margin-left:-15px}.col-lg-1,.col-lg-10,.col-lg-11,.col-lg-12,.col-lg-2,.col-lg-3,.col-lg-4,.col-lg-5,.col-lg-6,.col-lg-7,.col-lg-8,.col-lg-9,.col-md-1,.col-md-10,.col-md-11,.col-md-12,.col-md-2,.col-md-3,.col-md-4,.col-md-5,.col-md-6,.col-md-7,.col-md-8,.col-md-9,.col-sm-1,.col-sm-10,.col-sm-11,.col-sm-12,.col-sm-2,.col-sm-3,.col-sm-4,.col-sm-5,.col-sm-6,.col-sm-7,.col-sm-8,.col-sm-9,.col-xs-1,.col-xs-10,.col-xs-11,.col-xs-12,.col-xs-2,.col-xs-3,.col-xs-4,.col-xs-5,.col-xs-6,.col-xs-7,.col-xs-8,.col-xs-9{position:relative;min-height:1px;padding-right:15px;padding-left:15px}.col-xs-1,.col-xs-10,.col-xs-11,.col-xs-12,.col-xs-2,.col-xs-3,.col-xs-4,.col-xs-5,.col-xs-6,.col-xs-7,.col-xs-8,.col-xs-9{float:left}.col-xs-12{width:100%}.col-xs-11{width:91.66666667%}.col-xs-10{width:83.33333333%}.col-xs-9{width:75%}.col-xs-8{width:66.66666667%}.col-xs-7{width:58.33333333%}.col-xs-6{width:50%}.col-xs-5{width:41.66666667%}.col-xs-4{width:33.33333333%}.col-xs-3{width:25%}.col-xs-2{width:16.66666667%}.col-xs-1{width:8.33333333%}.col-xs-pull-12{right:100%}.col-xs-pull-11{right:91.66666667%}.col-xs-pull-10{right:83.33333333%}.col-xs-pull-9{right:75%}.col-xs-pull-8{right:66.66666667%}.col-xs-pull-7{right:58.33333333%}.col-xs-pull-6{right:50%}.col-xs-pull-5{right:41.66666667%}.col-xs-pull-4{right:33.33333333%}.col-xs-pull-3{right:25%}.col-xs-pull-2{right:16.66666667%}.col-xs-pull-1{right:8.33333333%}.col-xs-pull-0{right:auto}.col-xs-push-12{left:100%}.col-xs-push-11{left:91.66666667%}.col-xs-push-10{left:83.33333333%}.col-xs-push-9{left:75%}.col-xs-push-8{left:66.66666667%}.col-xs-push-7{left:58.33333333%}.col-xs-push-6{left:50%}.col-xs-push-5{left:41.66666667%}.col-xs-push-4{left:33.33333333%}.col-xs-push-3{left:25%}.col-xs-push-2{left:16.66666667%}.col-xs-push-1{left:8.33333333%}.col-xs-push-0{left:auto}.col-xs-offset-12{margin-left:100%}.col-xs-offset-11{margin-left:91.66666667%}.col-xs-offset-10{margin-left:83.33333333%}.col-xs-offset-9{margin-left:75%}.col-xs-offset-8{margin-left:66.66666667%}.col-xs-offset-7{margin-left:58.33333333%}.col-xs-offset-6{margin-left:50%}.col-xs-offset-5{margin-left:41.66666667%}.col-xs-offset-4{margin-left:33.33333333%}.col-xs-offset-3{margin-left:25%}.col-xs-offset-2{margin-left:16.66666667%}.col-xs-offset-1{margin-left:8.33333333%}.col-xs-offset-0{margin-left:0}@media (min-width:768px){.col-sm-1,.col-sm-10,.col-sm-11,.col-sm-12,.col-sm-2,.col-sm-3,.col-sm-4,.col-sm-5,.col-sm-6,.col-sm-7,.col-sm-8,.col-sm-9{float:left}.col-sm-12{width:100%}.col-sm-11{width:91.66666667%}.col-sm-10{width:83.33333333%}.col-sm-9{width:75%}.col-sm-8{width:66.66666667%}.col-sm-7{width:58.33333333%}.col-sm-6{width:50%}.col-sm-5{width:41.66666667%}.col-sm-4{width:33.33333333%}.col-sm-3{width:25%}.col-sm-2{width:16.66666667%}.col-sm-1{width:8.33333333%}.col-sm-pull-12{right:100%}.col-sm-pull-11{right:91.66666667%}.col-sm-pull-10{right:83.33333333%}.col-sm-pull-9{right:75%}.col-sm-pull-8{right:66.66666667%}.col-sm-pull-7{right:58.33333333%}.col-sm-pull-6{right:50%}.col-sm-pull-5{right:41.66666667%}.col-sm-pull-4{right:33.33333333%}.col-sm-pull-3{right:25%}.col-sm-pull-2{right:16.66666667%}.col-sm-pull-1{right:8.33333333%}.col-sm-pull-0{right:auto}.col-sm-push-12{left:100%}.col-sm-push-11{left:91.66666667%}.col-sm-push-10{left:83.33333333%}.col-sm-push-9{left:75%}.col-sm-push-8{left:66.66666667%}.col-sm-push-7{left:58.33333333%}.col-sm-push-6{left:50%}.col-sm-push-5{left:41.66666667%}.col-sm-push-4{left:33.33333333%}.col-sm-push-3{left:25%}.col-sm-push-2{left:16.66666667%}.col-sm-push-1{left:8.33333333%}.col-sm-push-0{left:auto}.col-sm-offset-12{margin-left:100%}.col-sm-offset-11{margin-left:91.66666667%}.col-sm-offset-10{margin-left:83.33333333%}.col-sm-offset-9{margin-left:75%}.col-sm-offset-8{margin-left:66.66666667%}.col-sm-offset-7{margin-left:58.33333333%}.col-sm-offset-6{margin-left:50%}.col-sm-offset-5{margin-left:41.66666667%}.col-sm-offset-4{margin-left:33.33333333%}.col-sm-offset-3{margin-left:25%}.col-sm-offset-2{margin-left:16.66666667%}.col-sm-offset-1{margin-left:8.33333333%}.col-sm-offset-0{margin-left:0}}@media (min-width:992px){.col-md-1,.col-md-10,.col-md-11,.col-md-12,.col-md-2,.col-md-3,.col-md-4,.col-md-5,.col-md-6,.col-md-7,.col-md-8,.col-md-9{float:left}.col-md-12{width:100%}.col-md-11{width:91.66666667%}.col-md-10{width:83.33333333%}.col-md-9{width:75%}.col-md-8{width:66.66666667%}.col-md-7{width:58.33333333%}.col-md-6{width:50%}.col-md-5{width:41.66666667%}.col-md-4{width:33.33333333%}.col-md-3{width:25%}.col-md-2{width:16.66666667%}.col-md-1{width:8.33333333%}.col-md-pull-12{right:100%}.col-md-pull-11{right:91.66666667%}.col-md-pull-10{right:83.33333333%}.col-md-pull-9{right:75%}.col-md-pull-8{right:66.66666667%}.col-md-pull-7{right:58.33333333%}.col-md-pull-6{right:50%}.col-md-pull-5{right:41.66666667%}.col-md-pull-4{right:33.33333333%}.col-md-pull-3{right:25%}.col-md-pull-2{right:16.66666667%}.col-md-pull-1{right:8.33333333%}.col-md-pull-0{right:auto}.col-md-push-12{left:100%}.col-md-push-11{left:91.66666667%}.col-md-push-10{left:83.33333333%}.col-md-push-9{left:75%}.col-md-push-8{left:66.66666667%}.col-md-push-7{left:58.33333333%}.col-md-push-6{left:50%}.col-md-push-5{left:41.66666667%}.col-md-push-4{left:33.33333333%}.col-md-push-3{left:25%}.col-md-push-2{left:16.66666667%}.col-md-push-1{left:8.33333333%}.col-md-push-0{left:auto}.col-md-offset-12{margin-left:100%}.col-md-offset-11{margin-left:91.66666667%}.col-md-offset-10{margin-left:83.33333333%}.col-md-offset-9{margin-left:75%}.col-md-offset-8{margin-left:66.66666667%}.col-md-offset-7{margin-left:58.33333333%}.col-md-offset-6{margin-left:50%}.col-md-offset-5{margin-left:41.66666667%}.col-md-offset-4{margin-left:33.33333333%}.col-md-offset-3{margin-left:25%}.col-md-offset-2{margin-left:16.66666667%}.col-md-offset-1{margin-left:8.33333333%}.col-md-offset-0{margin-left:0}}@media (min-width:1200px){.col-lg-1,.col-lg-10,.col-lg-11,.col-lg-12,.col-lg-2,.col-lg-3,.col-lg-4,.col-lg-5,.col-lg-6,.col-lg-7,.col-lg-8,.col-lg-9{float:left}.col-lg-12{width:100%}.col-lg-11{width:91.66666667%}.col-lg-10{width:83.33333333%}.col-lg-9{width:75%}.col-lg-8{width:66.66666667%}.col-lg-7{width:58.33333333%}.col-lg-6{width:50%}.col-lg-5{width:41.66666667%}.col-lg-4{width:33.33333333%}.col-lg-3{width:25%}.col-lg-2{width:16.66666667%}.col-lg-1{width:8.33333333%}.col-lg-pull-12{right:100%}.col-lg-pull-11{right:91.66666667%}.col-lg-pull-10{right:83.33333333%}.col-lg-pull-9{right:75%}.col-lg-pull-8{right:66.66666667%}.col-lg-pull-7{right:58.33333333%}.col-lg-pull-6{right:50%}.col-lg-pull-5{right:41.66666667%}.col-lg-pull-4{right:33.33333333%}.col-lg-pull-3{right:25%}.col-lg-pull-2{right:16.66666667%}.col-lg-pull-1{right:8.33333333%}.col-lg-pull-0{right:auto}.col-lg-push-12{left:100%}.col-lg-push-11{left:91.66666667%}.col-lg-push-10{left:83.33333333%}.col-lg-push-9{left:75%}.col-lg-push-8{left:66.66666667%}.col-lg-push-7{left:58.33333333%}.col-lg-push-6{left:50%}.col-lg-push-5{left:41.66666667%}.col-lg-push-4{left:33.33333333%}.col-lg-push-3{left:25%}.col-lg-push-2{left:16.66666667%}.col-lg-push-1{left:8.33333333%}.col-lg-push-0{left:auto}.col-lg-offset-12{margin-left:100%}.col-lg-offset-11{margin-left:91.66666667%}.col-lg-offset-10{margin-left:83.33333333%}.col-lg-offset-9{margin-left:75%}.col-lg-offset-8{margin-left:66.66666667%}.col-lg-offset-7{margin-left:58.33333333%}.col-lg-offset-6{margin-left:50%}.col-lg-offset-5{margin-left:41.66666667%}.col-lg-offset-4{margin-left:33.33333333%}.col-lg-offset-3{margin-left:25%}.col-lg-offset-2{margin-left:16.66666667%}.col-lg-offset-1{margin-left:8.33333333%}.col-lg-offset-0{margin-left:0}}table{background-color:transparent}caption{padding-top:8px;padding-bottom:8px;color:#777;text-align:left}th{text-align:left}.table{width:100%;max-width:100%;margin-bottom:20px}.table>tbody>tr>td,.table>tbody>tr>th,.table>tfoot>tr>td,.table>tfoot>tr>th,.table>thead>tr>td,.table>thead>tr>th{padding:8px;line-height:1.42857143;vertical-align:top;border-top:1px solid #ddd}.table>thead>tr>th{vertical-align:bottom;border-bottom:2px solid #ddd}.table>caption+thead>tr:first-child>td,.table>caption+thead>tr:first-child>th,.table>colgroup+thead>tr:first-child>td,.table>colgroup+thead>tr:first-child>th,.table>thead:first-child>tr:first-child>td,.table>thead:first-child>tr:first-child>th{border-top:0}.table>tbody+tbody{border-top:2px solid #ddd}.table .table{background-color:#fff}.table-condensed>tbody>tr>td,.table-condensed>tbody>tr>th,.table-condensed>tfoot>tr>td,.table-condensed>tfoot>tr>th,.table-condensed>thead>tr>td,.table-condensed>thead>tr>th{padding:5px}.table-bordered{border:1px solid #ddd}.table-bordered>tbody>tr>td,.table-bordered>tbody>tr>th,.table-bordered>tfoot>tr>td,.table-bordered>tfoot>tr>th,.table-bordered>thead>tr>td,.table-bordered>thead>tr>th{border:1px solid #ddd}.table-bordered>thead>tr>td,.table-bordered>thead>tr>th{border-bottom-width:2px}.table-striped>tbody>tr:nth-of-type(odd){background-color:#f9f9f9}.table-hover>tbody>tr:hover{background-color:#f5f5f5}table col[class*=col-]{position:static;display:table-column;float:none}table td[class*=col-],table th[class*=col-]{position:static;display:table-cell;float:none}.table>tbody>tr.active>td,.table>tbody>tr.active>th,.table>tbody>tr>td.active,.table>tbody>tr>th.active,.table>tfoot>tr.active>td,.table>tfoot>tr.active>th,.table>tfoot>tr>td.active,.table>tfoot>tr>th.active,.table>thead>tr.active>td,.table>thead>tr.active>th,.table>thead>tr>td.active,.table>thead>tr>th.active{background-color:#f5f5f5}.table-hover>tbody>tr.active:hover>td,.table-hover>tbody>tr.active:hover>th,.table-hover>tbody>tr:hover>.active,.table-hover>tbody>tr>td.active:hover,.table-hover>tbody>tr>th.active:hover{background-color:#e8e8e8}.table>tbody>tr.success>td,.table>tbody>tr.success>th,.table>tbody>tr>td.success,.table>tbody>tr>th.success,.table>tfoot>tr.success>td,.table>tfoot>tr.success>th,.table>tfoot>tr>td.success,.table>tfoot>tr>th.success,.table>thead>tr.success>td,.table>thead>tr.success>th,.table>thead>tr>td.success,.table>thead>tr>th.success{background-color:#dff0d8}.table-hover>tbody>tr.success:hover>td,.table-hover>tbody>tr.success:hover>th,.table-hover>tbody>tr:hover>.success,.table-hover>tbody>tr>td.success:hover,.table-hover>tbody>tr>th.success:hover{background-color:#d0e9c6}.table>tbody>tr.info>td,.table>tbody>tr.info>th,.table>tbody>tr>td.info,.table>tbody>tr>th.info,.table>tfoot>tr.info>td,.table>tfoot>tr.info>th,.table>tfoot>tr>td.info,.table>tfoot>tr>th.info,.table>thead>tr.info>td,.table>thead>tr.info>th,.table>thead>tr>td.info,.table>thead>tr>th.info{background-color:#d9edf7}.table-hover>tbody>tr.info:hover>td,.table-hover>tbody>tr.info:hover>th,.table-hover>tbody>tr:hover>.info,.table-hover>tbody>tr>td.info:hover,.table-hover>tbody>tr>th.info:hover{background-color:#c4e3f3}.table>tbody>tr.warning>td,.table>tbody>tr.warning>th,.table>tbody>tr>td.warning,.table>tbody>tr>th.warning,.table>tfoot>tr.warning>td,.table>tfoot>tr.warning>th,.table>tfoot>tr>td.warning,.table>tfoot>tr>th.warning,.table>thead>tr.warning>td,.table>thead>tr.warning>th,.table>thead>tr>td.warning,.table>thead>tr>th.warning{background-color:#fcf8e3}.table-hover>tbody>tr.warning:hover>td,.table-hover>tbody>tr.warning:hover>th,.table-hover>tbody>tr:hover>.warning,.table-hover>tbody>tr>td.warning:hover,.table-hover>tbody>tr>th.warning:hover{background-color:#faf2cc}.table>tbody>tr.danger>td,.table>tbody>tr.danger>th,.table>tbody>tr>td.danger,.table>tbody>tr>th.danger,.table>tfoot>tr.danger>td,.table>tfoot>tr.danger>th,.table>tfoot>tr>td.danger,.table>tfoot>tr>th.danger,.table>thead>tr.danger>td,.table>thead>tr.danger>th,.table>thead>tr>td.danger,.table>thead>tr>th.danger{background-color:#f2dede}.table-hover>tbody>tr.danger:hover>td,.table-hover>tbody>tr.danger:hover>th,.table-hover>tbody>tr:hover>.danger,.table-hover>tbody>tr>td.danger:hover,.table-hover>tbody>tr>th.danger:hover{background-color:#ebcccc}.table-responsive{min-height:.01%;overflow-x:auto}@media screen and (max-width:767px){.table-responsive{width:100%;margin-bottom:15px;overflow-y:hidden;-ms-overflow-style:-ms-autohiding-scrollbar;border:1px solid #ddd}.table-responsive>.table{margin-bottom:0}.table-responsive>.table>tbody>tr>td,.table-responsive>.table>tbody>tr>th,.table-responsive>.table>tfoot>tr>td,.table-responsive>.table>tfoot>tr>th,.table-responsive>.table>thead>tr>td,.table-responsive>.table>thead>tr>th{white-space:nowrap}.table-responsive>.table-bordered{border:0}.table-responsive>.table-bordered>tbody>tr>td:first-child,.table-responsive>.table-bordered>tbody>tr>th:first-child,.table-responsive>.table-bordered>tfoot>tr>td:first-child,.table-responsive>.table-bordered>tfoot>tr>th:first-child,.table-responsive>.table-bordered>thead>tr>td:first-child,.table-responsive>.table-bordered>thead>tr>th:first-child{border-left:0}.table-responsive>.table-bordered>tbody>tr>td:last-child,.table-responsive>.table-bordered>tbody>tr>th:last-child,.table-responsive>.table-bordered>tfoot>tr>td:last-child,.table-responsive>.table-bordered>tfoot>tr>th:last-child,.table-responsive>.table-bordered>thead>tr>td:last-child,.table-responsive>.table-bordered>thead>tr>th:last-child{border-right:0}.table-responsive>.table-bordered>tbody>tr:last-child>td,.table-responsive>.table-bordered>tbody>tr:last-child>th,.table-responsive>.table-bordered>tfoot>tr:last-child>td,.table-responsive>.table-bordered>tfoot>tr:last-child>th{border-bottom:0}}fieldset{min-width:0;padding:0;margin:0;border:0}legend{display:block;width:100%;padding:0;margin-bottom:20px;font-size:21px;line-height:inherit;color:#333;border:0;border-bottom:1px solid #e5e5e5}label{display:inline-block;max-width:100%;margin-bottom:5px;font-weight:700}input[type=search]{-webkit-box-sizing:border-box;-moz-box-sizing:border-box;box-sizing:border-box}input[type=checkbox],input[type=radio]{margin:4px 0 0;margin-top:1px\9;line-height:normal}input[type=file]{display:block}input[type=range]{display:block;width:100%}select[multiple],select[size]{height:auto}input[type=file]:focus,input[type=checkbox]:focus,input[type=radio]:focus{outline:thin dotted;outline:5px auto -webkit-focus-ring-color;outline-offset:-2px}output{display:block;padding-top:7px;font-size:14px;line-height:1.42857143;color:#555}.form-control{display:block;width:100%;height:34px;padding:6px 12px;font-size:14px;line-height:1.42857143;color:#555;background-color:#fff;background-image:none;border:1px solid #ccc;border-radius:4px;-webkit-box-shadow:inset 0 1px 1px rgba(0,0,0,.075);box-shadow:inset 0 1px 1px rgba(0,0,0,.075);-webkit-transition:border-color ease-in-out .15s,-webkit-box-shadow ease-in-out .15s;-o-transition:border-color ease-in-out .15s,box-shadow ease-in-out .15s;transition:border-color ease-in-out .15s,box-shadow ease-in-out .15s}.form-control:focus{border-color:#66afe9;outline:0;-webkit-box-shadow:inset 0 1px 1px rgba(0,0,0,.075),0 0 8px rgba(102,175,233,.6);box-shadow:inset 0 1px 1px rgba(0,0,0,.075),0 0 8px rgba(102,175,233,.6)}.form-control::-moz-placeholder{color:#999;opacity:1}.form-control:-ms-input-placeholder{color:#999}.form-control::-webkit-input-placeholder{color:#999}.form-control[disabled],.form-control[readonly],fieldset[disabled] .form-control{background-color:#eee;opacity:1}.form-control[disabled],fieldset[disabled] .form-control{cursor:not-allowed}textarea.form-control{height:auto}input[type=search]{-webkit-appearance:none}@media screen and (-webkit-min-device-pixel-ratio:0){input[type=date].form-control,input[type=time].form-control,input[type=datetime-local].form-control,input[type=month].form-control{line-height:34px}.input-group-sm input[type=date],.input-group-sm input[type=time],.input-group-sm input[type=datetime-local],.input-group-sm input[type=month],input[type=date].input-sm,input[type=time].input-sm,input[type=datetime-local].input-sm,input[type=month].input-sm{line-height:30px}.input-group-lg input[type=date],.input-group-lg input[type=time],.input-group-lg input[type=datetime-local],.input-group-lg input[type=month],input[type=date].input-lg,input[type=time].input-lg,input[type=datetime-local].input-lg,input[type=month].input-lg{line-height:46px}}.form-group{margin-bottom:15px}.checkbox,.radio{position:relative;display:block;margin-top:10px;margin-bottom:10px}.checkbox label,.radio label{min-height:20px;padding-left:20px;margin-bottom:0;font-weight:400;cursor:pointer}.checkbox input[type=checkbox],.checkbox-inline input[type=checkbox],.radio input[type=radio],.radio-inline input[type=radio]{position:absolute;margin-top:4px\9;margin-left:-20px}.checkbox+.checkbox,.radio+.radio{margin-top:-5px}.checkbox-inline,.radio-inline{position:relative;display:inline-block;padding-left:20px;margin-bottom:0;font-weight:400;vertical-align:middle;cursor:pointer}.checkbox-inline+.checkbox-inline,.radio-inline+.radio-inline{margin-top:0;margin-left:10px}fieldset[disabled] input[type=checkbox],fieldset[disabled] input[type=radio],input[type=checkbox].disabled,input[type=checkbox][disabled],input[type=radio].disabled,input[type=radio][disabled]{cursor:not-allowed}.checkbox-inline.disabled,.radio-inline.disabled,fieldset[disabled] .checkbox-inline,fieldset[disabled] .radio-inline{cursor:not-allowed}.checkbox.disabled label,.radio.disabled label,fieldset[disabled] .checkbox label,fieldset[disabled] .radio label{cursor:not-allowed}.form-control-static{min-height:34px;padding-top:7px;padding-bottom:7px;margin-bottom:0}.form-control-static.input-lg,.form-control-static.input-sm{padding-right:0;padding-left:0}.input-sm{height:30px;padding:5px 10px;font-size:12px;line-height:1.5;border-radius:3px}select.input-sm{height:30px;line-height:30px}select[multiple].input-sm,textarea.input-sm{height:auto}.form-group-sm .form-control{height:30px;padding:5px 10px;font-size:12px;line-height:1.5;border-radius:3px}.form-group-sm select.form-control{height:30px;line-height:30px}.form-group-sm select[multiple].form-control,.form-group-sm textarea.form-control{height:auto}.form-group-sm .form-control-static{height:30px;min-height:32px;padding:6px 10px;font-size:12px;line-height:1.5}.input-lg{height:46px;padding:10px 16px;font-size:18px;line-height:1.3333333;border-radius:6px}select.input-lg{height:46px;line-height:46px}select[multiple].input-lg,textarea.input-lg{height:auto}.form-group-lg .form-control{height:46px;padding:10px 16px;font-size:18px;line-height:1.3333333;border-radius:6px}.form-group-lg select.form-control{height:46px;line-height:46px}.form-group-lg select[multiple].form-control,.form-group-lg textarea.form-control{height:auto}.form-group-lg .form-control-static{height:46px;min-height:38px;padding:11px 16px;font-size:18px;line-height:1.3333333}.has-feedback{position:relative}.has-feedback .form-control{padding-right:42.5px}.form-control-feedback{position:absolute;top:0;right:0;z-index:2;display:block;width:34px;height:34px;line-height:34px;text-align:center;pointer-events:none}.form-group-lg .form-control+.form-control-feedback,.input-group-lg+.form-control-feedback,.input-lg+.form-control-feedback{width:46px;height:46px;line-height:46px}.form-group-sm .form-control+.form-control-feedback,.input-group-sm+.form-control-feedback,.input-sm+.form-control-feedback{width:30px;height:30px;line-height:30px}.has-success .checkbox,.has-success .checkbox-inline,.has-success .control-label,.has-success .help-block,.has-success .radio,.has-success .radio-inline,.has-success.checkbox label,.has-success.checkbox-inline label,.has-success.radio label,.has-success.radio-inline label{color:#3c763d}.has-success .form-control{border-color:#3c763d;-webkit-box-shadow:inset 0 1px 1px rgba(0,0,0,.075);box-shadow:inset 0 1px 1px rgba(0,0,0,.075)}.has-success .form-control:focus{border-color:#2b542c;-webkit-box-shadow:inset 0 1px 1px rgba(0,0,0,.075),0 0 6px #67b168;box-shadow:inset 0 1px 1px rgba(0,0,0,.075),0 0 6px #67b168}.has-success .input-group-addon{color:#3c763d;background-color:#dff0d8;border-color:#3c763d}.has-success .form-control-feedback{color:#3c763d}.has-warning .checkbox,.has-warning .checkbox-inline,.has-warning .control-label,.has-warning .help-block,.has-warning .radio,.has-warning .radio-inline,.has-warning.checkbox label,.has-warning.checkbox-inline label,.has-warning.radio label,.has-warning.radio-inline label{color:#8a6d3b}.has-warning .form-control{border-color:#8a6d3b;-webkit-box-shadow:inset 0 1px 1px rgba(0,0,0,.075);box-shadow:inset 0 1px 1px rgba(0,0,0,.075)}.has-warning .form-control:focus{border-color:#66512c;-webkit-box-shadow:inset 0 1px 1px rgba(0,0,0,.075),0 0 6px #c0a16b;box-shadow:inset 0 1px 1px rgba(0,0,0,.075),0 0 6px #c0a16b}.has-warning .input-group-addon{color:#8a6d3b;background-color:#fcf8e3;border-color:#8a6d3b}.has-warning .form-control-feedback{color:#8a6d3b}.has-error .checkbox,.has-error .checkbox-inline,.has-error .control-label,.has-error .help-block,.has-error .radio,.has-error .radio-inline,.has-error.checkbox label,.has-error.checkbox-inline label,.has-error.radio label,.has-error.radio-inline label{color:#a94442}.has-error .form-control{border-color:#a94442;-webkit-box-shadow:inset 0 1px 1px rgba(0,0,0,.075);box-shadow:inset 0 1px 1px rgba(0,0,0,.075)}.has-error .form-control:focus{border-color:#843534;-webkit-box-shadow:inset 0 1px 1px rgba(0,0,0,.075),0 0 6px #ce8483;box-shadow:inset 0 1px 1px rgba(0,0,0,.075),0 0 6px #ce8483}.has-error .input-group-addon{color:#a94442;background-color:#f2dede;border-color:#a94442}.has-error .form-control-feedback{color:#a94442}.has-feedback label~.form-control-feedback{top:25px}.has-feedback label.sr-only~.form-control-feedback{top:0}.help-block{display:block;margin-top:5px;margin-bottom:10px;color:#737373}@media (min-width:768px){.form-inline .form-group{display:inline-block;margin-bottom:0;vertical-align:middle}.form-inline .form-control{display:inline-block;width:auto;vertical-align:middle}.form-inline .form-control-static{display:inline-block}.form-inline .input-group{display:inline-table;vertical-align:middle}.form-inline .input-group .form-control,.form-inline .input-group .input-group-addon,.form-inline .input-group .input-group-btn{width:auto}.form-inline .input-group>.form-control{width:100%}.form-inline .control-label{margin-bottom:0;vertical-align:middle}.form-inline .checkbox,.form-inline .radio{display:inline-block;margin-top:0;margin-bottom:0;vertical-align:middle}.form-inline .checkbox label,.form-inline .radio label{padding-left:0}.form-inline .checkbox input[type=checkbox],.form-inline .radio input[type=radio]{position:relative;margin-left:0}.form-inline .has-feedback .form-control-feedback{top:0}}.form-horizontal .checkbox,.form-horizontal .checkbox-inline,.form-horizontal .radio,.form-horizontal .radio-inline{padding-top:7px;margin-top:0;margin-bottom:0}.form-horizontal .checkbox,.form-horizontal .radio{min-height:27px}.form-horizontal .form-group{margin-right:-15px;margin-left:-15px}@media (min-width:768px){.form-horizontal .control-label{padding-top:7px;margin-bottom:0;text-align:right}}.form-horizontal .has-feedback .form-control-feedback{right:15px}@media (min-width:768px){.form-horizontal .form-group-lg .control-label{padding-top:14.33px;font-size:18px}}@media (min-width:768px){.form-horizontal .form-group-sm .control-label{padding-top:6px;font-size:12px}}.btn{display:inline-block;padding:6px 12px;margin-bottom:0;font-size:14px;font-weight:400;line-height:1.42857143;text-align:center;white-space:nowrap;vertical-align:middle;-ms-touch-action:manipulation;touch-action:manipulation;cursor:pointer;-webkit-user-select:none;-moz-user-select:none;-ms-user-select:none;user-select:none;background-image:none;border:1px solid transparent;border-radius:4px}.btn.active.focus,.btn.active:focus,.btn.focus,.btn:active.focus,.btn:active:focus,.btn:focus{outline:thin dotted;outline:5px auto -webkit-focus-ring-color;outline-offset:-2px}.btn.focus,.btn:focus,.btn:hover{color:#333;text-decoration:none}.btn.active,.btn:active{background-image:none;outline:0;-webkit-box-shadow:inset 0 3px 5px rgba(0,0,0,.125);box-shadow:inset 0 3px 5px rgba(0,0,0,.125)}.btn.disabled,.btn[disabled],fieldset[disabled] .btn{cursor:not-allowed;filter:alpha(opacity=65);-webkit-box-shadow:none;box-shadow:none;opacity:.65}a.btn.disabled,fieldset[disabled] a.btn{pointer-events:none}.btn-default{color:#333;background-color:#fff;border-color:#ccc}.btn-default.focus,.btn-default:focus{color:#333;background-color:#e6e6e6;border-color:#8c8c8c}.btn-default:hover{color:#333;background-color:#e6e6e6;border-color:#adadad}.btn-default.active,.btn-default:active,.open>.dropdown-toggle.btn-default{color:#333;background-color:#e6e6e6;border-color:#adadad}.btn-default.active.focus,.btn-default.active:focus,.btn-default.active:hover,.btn-default:active.focus,.btn-default:active:focus,.btn-default:active:hover,.open>.dropdown-toggle.btn-default.focus,.open>.dropdown-toggle.btn-default:focus,.open>.dropdown-toggle.btn-default:hover{color:#333;background-color:#d4d4d4;border-color:#8c8c8c}.btn-default.active,.btn-default:active,.open>.dropdown-toggle.btn-default{background-image:none}.btn-default.disabled,.btn-default.disabled.active,.btn-default.disabled.focus,.btn-default.disabled:active,.btn-default.disabled:focus,.btn-default.disabled:hover,.btn-default[disabled],.btn-default[disabled].active,.btn-default[disabled].focus,.btn-default[disabled]:active,.btn-default[disabled]:focus,.btn-default[disabled]:hover,fieldset[disabled] .btn-default,fieldset[disabled] .btn-default.active,fieldset[disabled] .btn-default.focus,fieldset[disabled] .btn-default:active,fieldset[disabled] .btn-default:focus,fieldset[disabled] .btn-default:hover{background-color:#fff;border-color:#ccc}.btn-default .badge{color:#fff;background-color:#333}.btn-primary{color:#fff;background-color:#337ab7;border-color:#2e6da4}.btn-primary.focus,.btn-primary:focus{color:#fff;background-color:#286090;border-color:#122b40}.btn-primary:hover{color:#fff;background-color:#286090;border-color:#204d74}.btn-primary.active,.btn-primary:active,.open>.dropdown-toggle.btn-primary{color:#fff;background-color:#286090;border-color:#204d74}.btn-primary.active.focus,.btn-primary.active:focus,.btn-primary.active:hover,.btn-primary:active.focus,.btn-primary:active:focus,.btn-primary:active:hover,.open>.dropdown-toggle.btn-primary.focus,.open>.dropdown-toggle.btn-primary:focus,.open>.dropdown-toggle.btn-primary:hover{color:#fff;background-color:#204d74;border-color:#122b40}.btn-primary.active,.btn-primary:active,.open>.dropdown-toggle.btn-primary{background-image:none}.btn-primary.disabled,.btn-primary.disabled.active,.btn-primary.disabled.focus,.btn-primary.disabled:active,.btn-primary.disabled:focus,.btn-primary.disabled:hover,.btn-primary[disabled],.btn-primary[disabled].active,.btn-primary[disabled].focus,.btn-primary[disabled]:active,.btn-primary[disabled]:focus,.btn-primary[disabled]:hover,fieldset[disabled] .btn-primary,fieldset[disabled] .btn-primary.active,fieldset[disabled] .btn-primary.focus,fieldset[disabled] .btn-primary:active,fieldset[disabled] .btn-primary:focus,fieldset[disabled] .btn-primary:hover{background-color:#337ab7;border-color:#2e6da4}.btn-primary .badge{color:#337ab7;background-color:#fff}.btn-success{color:#fff;background-color:#5cb85c;border-color:#4cae4c}.btn-success.focus,.btn-success:focus{color:#fff;background-color:#449d44;border-color:#255625}.btn-success:hover{color:#fff;background-color:#449d44;border-color:#398439}.btn-success.active,.btn-success:active,.open>.dropdown-toggle.btn-success{color:#fff;background-color:#449d44;border-color:#398439}.btn-success.active.focus,.btn-success.active:focus,.btn-success.active:hover,.btn-success:active.focus,.btn-success:active:focus,.btn-success:active:hover,.open>.dropdown-toggle.btn-success.focus,.open>.dropdown-toggle.btn-success:focus,.open>.dropdown-toggle.btn-success:hover{color:#fff;background-color:#398439;border-color:#255625}.btn-success.active,.btn-success:active,.open>.dropdown-toggle.btn-success{background-image:none}.btn-success.disabled,.btn-success.disabled.active,.btn-success.disabled.focus,.btn-success.disabled:active,.btn-success.disabled:focus,.btn-success.disabled:hover,.btn-success[disabled],.btn-success[disabled].active,.btn-success[disabled].focus,.btn-success[disabled]:active,.btn-success[disabled]:focus,.btn-success[disabled]:hover,fieldset[disabled] .btn-success,fieldset[disabled] .btn-success.active,fieldset[disabled] .btn-success.focus,fieldset[disabled] .btn-success:active,fieldset[disabled] .btn-success:focus,fieldset[disabled] .btn-success:hover{background-color:#5cb85c;border-color:#4cae4c}.btn-success .badge{color:#5cb85c;background-color:#fff}.btn-info{color:#fff;background-color:#5bc0de;border-color:#46b8da}.btn-info.focus,.btn-info:focus{color:#fff;background-color:#31b0d5;border-color:#1b6d85}.btn-info:hover{color:#fff;background-color:#31b0d5;border-color:#269abc}.btn-info.active,.btn-info:active,.open>.dropdown-toggle.btn-info{color:#fff;background-color:#31b0d5;border-color:#269abc}.btn-info.active.focus,.btn-info.active:focus,.btn-info.active:hover,.btn-info:active.focus,.btn-info:active:focus,.btn-info:active:hover,.open>.dropdown-toggle.btn-info.focus,.open>.dropdown-toggle.btn-info:focus,.open>.dropdown-toggle.btn-info:hover{color:#fff;background-color:#269abc;border-color:#1b6d85}.btn-info.active,.btn-info:active,.open>.dropdown-toggle.btn-info{background-image:none}.btn-info.disabled,.btn-info.disabled.active,.btn-info.disabled.focus,.btn-info.disabled:active,.btn-info.disabled:focus,.btn-info.disabled:hover,.btn-info[disabled],.btn-info[disabled].active,.btn-info[disabled].focus,.btn-info[disabled]:active,.btn-info[disabled]:focus,.btn-info[disabled]:hover,fieldset[disabled] .btn-info,fieldset[disabled] .btn-info.active,fieldset[disabled] .btn-info.focus,fieldset[disabled] .btn-info:active,fieldset[disabled] .btn-info:focus,fieldset[disabled] .btn-info:hover{background-color:#5bc0de;border-color:#46b8da}.btn-info .badge{color:#5bc0de;background-color:#fff}.btn-warning{color:#fff;background-color:#f0ad4e;border-color:#eea236}.btn-warning.focus,.btn-warning:focus{color:#fff;background-color:#ec971f;border-color:#985f0d}.btn-warning:hover{color:#fff;background-color:#ec971f;border-color:#d58512}.btn-warning.active,.btn-warning:active,.open>.dropdown-toggle.btn-warning{color:#fff;background-color:#ec971f;border-color:#d58512}.btn-warning.active.focus,.btn-warning.active:focus,.btn-warning.active:hover,.btn-warning:active.focus,.btn-warning:active:focus,.btn-warning:active:hover,.open>.dropdown-toggle.btn-warning.focus,.open>.dropdown-toggle.btn-warning:focus,.open>.dropdown-toggle.btn-warning:hover{color:#fff;background-color:#d58512;border-color:#985f0d}.btn-warning.active,.btn-warning:active,.open>.dropdown-toggle.btn-warning{background-image:none}.btn-warning.disabled,.btn-warning.disabled.active,.btn-warning.disabled.focus,.btn-warning.disabled:active,.btn-warning.disabled:focus,.btn-warning.disabled:hover,.btn-warning[disabled],.btn-warning[disabled].active,.btn-warning[disabled].focus,.btn-warning[disabled]:active,.btn-warning[disabled]:focus,.btn-warning[disabled]:hover,fieldset[disabled] .btn-warning,fieldset[disabled] .btn-warning.active,fieldset[disabled] .btn-warning.focus,fieldset[disabled] .btn-warning:active,fieldset[disabled] .btn-warning:focus,fieldset[disabled] .btn-warning:hover{background-color:#f0ad4e;border-color:#eea236}.btn-warning .badge{color:#f0ad4e;background-color:#fff}.btn-danger{color:#fff;background-color:#d9534f;border-color:#d43f3a}.btn-danger.focus,.btn-danger:focus{color:#fff;background-color:#c9302c;border-color:#761c19}.btn-danger:hover{color:#fff;background-color:#c9302c;border-color:#ac2925}.btn-danger.active,.btn-danger:active,.open>.dropdown-toggle.btn-danger{color:#fff;background-color:#c9302c;border-color:#ac2925}.btn-danger.active.focus,.btn-danger.active:focus,.btn-danger.active:hover,.btn-danger:active.focus,.btn-danger:active:focus,.btn-danger:active:hover,.open>.dropdown-toggle.btn-danger.focus,.open>.dropdown-toggle.btn-danger:focus,.open>.dropdown-toggle.btn-danger:hover{color:#fff;background-color:#ac2925;border-color:#761c19}.btn-danger.active,.btn-danger:active,.open>.dropdown-toggle.btn-danger{background-image:none}.btn-danger.disabled,.btn-danger.disabled.active,.btn-danger.disabled.focus,.btn-danger.disabled:active,.btn-danger.disabled:focus,.btn-danger.disabled:hover,.btn-danger[disabled],.btn-danger[disabled].active,.btn-danger[disabled].focus,.btn-danger[disabled]:active,.btn-danger[disabled]:focus,.btn-danger[disabled]:hover,fieldset[disabled] .btn-danger,fieldset[disabled] .btn-danger.active,fieldset[disabled] .btn-danger.focus,fieldset[disabled] .btn-danger:active,fieldset[disabled] .btn-danger:focus,fieldset[disabled] .btn-danger:hover{background-color:#d9534f;border-color:#d43f3a}.btn-danger .badge{color:#d9534f;background-color:#fff}.btn-link{font-weight:400;color:#337ab7;border-radius:0}.btn-link,.btn-link.active,.btn-link:active,.btn-link[disabled],fieldset[disabled] .btn-link{background-color:transparent;-webkit-box-shadow:none;box-shadow:none}.btn-link,.btn-link:active,.btn-link:focus,.btn-link:hover{border-color:transparent}.btn-link:focus,.btn-link:hover{color:#23527c;text-decoration:underline;background-color:transparent}.btn-link[disabled]:focus,.btn-link[disabled]:hover,fieldset[disabled] .btn-link:focus,fieldset[disabled] .btn-link:hover{color:#777;text-decoration:none}.btn-group-lg>.btn,.btn-lg{padding:10px 16px;font-size:18px;line-height:1.3333333;border-radius:6px}.btn-group-sm>.btn,.btn-sm{padding:5px 10px;font-size:12px;line-height:1.5;border-radius:3px}.btn-group-xs>.btn,.btn-xs{padding:1px 5px;font-size:12px;line-height:1.5;border-radius:3px}.btn-block{display:block;width:100%}.btn-block+.btn-block{margin-top:5px}input[type=button].btn-block,input[type=reset].btn-block,input[type=submit].btn-block{width:100%}.fade{opacity:0;-webkit-transition:opacity .15s linear;-o-transition:opacity .15s linear;transition:opacity .15s linear}.fade.in{opacity:1}.collapse{display:none}.collapse.in{display:block}tr.collapse.in{display:table-row}tbody.collapse.in{display:table-row-group}.collapsing{position:relative;height:0;overflow:hidden;-webkit-transition-timing-function:ease;-o-transition-timing-function:ease;transition-timing-function:ease;-webkit-transition-duration:.35s;-o-transition-duration:.35s;transition-duration:.35s;-webkit-transition-property:height,visibility;-o-transition-property:height,visibility;transition-property:height,visibility}.caret{display:inline-block;width:0;height:0;margin-left:2px;vertical-align:middle;border-top:4px dashed;border-top:4px solid\9;border-right:4px solid transparent;border-left:4px solid transparent}.dropdown,.dropup{position:relative}.dropdown-toggle:focus{outline:0}.dropdown-menu{position:absolute;top:100%;left:0;z-index:1000;display:none;float:left;min-width:160px;padding:5px 0;margin:2px 0 0;font-size:14px;text-align:left;list-style:none;background-color:#fff;-webkit-background-clip:padding-box;background-clip:padding-box;border:1px solid #ccc;border:1px solid rgba(0,0,0,.15);border-radius:4px;-webkit-box-shadow:0 6px 12px rgba(0,0,0,.175);box-shadow:0 6px 12px rgba(0,0,0,.175)}.dropdown-menu.pull-right{right:0;left:auto}.dropdown-menu .divider{height:1px;margin:9px 0;overflow:hidden;background-color:#e5e5e5}.dropdown-menu>li>a{display:block;padding:3px 20px;clear:both;font-weight:400;line-height:1.42857143;color:#333;white-space:nowrap}.dropdown-menu>li>a:focus,.dropdown-menu>li>a:hover{color:#262626;text-decoration:none;background-color:#f5f5f5}.dropdown-menu>.active>a,.dropdown-menu>.active>a:focus,.dropdown-menu>.active>a:hover{color:#fff;text-decoration:none;background-color:#337ab7;outline:0}.dropdown-menu>.disabled>a,.dropdown-menu>.disabled>a:focus,.dropdown-menu>.disabled>a:hover{color:#777}.dropdown-menu>.disabled>a:focus,.dropdown-menu>.disabled>a:hover{text-decoration:none;cursor:not-allowed;background-color:transparent;background-image:none;filter:progid:DXImageTransform.Microsoft.gradient(enabled=false)}.open>.dropdown-menu{display:block}.open>a{outline:0}.dropdown-menu-right{right:0;left:auto}.dropdown-menu-left{right:auto;left:0}.dropdown-header{display:block;padding:3px 20px;font-size:12px;line-height:1.42857143;color:#777;white-space:nowrap}.dropdown-backdrop{position:fixed;top:0;right:0;bottom:0;left:0;z-index:990}.pull-right>.dropdown-menu{right:0;left:auto}.dropup .caret,.navbar-fixed-bottom .dropdown .caret{content:"";border-top:0;border-bottom:4px dashed;border-bottom:4px solid\9}.dropup .dropdown-menu,.navbar-fixed-bottom .dropdown .dropdown-menu{top:auto;bottom:100%;margin-bottom:2px}@media (min-width:768px){.navbar-right .dropdown-menu{right:0;left:auto}.navbar-right .dropdown-menu-left{right:auto;left:0}}.btn-group,.btn-group-vertical{position:relative;display:inline-block;vertical-align:middle}.btn-group-vertical>.btn,.btn-group>.btn{position:relative;float:left}.btn-group-vertical>.btn.active,.btn-group-vertical>.btn:active,.btn-group-vertical>.btn:focus,.btn-group-vertical>.btn:hover,.btn-group>.btn.active,.btn-group>.btn:active,.btn-group>.btn:focus,.btn-group>.btn:hover{z-index:2}.btn-group .btn+.btn,.btn-group .btn+.btn-group,.btn-group .btn-group+.btn,.btn-group .btn-group+.btn-group{margin-left:-1px}.btn-toolbar{margin-left:-5px}.btn-toolbar .btn,.btn-toolbar .btn-group,.btn-toolbar .input-group{float:left}.btn-toolbar>.btn,.btn-toolbar>.btn-group,.btn-toolbar>.input-group{margin-left:5px}.btn-group>.btn:not(:first-child):not(:last-child):not(.dropdown-toggle){border-radius:0}.btn-group>.btn:first-child{margin-left:0}.btn-group>.btn:first-child:not(:last-child):not(.dropdown-toggle){border-top-right-radius:0;border-bottom-right-radius:0}.btn-group>.btn:last-child:not(:first-child),.btn-group>.dropdown-toggle:not(:first-child){border-top-left-radius:0;border-bottom-left-radius:0}.btn-group>.btn-group{float:left}.btn-group>.btn-group:not(:first-child):not(:last-child)>.btn{border-radius:0}.btn-group>.btn-group:first-child:not(:last-child)>.btn:last-child,.btn-group>.btn-group:first-child:not(:last-child)>.dropdown-toggle{border-top-right-radius:0;border-bottom-right-radius:0}.btn-group>.btn-group:last-child:not(:first-child)>.btn:first-child{border-top-left-radius:0;border-bottom-left-radius:0}.btn-group .dropdown-toggle:active,.btn-group.open .dropdown-toggle{outline:0}.btn-group>.btn+.dropdown-toggle{padding-right:8px;padding-left:8px}.btn-group>.btn-lg+.dropdown-toggle{padding-right:12px;padding-left:12px}.btn-group.open .dropdown-toggle{-webkit-box-shadow:inset 0 3px 5px rgba(0,0,0,.125);box-shadow:inset 0 3px 5px rgba(0,0,0,.125)}.btn-group.open .dropdown-toggle.btn-link{-webkit-box-shadow:none;box-shadow:none}.btn .caret{margin-left:0}.btn-lg .caret{border-width:5px 5px 0;border-bottom-width:0}.dropup .btn-lg .caret{border-width:0 5px 5px}.btn-group-vertical>.btn,.btn-group-vertical>.btn-group,.btn-group-vertical>.btn-group>.btn{display:block;float:none;width:100%;max-width:100%}.btn-group-vertical>.btn-group>.btn{float:none}.btn-group-vertical>.btn+.btn,.btn-group-vertical>.btn+.btn-group,.btn-group-vertical>.btn-group+.btn,.btn-group-vertical>.btn-group+.btn-group{margin-top:-1px;margin-left:0}.btn-group-vertical>.btn:not(:first-child):not(:last-child){border-radius:0}.btn-group-vertical>.btn:first-child:not(:last-child){border-top-right-radius:4px;border-bottom-right-radius:0;border-bottom-left-radius:0}.btn-group-vertical>.btn:last-child:not(:first-child){border-top-left-radius:0;border-top-right-radius:0;border-bottom-left-radius:4px}.btn-group-vertical>.btn-group:not(:first-child):not(:last-child)>.btn{border-radius:0}.btn-group-vertical>.btn-group:first-child:not(:last-child)>.btn:last-child,.btn-group-vertical>.btn-group:first-child:not(:last-child)>.dropdown-toggle{border-bottom-right-radius:0;border-bottom-left-radius:0}.btn-group-vertical>.btn-group:last-child:not(:first-child)>.btn:first-child{border-top-left-radius:0;border-top-right-radius:0}.btn-group-justified{display:table;width:100%;table-layout:fixed;border-collapse:separate}.btn-group-justified>.btn,.btn-group-justified>.btn-group{display:table-cell;float:none;width:1%}.btn-group-justified>.btn-group .btn{width:100%}.btn-group-justified>.btn-group .dropdown-menu{left:auto}[data-toggle=buttons]>.btn input[type=checkbox],[data-toggle=buttons]>.btn input[type=radio],[data-toggle=buttons]>.btn-group>.btn input[type=checkbox],[data-toggle=buttons]>.btn-group>.btn input[type=radio]{position:absolute;clip:rect(0,0,0,0);pointer-events:none}.input-group{position:relative;display:table;border-collapse:separate}.input-group[class*=col-]{float:none;padding-right:0;padding-left:0}.input-group .form-control{position:relative;z-index:2;float:left;width:100%;margin-bottom:0}.input-group-lg>.form-control,.input-group-lg>.input-group-addon,.input-group-lg>.input-group-btn>.btn{height:46px;padding:10px 16px;font-size:18px;line-height:1.3333333;border-radius:6px}select.input-group-lg>.form-control,select.input-group-lg>.input-group-addon,select.input-group-lg>.input-group-btn>.btn{height:46px;line-height:46px}select[multiple].input-group-lg>.form-control,select[multiple].input-group-lg>.input-group-addon,select[multiple].input-group-lg>.input-group-btn>.btn,textarea.input-group-lg>.form-control,textarea.input-group-lg>.input-group-addon,textarea.input-group-lg>.input-group-btn>.btn{height:auto}.input-group-sm>.form-control,.input-group-sm>.input-group-addon,.input-group-sm>.input-group-btn>.btn{height:30px;padding:5px 10px;font-size:12px;line-height:1.5;border-radius:3px}select.input-group-sm>.form-control,select.input-group-sm>.input-group-addon,select.input-group-sm>.input-group-btn>.btn{height:30px;line-height:30px}select[multiple].input-group-sm>.form-control,select[multiple].input-group-sm>.input-group-addon,select[multiple].input-group-sm>.input-group-btn>.btn,textarea.input-group-sm>.form-control,textarea.input-group-sm>.input-group-addon,textarea.input-group-sm>.input-group-btn>.btn{height:auto}.input-group .form-control,.input-group-addon,.input-group-btn{display:table-cell}.input-group .form-control:not(:first-child):not(:last-child),.input-group-addon:not(:first-child):not(:last-child),.input-group-btn:not(:first-child):not(:last-child){border-radius:0}.input-group-addon,.input-group-btn{width:1%;white-space:nowrap;vertical-align:middle}.input-group-addon{padding:6px 12px;font-size:14px;font-weight:400;line-height:1;color:#555;text-align:center;background-color:#eee;border:1px solid #ccc;border-radius:4px}.input-group-addon.input-sm{padding:5px 10px;font-size:12px;border-radius:3px}.input-group-addon.input-lg{padding:10px 16px;font-size:18px;border-radius:6px}.input-group-addon input[type=checkbox],.input-group-addon input[type=radio]{margin-top:0}.input-group .form-control:first-child,.input-group-addon:first-child,.input-group-btn:first-child>.btn,.input-group-btn:first-child>.btn-group>.btn,.input-group-btn:first-child>.dropdown-toggle,.input-group-btn:last-child>.btn-group:not(:last-child)>.btn,.input-group-btn:last-child>.btn:not(:last-child):not(.dropdown-toggle){border-top-right-radius:0;border-bottom-right-radius:0}.input-group-addon:first-child{border-right:0}.input-group .form-control:last-child,.input-group-addon:last-child,.input-group-btn:first-child>.btn-group:not(:first-child)>.btn,.input-group-btn:first-child>.btn:not(:first-child),.input-group-btn:last-child>.btn,.input-group-btn:last-child>.btn-group>.btn,.input-group-btn:last-child>.dropdown-toggle{border-top-left-radius:0;border-bottom-left-radius:0}.input-group-addon:last-child{border-left:0}.input-group-btn{position:relative;font-size:0;white-space:nowrap}.input-group-btn>.btn{position:relative}.input-group-btn>.btn+.btn{margin-left:-1px}.input-group-btn>.btn:active,.input-group-btn>.btn:focus,.input-group-btn>.btn:hover{z-index:2}.input-group-btn:first-child>.btn,.input-group-btn:first-child>.btn-group{margin-right:-1px}.input-group-btn:last-child>.btn,.input-group-btn:last-child>.btn-group{z-index:2;margin-left:-1px}.nav{padding-left:0;margin-bottom:0;list-style:none}.nav>li{position:relative;display:block}.nav>li>a{position:relative;display:block;padding:10px 15px}.nav>li>a:focus,.nav>li>a:hover{text-decoration:none;background-color:#eee}.nav>li.disabled>a{color:#777}.nav>li.disabled>a:focus,.nav>li.disabled>a:hover{color:#777;text-decoration:none;cursor:not-allowed;background-color:transparent}.nav .open>a,.nav .open>a:focus,.nav .open>a:hover{background-color:#eee;border-color:#337ab7}.nav .nav-divider{height:1px;margin:9px 0;overflow:hidden;background-color:#e5e5e5}.nav>li>a>img{max-width:none}.nav-tabs{border-bottom:1px solid #ddd}.nav-tabs>li{float:left;margin-bottom:-1px}.nav-tabs>li>a{margin-right:2px;line-height:1.42857143;border:1px solid transparent;border-radius:4px 4px 0 0}.nav-tabs>li>a:hover{border-color:#eee #eee #ddd}.nav-tabs>li.active>a,.nav-tabs>li.active>a:focus,.nav-tabs>li.active>a:hover{color:#555;cursor:default;background-color:#fff;border:1px solid #ddd;border-bottom-color:transparent}.nav-tabs.nav-justified{width:100%;border-bottom:0}.nav-tabs.nav-justified>li{float:none}.nav-tabs.nav-justified>li>a{margin-bottom:5px;text-align:center}.nav-tabs.nav-justified>.dropdown .dropdown-menu{top:auto;left:auto}@media (min-width:768px){.nav-tabs.nav-justified>li{display:table-cell;width:1%}.nav-tabs.nav-justified>li>a{margin-bottom:0}}.nav-tabs.nav-justified>li>a{margin-right:0;border-radius:4px}.nav-tabs.nav-justified>.active>a,.nav-tabs.nav-justified>.active>a:focus,.nav-tabs.nav-justified>.active>a:hover{border:1px solid #ddd}@media (min-width:768px){.nav-tabs.nav-justified>li>a{border-bottom:1px solid #ddd;border-radius:4px 4px 0 0}.nav-tabs.nav-justified>.active>a,.nav-tabs.nav-justified>.active>a:focus,.nav-tabs.nav-justified>.active>a:hover{border-bottom-color:#fff}}.nav-pills>li{float:left}.nav-pills>li>a{border-radius:4px}.nav-pills>li+li{margin-left:2px}.nav-pills>li.active>a,.nav-pills>li.active>a:focus,.nav-pills>li.active>a:hover{color:#fff;background-color:#337ab7}.nav-stacked>li{float:none}.nav-stacked>li+li{margin-top:2px;margin-left:0}.nav-justified{width:100%}.nav-justified>li{float:none}.nav-justified>li>a{margin-bottom:5px;text-align:center}.nav-justified>.dropdown .dropdown-menu{top:auto;left:auto}@media (min-width:768px){.nav-justified>li{display:table-cell;width:1%}.nav-justified>li>a{margin-bottom:0}}.nav-tabs-justified{border-bottom:0}.nav-tabs-justified>li>a{margin-right:0;border-radius:4px}.nav-tabs-justified>.active>a,.nav-tabs-justified>.active>a:focus,.nav-tabs-justified>.active>a:hover{border:1px solid #ddd}@media (min-width:768px){.nav-tabs-justified>li>a{border-bottom:1px solid #ddd;border-radius:4px 4px 0 0}.nav-tabs-justified>.active>a,.nav-tabs-justified>.active>a:focus,.nav-tabs-justified>.active>a:hover{border-bottom-color:#fff}}.tab-content>.tab-pane{display:none}.tab-content>.active{display:block}.nav-tabs .dropdown-menu{margin-top:-1px;border-top-left-radius:0;border-top-right-radius:0}.navbar{position:relative;min-height:50px;margin-bottom:20px;border:1px solid transparent}@media (min-width:768px){.navbar{border-radius:4px}}@media (min-width:768px){.navbar-header{float:left}}.navbar-collapse{padding-right:15px;padding-left:15px;overflow-x:visible;-webkit-overflow-scrolling:touch;border-top:1px solid transparent;-webkit-box-shadow:inset 0 1px 0 rgba(255,255,255,.1);box-shadow:inset 0 1px 0 rgba(255,255,255,.1)}.navbar-collapse.in{overflow-y:auto}@media (min-width:768px){.navbar-collapse{width:auto;border-top:0;-webkit-box-shadow:none;box-shadow:none}.navbar-collapse.collapse{display:block!important;height:auto!important;padding-bottom:0;overflow:visible!important}.navbar-collapse.in{overflow-y:visible}.navbar-fixed-bottom .navbar-collapse,.navbar-fixed-top .navbar-collapse,.navbar-static-top .navbar-collapse{padding-right:0;padding-left:0}}.navbar-fixed-bottom .navbar-collapse,.navbar-fixed-top .navbar-collapse{max-height:340px}@media (max-device-width:480px) and (orientation:landscape){.navbar-fixed-bottom .navbar-collapse,.navbar-fixed-top .navbar-collapse{max-height:200px}}.container-fluid>.navbar-collapse,.container-fluid>.navbar-header,.container>.navbar-collapse,.container>.navbar-header{margin-right:-15px;margin-left:-15px}@media (min-width:768px){.container-fluid>.navbar-collapse,.container-fluid>.navbar-header,.container>.navbar-collapse,.container>.navbar-header{margin-right:0;margin-left:0}}.navbar-static-top{z-index:1000;border-width:0 0 1px}@media (min-width:768px){.navbar-static-top{border-radius:0}}.navbar-fixed-bottom,.navbar-fixed-top{position:fixed;right:0;left:0;z-index:1030}@media (min-width:768px){.navbar-fixed-bottom,.navbar-fixed-top{border-radius:0}}.navbar-fixed-top{top:0;border-width:0 0 1px}.navbar-fixed-bottom{bottom:0;margin-bottom:0;border-width:1px 0 0}.navbar-brand{float:left;height:50px;padding:15px 15px;font-size:18px;line-height:20px}.navbar-brand:focus,.navbar-brand:hover{text-decoration:none}.navbar-brand>img{display:block}@media (min-width:768px){.navbar>.container .navbar-brand,.navbar>.container-fluid .navbar-brand{margin-left:-15px}}.navbar-toggle{position:relative;float:right;padding:9px 10px;margin-top:8px;margin-right:15px;margin-bottom:8px;background-color:transparent;background-image:none;border:1px solid transparent;border-radius:4px}.navbar-toggle:focus{outline:0}.navbar-toggle .icon-bar{display:block;width:22px;height:2px;border-radius:1px}.navbar-toggle .icon-bar+.icon-bar{margin-top:4px}@media (min-width:768px){.navbar-toggle{display:none}}.navbar-nav{margin:7.5px -15px}.navbar-nav>li>a{padding-top:10px;padding-bottom:10px;line-height:20px}@media (max-width:767px){.navbar-nav .open .dropdown-menu{position:static;float:none;width:auto;margin-top:0;background-color:transparent;border:0;-webkit-box-shadow:none;box-shadow:none}.navbar-nav .open .dropdown-menu .dropdown-header,.navbar-nav .open .dropdown-menu>li>a{padding:5px 15px 5px 25px}.navbar-nav .open .dropdown-menu>li>a{line-height:20px}.navbar-nav .open .dropdown-menu>li>a:focus,.navbar-nav .open .dropdown-menu>li>a:hover{background-image:none}}@media (min-width:768px){.navbar-nav{float:left;margin:0}.navbar-nav>li{float:left}.navbar-nav>li>a{padding-top:15px;padding-bottom:15px}}.navbar-form{padding:10px 15px;margin-top:8px;margin-right:-15px;margin-bottom:8px;margin-left:-15px;border-top:1px solid transparent;border-bottom:1px solid transparent;-webkit-box-shadow:inset 0 1px 0 rgba(255,255,255,.1),0 1px 0 rgba(255,255,255,.1);box-shadow:inset 0 1px 0 rgba(255,255,255,.1),0 1px 0 rgba(255,255,255,.1)}@media (min-width:768px){.navbar-form .form-group{display:inline-block;margin-bottom:0;vertical-align:middle}.navbar-form .form-control{display:inline-block;width:auto;vertical-align:middle}.navbar-form .form-control-static{display:inline-block}.navbar-form .input-group{display:inline-table;vertical-align:middle}.navbar-form .input-group .form-control,.navbar-form .input-group .input-group-addon,.navbar-form .input-group .input-group-btn{width:auto}.navbar-form .input-group>.form-control{width:100%}.navbar-form .control-label{margin-bottom:0;vertical-align:middle}.navbar-form .checkbox,.navbar-form .radio{display:inline-block;margin-top:0;margin-bottom:0;vertical-align:middle}.navbar-form .checkbox label,.navbar-form .radio label{padding-left:0}.navbar-form .checkbox input[type=checkbox],.navbar-form .radio input[type=radio]{position:relative;margin-left:0}.navbar-form .has-feedback .form-control-feedback{top:0}}@media (max-width:767px){.navbar-form .form-group{margin-bottom:5px}.navbar-form .form-group:last-child{margin-bottom:0}}@media (min-width:768px){.navbar-form{width:auto;padding-top:0;padding-bottom:0;margin-right:0;margin-left:0;border:0;-webkit-box-shadow:none;box-shadow:none}}.navbar-nav>li>.dropdown-menu{margin-top:0;border-top-left-radius:0;border-top-right-radius:0}.navbar-fixed-bottom .navbar-nav>li>.dropdown-menu{margin-bottom:0;border-top-left-radius:4px;border-top-right-radius:4px;border-bottom-right-radius:0;border-bottom-left-radius:0}.navbar-btn{margin-top:8px;margin-bottom:8px}.navbar-btn.btn-sm{margin-top:10px;margin-bottom:10px}.navbar-btn.btn-xs{margin-top:14px;margin-bottom:14px}.navbar-text{margin-top:15px;margin-bottom:15px}@media (min-width:768px){.navbar-text{float:left;margin-right:15px;margin-left:15px}}@media (min-width:768px){.navbar-left{float:left!important}.navbar-right{float:right!important;margin-right:-15px}.navbar-right~.navbar-right{margin-right:0}}.navbar-default{background-color:#f8f8f8;border-color:#e7e7e7}.navbar-default .navbar-brand{color:#777}.navbar-default .navbar-brand:focus,.navbar-default .navbar-brand:hover{color:#5e5e5e;background-color:transparent}.navbar-default .navbar-text{color:#777}.navbar-default .navbar-nav>li>a{color:#777}.navbar-default .navbar-nav>li>a:focus,.navbar-default .navbar-nav>li>a:hover{color:#333;background-color:transparent}.navbar-default .navbar-nav>.active>a,.navbar-default .navbar-nav>.active>a:focus,.navbar-default .navbar-nav>.active>a:hover{color:#555;background-color:#e7e7e7}.navbar-default .navbar-nav>.disabled>a,.navbar-default .navbar-nav>.disabled>a:focus,.navbar-default .navbar-nav>.disabled>a:hover{color:#ccc;background-color:transparent}.navbar-default .navbar-toggle{border-color:#ddd}.navbar-default .navbar-toggle:focus,.navbar-default .navbar-toggle:hover{background-color:#ddd}.navbar-default .navbar-toggle .icon-bar{background-color:#888}.navbar-default .navbar-collapse,.navbar-default .navbar-form{border-color:#e7e7e7}.navbar-default .navbar-nav>.open>a,.navbar-default .navbar-nav>.open>a:focus,.navbar-default .navbar-nav>.open>a:hover{color:#555;background-color:#e7e7e7}@media (max-width:767px){.navbar-default .navbar-nav .open .dropdown-menu>li>a{color:#777}.navbar-default .navbar-nav .open .dropdown-menu>li>a:focus,.navbar-default .navbar-nav .open .dropdown-menu>li>a:hover{color:#333;background-color:transparent}.navbar-default .navbar-nav .open .dropdown-menu>.active>a,.navbar-default .navbar-nav .open .dropdown-menu>.active>a:focus,.navbar-default .navbar-nav .open .dropdown-menu>.active>a:hover{color:#555;background-color:#e7e7e7}.navbar-default .navbar-nav .open .dropdown-menu>.disabled>a,.navbar-default .navbar-nav .open .dropdown-menu>.disabled>a:focus,.navbar-default .navbar-nav .open .dropdown-menu>.disabled>a:hover{color:#ccc;background-color:transparent}}.navbar-default .navbar-link{color:#777}.navbar-default .navbar-link:hover{color:#333}.navbar-default .btn-link{color:#777}.navbar-default .btn-link:focus,.navbar-default .btn-link:hover{color:#333}.navbar-default .btn-link[disabled]:focus,.navbar-default .btn-link[disabled]:hover,fieldset[disabled] .navbar-default .btn-link:focus,fieldset[disabled] .navbar-default .btn-link:hover{color:#ccc}.navbar-inverse{background-color:#222;border-color:#080808}.navbar-inverse .navbar-brand{color:#9d9d9d}.navbar-inverse .navbar-brand:focus,.navbar-inverse .navbar-brand:hover{color:#fff;background-color:transparent}.navbar-inverse .navbar-text{color:#9d9d9d}.navbar-inverse .navbar-nav>li>a{color:#9d9d9d}.navbar-inverse .navbar-nav>li>a:focus,.navbar-inverse .navbar-nav>li>a:hover{color:#fff;background-color:transparent}.navbar-inverse .navbar-nav>.active>a,.navbar-inverse .navbar-nav>.active>a:focus,.navbar-inverse .navbar-nav>.active>a:hover{color:#fff;background-color:#080808}.navbar-inverse .navbar-nav>.disabled>a,.navbar-inverse .navbar-nav>.disabled>a:focus,.navbar-inverse .navbar-nav>.disabled>a:hover{color:#444;background-color:transparent}.navbar-inverse .navbar-toggle{border-color:#333}.navbar-inverse .navbar-toggle:focus,.navbar-inverse .navbar-toggle:hover{background-color:#333}.navbar-inverse .navbar-toggle .icon-bar{background-color:#fff}.navbar-inverse .navbar-collapse,.navbar-inverse .navbar-form{border-color:#101010}.navbar-inverse .navbar-nav>.open>a,.navbar-inverse .navbar-nav>.open>a:focus,.navbar-inverse .navbar-nav>.open>a:hover{color:#fff;background-color:#080808}@media (max-width:767px){.navbar-inverse .navbar-nav .open .dropdown-menu>.dropdown-header{border-color:#080808}.navbar-inverse .navbar-nav .open .dropdown-menu .divider{background-color:#080808}.navbar-inverse .navbar-nav .open .dropdown-menu>li>a{color:#9d9d9d}.navbar-inverse .navbar-nav .open .dropdown-menu>li>a:focus,.navbar-inverse .navbar-nav .open .dropdown-menu>li>a:hover{color:#fff;background-color:transparent}.navbar-inverse .navbar-nav .open .dropdown-menu>.active>a,.navbar-inverse .navbar-nav .open .dropdown-menu>.active>a:focus,.navbar-inverse .navbar-nav .open .dropdown-menu>.active>a:hover{color:#fff;background-color:#080808}.navbar-inverse .navbar-nav .open .dropdown-menu>.disabled>a,.navbar-inverse .navbar-nav .open .dropdown-menu>.disabled>a:focus,.navbar-inverse .navbar-nav .open .dropdown-menu>.disabled>a:hover{color:#444;background-color:transparent}}.navbar-inverse .navbar-link{color:#9d9d9d}.navbar-inverse .navbar-link:hover{color:#fff}.navbar-inverse .btn-link{color:#9d9d9d}.navbar-inverse .btn-link:focus,.navbar-inverse .btn-link:hover{color:#fff}.navbar-inverse .btn-link[disabled]:focus,.navbar-inverse .btn-link[disabled]:hover,fieldset[disabled] .navbar-inverse .btn-link:focus,fieldset[disabled] .navbar-inverse .btn-link:hover{color:#444}.breadcrumb{padding:8px 15px;margin-bottom:20px;list-style:none;background-color:#f5f5f5;border-radius:4px}.breadcrumb>li{display:inline-block}.breadcrumb>li+li:before{padding:0 5px;color:#ccc;content:"/\00a0"}.breadcrumb>.active{color:#777}.pagination{display:inline-block;padding-left:0;margin:20px 0;border-radius:4px}.pagination>li{display:inline}.pagination>li>a,.pagination>li>span{position:relative;float:left;padding:6px 12px;margin-left:-1px;line-height:1.42857143;color:#337ab7;text-decoration:none;background-color:#fff;border:1px solid #ddd}.pagination>li:first-child>a,.pagination>li:first-child>span{margin-left:0;border-top-left-radius:4px;border-bottom-left-radius:4px}.pagination>li:last-child>a,.pagination>li:last-child>span{border-top-right-radius:4px;border-bottom-right-radius:4px}.pagination>li>a:focus,.pagination>li>a:hover,.pagination>li>span:focus,.pagination>li>span:hover{z-index:3;color:#23527c;background-color:#eee;border-color:#ddd}.pagination>.active>a,.pagination>.active>a:focus,.pagination>.active>a:hover,.pagination>.active>span,.pagination>.active>span:focus,.pagination>.active>span:hover{z-index:2;color:#fff;cursor:default;background-color:#337ab7;border-color:#337ab7}.pagination>.disabled>a,.pagination>.disabled>a:focus,.pagination>.disabled>a:hover,.pagination>.disabled>span,.pagination>.disabled>span:focus,.pagination>.disabled>span:hover{color:#777;cursor:not-allowed;background-color:#fff;border-color:#ddd}.pagination-lg>li>a,.pagination-lg>li>span{padding:10px 16px;font-size:18px;line-height:1.3333333}.pagination-lg>li:first-child>a,.pagination-lg>li:first-child>span{border-top-left-radius:6px;border-bottom-left-radius:6px}.pagination-lg>li:last-child>a,.pagination-lg>li:last-child>span{border-top-right-radius:6px;border-bottom-right-radius:6px}.pagination-sm>li>a,.pagination-sm>li>span{padding:5px 10px;font-size:12px;line-height:1.5}.pagination-sm>li:first-child>a,.pagination-sm>li:first-child>span{border-top-left-radius:3px;border-bottom-left-radius:3px}.pagination-sm>li:last-child>a,.pagination-sm>li:last-child>span{border-top-right-radius:3px;border-bottom-right-radius:3px}.pager{padding-left:0;margin:20px 0;text-align:center;list-style:none}.pager li{display:inline}.pager li>a,.pager li>span{display:inline-block;padding:5px 14px;background-color:#fff;border:1px solid #ddd;border-radius:15px}.pager li>a:focus,.pager li>a:hover{text-decoration:none;background-color:#eee}.pager .next>a,.pager .next>span{float:right}.pager .previous>a,.pager .previous>span{float:left}.pager .disabled>a,.pager .disabled>a:focus,.pager .disabled>a:hover,.pager .disabled>span{color:#777;cursor:not-allowed;background-color:#fff}.label{display:inline;padding:.2em .6em .3em;font-size:75%;font-weight:700;line-height:1;color:#fff;text-align:center;white-space:nowrap;vertical-align:baseline;border-radius:.25em}a.label:focus,a.label:hover{color:#fff;text-decoration:none;cursor:pointer}.label:empty{display:none}.btn .label{position:relative;top:-1px}.label-default{background-color:#777}.label-default[href]:focus,.label-default[href]:hover{background-color:#5e5e5e}.label-primary{background-color:#337ab7}.label-primary[href]:focus,.label-primary[href]:hover{background-color:#286090}.label-success{background-color:#5cb85c}.label-success[href]:focus,.label-success[href]:hover{background-color:#449d44}.label-info{background-color:#5bc0de}.label-info[href]:focus,.label-info[href]:hover{background-color:#31b0d5}.label-warning{background-color:#f0ad4e}.label-warning[href]:focus,.label-warning[href]:hover{background-color:#ec971f}.label-danger{background-color:#d9534f}.label-danger[href]:focus,.label-danger[href]:hover{background-color:#c9302c}.badge{display:inline-block;min-width:10px;padding:3px 7px;font-size:12px;font-weight:700;line-height:1;color:#fff;text-align:center;white-space:nowrap;vertical-align:middle;background-color:#777;border-radius:10px}.badge:empty{display:none}.btn .badge{position:relative;top:-1px}.btn-group-xs>.btn .badge,.btn-xs .badge{top:0;padding:1px 5px}a.badge:focus,a.badge:hover{color:#fff;text-decoration:none;cursor:pointer}.list-group-item.active>.badge,.nav-pills>.active>a>.badge{color:#337ab7;background-color:#fff}.list-group-item>.badge{float:right}.list-group-item>.badge+.badge{margin-right:5px}.nav-pills>li>a>.badge{margin-left:3px}.jumbotron{padding-top:30px;padding-bottom:30px;margin-bottom:30px;color:inherit;background-color:#eee}.jumbotron .h1,.jumbotron h1{color:inherit}.jumbotron p{margin-bottom:15px;font-size:21px;font-weight:200}.jumbotron>hr{border-top-color:#d5d5d5}.container .jumbotron,.container-fluid .jumbotron{border-radius:6px}.jumbotron .container{max-width:100%}@media screen and (min-width:768px){.jumbotron{padding-top:48px;padding-bottom:48px}.container .jumbotron,.container-fluid .jumbotron{padding-right:60px;padding-left:60px}.jumbotron .h1,.jumbotron h1{font-size:63px}}.thumbnail{display:block;padding:4px;margin-bottom:20px;line-height:1.42857143;background-color:#fff;border:1px solid #ddd;border-radius:4px;-webkit-transition:border .2s ease-in-out;-o-transition:border .2s ease-in-out;transition:border .2s ease-in-out}.thumbnail a>img,.thumbnail>img{margin-right:auto;margin-left:auto}a.thumbnail.active,a.thumbnail:focus,a.thumbnail:hover{border-color:#337ab7}.thumbnail .caption{padding:9px;color:#333}.alert{padding:15px;margin-bottom:20px;border:1px solid transparent;border-radius:4px}.alert h4{margin-top:0;color:inherit}.alert .alert-link{font-weight:700}.alert>p,.alert>ul{margin-bottom:0}.alert>p+p{margin-top:5px}.alert-dismissable,.alert-dismissible{padding-right:35px}.alert-dismissable .close,.alert-dismissible .close{position:relative;top:-2px;right:-21px;color:inherit}.alert-success{color:#3c763d;background-color:#dff0d8;border-color:#d6e9c6}.alert-success hr{border-top-color:#c9e2b3}.alert-success .alert-link{color:#2b542c}.alert-info{color:#31708f;background-color:#d9edf7;border-color:#bce8f1}.alert-info hr{border-top-color:#a6e1ec}.alert-info .alert-link{color:#245269}.alert-warning{color:#8a6d3b;background-color:#fcf8e3;border-color:#faebcc}.alert-warning hr{border-top-color:#f7e1b5}.alert-warning .alert-link{color:#66512c}.alert-danger{color:#a94442;background-color:#f2dede;border-color:#ebccd1}.alert-danger hr{border-top-color:#e4b9c0}.alert-danger .alert-link{color:#843534}@-webkit-keyframes progress-bar-stripes{from{background-position:40px 0}to{background-position:0 0}}@-o-keyframes progress-bar-stripes{from{background-position:40px 0}to{background-position:0 0}}@keyframes progress-bar-stripes{from{background-position:40px 0}to{background-position:0 0}}.progress{height:20px;margin-bottom:20px;overflow:hidden;background-color:#f5f5f5;border-radius:4px;-webkit-box-shadow:inset 0 1px 2px rgba(0,0,0,.1);box-shadow:inset 0 1px 2px rgba(0,0,0,.1)}.progress-bar{float:left;width:0;height:100%;font-size:12px;line-height:20px;color:#fff;text-align:center;background-color:#337ab7;-webkit-box-shadow:inset 0 -1px 0 rgba(0,0,0,.15);box-shadow:inset 0 -1px 0 rgba(0,0,0,.15);-webkit-transition:width .6s ease;-o-transition:width .6s ease;transition:width .6s ease}.progress-bar-striped,.progress-striped .progress-bar{background-image:-webkit-linear-gradient(45deg,rgba(255,255,255,.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,.15) 50%,rgba(255,255,255,.15) 75%,transparent 75%,transparent);background-image:-o-linear-gradient(45deg,rgba(255,255,255,.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,.15) 50%,rgba(255,255,255,.15) 75%,transparent 75%,transparent);background-image:linear-gradient(45deg,rgba(255,255,255,.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,.15) 50%,rgba(255,255,255,.15) 75%,transparent 75%,transparent);-webkit-background-size:40px 40px;background-size:40px 40px}.progress-bar.active,.progress.active .progress-bar{-webkit-animation:progress-bar-stripes 2s linear infinite;-o-animation:progress-bar-stripes 2s linear infinite;animation:progress-bar-stripes 2s linear infinite}.progress-bar-success{background-color:#5cb85c}.progress-striped .progress-bar-success{background-image:-webkit-linear-gradient(45deg,rgba(255,255,255,.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,.15) 50%,rgba(255,255,255,.15) 75%,transparent 75%,transparent);background-image:-o-linear-gradient(45deg,rgba(255,255,255,.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,.15) 50%,rgba(255,255,255,.15) 75%,transparent 75%,transparent);background-image:linear-gradient(45deg,rgba(255,255,255,.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,.15) 50%,rgba(255,255,255,.15) 75%,transparent 75%,transparent)}.progress-bar-info{background-color:#5bc0de}.progress-striped .progress-bar-info{background-image:-webkit-linear-gradient(45deg,rgba(255,255,255,.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,.15) 50%,rgba(255,255,255,.15) 75%,transparent 75%,transparent);background-image:-o-linear-gradient(45deg,rgba(255,255,255,.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,.15) 50%,rgba(255,255,255,.15) 75%,transparent 75%,transparent);background-image:linear-gradient(45deg,rgba(255,255,255,.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,.15) 50%,rgba(255,255,255,.15) 75%,transparent 75%,transparent)}.progress-bar-warning{background-color:#f0ad4e}.progress-striped .progress-bar-warning{background-image:-webkit-linear-gradient(45deg,rgba(255,255,255,.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,.15) 50%,rgba(255,255,255,.15) 75%,transparent 75%,transparent);background-image:-o-linear-gradient(45deg,rgba(255,255,255,.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,.15) 50%,rgba(255,255,255,.15) 75%,transparent 75%,transparent);background-image:linear-gradient(45deg,rgba(255,255,255,.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,.15) 50%,rgba(255,255,255,.15) 75%,transparent 75%,transparent)}.progress-bar-danger{background-color:#d9534f}.progress-striped .progress-bar-danger{background-image:-webkit-linear-gradient(45deg,rgba(255,255,255,.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,.15) 50%,rgba(255,255,255,.15) 75%,transparent 75%,transparent);background-image:-o-linear-gradient(45deg,rgba(255,255,255,.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,.15) 50%,rgba(255,255,255,.15) 75%,transparent 75%,transparent);background-image:linear-gradient(45deg,rgba(255,255,255,.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,.15) 50%,rgba(255,255,255,.15) 75%,transparent 75%,transparent)}.media{margin-top:15px}.media:first-child{margin-top:0}.media,.media-body{overflow:hidden;zoom:1}.media-body{width:10000px}.media-object{display:block}.media-object.img-thumbnail{max-width:none}.media-right,.media>.pull-right{padding-left:10px}.media-left,.media>.pull-left{padding-right:10px}.media-body,.media-left,.media-right{display:table-cell;vertical-align:top}.media-middle{vertical-align:middle}.media-bottom{vertical-align:bottom}.media-heading{margin-top:0;margin-bottom:5px}.media-list{padding-left:0;list-style:none}.list-group{padding-left:0;margin-bottom:20px}.list-group-item{position:relative;display:block;padding:10px 15px;margin-bottom:-1px;background-color:#fff;border:1px solid #ddd}.list-group-item:first-child{border-top-left-radius:4px;border-top-right-radius:4px}.list-group-item:last-child{margin-bottom:0;border-bottom-right-radius:4px;border-bottom-left-radius:4px}a.list-group-item,button.list-group-item{color:#555}a.list-group-item .list-group-item-heading,button.list-group-item .list-group-item-heading{color:#333}a.list-group-item:focus,a.list-group-item:hover,button.list-group-item:focus,button.list-group-item:hover{color:#555;text-decoration:none;background-color:#f5f5f5}button.list-group-item{width:100%;text-align:left}.list-group-item.disabled,.list-group-item.disabled:focus,.list-group-item.disabled:hover{color:#777;cursor:not-allowed;background-color:#eee}.list-group-item.disabled .list-group-item-heading,.list-group-item.disabled:focus .list-group-item-heading,.list-group-item.disabled:hover .list-group-item-heading{color:inherit}.list-group-item.disabled .list-group-item-text,.list-group-item.disabled:focus .list-group-item-text,.list-group-item.disabled:hover .list-group-item-text{color:#777}.list-group-item.active,.list-group-item.active:focus,.list-group-item.active:hover{z-index:2;color:#fff;background-color:#337ab7;border-color:#337ab7}.list-group-item.active .list-group-item-heading,.list-group-item.active .list-group-item-heading>.small,.list-group-item.active .list-group-item-heading>small,.list-group-item.active:focus .list-group-item-heading,.list-group-item.active:focus .list-group-item-heading>.small,.list-group-item.active:focus .list-group-item-heading>small,.list-group-item.active:hover .list-group-item-heading,.list-group-item.active:hover .list-group-item-heading>.small,.list-group-item.active:hover .list-group-item-heading>small{color:inherit}.list-group-item.active .list-group-item-text,.list-group-item.active:focus .list-group-item-text,.list-group-item.active:hover .list-group-item-text{color:#c7ddef}.list-group-item-success{color:#3c763d;background-color:#dff0d8}a.list-group-item-success,button.list-group-item-success{color:#3c763d}a.list-group-item-success .list-group-item-heading,button.list-group-item-success .list-group-item-heading{color:inherit}a.list-group-item-success:focus,a.list-group-item-success:hover,button.list-group-item-success:focus,button.list-group-item-success:hover{color:#3c763d;background-color:#d0e9c6}a.list-group-item-success.active,a.list-group-item-success.active:focus,a.list-group-item-success.active:hover,button.list-group-item-success.active,button.list-group-item-success.active:focus,button.list-group-item-success.active:hover{color:#fff;background-color:#3c763d;border-color:#3c763d}.list-group-item-info{color:#31708f;background-color:#d9edf7}a.list-group-item-info,button.list-group-item-info{color:#31708f}a.list-group-item-info .list-group-item-heading,button.list-group-item-info .list-group-item-heading{color:inherit}a.list-group-item-info:focus,a.list-group-item-info:hover,button.list-group-item-info:focus,button.list-group-item-info:hover{color:#31708f;background-color:#c4e3f3}a.list-group-item-info.active,a.list-group-item-info.active:focus,a.list-group-item-info.active:hover,button.list-group-item-info.active,button.list-group-item-info.active:focus,button.list-group-item-info.active:hover{color:#fff;background-color:#31708f;border-color:#31708f}.list-group-item-warning{color:#8a6d3b;background-color:#fcf8e3}a.list-group-item-warning,button.list-group-item-warning{color:#8a6d3b}a.list-group-item-warning .list-group-item-heading,button.list-group-item-warning .list-group-item-heading{color:inherit}a.list-group-item-warning:focus,a.list-group-item-warning:hover,button.list-group-item-warning:focus,button.list-group-item-warning:hover{color:#8a6d3b;background-color:#faf2cc}a.list-group-item-warning.active,a.list-group-item-warning.active:focus,a.list-group-item-warning.active:hover,button.list-group-item-warning.active,button.list-group-item-warning.active:focus,button.list-group-item-warning.active:hover{color:#fff;background-color:#8a6d3b;border-color:#8a6d3b}.list-group-item-danger{color:#a94442;background-color:#f2dede}a.list-group-item-danger,button.list-group-item-danger{color:#a94442}a.list-group-item-danger .list-group-item-heading,button.list-group-item-danger .list-group-item-heading{color:inherit}a.list-group-item-danger:focus,a.list-group-item-danger:hover,button.list-group-item-danger:focus,button.list-group-item-danger:hover{color:#a94442;background-color:#ebcccc}a.list-group-item-danger.active,a.list-group-item-danger.active:focus,a.list-group-item-danger.active:hover,button.list-group-item-danger.active,button.list-group-item-danger.active:focus,button.list-group-item-danger.active:hover{color:#fff;background-color:#a94442;border-color:#a94442}.list-group-item-heading{margin-top:0;margin-bottom:5px}.list-group-item-text{margin-bottom:0;line-height:1.3}.panel{margin-bottom:20px;background-color:#fff;border:1px solid transparent;border-radius:4px;-webkit-box-shadow:0 1px 1px rgba(0,0,0,.05);box-shadow:0 1px 1px rgba(0,0,0,.05)}.panel-body{padding:15px}.panel-heading{padding:10px 15px;border-bottom:1px solid transparent;border-top-left-radius:3px;border-top-right-radius:3px}.panel-heading>.dropdown .dropdown-toggle{color:inherit}.panel-title{margin-top:0;margin-bottom:0;font-size:16px;color:inherit}.panel-title>.small,.panel-title>.small>a,.panel-title>a,.panel-title>small,.panel-title>small>a{color:inherit}.panel-footer{padding:10px 15px;background-color:#f5f5f5;border-top:1px solid #ddd;border-bottom-right-radius:3px;border-bottom-left-radius:3px}.panel>.list-group,.panel>.panel-collapse>.list-group{margin-bottom:0}.panel>.list-group .list-group-item,.panel>.panel-collapse>.list-group .list-group-item{border-width:1px 0;border-radius:0}.panel>.list-group:first-child .list-group-item:first-child,.panel>.panel-collapse>.list-group:first-child .list-group-item:first-child{border-top:0;border-top-left-radius:3px;border-top-right-radius:3px}.panel>.list-group:last-child .list-group-item:last-child,.panel>.panel-collapse>.list-group:last-child .list-group-item:last-child{border-bottom:0;border-bottom-right-radius:3px;border-bottom-left-radius:3px}.panel>.panel-heading+.panel-collapse>.list-group .list-group-item:first-child{border-top-left-radius:0;border-top-right-radius:0}.panel-heading+.list-group .list-group-item:first-child{border-top-width:0}.list-group+.panel-footer{border-top-width:0}.panel>.panel-collapse>.table,.panel>.table,.panel>.table-responsive>.table{margin-bottom:0}.panel>.panel-collapse>.table caption,.panel>.table caption,.panel>.table-responsive>.table caption{padding-right:15px;padding-left:15px}.panel>.table-responsive:first-child>.table:first-child,.panel>.table:first-child{border-top-left-radius:3px;border-top-right-radius:3px}.panel>.table-responsive:first-child>.table:first-child>tbody:first-child>tr:first-child,.panel>.table-responsive:first-child>.table:first-child>thead:first-child>tr:first-child,.panel>.table:first-child>tbody:first-child>tr:first-child,.panel>.table:first-child>thead:first-child>tr:first-child{border-top-left-radius:3px;border-top-right-radius:3px}.panel>.table-responsive:first-child>.table:first-child>tbody:first-child>tr:first-child td:first-child,.panel>.table-responsive:first-child>.table:first-child>tbody:first-child>tr:first-child th:first-child,.panel>.table-responsive:first-child>.table:first-child>thead:first-child>tr:first-child td:first-child,.panel>.table-responsive:first-child>.table:first-child>thead:first-child>tr:first-child th:first-child,.panel>.table:first-child>tbody:first-child>tr:first-child td:first-child,.panel>.table:first-child>tbody:first-child>tr:first-child th:first-child,.panel>.table:first-child>thead:first-child>tr:first-child td:first-child,.panel>.table:first-child>thead:first-child>tr:first-child th:first-child{border-top-left-radius:3px}.panel>.table-responsive:first-child>.table:first-child>tbody:first-child>tr:first-child td:last-child,.panel>.table-responsive:first-child>.table:first-child>tbody:first-child>tr:first-child th:last-child,.panel>.table-responsive:first-child>.table:first-child>thead:first-child>tr:first-child td:last-child,.panel>.table-responsive:first-child>.table:first-child>thead:first-child>tr:first-child th:last-child,.panel>.table:first-child>tbody:first-child>tr:first-child td:last-child,.panel>.table:first-child>tbody:first-child>tr:first-child th:last-child,.panel>.table:first-child>thead:first-child>tr:first-child td:last-child,.panel>.table:first-child>thead:first-child>tr:first-child th:last-child{border-top-right-radius:3px}.panel>.table-responsive:last-child>.table:last-child,.panel>.table:last-child{border-bottom-right-radius:3px;border-bottom-left-radius:3px}.panel>.table-responsive:last-child>.table:last-child>tbody:last-child>tr:last-child,.panel>.table-responsive:last-child>.table:last-child>tfoot:last-child>tr:last-child,.panel>.table:last-child>tbody:last-child>tr:last-child,.panel>.table:last-child>tfoot:last-child>tr:last-child{border-bottom-right-radius:3px;border-bottom-left-radius:3px}.panel>.table-responsive:last-child>.table:last-child>tbody:last-child>tr:last-child td:first-child,.panel>.table-responsive:last-child>.table:last-child>tbody:last-child>tr:last-child th:first-child,.panel>.table-responsive:last-child>.table:last-child>tfoot:last-child>tr:last-child td:first-child,.panel>.table-responsive:last-child>.table:last-child>tfoot:last-child>tr:last-child th:first-child,.panel>.table:last-child>tbody:last-child>tr:last-child td:first-child,.panel>.table:last-child>tbody:last-child>tr:last-child th:first-child,.panel>.table:last-child>tfoot:last-child>tr:last-child td:first-child,.panel>.table:last-child>tfoot:last-child>tr:last-child th:first-child{border-bottom-left-radius:3px}.panel>.table-responsive:last-child>.table:last-child>tbody:last-child>tr:last-child td:last-child,.panel>.table-responsive:last-child>.table:last-child>tbody:last-child>tr:last-child th:last-child,.panel>.table-responsive:last-child>.table:last-child>tfoot:last-child>tr:last-child td:last-child,.panel>.table-responsive:last-child>.table:last-child>tfoot:last-child>tr:last-child th:last-child,.panel>.table:last-child>tbody:last-child>tr:last-child td:last-child,.panel>.table:last-child>tbody:last-child>tr:last-child th:last-child,.panel>.table:last-child>tfoot:last-child>tr:last-child td:last-child,.panel>.table:last-child>tfoot:last-child>tr:last-child th:last-child{border-bottom-right-radius:3px}.panel>.panel-body+.table,.panel>.panel-body+.table-responsive,.panel>.table+.panel-body,.panel>.table-responsive+.panel-body{border-top:1px solid #ddd}.panel>.table>tbody:first-child>tr:first-child td,.panel>.table>tbody:first-child>tr:first-child th{border-top:0}.panel>.table-bordered,.panel>.table-responsive>.table-bordered{border:0}.panel>.table-bordered>tbody>tr>td:first-child,.panel>.table-bordered>tbody>tr>th:first-child,.panel>.table-bordered>tfoot>tr>td:first-child,.panel>.table-bordered>tfoot>tr>th:first-child,.panel>.table-bordered>thead>tr>td:first-child,.panel>.table-bordered>thead>tr>th:first-child,.panel>.table-responsive>.table-bordered>tbody>tr>td:first-child,.panel>.table-responsive>.table-bordered>tbody>tr>th:first-child,.panel>.table-responsive>.table-bordered>tfoot>tr>td:first-child,.panel>.table-responsive>.table-bordered>tfoot>tr>th:first-child,.panel>.table-responsive>.table-bordered>thead>tr>td:first-child,.panel>.table-responsive>.table-bordered>thead>tr>th:first-child{border-left:0}.panel>.table-bordered>tbody>tr>td:last-child,.panel>.table-bordered>tbody>tr>th:last-child,.panel>.table-bordered>tfoot>tr>td:last-child,.panel>.table-bordered>tfoot>tr>th:last-child,.panel>.table-bordered>thead>tr>td:last-child,.panel>.table-bordered>thead>tr>th:last-child,.panel>.table-responsive>.table-bordered>tbody>tr>td:last-child,.panel>.table-responsive>.table-bordered>tbody>tr>th:last-child,.panel>.table-responsive>.table-bordered>tfoot>tr>td:last-child,.panel>.table-responsive>.table-bordered>tfoot>tr>th:last-child,.panel>.table-responsive>.table-bordered>thead>tr>td:last-child,.panel>.table-responsive>.table-bordered>thead>tr>th:last-child{border-right:0}.panel>.table-bordered>tbody>tr:first-child>td,.panel>.table-bordered>tbody>tr:first-child>th,.panel>.table-bordered>thead>tr:first-child>td,.panel>.table-bordered>thead>tr:first-child>th,.panel>.table-responsive>.table-bordered>tbody>tr:first-child>td,.panel>.table-responsive>.table-bordered>tbody>tr:first-child>th,.panel>.table-responsive>.table-bordered>thead>tr:first-child>td,.panel>.table-responsive>.table-bordered>thead>tr:first-child>th{border-bottom:0}.panel>.table-bordered>tbody>tr:last-child>td,.panel>.table-bordered>tbody>tr:last-child>th,.panel>.table-bordered>tfoot>tr:last-child>td,.panel>.table-bordered>tfoot>tr:last-child>th,.panel>.table-responsive>.table-bordered>tbody>tr:last-child>td,.panel>.table-responsive>.table-bordered>tbody>tr:last-child>th,.panel>.table-responsive>.table-bordered>tfoot>tr:last-child>td,.panel>.table-responsive>.table-bordered>tfoot>tr:last-child>th{border-bottom:0}.panel>.table-responsive{margin-bottom:0;border:0}.panel-group{margin-bottom:20px}.panel-group .panel{margin-bottom:0;border-radius:4px}.panel-group .panel+.panel{margin-top:5px}.panel-group .panel-heading{border-bottom:0}.panel-group .panel-heading+.panel-collapse>.list-group,.panel-group .panel-heading+.panel-collapse>.panel-body{border-top:1px solid #ddd}.panel-group .panel-footer{border-top:0}.panel-group .panel-footer+.panel-collapse .panel-body{border-bottom:1px solid #ddd}.panel-default{border-color:#ddd}.panel-default>.panel-heading{color:#333;background-color:#f5f5f5;border-color:#ddd}.panel-default>.panel-heading+.panel-collapse>.panel-body{border-top-color:#ddd}.panel-default>.panel-heading .badge{color:#f5f5f5;background-color:#333}.panel-default>.panel-footer+.panel-collapse>.panel-body{border-bottom-color:#ddd}.panel-primary{border-color:#337ab7}.panel-primary>.panel-heading{color:#fff;background-color:#337ab7;border-color:#337ab7}.panel-primary>.panel-heading+.panel-collapse>.panel-body{border-top-color:#337ab7}.panel-primary>.panel-heading .badge{color:#337ab7;background-color:#fff}.panel-primary>.panel-footer+.panel-collapse>.panel-body{border-bottom-color:#337ab7}.panel-success{border-color:#d6e9c6}.panel-success>.panel-heading{color:#3c763d;background-color:#dff0d8;border-color:#d6e9c6}.panel-success>.panel-heading+.panel-collapse>.panel-body{border-top-color:#d6e9c6}.panel-success>.panel-heading .badge{color:#dff0d8;background-color:#3c763d}.panel-success>.panel-footer+.panel-collapse>.panel-body{border-bottom-color:#d6e9c6}.panel-info{border-color:#bce8f1}.panel-info>.panel-heading{color:#31708f;background-color:#d9edf7;border-color:#bce8f1}.panel-info>.panel-heading+.panel-collapse>.panel-body{border-top-color:#bce8f1}.panel-info>.panel-heading .badge{color:#d9edf7;background-color:#31708f}.panel-info>.panel-footer+.panel-collapse>.panel-body{border-bottom-color:#bce8f1}.panel-warning{border-color:#faebcc}.panel-warning>.panel-heading{color:#8a6d3b;background-color:#fcf8e3;border-color:#faebcc}.panel-warning>.panel-heading+.panel-collapse>.panel-body{border-top-color:#faebcc}.panel-warning>.panel-heading .badge{color:#fcf8e3;background-color:#8a6d3b}.panel-warning>.panel-footer+.panel-collapse>.panel-body{border-bottom-color:#faebcc}.panel-danger{border-color:#ebccd1}.panel-danger>.panel-heading{color:#a94442;background-color:#f2dede;border-color:#ebccd1}.panel-danger>.panel-heading+.panel-collapse>.panel-body{border-top-color:#ebccd1}.panel-danger>.panel-heading .badge{color:#f2dede;background-color:#a94442}.panel-danger>.panel-footer+.panel-collapse>.panel-body{border-bottom-color:#ebccd1}.embed-responsive{position:relative;display:block;height:0;padding:0;overflow:hidden}.embed-responsive .embed-responsive-item,.embed-responsive embed,.embed-responsive iframe,.embed-responsive object,.embed-responsive video{position:absolute;top:0;bottom:0;left:0;width:100%;height:100%;border:0}.embed-responsive-16by9{padding-bottom:56.25%}.embed-responsive-4by3{padding-bottom:75%}.well{min-height:20px;padding:19px;margin-bottom:20px;background-color:#f5f5f5;border:1px solid #e3e3e3;border-radius:4px;-webkit-box-shadow:inset 0 1px 1px rgba(0,0,0,.05);box-shadow:inset 0 1px 1px rgba(0,0,0,.05)}.well blockquote{border-color:#ddd;border-color:rgba(0,0,0,.15)}.well-lg{padding:24px;border-radius:6px}.well-sm{padding:9px;border-radius:3px}.close{float:right;font-size:21px;font-weight:700;line-height:1;color:#000;text-shadow:0 1px 0 #fff;filter:alpha(opacity=20);opacity:.2}.close:focus,.close:hover{color:#000;text-decoration:none;cursor:pointer;filter:alpha(opacity=50);opacity:.5}button.close{-webkit-appearance:none;padding:0;cursor:pointer;background:0 0;border:0}.modal-open{overflow:hidden}.modal{position:fixed;top:0;right:0;bottom:0;left:0;z-index:1050;display:none;overflow:hidden;-webkit-overflow-scrolling:touch;outline:0}.modal.fade .modal-dialog{-webkit-transition:-webkit-transform .3s ease-out;-o-transition:-o-transform .3s ease-out;transition:transform .3s ease-out;-webkit-transform:translate(0,-25%);-ms-transform:translate(0,-25%);-o-transform:translate(0,-25%);transform:translate(0,-25%)}.modal.in .modal-dialog{-webkit-transform:translate(0,0);-ms-transform:translate(0,0);-o-transform:translate(0,0);transform:translate(0,0)}.modal-open .modal{overflow-x:hidden;overflow-y:auto}.modal-dialog{position:relative;width:auto;margin:10px}.modal-content{position:relative;background-color:#fff;-webkit-background-clip:padding-box;background-clip:padding-box;border:1px solid #999;border:1px solid rgba(0,0,0,.2);border-radius:6px;outline:0;-webkit-box-shadow:0 3px 9px rgba(0,0,0,.5);box-shadow:0 3px 9px rgba(0,0,0,.5)}.modal-backdrop{position:fixed;top:0;right:0;bottom:0;left:0;z-index:1040;background-color:#000}.modal-backdrop.fade{filter:alpha(opacity=0);opacity:0}.modal-backdrop.in{filter:alpha(opacity=50);opacity:.5}.modal-header{min-height:16.43px;padding:15px;border-bottom:1px solid #e5e5e5}.modal-header .close{margin-top:-2px}.modal-title{margin:0;line-height:1.42857143}.modal-body{position:relative;padding:15px}.modal-footer{padding:15px;text-align:right;border-top:1px solid #e5e5e5}.modal-footer .btn+.btn{margin-bottom:0;margin-left:5px}.modal-footer .btn-group .btn+.btn{margin-left:-1px}.modal-footer .btn-block+.btn-block{margin-left:0}.modal-scrollbar-measure{position:absolute;top:-9999px;width:50px;height:50px;overflow:scroll}@media (min-width:768px){.modal-dialog{width:600px;margin:30px auto}.modal-content{-webkit-box-shadow:0 5px 15px rgba(0,0,0,.5);box-shadow:0 5px 15px rgba(0,0,0,.5)}.modal-sm{width:300px}}@media (min-width:992px){.modal-lg{width:900px}}.tooltip{position:absolute;z-index:1070;display:block;font-family:"Helvetica Neue",Helvetica,Arial,sans-serif;font-size:12px;font-style:normal;font-weight:400;line-height:1.42857143;text-align:left;text-align:start;text-decoration:none;text-shadow:none;text-transform:none;letter-spacing:normal;word-break:normal;word-spacing:normal;word-wrap:normal;white-space:normal;filter:alpha(opacity=0);opacity:0;line-break:auto}.tooltip.in{filter:alpha(opacity=90);opacity:.9}.tooltip.top{padding:5px 0;margin-top:-3px}.tooltip.right{padding:0 5px;margin-left:3px}.tooltip.bottom{padding:5px 0;margin-top:3px}.tooltip.left{padding:0 5px;margin-left:-3px}.tooltip-inner{max-width:200px;padding:3px 8px;color:#fff;text-align:center;background-color:#000;border-radius:4px}.tooltip-arrow{position:absolute;width:0;height:0;border-color:transparent;border-style:solid}.tooltip.top .tooltip-arrow{bottom:0;left:50%;margin-left:-5px;border-width:5px 5px 0;border-top-color:#000}.tooltip.top-left .tooltip-arrow{right:5px;bottom:0;margin-bottom:-5px;border-width:5px 5px 0;border-top-color:#000}.tooltip.top-right .tooltip-arrow{bottom:0;left:5px;margin-bottom:-5px;border-width:5px 5px 0;border-top-color:#000}.tooltip.right .tooltip-arrow{top:50%;left:0;margin-top:-5px;border-width:5px 5px 5px 0;border-right-color:#000}.tooltip.left .tooltip-arrow{top:50%;right:0;margin-top:-5px;border-width:5px 0 5px 5px;border-left-color:#000}.tooltip.bottom .tooltip-arrow{top:0;left:50%;margin-left:-5px;border-width:0 5px 5px;border-bottom-color:#000}.tooltip.bottom-left .tooltip-arrow{top:0;right:5px;margin-top:-5px;border-width:0 5px 5px;border-bottom-color:#000}.tooltip.bottom-right .tooltip-arrow{top:0;left:5px;margin-top:-5px;border-width:0 5px 5px;border-bottom-color:#000}.popover{position:absolute;top:0;left:0;z-index:1060;display:none;max-width:276px;padding:1px;font-family:"Helvetica Neue",Helvetica,Arial,sans-serif;font-size:14px;font-style:normal;font-weight:400;line-height:1.42857143;text-align:left;text-align:start;text-decoration:none;text-shadow:none;text-transform:none;letter-spacing:normal;word-break:normal;word-spacing:normal;word-wrap:normal;white-space:normal;background-color:#fff;-webkit-background-clip:padding-box;background-clip:padding-box;border:1px solid #ccc;border:1px solid rgba(0,0,0,.2);border-radius:6px;-webkit-box-shadow:0 5px 10px rgba(0,0,0,.2);box-shadow:0 5px 10px rgba(0,0,0,.2);line-break:auto}.popover.top{margin-top:-10px}.popover.right{margin-left:10px}.popover.bottom{margin-top:10px}.popover.left{margin-left:-10px}.popover-title{padding:8px 14px;margin:0;font-size:14px;background-color:#f7f7f7;border-bottom:1px solid #ebebeb;border-radius:5px 5px 0 0}.popover-content{padding:9px 14px}.popover>.arrow,.popover>.arrow:after{position:absolute;display:block;width:0;height:0;border-color:transparent;border-style:solid}.popover>.arrow{border-width:11px}.popover>.arrow:after{content:"";border-width:10px}.popover.top>.arrow{bottom:-11px;left:50%;margin-left:-11px;border-top-color:#999;border-top-color:rgba(0,0,0,.25);border-bottom-width:0}.popover.top>.arrow:after{bottom:1px;margin-left:-10px;content:" ";border-top-color:#fff;border-bottom-width:0}.popover.right>.arrow{top:50%;left:-11px;margin-top:-11px;border-right-color:#999;border-right-color:rgba(0,0,0,.25);border-left-width:0}.popover.right>.arrow:after{bottom:-10px;left:1px;content:" ";border-right-color:#fff;border-left-width:0}.popover.bottom>.arrow{top:-11px;left:50%;margin-left:-11px;border-top-width:0;border-bottom-color:#999;border-bottom-color:rgba(0,0,0,.25)}.popover.bottom>.arrow:after{top:1px;margin-left:-10px;content:" ";border-top-width:0;border-bottom-color:#fff}.popover.left>.arrow{top:50%;right:-11px;margin-top:-11px;border-right-width:0;border-left-color:#999;border-left-color:rgba(0,0,0,.25)}.popover.left>.arrow:after{right:1px;bottom:-10px;content:" ";border-right-width:0;border-left-color:#fff}.carousel{position:relative}.carousel-inner{position:relative;width:100%;overflow:hidden}.carousel-inner>.item{position:relative;display:none;-webkit-transition:.6s ease-in-out left;-o-transition:.6s ease-in-out left;transition:.6s ease-in-out left}.carousel-inner>.item>a>img,.carousel-inner>.item>img{line-height:1}@media all and (transform-3d),(-webkit-transform-3d){.carousel-inner>.item{-webkit-transition:-webkit-transform .6s ease-in-out;-o-transition:-o-transform .6s ease-in-out;transition:transform .6s ease-in-out;-webkit-backface-visibility:hidden;backface-visibility:hidden;-webkit-perspective:1000px;perspective:1000px}.carousel-inner>.item.active.right,.carousel-inner>.item.next{left:0;-webkit-transform:translate3d(100%,0,0);transform:translate3d(100%,0,0)}.carousel-inner>.item.active.left,.carousel-inner>.item.prev{left:0;-webkit-transform:translate3d(-100%,0,0);transform:translate3d(-100%,0,0)}.carousel-inner>.item.active,.carousel-inner>.item.next.left,.carousel-inner>.item.prev.right{left:0;-webkit-transform:translate3d(0,0,0);transform:translate3d(0,0,0)}}.carousel-inner>.active,.carousel-inner>.next,.carousel-inner>.prev{display:block}.carousel-inner>.active{left:0}.carousel-inner>.next,.carousel-inner>.prev{position:absolute;top:0;width:100%}.carousel-inner>.next{left:100%}.carousel-inner>.prev{left:-100%}.carousel-inner>.next.left,.carousel-inner>.prev.right{left:0}.carousel-inner>.active.left{left:-100%}.carousel-inner>.active.right{left:100%}.carousel-control{position:absolute;top:0;bottom:0;left:0;width:15%;font-size:20px;color:#fff;text-align:center;text-shadow:0 1px 2px rgba(0,0,0,.6);filter:alpha(opacity=50);opacity:.5}.carousel-control.left{background-image:-webkit-linear-gradient(left,rgba(0,0,0,.5) 0,rgba(0,0,0,.0001) 100%);background-image:-o-linear-gradient(left,rgba(0,0,0,.5) 0,rgba(0,0,0,.0001) 100%);background-image:-webkit-gradient(linear,left top,right top,from(rgba(0,0,0,.5)),to(rgba(0,0,0,.0001)));background-image:linear-gradient(to right,rgba(0,0,0,.5) 0,rgba(0,0,0,.0001) 100%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#80000000', endColorstr='#00000000', GradientType=1);background-repeat:repeat-x}.carousel-control.right{right:0;left:auto;background-image:-webkit-linear-gradient(left,rgba(0,0,0,.0001) 0,rgba(0,0,0,.5) 100%);background-image:-o-linear-gradient(left,rgba(0,0,0,.0001) 0,rgba(0,0,0,.5) 100%);background-image:-webkit-gradient(linear,left top,right top,from(rgba(0,0,0,.0001)),to(rgba(0,0,0,.5)));background-image:linear-gradient(to right,rgba(0,0,0,.0001) 0,rgba(0,0,0,.5) 100%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#00000000', endColorstr='#80000000', GradientType=1);background-repeat:repeat-x}.carousel-control:focus,.carousel-control:hover{color:#fff;text-decoration:none;filter:alpha(opacity=90);outline:0;opacity:.9}.carousel-control .glyphicon-chevron-left,.carousel-control .glyphicon-chevron-right,.carousel-control .icon-next,.carousel-control .icon-prev{position:absolute;top:50%;z-index:5;display:inline-block;margin-top:-10px}.carousel-control .glyphicon-chevron-left,.carousel-control .icon-prev{left:50%;margin-left:-10px}.carousel-control .glyphicon-chevron-right,.carousel-control .icon-next{right:50%;margin-right:-10px}.carousel-control .icon-next,.carousel-control .icon-prev{width:20px;height:20px;font-family:serif;line-height:1}.carousel-control .icon-prev:before{content:'\2039'}.carousel-control .icon-next:before{content:'\203a'}.carousel-indicators{position:absolute;bottom:10px;left:50%;z-index:15;width:60%;padding-left:0;margin-left:-30%;text-align:center;list-style:none}.carousel-indicators li{display:inline-block;width:10px;height:10px;margin:1px;text-indent:-999px;cursor:pointer;background-color:#000\9;background-color:rgba(0,0,0,0);border:1px solid #fff;border-radius:10px}.carousel-indicators .active{width:12px;height:12px;margin:0;background-color:#fff}.carousel-caption{position:absolute;right:15%;bottom:20px;left:15%;z-index:10;padding-top:20px;padding-bottom:20px;color:#fff;text-align:center;text-shadow:0 1px 2px rgba(0,0,0,.6)}.carousel-caption .btn{text-shadow:none}@media screen and (min-width:768px){.carousel-control .glyphicon-chevron-left,.carousel-control .glyphicon-chevron-right,.carousel-control .icon-next,.carousel-control .icon-prev{width:30px;height:30px;margin-top:-15px;font-size:30px}.carousel-control .glyphicon-chevron-left,.carousel-control .icon-prev{margin-left:-15px}.carousel-control .glyphicon-chevron-right,.carousel-control .icon-next{margin-right:-15px}.carousel-caption{right:20%;left:20%;padding-bottom:30px}.carousel-indicators{bottom:20px}}.btn-group-vertical>.btn-group:after,.btn-group-vertical>.btn-group:before,.btn-toolbar:after,.btn-toolbar:before,.clearfix:after,.clearfix:before,.container-fluid:after,.container-fluid:before,.container:after,.container:before,.dl-horizontal dd:after,.dl-horizontal dd:before,.form-horizontal .form-group:after,.form-horizontal .form-group:before,.modal-footer:after,.modal-footer:before,.nav:after,.nav:before,.navbar-collapse:after,.navbar-collapse:before,.navbar-header:after,.navbar-header:before,.navbar:after,.navbar:before,.pager:after,.pager:before,.panel-body:after,.panel-body:before,.row:after,.row:before{display:table;content:" "}.btn-group-vertical>.btn-group:after,.btn-toolbar:after,.clearfix:after,.container-fluid:after,.container:after,.dl-horizontal dd:after,.form-horizontal .form-group:after,.modal-footer:after,.nav:after,.navbar-collapse:after,.navbar-header:after,.navbar:after,.pager:after,.panel-body:after,.row:after{clear:both}.center-block{display:block;margin-right:auto;margin-left:auto}.pull-right{float:right!important}.pull-left{float:left!important}.hide{display:none!important}.show{display:block!important}.invisible{visibility:hidden}.text-hide{font:0/0 a;color:transparent;text-shadow:none;background-color:transparent;border:0}.hidden{display:none!important}.affix{position:fixed}@-ms-viewport{width:device-width}.visible-lg,.visible-md,.visible-sm,.visible-xs{display:none!important}.visible-lg-block,.visible-lg-inline,.visible-lg-inline-block,.visible-md-block,.visible-md-inline,.visible-md-inline-block,.visible-sm-block,.visible-sm-inline,.visible-sm-inline-block,.visible-xs-block,.visible-xs-inline,.visible-xs-inline-block{display:none!important}@media (max-width:767px){.visible-xs{display:block!important}table.visible-xs{display:table!important}tr.visible-xs{display:table-row!important}td.visible-xs,th.visible-xs{display:table-cell!important}}@media (max-width:767px){.visible-xs-block{display:block!important}}@media (max-width:767px){.visible-xs-inline{display:inline!important}}@media (max-width:767px){.visible-xs-inline-block{display:inline-block!important}}@media (min-width:768px) and (max-width:991px){.visible-sm{display:block!important}table.visible-sm{display:table!important}tr.visible-sm{display:table-row!important}td.visible-sm,th.visible-sm{display:table-cell!important}}@media (min-width:768px) and (max-width:991px){.visible-sm-block{display:block!important}}@media (min-width:768px) and (max-width:991px){.visible-sm-inline{display:inline!important}}@media (min-width:768px) and (max-width:991px){.visible-sm-inline-block{display:inline-block!important}}@media (min-width:992px) and (max-width:1199px){.visible-md{display:block!important}table.visible-md{display:table!important}tr.visible-md{display:table-row!important}td.visible-md,th.visible-md{display:table-cell!important}}@media (min-width:992px) and (max-width:1199px){.visible-md-block{display:block!important}}@media (min-width:992px) and (max-width:1199px){.visible-md-inline{display:inline!important}}@media (min-width:992px) and (max-width:1199px){.visible-md-inline-block{display:inline-block!important}}@media (min-width:1200px){.visible-lg{display:block!important}table.visible-lg{display:table!important}tr.visible-lg{display:table-row!important}td.visible-lg,th.visible-lg{display:table-cell!important}}@media (min-width:1200px){.visible-lg-block{display:block!important}}@media (min-width:1200px){.visible-lg-inline{display:inline!important}}@media (min-width:1200px){.visible-lg-inline-block{display:inline-block!important}}@media (max-width:767px){.hidden-xs{display:none!important}}@media (min-width:768px) and (max-width:991px){.hidden-sm{display:none!important}}@media (min-width:992px) and (max-width:1199px){.hidden-md{display:none!important}}@media (min-width:1200px){.hidden-lg{display:none!important}}.visible-print{display:none!important}@media print{.visible-print{display:block!important}table.visible-print{display:table!important}tr.visible-print{display:table-row!important}td.visible-print,th.visible-print{display:table-cell!important}}.visible-print-block{display:none!important}@media print{.visible-print-block{display:block!important}}.visible-print-inline{display:none!important}@media print{.visible-print-inline{display:inline!important}}.visible-print-inline-block{display:none!important}@media print{.visible-print-inline-block{display:inline-block!important}}@media print{.hidden-print{display:none!important}} \ No newline at end of file diff --git a/routes/clusterState.js b/routes/clusterState.js new file mode 100644 index 0000000..bf7e8be --- /dev/null +++ b/routes/clusterState.js @@ -0,0 +1,13 @@ +FetchStatus = require('./fetchStatus.js'); + +class ClusterState { + constructor(clusterName) { + this.clusterName = clusterName; + this.fetchStatus = FetchStatus.INITIAL; + this.createTimestamp = new Date(); + this.instanceSummaries = {}; + this.errorDetails = null; + } +} + +module.exports = ClusterState; diff --git a/routes/clusterStateCache.js b/routes/clusterStateCache.js new file mode 100644 index 0000000..57d9dbf --- /dev/null +++ b/routes/clusterStateCache.js @@ -0,0 +1,22 @@ +const cache = require('memory-cache'); + +class ClusterStateCache { + constructor(ttl) { + this.ttl = ttl; + } + + put(key, value, ttl = this.ttl) { + console.debug(`Added cache entry for '${key}' with ${ttl}ms TTL`); + cache.put(key, value, ttl, function(key, value) { + console.debug(`Cached value for '${key}' cluster expired after ${ttl}ms`); + }); + }; + + get(key) { + return cache.get(key); + }; + +} + +module.exports = new ClusterStateCache(); +module.exports.ClusterStateCache = ClusterStateCache; diff --git a/routes/fetchStatus.js b/routes/fetchStatus.js new file mode 100644 index 0000000..c48a119 --- /dev/null +++ b/routes/fetchStatus.js @@ -0,0 +1,6 @@ +module.exports = Object.freeze({ + INITIAL: "initial", + FETCHING: "fetching", + FETCHED: "fetched", + ERROR: "error" +}); diff --git a/routes/index.js b/routes/index.js index 7bfd271..80f6dc3 100644 --- a/routes/index.js +++ b/routes/index.js @@ -1,29 +1,30 @@ -const AWS_API_DELAY = 100; +const config = require('../config/config'); const debug = require('debug')('api'); const express = require('express'); const router = express.Router(); const fs = require('fs'); -// See: https://docs.aws.amazon.com/AWSJavaScriptSDK/guide/node-configuring.html +const moment = require('moment'); const AWS = require('aws-sdk-promise'); const batchPromises = require('batch-promises'); -// AWS variable now has default credentials from Shared Credentials File or Environment Variables. -// Override default credentials with ./aws_config.json if it exists -const AWS_CONFIG_FILE = './aws_config.json'; -if (fs.existsSync(AWS_CONFIG_FILE)) { - console.log(`Updating with settings from '${AWS_CONFIG_FILE}'...`); - AWS.config.update(JSON.parse(fs.readFileSync(AWS_CONFIG_FILE, 'utf8'))); +// AWS variable has default credentials from Shared Credentials File or Environment Variables. +// (see: https://docs.aws.amazon.com/AWSJavaScriptSDK/guide/node-configuring.html) +// Override default credentials with configFile (e.g. './aws_config.json') if it exists +if (fs.existsSync(config.aws.configFile)) { + console.log(`Updating with settings from '${config.aws.configFile}'...`); + AWS.config.update(JSON.parse(fs.readFileSync(config.aws.configFile, 'utf8'))); } console.log(`Targeting AWS region '${AWS.config.region}'`); const utils = require('./utils'); +const FetchStatus = require('./fetchStatus'); +const ClusterState = require('./clusterState'); +const clusterStateCache = require('../routes/clusterStateCache'); +const clusterStateCacheTtl = config.clusterStateCacheTtl; const taskDefinitionCache = require('memory-cache'); -var clusterStore = {}; - const ecs = new AWS.ECS(); const ec2 = new AWS.EC2(); -const maxSize = 100; /* Home page */ @@ -40,67 +41,106 @@ router.get('/', function (req, res, next) { * Endpoints take a "?static=true" query param to enable testing with static data when AWS credentials aren't available */ -const STATUS_INITIAL = "initial"; -const STATUS_FETCHING = "fetching"; -const STATUS_FETCHED = "fetched"; - router.get('/api/instance_summaries_with_tasks', function (req, res, next) { - console.log(`/api/instance_summaries_with_tasks: cluster='${req.query.cluster}', live=${!req.query.static})`); - debugLog(`Headers: ${JSON.stringify(req.headers, null, 4)}`); + Promise.resolve() + .then(function() { + debugLog(`Headers: ${JSON.stringify(req.headers, null, 4)}`); + if (!req.query.cluster) { + send400Response("Please provide a 'cluster' parameter", res); + reject("No 'cluster' parameter provided."); + } else { + const clusterName = req.query.cluster; + const useStaticData = req.query.static === 'true'; + const forceRefresh = req.query.forceRefresh === 'true'; + return getInstanceSummariesWithTasks(res, clusterName, useStaticData, forceRefresh); + } + }) + .catch(function (err) { + const reason = new Error(`Failed getting instance summaries: ${err}`); + reason.stack += `\nCaused By:\n` + err.stack; + sendErrorResponse(reason, res); + }); +}); - if (!req.query.cluster) { - send400Response("Please provide a 'cluster' parameter", res); - } else { - const clusterName = req.query.cluster; - const live = req.query.static !== 'true'; - if (live) { - if (!getClusterStore(clusterName)) { - new Promise(function (resolve) { - initialiseClusterStore(clusterName); - resolve(); - }) - .then(function () { - // Return to client - res.json(getClusterStore(clusterName)); - }); - // Continue populating cluster store while client polls asynchronously - populateClusterStore(clusterName) +function getInstanceSummariesWithTasks(res, clusterName, useStaticData, forceRefresh) { + return Promise.resolve(getOrInitializeClusterState(clusterName, forceRefresh)) + .then(function(clusterState) { + if (clusterState == null) { + throw new Error(`clusterState for '${clusterName}' cluster not cached and could not be initialised.`); + } else if (clusterState.fetchStatus === FetchStatus.ERROR) { + // Server previously encountered an error while asynchronously processing cluster. Send error to client. + console.log(`Sending current state to client with fetchStatus '${clusterState.fetchStatus}'.`); + sendErrorResponse(clusterState.errorDetails, res); + return clusterState; + } else { + // Send current state to client. If only just initialised, next then() block will process in background while client polls periodically + console.log(`Sending current state to client with fetchStatus '${clusterState.fetchStatus}'.`); + res.json(clusterState); + return clusterState; + } + }) + .then(function(clusterState) { + if (clusterState.fetchStatus === FetchStatus.INITIAL) { + // Populate cluster state in the background while client polls asynchronously + if (useStaticData) { + populateStaticClusterStateWithInstanceSummaries(clusterName); } else { - console.log("After populateState... responding with ", getClusterStore(clusterName)); - // Return currently fetched cluster store to client - res.json(getClusterStore(clusterName)); + populateClusterStateWithInstanceSummaries(clusterName); } - } else { - // Return some static instance details with task details - const instanceSummaries = JSON.parse(fs.readFileSync("public/test_data/ecs_instance_summaries_with_tasks-" + clusterName + ".json", "utf8")); - res.json(instanceSummaries); } + }) + .catch(function(err) { + console.log(`${err}\n${err.stack}`); + setClusterStateError(clusterName, err); + // NOTE: Don't re-throw here, to avoid 'UnhandledPromiseRejectionWarning' in router calling function + }); +} + +function populateStaticClusterStateWithInstanceSummaries(clusterName) { + console.log(`populateStaticClusterStateWithInstanceSummaries(${clusterName})`); + updateClusterState(clusterName, FetchStatus.FETCHING, {}); + try { + // Return some static instance details with task details + const text = fs.readFileSync("public/test_data/ecs_instance_summaries_with_tasks-" + clusterName + ".json", "utf8"); + const instanceSummaries = JSON.parse(text); + updateClusterState(clusterName, FetchStatus.FETCHED, instanceSummaries); + } catch (err) { + console.log(`${err}\n${err.stack}`); + setClusterStateError(clusterName, `Encountered error processing static file for '${clusterName}' cluster: ${err}`); } -}); +} -function setClusterStore(clusterName, status, instanceSummaries) { +function updateClusterState(clusterName, status, instanceSummaries) { console.log(`Setting fetch status to "${status}" for cluster "${clusterName}"`); - clusterStore[clusterName] = { - clusterName: clusterName, - fetchStatus: status, - instanceSummaries: instanceSummaries - }; - console.log(`clusterStore[${clusterName}] = ${getClusterStore(clusterName)}`) + const clusterState = getOrInitializeClusterState(clusterName); + clusterState.fetchStatus = status; + clusterState.instanceSummaries = instanceSummaries; + console.log(`Updated: clusterState for '${clusterName}' cluster = ${JSON.stringify(clusterState)}`) } -function initialiseClusterStore(clusterName) { - console.log("initialiseClusterStore"); - setClusterStore(clusterName, STATUS_INITIAL, {}); - console.log("clusterStore[" + clusterName + "]"); +function getOrInitializeClusterState(clusterName, forceRefresh = false) { + // NOTE: Cache will return null if cluster is not yet cached OR if cluster entry has expired + let clusterState = clusterStateCache.get(clusterName); + if (clusterState != null && forceRefresh) { + console.log(`Client requested a force refresh of cluster data already cached at ${clusterState.createTimestamp} (${moment(clusterState.createTimestamp).fromNow()})`); + } + if (clusterState == null || forceRefresh) { + clusterState = new ClusterState(clusterName); + clusterStateCache.put(clusterName, clusterState, clusterStateCacheTtl); + } + return clusterState; } -function getClusterStore(clusterName) { - return clusterStore[clusterName]; +function setClusterStateError(clusterName, errorDetails) { + console.log(`Setting errorDetails for '${clusterName}' cluster to: ${errorDetails}`); + const clusterState = getOrInitializeClusterState(clusterName); + clusterState.fetchStatus = FetchStatus.ERROR; + clusterState.errorDetails = errorDetails; } -function populateClusterStore(clusterName) { - console.log(`populateClusterStore(${clusterName})`); - setClusterStore(clusterName, STATUS_FETCHING, {}); +function populateClusterStateWithInstanceSummaries(cluster) { + console.log(`populateClusterStateWithInstanceSummaries(${cluster})`); + updateClusterState(cluster, FetchStatus.FETCHING, {}); let tasksArray = []; getTasksWithTaskDefinitions(cluster) @@ -116,7 +156,7 @@ function populateClusterStore(clusterName) { }); } else { const containerInstanceBatches = listAllContainerInstanceArns.map(function (instances, index) { - return index % maxSize === 0 ? listAllContainerInstanceArns.slice(index, index + maxSize) : null; + return index % config.aws.describeInstancesPageSize === 0 ? listAllContainerInstanceArns.slice(index, index + config.aws.describeInstancesPageSize) : null; }).filter(function (instances) { return instances; }); @@ -126,7 +166,7 @@ function populateClusterStore(clusterName) { resolve(ecs.describeContainerInstances({ cluster: cluster, containerInstances: containerInstanceBatch - }).promise().then(delayPromise(AWS_API_DELAY))); + }).promise().then(delayPromise(config.aws.apiDelay))); })); } }) @@ -134,7 +174,7 @@ function populateClusterStore(clusterName) { if (!describeContainerInstancesResponses || describeContainerInstancesResponses.length === 0) { return new Promise(function (resolve, reject) { console.warn("No Container Instances found"); - res.json([]); + updateClusterState(cluster, FetchStatus.FETCHED, []); }); } else { const containerInstances = describeContainerInstancesResponses.reduce(function (acc, current) { @@ -143,7 +183,7 @@ function populateClusterStore(clusterName) { const ec2instanceIds = containerInstances.map(function (i) { return i.ec2InstanceId; }); - console.log(`Found ${ec2instanceIds.length} ec2InstanceIds: ${ec2instanceIds}`); + console.log(`Found ${ec2instanceIds.length} ec2InstanceIds for cluster '${cluster}': ${ec2instanceIds}`); return ec2.describeInstances({InstanceIds: ec2instanceIds}).promise() .then(function (ec2Instances) { const instances = [].concat.apply([], ec2Instances.data.Reservations.map(function (r) { @@ -171,13 +211,12 @@ function populateClusterStore(clusterName) { }) } }); - setClusterStore(clusterName, STATUS_FETCHED, instanceSummaries); - res.json(getClusterStore(clusterName)); + updateClusterState(cluster, FetchStatus.FETCHED, instanceSummaries); }); } }) - .catch(function (err) { - sendErrorResponse(err, res); + .catch(function(err) { + setClusterStateError(cluster, err); }); } @@ -194,7 +233,7 @@ router.get('/api/cluster_names', function (req, res, next) { } }); } else { - res.json(["demo-cluster-8", "demo-cluster-50", "demo-cluster-75", "demo-cluster-100"]); + res.json(["demo-cluster-8", "demo-cluster-50", "demo-cluster-75", "demo-cluster-100", "invalid"]); } }); @@ -211,7 +250,7 @@ function listAllContainerInstances(cluster) { } function listContainerInstanceWithToken(cluster, token, instanceArns) { - const params = {cluster: cluster, maxResults: maxSize}; + const params = {cluster: cluster, maxResults: config.aws.listInstancesPageSize}; if (token) { params['nextToken'] = token; } @@ -242,13 +281,14 @@ function listAllTasks(cluster) { } function listTasksWithToken(cluster, token, tasks) { - const params = {cluster: cluster, maxResults: maxSize}; + const params = {cluster: cluster, maxResults: config.aws.listTasksPageSize}; if (token) { params['nextToken'] = token; } debugLog(`\tCalling ecs.listTasks with token: ${token} ...`); + // TODO: Handle errors, e.g.: (node:27333) UnhandledPromiseRejectionWarning: ClusterNotFoundException: Cluster not found. return ecs.listTasks(params).promise() - .then(delayPromise(AWS_API_DELAY)) + .then(delayPromise(config.aws.apiDelay)) .then(function (tasksResponse) { debugLog(`\t\tReceived tasksResponse with ${tasksResponse.data.taskArns.length} Task ARNs`); const taskArns = tasks.concat(tasksResponse.data.taskArns); @@ -275,30 +315,30 @@ function getTasksWithTaskDefinitions(cluster) { resolve([]); } else { return allTaskArns.map(function (tasks, index) { - return index % maxSize === 0 ? allTaskArns.slice(index, index + maxSize) : null; + return index % config.aws.describeTasksPageSize === 0 ? allTaskArns.slice(index, index + config.aws.describeTasksPageSize) : null; }).filter(function (tasks) { return tasks; }); } }) .then(function (taskBatches) { - // Batch the batches :) - describe up to 2 batches of batches of maxSize ARNs at a time + // Describe up to maxSimultaneousDescribeTasksCalls (e.g. 2) pages of describeTasksPageSize (e.g. 100) ARNs at a time // Without batchPromises, we will fire all ecs.describeTasks calls one after the other and could run into API rate limit issues - return batchPromises(2, taskBatches, taskBatch => new Promise((resolve, reject) => { + return batchPromises(config.aws.maxSimultaneousDescribeTasksCalls, taskBatches, taskBatch => new Promise((resolve, reject) => { // The iteratee will fire after each batch debugLog(`\tCalling ecs.describeTasks for Task batch: ${taskBatch}`); resolve(ecs.describeTasks({cluster: cluster, tasks: taskBatch}).promise() - .then(delayPromise(AWS_API_DELAY))); + .then(delayPromise(config.aws.apiDelay))); })); }) .then(function (describeTasksResponses) { tasksArray = describeTasksResponses.reduce(function (acc, current) { return acc.concat(current.data.tasks); }, []); - console.log(`Found ${tasksArray.length} tasks`); - // Wait for the responses from 20 describeTaskDefinition calls before invoking another 20 calls + console.log(`Found ${tasksArray.length} tasks for cluster '${cluster}'`); + // Wait for the responses from maxSimultaneousDescribeTaskDefinitionCalls describeTaskDefinition calls before invoking another maxSimultaneousDescribeTaskDefinitionCalls calls // Without batchPromises, we will fire all ecs.describeTaskDefinition calls one after the other and could run into API rate limit issues - return batchPromises(1, tasksArray, task => new Promise((resolve, reject) => { + return batchPromises(config.aws.maxSimultaneousDescribeTaskDefinitionCalls, tasksArray, task => new Promise((resolve, reject) => { const cachedTaskDef = taskDefinitionCache.get(task.taskDefinitionArn); if (cachedTaskDef) { debugLog(`\tReusing cached Task Definition for Task Definition ARN: ${task.taskDefinitionArn}`); @@ -306,7 +346,7 @@ function getTasksWithTaskDefinitions(cluster) { } else { debugLog(`\tCalling ecs.describeTaskDefinition for Task Definition ARN: ${task.taskDefinitionArn}`); resolve(ecs.describeTaskDefinition({taskDefinition: task.taskDefinitionArn}).promise() - .then(delayPromise(AWS_API_DELAY)) + .then(delayPromise(config.aws.apiDelay)) .then(function (taskDefinition) { debugLog(`\t\tReceived taskDefinition for ARN "${task.taskDefinitionArn}". Caching in memory.`); taskDefinitionCache.put(task.taskDefinitionArn, taskDefinition); @@ -320,7 +360,7 @@ function getTasksWithTaskDefinitions(cluster) { })); }) .then(function (taskDefs) { - console.log(`Found ${taskDefs.length} task definitions`); + console.log(`Found ${taskDefs.length} task definitions for cluster '${cluster}'`); // Fill in task details in tasksArray with taskDefinition details (e.g. memory, cpu) taskDefs.forEach(function (taskDef) { tasksArray @@ -347,8 +387,7 @@ function send400Response(errMsg, res) { function sendErrorResponse(err, res) { console.log(err); - console.log(err.stack.split("\n")); - res.status(500).send(err.message); + res.status(500).send(err.message || err); } function debugLog(msg) { diff --git a/test/testClusterState.js b/test/testClusterState.js new file mode 100644 index 0000000..92769d2 --- /dev/null +++ b/test/testClusterState.js @@ -0,0 +1,16 @@ +const ClusterState = require('../routes/clusterState'); +const FetchStatus = require("../routes/fetchStatus"); +const subject = new ClusterState(); +const assert = require('chai').assert; + +describe('ClusterState', function() { + describe('#constructor()', function() { + it('should set default values', function() { + const subject = new ClusterState("MyClusterName"); + assert.equal(subject.clusterName, "MyClusterName"); + assert.equal(subject.errorDetails, null); + assert.equal(subject.fetchStatus, FetchStatus.INITIAL); + assert.deepEqual(subject.instanceSummaries, {}); + }); + }); +}); diff --git a/test/testClusterStateCache.js b/test/testClusterStateCache.js new file mode 100644 index 0000000..501a6e3 --- /dev/null +++ b/test/testClusterStateCache.js @@ -0,0 +1,23 @@ +const subject = require('../routes/clusterStateCache'); +const assert = require('chai').assert; + +describe('ClusterStateCache', function() { + describe('#put()', function() { + it('should return null for missing cluster', function() { + assert.equal(subject.get("a"), null); + }); + // TODO: Solve subject.put() preventing test from stopping + // it('should store value', function() { + // subject.put("a", 123); + // assert.equal(subject.get("a"), 123); + // }); + }); + describe('#get()', function() { + it('should expire after TTL', function() { + subject.put("b", 123, 200); + setTimeout(() => { + assert.equal(subject.get("b"), null); + }, 400); + }); + }); +}); \ No newline at end of file diff --git a/test/testConfig.js b/test/testConfig.js new file mode 100644 index 0000000..9244ccb --- /dev/null +++ b/test/testConfig.js @@ -0,0 +1,17 @@ +const subject = require('../config/config'); +const assert = require('chai').assert; + +describe('config', function() { + it('should have default values', function() { + assert.equal(subject.port, 3000); + assert.equal(subject.clusterStateCacheTtl, 30 * 60 * 1000); + assert.equal(subject.aws.apiDelay, 100); + assert.equal(subject.aws.configFile, './aws_config.json'); + assert.equal(subject.aws.listInstancesPageSize, 100); + assert.equal(subject.aws.describeInstancesPageSize, 100); + assert.equal(subject.aws.listTasksPageSize, 100); + assert.equal(subject.aws.describeTasksPageSize, 100); + assert.equal(subject.aws.maxSimultaneousDescribeTasksCalls, 2); + assert.equal(subject.aws.maxSimultaneousDescribeTaskDefinitionCalls, 1); + }); +}); diff --git a/views/index.ejs b/views/index.ejs index 6113a2a..0069ada 100644 --- a/views/index.ejs +++ b/views/index.ejs @@ -5,14 +5,14 @@ <%= title %> - + - + @@ -20,7 +20,7 @@ From 43186af688783d820a86302e147600b0b2a5e00c Mon Sep 17 00:00:00 2001 From: mcallanan Date: Mon, 21 May 2018 23:22:07 +1000 Subject: [PATCH 09/16] Merge from master. --- package-lock.json | 108 +++++++++++++++++++++++++--------------------- 1 file changed, 59 insertions(+), 49 deletions(-) diff --git a/package-lock.json b/package-lock.json index f8af444..b523d20 100644 --- a/package-lock.json +++ b/package-lock.json @@ -9,7 +9,7 @@ "resolved": "https://registry.npmjs.org/accepts/-/accepts-1.2.13.tgz", "integrity": "sha1-5fHzkoxtlf2WVYw27D2dDeSm7Oo=", "requires": { - "mime-types": "2.1.17", + "mime-types": "~2.1.6", "negotiator": "0.5.3" } }, @@ -39,13 +39,13 @@ "resolved": "https://registry.npmjs.org/aws-sdk-promise/-/aws-sdk-promise-0.0.2.tgz", "integrity": "sha1-KIa9H8TEfry5r91sWXz0Jg+55eg=", "requires": { - "promise": "6.1.0" + "promise": "^6.1.0" } }, "base64-js": { "version": "1.2.1", "resolved": "https://registry.npmjs.org/base64-js/-/base64-js-1.2.1.tgz", - "integrity": "sha512-dwVUVIXsBZXwTuwnXI9RK8sBmgq09NDHzyR9SAph9eqk76gKK2JSQmZARC2zRC81JC2QTtxD0ARU5qTS25gIGw==" + "integrity": "sha1-qRlH2h9KUW6jjltOwOw3c2deCIY=" }, "basic-auth": { "version": "1.0.4", @@ -63,14 +63,14 @@ "integrity": "sha1-CQcAxLoohiqFIO83g5X97l9hwik=", "requires": { "bytes": "1.0.0", - "content-type": "1.0.4", - "debug": "2.2.0", - "depd": "1.0.1", + "content-type": "~1.0.1", + "debug": "~2.2.0", + "depd": "~1.0.1", "iconv-lite": "0.4.8", - "on-finished": "2.2.1", + "on-finished": "~2.2.1", "qs": "2.4.2", - "raw-body": "2.0.2", - "type-is": "1.6.15" + "raw-body": "~2.0.1", + "type-is": "~1.6.2" } }, "buffer": { @@ -78,8 +78,8 @@ "resolved": "https://registry.npmjs.org/buffer/-/buffer-5.0.6.tgz", "integrity": "sha1-LqZp9+7Atu2gWwj4tf9mGyhXNYg=", "requires": { - "base64-js": "1.2.1", - "ieee754": "1.1.8" + "base64-js": "^1.0.2", + "ieee754": "^1.1.4" } }, "bytes": { @@ -172,30 +172,30 @@ "resolved": "https://registry.npmjs.org/express/-/express-4.12.4.tgz", "integrity": "sha1-j+wlECVbxrLlgQfEgjnA+jB8GqI=", "requires": { - "accepts": "1.2.13", + "accepts": "~1.2.7", "content-disposition": "0.5.0", - "content-type": "1.0.4", + "content-type": "~1.0.1", "cookie": "0.1.2", "cookie-signature": "1.0.6", - "debug": "2.2.0", - "depd": "1.0.1", + "debug": "~2.2.0", + "depd": "~1.0.1", "escape-html": "1.0.1", - "etag": "1.6.0", + "etag": "~1.6.0", "finalhandler": "0.3.6", "fresh": "0.2.4", "merge-descriptors": "1.0.0", - "methods": "1.1.2", - "on-finished": "2.2.1", - "parseurl": "1.3.2", + "methods": "~1.1.1", + "on-finished": "~2.2.1", + "parseurl": "~1.3.0", "path-to-regexp": "0.1.3", - "proxy-addr": "1.0.10", + "proxy-addr": "~1.0.8", "qs": "2.4.2", - "range-parser": "1.0.3", + "range-parser": "~1.0.2", "send": "0.12.3", - "serve-static": "1.9.3", - "type-is": "1.6.15", + "serve-static": "~1.9.3", + "type-is": "~1.6.2", "utils-merge": "1.0.0", - "vary": "1.0.1" + "vary": "~1.0.0" }, "dependencies": { "cookie": { @@ -210,9 +210,9 @@ "resolved": "https://registry.npmjs.org/finalhandler/-/finalhandler-0.3.6.tgz", "integrity": "sha1-2vnEFhsbBuABRmsUEd/baXO+E4s=", "requires": { - "debug": "2.2.0", + "debug": "~2.2.0", "escape-html": "1.0.1", - "on-finished": "2.2.1" + "on-finished": "~2.2.1" } }, "forwarded": { @@ -246,15 +246,20 @@ "integrity": "sha1-o/Iiqarp+Wb10nx5ZRDigJF2Qhc=" }, "lodash": { - "version": "4.17.4", - "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.4.tgz", - "integrity": "sha1-eCA6TRwyiuHYbcpkYONptX9AVa4=" + "version": "4.17.10", + "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.10.tgz", + "integrity": "sha512-UejweD1pDoXu+AD825lWwp4ZGtSwgnpZxb3JDViD7StjQz+Nb/6l093lx4OQ0foGWNRoc19mWy7BzL+UAK2iVg==" }, "media-typer": { "version": "0.3.0", "resolved": "https://registry.npmjs.org/media-typer/-/media-typer-0.3.0.tgz", "integrity": "sha1-hxDXrwqmJvj/+hzgAWhUUmMlV0g=" }, + "memory-cache": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/memory-cache/-/memory-cache-0.2.0.tgz", + "integrity": "sha1-eJCwHVLADI68nVM+H46xfjA0hxo=" + }, "merge-descriptors": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/merge-descriptors/-/merge-descriptors-1.0.0.tgz", @@ -280,18 +285,23 @@ "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.17.tgz", "integrity": "sha1-Cdejk/A+mVp5+K+Fe3Cp4KsWVXo=", "requires": { - "mime-db": "1.30.0" + "mime-db": "~1.30.0" } }, + "moment": { + "version": "2.22.1", + "resolved": "https://registry.npmjs.org/moment/-/moment-2.22.1.tgz", + "integrity": "sha512-shJkRTSebXvsVqk56I+lkb2latjBs8I+pc2TzWc545y2iFnSjm7Wg0QMh+ZWcdSLQyGEau5jI8ocnmkyTgr9YQ==" + }, "morgan": { "version": "1.5.3", "resolved": "https://registry.npmjs.org/morgan/-/morgan-1.5.3.tgz", "integrity": "sha1-ittOcvnlxUNuXZP0KRCDX3nan98=", "requires": { - "basic-auth": "1.0.4", - "debug": "2.2.0", - "depd": "1.0.1", - "on-finished": "2.2.1" + "basic-auth": "~1.0.1", + "debug": "~2.2.0", + "depd": "~1.0.1", + "on-finished": "~2.2.1" } }, "ms": { @@ -332,7 +342,7 @@ "resolved": "https://registry.npmjs.org/promise/-/promise-6.1.0.tgz", "integrity": "sha1-LOcp9rlLRcJoka0GAsXJDgTG7vY=", "requires": { - "asap": "1.0.0" + "asap": "~1.0.0" } }, "proxy-addr": { @@ -340,7 +350,7 @@ "resolved": "https://registry.npmjs.org/proxy-addr/-/proxy-addr-1.0.10.tgz", "integrity": "sha1-DUCoL4Afw1VWfS7LZe/j8HfxIcU=", "requires": { - "forwarded": "0.1.2", + "forwarded": "~0.1.0", "ipaddr.js": "1.0.5" } }, @@ -390,16 +400,16 @@ "resolved": "https://registry.npmjs.org/send/-/send-0.12.3.tgz", "integrity": "sha1-zRLcWP3iHk+RkCs5sv2gWnptm9w=", "requires": { - "debug": "2.2.0", - "depd": "1.0.1", + "debug": "~2.2.0", + "depd": "~1.0.1", "destroy": "1.0.3", "escape-html": "1.0.1", - "etag": "1.6.0", + "etag": "~1.6.0", "fresh": "0.2.4", "mime": "1.3.4", "ms": "0.7.1", - "on-finished": "2.2.1", - "range-parser": "1.0.3" + "on-finished": "~2.2.1", + "range-parser": "~1.0.2" } }, "serve-favicon": { @@ -407,10 +417,10 @@ "resolved": "https://registry.npmjs.org/serve-favicon/-/serve-favicon-2.2.1.tgz", "integrity": "sha1-2XuswVD2b+DlzEx4qEuhW65aWEo=", "requires": { - "etag": "1.6.0", + "etag": "~1.6.0", "fresh": "0.2.4", "ms": "0.7.1", - "parseurl": "1.3.2" + "parseurl": "~1.3.0" } }, "serve-static": { @@ -419,7 +429,7 @@ "integrity": "sha1-X42gcyOtOF/z3FQfGnkXsuQ261c=", "requires": { "escape-html": "1.0.1", - "parseurl": "1.3.2", + "parseurl": "~1.3.0", "send": "0.12.3", "utils-merge": "1.0.0" } @@ -429,7 +439,7 @@ "resolved": "https://registry.npmjs.org/sleep/-/sleep-3.0.1.tgz", "integrity": "sha1-vk0XxXk2DgfgTtgXK6KxCmkFTfM=", "requires": { - "nan": "2.8.0" + "nan": ">=2.0.0" } }, "type-is": { @@ -438,7 +448,7 @@ "integrity": "sha1-yrEPtJCeRByChC6v4a1kbIGARBA=", "requires": { "media-typer": "0.3.0", - "mime-types": "2.1.17" + "mime-types": "~2.1.15" } }, "url": { @@ -470,8 +480,8 @@ "resolved": "https://registry.npmjs.org/xml2js/-/xml2js-0.4.17.tgz", "integrity": "sha1-F76T6q4/O3eTWceVtBlwWogX6Gg=", "requires": { - "sax": "1.2.1", - "xmlbuilder": "4.2.1" + "sax": ">=0.6.0", + "xmlbuilder": "^4.1.0" } }, "xmlbuilder": { @@ -479,7 +489,7 @@ "resolved": "https://registry.npmjs.org/xmlbuilder/-/xmlbuilder-4.2.1.tgz", "integrity": "sha1-qlijBBoGb5DqoWwvU4n/GfP0YaU=", "requires": { - "lodash": "4.17.4" + "lodash": "^4.0.0" } } } From d6e65c6bf3dae1fa2da1dbd0d89fca6c37390fc0 Mon Sep 17 00:00:00 2001 From: mcallanan Date: Mon, 21 May 2018 23:35:42 +1000 Subject: [PATCH 10/16] Upgraded packages per 'npm audit' recommendations. --- package-lock.json | 3447 ++++++++++++++++++++++++++++++++++++++++++--- package.json | 14 +- 2 files changed, 3222 insertions(+), 239 deletions(-) diff --git a/package-lock.json b/package-lock.json index b523d20..40cfaa5 100644 --- a/package-lock.json +++ b/package-lock.json @@ -4,20 +4,113 @@ "lockfileVersion": 1, "requires": true, "dependencies": { + "abbrev": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/abbrev/-/abbrev-1.1.1.tgz", + "integrity": "sha512-nne9/IiQ/hzIhY6pdDnbBtz7DjPTKrY00P/zvPSm5pOFkl6xuGrGnXn/VtTNNfNtAfZ9/1RtehkszU9qcTii0Q==", + "dev": true + }, "accepts": { - "version": "1.2.13", - "resolved": "https://registry.npmjs.org/accepts/-/accepts-1.2.13.tgz", - "integrity": "sha1-5fHzkoxtlf2WVYw27D2dDeSm7Oo=", + "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" + } + }, + "ansi-align": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ansi-align/-/ansi-align-2.0.0.tgz", + "integrity": "sha1-w2rsy6VjuJzrVW82kPCx2eNUf38=", + "dev": true, + "requires": { + "string-width": "^2.0.0" + } + }, + "ansi-regex": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-3.0.0.tgz", + "integrity": "sha1-7QMXwyIGT3lGbAKWa922Bas32Zg=", + "dev": true + }, + "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==", + "dev": true, + "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==", + "dev": true, "requires": { - "mime-types": "~2.1.6", - "negotiator": "0.5.3" + "micromatch": "^3.1.4", + "normalize-path": "^2.1.1" } }, + "arr-diff": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/arr-diff/-/arr-diff-4.0.0.tgz", + "integrity": "sha1-1kYQdP6/7HHn4VI1dhoyml3HxSA=", + "dev": true + }, + "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==", + "dev": true + }, + "arr-union": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/arr-union/-/arr-union-3.1.0.tgz", + "integrity": "sha1-45sJrqne+Gao8gbiiK9jkZuuOcQ=", + "dev": true + }, + "array-flatten": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/array-flatten/-/array-flatten-1.1.1.tgz", + "integrity": "sha1-ml9pkFGx5wczKPKgCJaLZOopVdI=" + }, + "array-unique": { + "version": "0.3.2", + "resolved": "https://registry.npmjs.org/array-unique/-/array-unique-0.3.2.tgz", + "integrity": "sha1-qJS3XUvE9s1nnvMkSp/Y9Gri1Cg=", + "dev": true + }, "asap": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/asap/-/asap-1.0.0.tgz", "integrity": "sha1-sqRdpf36ILBJb8N2jMJ8EvqRan0=" }, + "assertion-error": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/assertion-error/-/assertion-error-1.1.0.tgz", + "integrity": "sha512-jgsaNduz+ndvGyFt3uSuWqvy4lCnIJiovtouQN5JZHOKCS2QuhEdbcQHFhVksz2N2U9hXJo8odG7ETyWlEeuDw==", + "dev": true + }, + "assign-symbols": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/assign-symbols/-/assign-symbols-1.0.0.tgz", + "integrity": "sha1-WWZ/QfrdTyDMvCu5a41Pf3jsA2c=", + "dev": true + }, + "async-each": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/async-each/-/async-each-1.0.1.tgz", + "integrity": "sha1-GdOGodntxufByF04iu28xW0zYC0=", + "dev": true + }, + "atob": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/atob/-/atob-2.1.1.tgz", + "integrity": "sha1-ri1acpR38onWDdf5amMUoi3Wwio=", + "dev": true + }, "aws-sdk": { "version": "2.77.0", "resolved": "https://registry.npmjs.org/aws-sdk/-/aws-sdk-2.77.0.tgz", @@ -42,37 +135,178 @@ "promise": "^6.1.0" } }, + "balanced-match": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.0.tgz", + "integrity": "sha1-ibTRmasr7kneFk6gK4nORi1xt2c=", + "dev": true + }, + "base": { + "version": "0.11.2", + "resolved": "https://registry.npmjs.org/base/-/base-0.11.2.tgz", + "integrity": "sha512-5T6P4xPgpp0YDFvSWwEZ4NoE3aM4QBQXDzmVbraCkFj8zHM+mba8SyqB5DbZWyR7mYHo6Y7BdQo3MoA4m0TeQg==", + "dev": true, + "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=", + "dev": true, + "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==", + "dev": true, + "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==", + "dev": true, + "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==", + "dev": true, + "requires": { + "is-accessor-descriptor": "^1.0.0", + "is-data-descriptor": "^1.0.0", + "kind-of": "^6.0.2" + } + } + } + }, "base64-js": { "version": "1.2.1", "resolved": "https://registry.npmjs.org/base64-js/-/base64-js-1.2.1.tgz", "integrity": "sha1-qRlH2h9KUW6jjltOwOw3c2deCIY=" }, "basic-auth": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/basic-auth/-/basic-auth-1.0.4.tgz", - "integrity": "sha1-Awk1sB3nyblKgksp8/zLdQ06UpA=" + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/basic-auth/-/basic-auth-2.0.0.tgz", + "integrity": "sha1-AV2z81PgLlY3d1X5YnQuiYHnu7o=", + "requires": { + "safe-buffer": "5.1.1" + } }, "batch-promises": { "version": "0.0.3", "resolved": "https://registry.npmjs.org/batch-promises/-/batch-promises-0.0.3.tgz", "integrity": "sha1-BLhQ/mhHx6/wXzzKQQRZMkMLHDI=" }, + "binary-extensions": { + "version": "1.11.0", + "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-1.11.0.tgz", + "integrity": "sha1-RqoXUftqL5PuXmibsQh9SxTGwgU=", + "dev": true + }, "body-parser": { - "version": "1.12.4", - "resolved": "https://registry.npmjs.org/body-parser/-/body-parser-1.12.4.tgz", - "integrity": "sha1-CQcAxLoohiqFIO83g5X97l9hwik=", + "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": { + "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" + } + } + } + }, + "boxen": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/boxen/-/boxen-1.3.0.tgz", + "integrity": "sha512-TNPjfTr432qx7yOjQyaXm3dSR0MH9vXp7eT1BFSl/C51g+EFnOR9hTg1IreahGBmDNCehscshe45f+C1TBZbLw==", + "dev": true, "requires": { - "bytes": "1.0.0", - "content-type": "~1.0.1", - "debug": "~2.2.0", - "depd": "~1.0.1", - "iconv-lite": "0.4.8", - "on-finished": "~2.2.1", - "qs": "2.4.2", - "raw-body": "~2.0.1", - "type-is": "~1.6.2" + "ansi-align": "^2.0.0", + "camelcase": "^4.0.0", + "chalk": "^2.0.1", + "cli-boxes": "^1.0.0", + "string-width": "^2.0.0", + "term-size": "^1.2.0", + "widest-line": "^2.0.0" + } + }, + "brace-expansion": { + "version": "1.1.11", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", + "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", + "dev": true, + "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==", + "dev": true, + "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=", + "dev": true, + "requires": { + "is-extendable": "^0.1.0" + } + } } }, + "browser-stdout": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/browser-stdout/-/browser-stdout-1.3.1.tgz", + "integrity": "sha512-qhAVI1+Av2X7qelOfAIYwXONood6XlZE/fXaBSmW/T5SzLAmCgzi+eiWE7fUvbHaeNBQH13UftjpXxsfLkMpgw==", + "dev": true + }, "buffer": { "version": "5.0.6", "resolved": "https://registry.npmjs.org/buffer/-/buffer-5.0.6.tgz", @@ -83,14 +317,203 @@ } }, "bytes": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.0.0.tgz", + "integrity": "sha1-0ygVQE1olpn4Wk6k+odV3ROpYEg=" + }, + "cache-base": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/cache-base/-/cache-base-1.0.1.tgz", + "integrity": "sha512-AKcdTnFSWATd5/GCPRxr2ChwIJ85CeyrEyjRHlKxQ56d4XJMGym0uAiKn0xbLOGOl3+yRpOTi484dVCEc5AUzQ==", + "dev": true, + "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" + } + }, + "camelcase": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-4.1.0.tgz", + "integrity": "sha1-1UVjW+HjPFQmScaRc+Xeas+uNN0=", + "dev": true + }, + "capture-stack-trace": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/capture-stack-trace/-/capture-stack-trace-1.0.0.tgz", + "integrity": "sha1-Sm+gc5nCa7pH8LJJa00PtAjFVQ0=", + "dev": true + }, + "chai": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chai/-/chai-4.1.2.tgz", + "integrity": "sha1-D2RYS6ZC8PKs4oBiefTwbKI61zw=", + "dev": true, + "requires": { + "assertion-error": "^1.0.1", + "check-error": "^1.0.1", + "deep-eql": "^3.0.0", + "get-func-name": "^2.0.0", + "pathval": "^1.0.0", + "type-detect": "^4.0.0" + } + }, + "chalk": { + "version": "2.4.1", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.1.tgz", + "integrity": "sha512-ObN6h1v2fTJSmUXoS3nMQ92LbDK9be4TV+6G+omQlGJFdcUX5heKi1LZ1YnRMIgwTLEj3E24bT6tYni50rlCfQ==", + "dev": true, + "requires": { + "ansi-styles": "^3.2.1", + "escape-string-regexp": "^1.0.5", + "supports-color": "^5.3.0" + }, + "dependencies": { + "has-flag": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", + "integrity": "sha1-tdRU3CGZriJWmfNGfloH87lVuv0=", + "dev": true + }, + "supports-color": { + "version": "5.4.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.4.0.tgz", + "integrity": "sha512-zjaXglF5nnWpsq470jSv6P9DwPvgLkuapYmfDm3JWOm0vkNTVF2tI4UrN2r6jH1qM/uc/WtxYY1hYoA2dOKj5w==", + "dev": true, + "requires": { + "has-flag": "^3.0.0" + } + } + } + }, + "check-error": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/check-error/-/check-error-1.0.2.tgz", + "integrity": "sha1-V00xLt2Iu13YkS6Sht1sCu1KrII=", + "dev": true + }, + "chokidar": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-2.0.3.tgz", + "integrity": "sha512-zW8iXYZtXMx4kux/nuZVXjkLP+CyIK5Al5FHnj1OgTKGZfp4Oy6/ymtMSKFv3GD8DviEmUPmJg9eFdJ/JzudMg==", + "dev": true, + "requires": { + "anymatch": "^2.0.0", + "async-each": "^1.0.0", + "braces": "^2.3.0", + "fsevents": "^1.1.2", + "glob-parent": "^3.1.0", + "inherits": "^2.0.1", + "is-binary-path": "^1.0.0", + "is-glob": "^4.0.0", + "normalize-path": "^2.1.1", + "path-is-absolute": "^1.0.0", + "readdirp": "^2.0.0", + "upath": "^1.0.0" + } + }, + "ci-info": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/ci-info/-/ci-info-1.1.3.tgz", + "integrity": "sha512-SK/846h/Rcy8q9Z9CAwGBLfCJ6EkjJWdpelWDufQpqVDYq2Wnnv8zlSO6AMQap02jvhVruKKpEtQOufo3pFhLg==", + "dev": true + }, + "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==", + "dev": true, + "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=", + "dev": true, + "requires": { + "is-descriptor": "^0.1.0" + } + } + } + }, + "cli-boxes": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/cli-boxes/-/cli-boxes-1.0.0.tgz", + "integrity": "sha1-T6kXw+WclKAEzWH47lCdplFocUM=", + "dev": true + }, + "collection-visit": { "version": "1.0.0", - "resolved": "https://registry.npmjs.org/bytes/-/bytes-1.0.0.tgz", - "integrity": "sha1-NWnt6Lo0MV+rmcPpLLBMciDeH6g=" + "resolved": "https://registry.npmjs.org/collection-visit/-/collection-visit-1.0.0.tgz", + "integrity": "sha1-S8A3PBZLwykbTTaMgpzxqApZ3KA=", + "dev": true, + "requires": { + "map-visit": "^1.0.0", + "object-visit": "^1.0.0" + } + }, + "color-convert": { + "version": "1.9.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.1.tgz", + "integrity": "sha512-mjGanIiwQJskCC18rPR6OmrZ6fm2Lc7PeGFYwCmy5J34wC6F1PzdGL6xeMfmgicfYcNLGuVFA3WzXtIDCQSZxQ==", + "dev": true, + "requires": { + "color-name": "^1.1.1" + } + }, + "color-name": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", + "integrity": "sha1-p9BVi9icQveV3UIyj3QIMcpTvCU=", + "dev": true + }, + "commander": { + "version": "2.11.0", + "resolved": "https://registry.npmjs.org/commander/-/commander-2.11.0.tgz", + "integrity": "sha512-b0553uYA5YAEGgyYIGYROzKQ7X5RAqedkfjiZxwi0kL1g3bOaBNNZfYkzt/CL0umgD5wc9Jec2FbB98CjkMRvQ==", + "dev": true + }, + "component-emitter": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/component-emitter/-/component-emitter-1.2.1.tgz", + "integrity": "sha1-E3kY1teCg/ffemt8WmPhQOaUJeY=", + "dev": true + }, + "concat-map": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", + "integrity": "sha1-2Klr13/Wjfd5OnMDajug1UBdR3s=", + "dev": true + }, + "configstore": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/configstore/-/configstore-3.1.2.tgz", + "integrity": "sha512-vtv5HtGjcYUgFrXc6Kx747B83MRRVS5R1VTEQoXvuP+kMI+if6uywV0nDGoiydJRy4yk7h9od5Og0kxx4zUXmw==", + "dev": true, + "requires": { + "dot-prop": "^4.1.0", + "graceful-fs": "^4.1.2", + "make-dir": "^1.0.0", + "unique-string": "^1.0.0", + "write-file-atomic": "^2.0.0", + "xdg-basedir": "^3.0.0" + } }, "content-disposition": { - "version": "0.5.0", - "resolved": "https://registry.npmjs.org/content-disposition/-/content-disposition-0.5.0.tgz", - "integrity": "sha1-QoT+auBjCHRjnkToCkGMKTQTXp4=" + "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", @@ -116,226 +539,2009 @@ "resolved": "https://registry.npmjs.org/cookie-signature/-/cookie-signature-1.0.6.tgz", "integrity": "sha1-4wOogrNCzD7oylE6eZmXNNqzriw=" }, - "crc": { - "version": "3.2.1", - "resolved": "https://registry.npmjs.org/crc/-/crc-3.2.1.tgz", - "integrity": "sha1-XZyPt3okXNXsopHl0tAFM0urAII=" + "copy-descriptor": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/copy-descriptor/-/copy-descriptor-0.1.1.tgz", + "integrity": "sha1-Z29us8OZl8LuGsOpJP1hJHSPV40=", + "dev": true + }, + "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=", + "dev": true + }, + "create-error-class": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/create-error-class/-/create-error-class-3.0.2.tgz", + "integrity": "sha1-Br56vvlHo/FKMP1hBnHUAbyot7Y=", + "dev": true, + "requires": { + "capture-stack-trace": "^1.0.0" + } + }, + "cross-spawn": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-5.1.0.tgz", + "integrity": "sha1-6L0O/uWPz/b4+UUQoKVUu/ojVEk=", + "dev": true, + "requires": { + "lru-cache": "^4.0.1", + "shebang-command": "^1.2.0", + "which": "^1.2.9" + } }, "crypto-browserify": { "version": "1.0.9", "resolved": "https://registry.npmjs.org/crypto-browserify/-/crypto-browserify-1.0.9.tgz", "integrity": "sha1-zFRJaF37hesRyYKKzHy4erW7/MA=" }, + "crypto-random-string": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/crypto-random-string/-/crypto-random-string-1.0.0.tgz", + "integrity": "sha1-ojD2T1aDEOFJgAmUB5DsmVRbyn4=", + "dev": true + }, "debug": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/debug/-/debug-2.2.0.tgz", - "integrity": "sha1-+HBX6ZWxofauaklgZkE3vFbwOdo=", + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/debug/-/debug-3.1.0.tgz", + "integrity": "sha512-OX8XqP7/1a9cqkxYw2yXss15f26NKWBpDXQd0/uK/KPqdQhxbPa994hnzjcE2VqQpDslf55723cKPUOGSmMY3g==", + "requires": { + "ms": "2.0.0" + } + }, + "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=", + "dev": true + }, + "deep-eql": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/deep-eql/-/deep-eql-3.0.1.tgz", + "integrity": "sha512-+QeIQyN5ZuO+3Uk5DYh6/1eKO0m0YmJFGNmFHGACpf1ClL1nmlV/p4gNgbl2pJGxgXb4faqo6UE+M5ACEMyVcw==", + "dev": true, "requires": { - "ms": "0.7.1" + "type-detect": "^4.0.0" + } + }, + "deep-extend": { + "version": "0.5.1", + "resolved": "https://registry.npmjs.org/deep-extend/-/deep-extend-0.5.1.tgz", + "integrity": "sha512-N8vBdOa+DF7zkRrDCsaOXoCs/E2fJfx9B9MrKnnSiHNh4ws7eSys6YQE4KvT1cecKmOASYQBhbKjeuDD9lT81w==", + "dev": true + }, + "define-property": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/define-property/-/define-property-2.0.2.tgz", + "integrity": "sha512-jwK2UV4cnPpbcG7+VRARKTZPUWowwXA8bzH5NP6ud0oeAxyYPuGZUAC7hMugpCdz4BeSZl2Dl9k66CHJ/46ZYQ==", + "dev": true, + "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==", + "dev": true, + "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==", + "dev": true, + "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==", + "dev": true, + "requires": { + "is-accessor-descriptor": "^1.0.0", + "is-data-descriptor": "^1.0.0", + "kind-of": "^6.0.2" + } + } } }, "depd": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/depd/-/depd-1.0.1.tgz", - "integrity": "sha1-gK7GTJ1tl+ZcwqnKqTwKpqv3Oqo=" + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/depd/-/depd-1.1.2.tgz", + "integrity": "sha1-m81S4UwJd2PnSbJ0xDRu0uVgtak=" }, "destroy": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/destroy/-/destroy-1.0.3.tgz", - "integrity": "sha1-tDO0ck5x/YVR2YhRdIUcX8N34sk=" + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/destroy/-/destroy-1.0.4.tgz", + "integrity": "sha1-l4hXRCxEdJ5CBmE+N5RiBYJqvYA=" + }, + "diff": { + "version": "3.5.0", + "resolved": "https://registry.npmjs.org/diff/-/diff-3.5.0.tgz", + "integrity": "sha512-A46qtFgd+g7pDZinpnwiRJtxbC1hpgf0uzP3iG89scHk0AUC7A1TGxf5OiiOUv/JMZR8GOt8hL900hV0bOy5xA==", + "dev": true + }, + "dot-prop": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/dot-prop/-/dot-prop-4.2.0.tgz", + "integrity": "sha512-tUMXrxlExSW6U2EXiiKGSBVdYgtV8qlHL+C10TsW4PURY/ic+eaysnSkwB4kA/mBlCyy/IKDJ+Lc3wbWeaXtuQ==", + "dev": true, + "requires": { + "is-obj": "^1.0.0" + } + }, + "duplexer": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/duplexer/-/duplexer-0.1.1.tgz", + "integrity": "sha1-rOb/gIwc5mtX0ev5eXessCM0z8E=", + "dev": true + }, + "duplexer3": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/duplexer3/-/duplexer3-0.1.4.tgz", + "integrity": "sha1-7gHdHKwO08vH/b6jfcCo8c4ALOI=", + "dev": true }, "ee-first": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/ee-first/-/ee-first-1.1.0.tgz", - "integrity": "sha1-ag18YiHkkP7v2S7D9EHJzozQl/Q=" + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/ee-first/-/ee-first-1.1.1.tgz", + "integrity": "sha1-WQxhFWsK4vTwJVcyoViyZrxWsh0=" }, "ejs": { "version": "2.5.5", "resolved": "https://registry.npmjs.org/ejs/-/ejs-2.5.5.tgz", "integrity": "sha1-bvTpVOp9z1T2aq0v56pCGTLZ7Xc=" }, + "encodeurl": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-1.0.2.tgz", + "integrity": "sha1-rT/0yG7C0CkyL1oCw6mmBslbP1k=" + }, "escape-html": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/escape-html/-/escape-html-1.0.1.tgz", - "integrity": "sha1-GBoobq05ejmpKFfPsdQwUuNWv/A=" + "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=", + "dev": true }, "etag": { - "version": "1.6.0", - "resolved": "https://registry.npmjs.org/etag/-/etag-1.6.0.tgz", - "integrity": "sha1-i8ssavElTEgd/IuZfJBu9ORCwgc=", + "version": "1.8.1", + "resolved": "https://registry.npmjs.org/etag/-/etag-1.8.1.tgz", + "integrity": "sha1-Qa4u62XvpiJorr/qg6x9eSmbCIc=" + }, + "event-stream": { + "version": "3.3.4", + "resolved": "http://registry.npmjs.org/event-stream/-/event-stream-3.3.4.tgz", + "integrity": "sha1-SrTJoPWlTbkzi0w02Gv86PSzVXE=", + "dev": true, + "requires": { + "duplexer": "~0.1.1", + "from": "~0", + "map-stream": "~0.1.0", + "pause-stream": "0.0.11", + "split": "0.3", + "stream-combiner": "~0.0.4", + "through": "~2.3.1" + } + }, + "execa": { + "version": "0.7.0", + "resolved": "https://registry.npmjs.org/execa/-/execa-0.7.0.tgz", + "integrity": "sha1-lEvs00zEHuMqY6n68nrVpl/Fl3c=", + "dev": true, + "requires": { + "cross-spawn": "^5.0.1", + "get-stream": "^3.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" + } + }, + "expand-brackets": { + "version": "2.1.4", + "resolved": "https://registry.npmjs.org/expand-brackets/-/expand-brackets-2.1.4.tgz", + "integrity": "sha1-t3c14xXOMPa27/D4OwQVGiJEliI=", + "dev": true, "requires": { - "crc": "3.2.1" + "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": { + "debug": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "dev": true, + "requires": { + "ms": "2.0.0" + } + }, + "define-property": { + "version": "0.2.5", + "resolved": "https://registry.npmjs.org/define-property/-/define-property-0.2.5.tgz", + "integrity": "sha1-w1se+RjsPJkPmlvFe+BKrOxcgRY=", + "dev": true, + "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=", + "dev": true, + "requires": { + "is-extendable": "^0.1.0" + } + } } }, "express": { - "version": "4.12.4", - "resolved": "https://registry.npmjs.org/express/-/express-4.12.4.tgz", - "integrity": "sha1-j+wlECVbxrLlgQfEgjnA+jB8GqI=", - "requires": { - "accepts": "~1.2.7", - "content-disposition": "0.5.0", - "content-type": "~1.0.1", - "cookie": "0.1.2", + "version": "4.16.3", + "resolved": "https://registry.npmjs.org/express/-/express-4.16.3.tgz", + "integrity": "sha1-avilAjUNsyRuzEvs9rWjTSL37VM=", + "requires": { + "accepts": "~1.3.5", + "array-flatten": "1.1.1", + "body-parser": "1.18.2", + "content-disposition": "0.5.2", + "content-type": "~1.0.4", + "cookie": "0.3.1", "cookie-signature": "1.0.6", - "debug": "~2.2.0", - "depd": "~1.0.1", - "escape-html": "1.0.1", - "etag": "~1.6.0", - "finalhandler": "0.3.6", - "fresh": "0.2.4", - "merge-descriptors": "1.0.0", - "methods": "~1.1.1", - "on-finished": "~2.2.1", - "parseurl": "~1.3.0", - "path-to-regexp": "0.1.3", - "proxy-addr": "~1.0.8", - "qs": "2.4.2", - "range-parser": "~1.0.2", - "send": "0.12.3", - "serve-static": "~1.9.3", - "type-is": "~1.6.2", - "utils-merge": "1.0.0", - "vary": "~1.0.0" + "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.3", + "qs": "6.5.1", + "range-parser": "~1.2.0", + "safe-buffer": "5.1.1", + "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": { + "body-parser": { + "version": "1.18.2", + "resolved": "https://registry.npmjs.org/body-parser/-/body-parser-1.18.2.tgz", + "integrity": "sha1-h2eKGdhLR9hZuDGZvVm84iKxBFQ=", + "requires": { + "bytes": "3.0.0", + "content-type": "~1.0.4", + "debug": "2.6.9", + "depd": "~1.1.1", + "http-errors": "~1.6.2", + "iconv-lite": "0.4.19", + "on-finished": "~2.3.0", + "qs": "6.5.1", + "raw-body": "2.3.2", + "type-is": "~1.6.15" + } + }, "cookie": { - "version": "0.1.2", - "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.1.2.tgz", - "integrity": "sha1-cv7D0k5Io0Mgc9kMEmQgBQYQBLE=" + "version": "0.3.1", + "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.3.1.tgz", + "integrity": "sha1-5+Ch+e9DtMi6klxcWpboBtFoc7s=" + }, + "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" + } + }, + "iconv-lite": { + "version": "0.4.19", + "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.19.tgz", + "integrity": "sha512-oTZqweIP51xaGPI4uPa56/Pri/480R+mo7SeU+YETByQNhDG55ycFyNLIgta9vXhILrxXDmF7ZGhqZIcuN0gJQ==" + }, + "qs": { + "version": "6.5.1", + "resolved": "https://registry.npmjs.org/qs/-/qs-6.5.1.tgz", + "integrity": "sha512-eRzhrN1WSINYCDCbrz796z37LOe3m5tmW7RQf6oBntukAG1nmovJvhnwHHRMAfeoItc1m2Hk02WER2aQ/iqs+A==" + }, + "raw-body": { + "version": "2.3.2", + "resolved": "https://registry.npmjs.org/raw-body/-/raw-body-2.3.2.tgz", + "integrity": "sha1-vNYMd9Prk83gBQKVw/N5OJvIj4k=", + "requires": { + "bytes": "3.0.0", + "http-errors": "1.6.2", + "iconv-lite": "0.4.19", + "unpipe": "1.0.0" + }, + "dependencies": { + "depd": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/depd/-/depd-1.1.1.tgz", + "integrity": "sha1-V4O04cRZ8G+lyif5kfPQbnoxA1k=" + }, + "http-errors": { + "version": "1.6.2", + "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-1.6.2.tgz", + "integrity": "sha1-CgAsyFcHGSp+eUbO7cERVfYOxzY=", + "requires": { + "depd": "1.1.1", + "inherits": "2.0.3", + "setprototypeof": "1.0.3", + "statuses": ">= 1.3.1 < 2" + } + }, + "setprototypeof": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.0.3.tgz", + "integrity": "sha1-ZlZ+NwQ+608E2RvWWMDL77VbjgQ=" + } + } + }, + "statuses": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/statuses/-/statuses-1.4.0.tgz", + "integrity": "sha512-zhSCtt8v2NDrRlPQpCNtw/heZLtfUDqxBM1udqikb/Hbk52LK4nQSwr10u77iopCW5LsyHpuXS0GnEc48mLeew==" + } + } + }, + "extend-shallow": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-3.0.2.tgz", + "integrity": "sha1-Jqcarwc7OfshJxcnRhMcJwQCjbg=", + "dev": true, + "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==", + "dev": true, + "requires": { + "is-plain-object": "^2.0.4" + } + } + } + }, + "extglob": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/extglob/-/extglob-2.0.4.tgz", + "integrity": "sha512-Nmb6QXkELsuBr24CJSkilo6UHHgbekK5UiZgfE6UHD3Eb27YC6oD+bhcT+tJ6cl8dmsgdQxnWlcry8ksBIBLpw==", + "dev": true, + "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=", + "dev": true, + "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=", + "dev": true, + "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==", + "dev": true, + "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==", + "dev": true, + "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==", + "dev": true, + "requires": { + "is-accessor-descriptor": "^1.0.0", + "is-data-descriptor": "^1.0.0", + "kind-of": "^6.0.2" + } + } + } + }, + "fill-range": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-4.0.0.tgz", + "integrity": "sha1-1USBHUKPmOsGpj3EAtJAPDKMOPc=", + "dev": true, + "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=", + "dev": true, + "requires": { + "is-extendable": "^0.1.0" + } } } }, "finalhandler": { - "version": "0.3.6", - "resolved": "https://registry.npmjs.org/finalhandler/-/finalhandler-0.3.6.tgz", - "integrity": "sha1-2vnEFhsbBuABRmsUEd/baXO+E4s=", + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/finalhandler/-/finalhandler-1.1.1.tgz", + "integrity": "sha512-Y1GUDo39ez4aHAw7MysnUD5JzYX+WaIj8I57kO3aEPT1fFRL4sr7mjei97FgnwhAyyzRYmQZaTHb2+9uZ1dPtg==", "requires": { - "debug": "~2.2.0", - "escape-html": "1.0.1", - "on-finished": "~2.2.1" + "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" + }, + "dependencies": { + "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" + } + }, + "statuses": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/statuses/-/statuses-1.4.0.tgz", + "integrity": "sha512-zhSCtt8v2NDrRlPQpCNtw/heZLtfUDqxBM1udqikb/Hbk52LK4nQSwr10u77iopCW5LsyHpuXS0GnEc48mLeew==" + } } }, + "for-in": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/for-in/-/for-in-1.0.2.tgz", + "integrity": "sha1-gQaNKVqBQuwKxybG4iAMMPttXoA=", + "dev": true + }, "forwarded": { "version": "0.1.2", "resolved": "https://registry.npmjs.org/forwarded/-/forwarded-0.1.2.tgz", "integrity": "sha1-mMI9qxF1ZXuMBXPozszZGw/xjIQ=" }, - "fresh": { - "version": "0.2.4", - "resolved": "https://registry.npmjs.org/fresh/-/fresh-0.2.4.tgz", - "integrity": "sha1-NYJJkgbJcjcUGQ7ddLRgT+tKYUw=" + "fragment-cache": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/fragment-cache/-/fragment-cache-0.2.1.tgz", + "integrity": "sha1-QpD60n8T6Jvn8zeZxrxaCr//DRk=", + "dev": true, + "requires": { + "map-cache": "^0.2.2" + } }, - "iconv-lite": { - "version": "0.4.8", - "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.8.tgz", - "integrity": "sha1-xgGadZXyzvynAuq2lKAQvNkpjSA=" + "fresh": { + "version": "0.5.2", + "resolved": "https://registry.npmjs.org/fresh/-/fresh-0.5.2.tgz", + "integrity": "sha1-PYyt2Q2XZWn6g1qx+OSyOhBWBac=" }, - "ieee754": { - "version": "1.1.8", - "resolved": "https://registry.npmjs.org/ieee754/-/ieee754-1.1.8.tgz", - "integrity": "sha1-vjPUCsEO8ZJnAfbwii2G+/0a0+Q=" + "from": { + "version": "0.1.7", + "resolved": "https://registry.npmjs.org/from/-/from-0.1.7.tgz", + "integrity": "sha1-g8YK/Fi5xWmXAH7Rp2izqzA6RP4=", + "dev": true }, - "ipaddr.js": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-1.0.5.tgz", - "integrity": "sha1-X6eM8wG4JceKvDBC2BJyMEnqI8c=" + "fs.realpath": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", + "integrity": "sha1-FQStJSMVjKpA20onh8sBQRmU6k8=", + "dev": true + }, + "fsevents": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-1.2.4.tgz", + "integrity": "sha512-z8H8/diyk76B7q5wg+Ud0+CqzcAF3mBBI/bA5ne5zrRUUIvNkJY//D3BqyH571KuAC4Nr7Rw7CjWX4r0y9DvNg==", + "dev": true, + "optional": true, + "requires": { + "nan": "^2.9.2", + "node-pre-gyp": "^0.10.0" + }, + "dependencies": { + "abbrev": { + "version": "1.1.1", + "bundled": true, + "dev": true, + "optional": true + }, + "ansi-regex": { + "version": "2.1.1", + "bundled": true, + "dev": true + }, + "aproba": { + "version": "1.2.0", + "bundled": true, + "dev": true, + "optional": true + }, + "are-we-there-yet": { + "version": "1.1.4", + "bundled": true, + "dev": true, + "optional": true, + "requires": { + "delegates": "^1.0.0", + "readable-stream": "^2.0.6" + } + }, + "balanced-match": { + "version": "1.0.0", + "bundled": true, + "dev": true + }, + "brace-expansion": { + "version": "1.1.11", + "bundled": true, + "dev": true, + "requires": { + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" + } + }, + "chownr": { + "version": "1.0.1", + "bundled": true, + "dev": true, + "optional": true + }, + "code-point-at": { + "version": "1.1.0", + "bundled": true, + "dev": true + }, + "concat-map": { + "version": "0.0.1", + "bundled": true, + "dev": true + }, + "console-control-strings": { + "version": "1.1.0", + "bundled": true, + "dev": true + }, + "core-util-is": { + "version": "1.0.2", + "bundled": true, + "dev": true, + "optional": true + }, + "debug": { + "version": "2.6.9", + "bundled": true, + "dev": true, + "optional": true, + "requires": { + "ms": "2.0.0" + } + }, + "deep-extend": { + "version": "0.5.1", + "bundled": true, + "dev": true, + "optional": true + }, + "delegates": { + "version": "1.0.0", + "bundled": true, + "dev": true, + "optional": true + }, + "detect-libc": { + "version": "1.0.3", + "bundled": true, + "dev": true, + "optional": true + }, + "fs-minipass": { + "version": "1.2.5", + "bundled": true, + "dev": true, + "optional": true, + "requires": { + "minipass": "^2.2.1" + } + }, + "fs.realpath": { + "version": "1.0.0", + "bundled": true, + "dev": true, + "optional": true + }, + "gauge": { + "version": "2.7.4", + "bundled": true, + "dev": 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.2", + "bundled": true, + "dev": 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, + "dev": true, + "optional": true + }, + "iconv-lite": { + "version": "0.4.21", + "bundled": true, + "dev": true, + "optional": true, + "requires": { + "safer-buffer": "^2.1.0" + } + }, + "ignore-walk": { + "version": "3.0.1", + "bundled": true, + "dev": true, + "optional": true, + "requires": { + "minimatch": "^3.0.4" + } + }, + "inflight": { + "version": "1.0.6", + "bundled": true, + "dev": true, + "optional": true, + "requires": { + "once": "^1.3.0", + "wrappy": "1" + } + }, + "inherits": { + "version": "2.0.3", + "bundled": true, + "dev": true + }, + "ini": { + "version": "1.3.5", + "bundled": true, + "dev": true, + "optional": true + }, + "is-fullwidth-code-point": { + "version": "1.0.0", + "bundled": true, + "dev": true, + "requires": { + "number-is-nan": "^1.0.0" + } + }, + "isarray": { + "version": "1.0.0", + "bundled": true, + "dev": true, + "optional": true + }, + "minimatch": { + "version": "3.0.4", + "bundled": true, + "dev": true, + "requires": { + "brace-expansion": "^1.1.7" + } + }, + "minimist": { + "version": "0.0.8", + "bundled": true, + "dev": true + }, + "minipass": { + "version": "2.2.4", + "bundled": true, + "dev": true, + "requires": { + "safe-buffer": "^5.1.1", + "yallist": "^3.0.0" + } + }, + "minizlib": { + "version": "1.1.0", + "bundled": true, + "dev": true, + "optional": true, + "requires": { + "minipass": "^2.2.1" + } + }, + "mkdirp": { + "version": "0.5.1", + "bundled": true, + "dev": true, + "requires": { + "minimist": "0.0.8" + } + }, + "ms": { + "version": "2.0.0", + "bundled": true, + "dev": true, + "optional": true + }, + "nan": { + "version": "2.10.0", + "resolved": "https://registry.npmjs.org/nan/-/nan-2.10.0.tgz", + "integrity": "sha512-bAdJv7fBLhWC+/Bls0Oza+mvTaNQtP+1RyhhhvD95pgUJz6XM5IzgmxOkItJ9tkoCiplvAnXI1tNmmUD/eScyA==", + "dev": true, + "optional": true + }, + "needle": { + "version": "2.2.0", + "bundled": true, + "dev": true, + "optional": true, + "requires": { + "debug": "^2.1.2", + "iconv-lite": "^0.4.4", + "sax": "^1.2.4" + } + }, + "node-pre-gyp": { + "version": "0.10.0", + "bundled": true, + "dev": true, + "optional": true, + "requires": { + "detect-libc": "^1.0.2", + "mkdirp": "^0.5.1", + "needle": "^2.2.0", + "nopt": "^4.0.1", + "npm-packlist": "^1.1.6", + "npmlog": "^4.0.2", + "rc": "^1.1.7", + "rimraf": "^2.6.1", + "semver": "^5.3.0", + "tar": "^4" + } + }, + "nopt": { + "version": "4.0.1", + "bundled": true, + "dev": true, + "optional": true, + "requires": { + "abbrev": "1", + "osenv": "^0.1.4" + } + }, + "npm-bundled": { + "version": "1.0.3", + "bundled": true, + "dev": true, + "optional": true + }, + "npm-packlist": { + "version": "1.1.10", + "bundled": true, + "dev": true, + "optional": true, + "requires": { + "ignore-walk": "^3.0.1", + "npm-bundled": "^1.0.1" + } + }, + "npmlog": { + "version": "4.1.2", + "bundled": true, + "dev": 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, + "dev": true + }, + "object-assign": { + "version": "4.1.1", + "bundled": true, + "dev": true, + "optional": true + }, + "once": { + "version": "1.4.0", + "bundled": true, + "dev": true, + "requires": { + "wrappy": "1" + } + }, + "os-homedir": { + "version": "1.0.2", + "bundled": true, + "dev": true, + "optional": true + }, + "os-tmpdir": { + "version": "1.0.2", + "bundled": true, + "dev": true, + "optional": true + }, + "osenv": { + "version": "0.1.5", + "bundled": true, + "dev": true, + "optional": true, + "requires": { + "os-homedir": "^1.0.0", + "os-tmpdir": "^1.0.0" + } + }, + "path-is-absolute": { + "version": "1.0.1", + "bundled": true, + "dev": true, + "optional": true + }, + "process-nextick-args": { + "version": "2.0.0", + "bundled": true, + "dev": true, + "optional": true + }, + "rc": { + "version": "1.2.7", + "bundled": true, + "dev": true, + "optional": true, + "requires": { + "deep-extend": "^0.5.1", + "ini": "~1.3.0", + "minimist": "^1.2.0", + "strip-json-comments": "~2.0.1" + }, + "dependencies": { + "minimist": { + "version": "1.2.0", + "bundled": true, + "dev": true, + "optional": true + } + } + }, + "readable-stream": { + "version": "2.3.6", + "bundled": true, + "dev": 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.2", + "bundled": true, + "dev": true, + "optional": true, + "requires": { + "glob": "^7.0.5" + } + }, + "safe-buffer": { + "version": "5.1.1", + "bundled": true, + "dev": true + }, + "safer-buffer": { + "version": "2.1.2", + "bundled": true, + "dev": true, + "optional": true + }, + "sax": { + "version": "1.2.4", + "bundled": true, + "dev": true, + "optional": true + }, + "semver": { + "version": "5.5.0", + "bundled": true, + "dev": true, + "optional": true + }, + "set-blocking": { + "version": "2.0.0", + "bundled": true, + "dev": true, + "optional": true + }, + "signal-exit": { + "version": "3.0.2", + "bundled": true, + "dev": true, + "optional": true + }, + "string-width": { + "version": "1.0.2", + "bundled": true, + "dev": 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, + "dev": true, + "optional": true, + "requires": { + "safe-buffer": "~5.1.0" + } + }, + "strip-ansi": { + "version": "3.0.1", + "bundled": true, + "dev": true, + "requires": { + "ansi-regex": "^2.0.0" + } + }, + "strip-json-comments": { + "version": "2.0.1", + "bundled": true, + "dev": true, + "optional": true + }, + "tar": { + "version": "4.4.1", + "bundled": true, + "dev": true, + "optional": true, + "requires": { + "chownr": "^1.0.1", + "fs-minipass": "^1.2.5", + "minipass": "^2.2.4", + "minizlib": "^1.1.0", + "mkdirp": "^0.5.0", + "safe-buffer": "^5.1.1", + "yallist": "^3.0.2" + } + }, + "util-deprecate": { + "version": "1.0.2", + "bundled": true, + "dev": true, + "optional": true + }, + "wide-align": { + "version": "1.1.2", + "bundled": true, + "dev": true, + "optional": true, + "requires": { + "string-width": "^1.0.2" + } + }, + "wrappy": { + "version": "1.0.2", + "bundled": true, + "dev": true + }, + "yallist": { + "version": "3.0.2", + "bundled": true, + "dev": true + } + } }, - "jmespath": { - "version": "0.15.0", - "resolved": "https://registry.npmjs.org/jmespath/-/jmespath-0.15.0.tgz", - "integrity": "sha1-o/Iiqarp+Wb10nx5ZRDigJF2Qhc=" + "get-func-name": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/get-func-name/-/get-func-name-2.0.0.tgz", + "integrity": "sha1-6td0q+5y4gQJQzoGY2YCPdaIekE=", + "dev": true + }, + "get-stream": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-3.0.0.tgz", + "integrity": "sha1-jpQ9E1jcN1VQVOy+LtsFqhdO3hQ=", + "dev": true + }, + "get-value": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/get-value/-/get-value-2.0.6.tgz", + "integrity": "sha1-3BXKHGcjh8p2vTesCjlbogQqLCg=", + "dev": true + }, + "glob": { + "version": "7.1.2", + "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.2.tgz", + "integrity": "sha512-MJTUg1kjuLeQCJ+ccE4Vpa6kKVXkPYJ2mOCQyUuKLcLQsdrMCpBPUi8qVE6+YuaJkozeA9NusTAw3hLr8Xe5EQ==", + "dev": 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" + } }, - "lodash": { - "version": "4.17.10", - "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.10.tgz", - "integrity": "sha512-UejweD1pDoXu+AD825lWwp4ZGtSwgnpZxb3JDViD7StjQz+Nb/6l093lx4OQ0foGWNRoc19mWy7BzL+UAK2iVg==" + "glob-parent": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-3.1.0.tgz", + "integrity": "sha1-nmr2KZ2NO9K9QEMIMr0RPfkGxa4=", + "dev": true, + "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=", + "dev": true, + "requires": { + "is-extglob": "^2.1.0" + } + } + } }, - "media-typer": { - "version": "0.3.0", - "resolved": "https://registry.npmjs.org/media-typer/-/media-typer-0.3.0.tgz", - "integrity": "sha1-hxDXrwqmJvj/+hzgAWhUUmMlV0g=" + "global-dirs": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/global-dirs/-/global-dirs-0.1.1.tgz", + "integrity": "sha1-sxnA3UYH81PzvpzKTHL8FIxJ9EU=", + "dev": true, + "requires": { + "ini": "^1.3.4" + } }, - "memory-cache": { - "version": "0.2.0", - "resolved": "https://registry.npmjs.org/memory-cache/-/memory-cache-0.2.0.tgz", - "integrity": "sha1-eJCwHVLADI68nVM+H46xfjA0hxo=" + "got": { + "version": "6.7.1", + "resolved": "https://registry.npmjs.org/got/-/got-6.7.1.tgz", + "integrity": "sha1-JAzQV4WpoY5WHcG0S0HHY+8ejbA=", + "dev": true, + "requires": { + "create-error-class": "^3.0.0", + "duplexer3": "^0.1.4", + "get-stream": "^3.0.0", + "is-redirect": "^1.0.0", + "is-retry-allowed": "^1.0.0", + "is-stream": "^1.0.0", + "lowercase-keys": "^1.0.0", + "safe-buffer": "^5.0.1", + "timed-out": "^4.0.0", + "unzip-response": "^2.0.1", + "url-parse-lax": "^1.0.0" + } }, - "merge-descriptors": { + "graceful-fs": { + "version": "4.1.11", + "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.1.11.tgz", + "integrity": "sha1-Dovf5NHduIVNZOBOp8AOKgJuVlg=", + "dev": true + }, + "growl": { + "version": "1.10.3", + "resolved": "https://registry.npmjs.org/growl/-/growl-1.10.3.tgz", + "integrity": "sha512-hKlsbA5Vu3xsh1Cg3J7jSmX/WaW6A5oBeqzM88oNbCRQFz+zUaXm6yxS4RVytp1scBoJzSYl4YAEOQIt6O8V1Q==", + "dev": true + }, + "has-flag": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-2.0.0.tgz", + "integrity": "sha1-6CB68cx7MNRGzHC3NLXovhj4jVE=", + "dev": true + }, + "has-value": { "version": "1.0.0", - "resolved": "https://registry.npmjs.org/merge-descriptors/-/merge-descriptors-1.0.0.tgz", - "integrity": "sha1-IWnPdTjhsMyH+4jhUC2EdLv3mGQ=" + "resolved": "https://registry.npmjs.org/has-value/-/has-value-1.0.0.tgz", + "integrity": "sha1-GLKB2lhbHFxR3vJMkw7SmgvmsXc=", + "dev": true, + "requires": { + "get-value": "^2.0.6", + "has-values": "^1.0.0", + "isobject": "^3.0.0" + } }, - "methods": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/methods/-/methods-1.1.2.tgz", - "integrity": "sha1-VSmk1nZUE07cxSZmVoNbD4Ua/O4=" + "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=", + "dev": true, + "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=", + "dev": true, + "requires": { + "is-buffer": "^1.1.5" + } + } + } }, - "mime": { - "version": "1.3.4", - "resolved": "https://registry.npmjs.org/mime/-/mime-1.3.4.tgz", - "integrity": "sha1-EV+eO2s9rylZmDyzjxSaLUDrXVM=" + "he": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/he/-/he-1.1.1.tgz", + "integrity": "sha1-k0EP0hsAlzUVH4howvJx80J+I/0=", + "dev": true }, - "mime-db": { - "version": "1.30.0", - "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.30.0.tgz", - "integrity": "sha1-dMZD2i3Z1qRTmZY0ZbJtXKfXHwE=" + "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" + } }, - "mime-types": { - "version": "2.1.17", - "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.17.tgz", - "integrity": "sha1-Cdejk/A+mVp5+K+Fe3Cp4KsWVXo=", + "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": { - "mime-db": "~1.30.0" + "safer-buffer": ">= 2.1.2 < 3" } }, - "moment": { - "version": "2.22.1", - "resolved": "https://registry.npmjs.org/moment/-/moment-2.22.1.tgz", - "integrity": "sha512-shJkRTSebXvsVqk56I+lkb2latjBs8I+pc2TzWc545y2iFnSjm7Wg0QMh+ZWcdSLQyGEau5jI8ocnmkyTgr9YQ==" + "ieee754": { + "version": "1.1.8", + "resolved": "https://registry.npmjs.org/ieee754/-/ieee754-1.1.8.tgz", + "integrity": "sha1-vjPUCsEO8ZJnAfbwii2G+/0a0+Q=" }, - "morgan": { - "version": "1.5.3", - "resolved": "https://registry.npmjs.org/morgan/-/morgan-1.5.3.tgz", - "integrity": "sha1-ittOcvnlxUNuXZP0KRCDX3nan98=", + "ignore-by-default": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/ignore-by-default/-/ignore-by-default-1.0.1.tgz", + "integrity": "sha1-SMptcvbGo68Aqa1K5odr44ieKwk=", + "dev": true + }, + "import-lazy": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/import-lazy/-/import-lazy-2.1.0.tgz", + "integrity": "sha1-BWmOPUXIjo1+nZLLBYTnfwlvPkM=", + "dev": true + }, + "imurmurhash": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/imurmurhash/-/imurmurhash-0.1.4.tgz", + "integrity": "sha1-khi5srkoojixPcT7a21XbyMUU+o=", + "dev": true + }, + "inflight": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", + "integrity": "sha1-Sb1jMdfQLQwJvJEKEHW6gWW1bfk=", + "dev": true, "requires": { - "basic-auth": "~1.0.1", - "debug": "~2.2.0", - "depd": "~1.0.1", - "on-finished": "~2.2.1" + "once": "^1.3.0", + "wrappy": "1" } }, - "ms": { - "version": "0.7.1", - "resolved": "https://registry.npmjs.org/ms/-/ms-0.7.1.tgz", - "integrity": "sha1-nNE8A62/8ltl7/3nzoZO6VIBcJg=" + "inherits": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.3.tgz", + "integrity": "sha1-Yzwsg+PaQqUC9SRmAiSA9CCCYd4=" }, - "nan": { - "version": "2.8.0", - "resolved": "https://registry.npmjs.org/nan/-/nan-2.8.0.tgz", - "integrity": "sha1-7XFfP+neArV6XmJS2QqWZ14fCFo=" + "ini": { + "version": "1.3.5", + "resolved": "https://registry.npmjs.org/ini/-/ini-1.3.5.tgz", + "integrity": "sha512-RZY5huIKCMRWDUqZlEi72f/lmXKMvuszcMBduliQ3nnWbx9X/ZBQO7DijMEYS9EhHBb2qacRUMtC7svLwe0lcw==", + "dev": true }, - "negotiator": { - "version": "0.5.3", - "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-0.5.3.tgz", - "integrity": "sha1-Jp1cR2gQ7JLtvntsLygxY4T5p+g=" + "ipaddr.js": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-1.6.0.tgz", + "integrity": "sha1-4/o1e3c9phnybpXwSdBVxyeW+Gs=" + }, + "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=", + "dev": true, + "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=", + "dev": true, + "requires": { + "is-buffer": "^1.1.5" + } + } + } }, - "on-finished": { - "version": "2.2.1", - "resolved": "https://registry.npmjs.org/on-finished/-/on-finished-2.2.1.tgz", - "integrity": "sha1-XIXBzDYpn3gCllP2Z/J7a5nrwCk=", + "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=", + "dev": true, "requires": { - "ee-first": "1.1.0" + "binary-extensions": "^1.0.0" } }, - "parseurl": { - "version": "1.3.2", - "resolved": "https://registry.npmjs.org/parseurl/-/parseurl-1.3.2.tgz", - "integrity": "sha1-/CidTtiZMRlGDBViUyYs3I3mW/M=" + "is-buffer": { + "version": "1.1.6", + "resolved": "https://registry.npmjs.org/is-buffer/-/is-buffer-1.1.6.tgz", + "integrity": "sha512-NcdALwpXkTm5Zvvbk7owOUSvVvBKDgKP5/ewfXEznmQFfs4ZRmanOeKBTjRVjka3QFoN6XJ+9F3USqfHqTaU5w==", + "dev": true }, - "path-to-regexp": { - "version": "0.1.3", - "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-0.1.3.tgz", - "integrity": "sha1-IbmrgidCed4lsVbqCP0SylG4rss=" + "is-ci": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/is-ci/-/is-ci-1.1.0.tgz", + "integrity": "sha512-c7TnwxLePuqIlxHgr7xtxzycJPegNHFuIrBkwbf8hc58//+Op1CqFkyS+xnIMkwn9UsJIwc174BIjkyBmSpjKg==", + "dev": true, + "requires": { + "ci-info": "^1.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=", + "dev": true, + "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=", + "dev": true, + "requires": { + "is-buffer": "^1.1.5" + } + } + } + }, + "is-descriptor": { + "version": "0.1.6", + "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-0.1.6.tgz", + "integrity": "sha512-avDYr0SB3DwO9zsMov0gKCESFYqCnE4hq/4z3TdUlukEy5t9C0YRq7HLrsN52NAcqXKaepeCD0n+B0arnVG3Hg==", + "dev": true, + "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==", + "dev": true + } + } + }, + "is-extendable": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/is-extendable/-/is-extendable-0.1.1.tgz", + "integrity": "sha1-YrEQ4omkcUGOPsNqYX1HLjAd/Ik=", + "dev": true + }, + "is-extglob": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", + "integrity": "sha1-qIwCU1eR8C7TfHahueqXc8gz+MI=", + "dev": true + }, + "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=", + "dev": true + }, + "is-glob": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.0.tgz", + "integrity": "sha1-lSHHaEXMJhCoUgPd8ICpWML/q8A=", + "dev": true, + "requires": { + "is-extglob": "^2.1.1" + } + }, + "is-installed-globally": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/is-installed-globally/-/is-installed-globally-0.1.0.tgz", + "integrity": "sha1-Df2Y9akRFxbdU13aZJL2e/PSWoA=", + "dev": true, + "requires": { + "global-dirs": "^0.1.0", + "is-path-inside": "^1.0.0" + } + }, + "is-npm": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-npm/-/is-npm-1.0.0.tgz", + "integrity": "sha1-8vtjpl5JBbQGyGBydloaTceTufQ=", + "dev": true + }, + "is-number": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-number/-/is-number-3.0.0.tgz", + "integrity": "sha1-JP1iAaR4LPUFYcgQJ2r8fRLXEZU=", + "dev": true, + "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=", + "dev": true, + "requires": { + "is-buffer": "^1.1.5" + } + } + } + }, + "is-obj": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/is-obj/-/is-obj-1.0.1.tgz", + "integrity": "sha1-PkcprB9f3gJc19g6iW2rn09n2w8=", + "dev": true + }, + "is-odd": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/is-odd/-/is-odd-2.0.0.tgz", + "integrity": "sha512-OTiixgpZAT1M4NHgS5IguFp/Vz2VI3U7Goh4/HA1adtwyLtSBrxYlcSYkhpAE07s4fKEcjrFxyvtQBND4vFQyQ==", + "dev": true, + "requires": { + "is-number": "^4.0.0" + }, + "dependencies": { + "is-number": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/is-number/-/is-number-4.0.0.tgz", + "integrity": "sha512-rSklcAIlf1OmFdyAqbnWTLVelsQ58uvZ66S/ZyawjWqIviTWCjg2PzVGw8WUA+nNuPTqb4wgA+NszrJ+08LlgQ==", + "dev": true + } + } + }, + "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=", + "dev": true, + "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==", + "dev": true, + "requires": { + "isobject": "^3.0.1" + } + }, + "is-redirect": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-redirect/-/is-redirect-1.0.0.tgz", + "integrity": "sha1-HQPd7VO9jbDzDCbk+V02/HyH3CQ=", + "dev": true + }, + "is-retry-allowed": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/is-retry-allowed/-/is-retry-allowed-1.1.0.tgz", + "integrity": "sha1-EaBgVotnM5REAz0BJaYaINVk+zQ=", + "dev": true + }, + "is-stream": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-1.1.0.tgz", + "integrity": "sha1-EtSj3U5o4Lec6428hBc66A2RykQ=", + "dev": true + }, + "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==", + "dev": true + }, + "isarray": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", + "integrity": "sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE=", + "dev": true + }, + "isexe": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", + "integrity": "sha1-6PvzdNxVb/iUehDcsFctYz8s+hA=", + "dev": true + }, + "isobject": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/isobject/-/isobject-3.0.1.tgz", + "integrity": "sha1-TkMekrEalzFjaqH5yNHMvP2reN8=", + "dev": true + }, + "jmespath": { + "version": "0.15.0", + "resolved": "https://registry.npmjs.org/jmespath/-/jmespath-0.15.0.tgz", + "integrity": "sha1-o/Iiqarp+Wb10nx5ZRDigJF2Qhc=" + }, + "kind-of": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-6.0.2.tgz", + "integrity": "sha512-s5kLOcnH0XqDO+FvuaLX8DDjZ18CGFk7VygH40QoKPUQhW4e2rvM0rwUq0t8IQDOwYSeLK01U90OjzBTme2QqA==", + "dev": true + }, + "latest-version": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/latest-version/-/latest-version-3.1.0.tgz", + "integrity": "sha1-ogU4P+oyKzO1rjsYq+4NwvNW7hU=", + "dev": true, + "requires": { + "package-json": "^4.0.0" + } + }, + "lodash": { + "version": "4.17.10", + "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.10.tgz", + "integrity": "sha512-UejweD1pDoXu+AD825lWwp4ZGtSwgnpZxb3JDViD7StjQz+Nb/6l093lx4OQ0foGWNRoc19mWy7BzL+UAK2iVg==" + }, + "lowercase-keys": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/lowercase-keys/-/lowercase-keys-1.0.1.tgz", + "integrity": "sha512-G2Lj61tXDnVFFOi8VZds+SoQjtQC3dgokKdDG2mTm1tx4m50NUHBOZSBwQQHyy0V12A0JTG4icfZQH+xPyh8VA==", + "dev": true + }, + "lru-cache": { + "version": "4.1.3", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-4.1.3.tgz", + "integrity": "sha512-fFEhvcgzuIoJVUF8fYr5KR0YqxD238zgObTps31YdADwPPAp82a4M8TrckkWyx7ekNlf9aBcVn81cFwwXngrJA==", + "dev": true, + "requires": { + "pseudomap": "^1.0.2", + "yallist": "^2.1.2" + } + }, + "make-dir": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/make-dir/-/make-dir-1.3.0.tgz", + "integrity": "sha512-2w31R7SJtieJJnQtGc7RVL2StM2vGYVfqUOvUDxH6bC6aJTxPxTF0GnIgCyu7tjockiUWAYQRbxa7vKn34s5sQ==", + "dev": true, + "requires": { + "pify": "^3.0.0" + } + }, + "map-cache": { + "version": "0.2.2", + "resolved": "https://registry.npmjs.org/map-cache/-/map-cache-0.2.2.tgz", + "integrity": "sha1-wyq9C9ZSXZsFFkW7TyasXcmKDb8=", + "dev": true + }, + "map-stream": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/map-stream/-/map-stream-0.1.0.tgz", + "integrity": "sha1-5WqpTEyAVaFkBKBnS3jyFffI4ZQ=", + "dev": true + }, + "map-visit": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/map-visit/-/map-visit-1.0.0.tgz", + "integrity": "sha1-7Nyo8TFE5mDxtb1B8S80edmN+48=", + "dev": true, + "requires": { + "object-visit": "^1.0.0" + } + }, + "media-typer": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/media-typer/-/media-typer-0.3.0.tgz", + "integrity": "sha1-hxDXrwqmJvj/+hzgAWhUUmMlV0g=" + }, + "memory-cache": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/memory-cache/-/memory-cache-0.2.0.tgz", + "integrity": "sha1-eJCwHVLADI68nVM+H46xfjA0hxo=" + }, + "merge-descriptors": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/merge-descriptors/-/merge-descriptors-1.0.1.tgz", + "integrity": "sha1-sAqqVW3YtEVoFQ7J0blT8/kMu2E=" + }, + "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==", + "dev": true, + "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" + } + }, + "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.33.0", + "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.33.0.tgz", + "integrity": "sha512-BHJ/EKruNIqJf/QahvxwQZXKygOQ256myeN/Ew+THcAa5q+PjyTTMMeNQC4DZw5AwfvelsUrA6B67NKMqXDbzQ==" + }, + "mime-types": { + "version": "2.1.18", + "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.18.tgz", + "integrity": "sha512-lc/aahn+t4/SWV/qcmumYjymLsWfN3ELhpmVuUFjgsORruuZPVSwAQryq+HHGvO/SI2KVX26bx+En+zhM8g8hQ==", + "requires": { + "mime-db": "~1.33.0" + } + }, + "minimatch": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.4.tgz", + "integrity": "sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA==", + "dev": true, + "requires": { + "brace-expansion": "^1.1.7" + } + }, + "minimist": { + "version": "0.0.8", + "resolved": "https://registry.npmjs.org/minimist/-/minimist-0.0.8.tgz", + "integrity": "sha1-hX/Kv8M5fSYluCKCYuhqp6ARsF0=", + "dev": true + }, + "mixin-deep": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/mixin-deep/-/mixin-deep-1.3.1.tgz", + "integrity": "sha512-8ZItLHeEgaqEvd5lYBXfm4EZSFCX29Jb9K+lAHhDKzReKBQKj3R+7NOF6tjqYi9t4oI8VUfaWITJQm86wnXGNQ==", + "dev": true, + "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==", + "dev": true, + "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=", + "dev": true, + "requires": { + "minimist": "0.0.8" + } + }, + "mocha": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/mocha/-/mocha-5.1.1.tgz", + "integrity": "sha512-kKKs/H1KrMMQIEsWNxGmb4/BGsmj0dkeyotEvbrAuQ01FcWRLssUNXCEUZk6SZtyJBi6EE7SL0zDDtItw1rGhw==", + "dev": true, + "requires": { + "browser-stdout": "1.3.1", + "commander": "2.11.0", + "debug": "3.1.0", + "diff": "3.5.0", + "escape-string-regexp": "1.0.5", + "glob": "7.1.2", + "growl": "1.10.3", + "he": "1.1.1", + "minimatch": "3.0.4", + "mkdirp": "0.5.1", + "supports-color": "4.4.0" + } + }, + "moment": { + "version": "2.22.1", + "resolved": "https://registry.npmjs.org/moment/-/moment-2.22.1.tgz", + "integrity": "sha512-shJkRTSebXvsVqk56I+lkb2latjBs8I+pc2TzWc545y2iFnSjm7Wg0QMh+ZWcdSLQyGEau5jI8ocnmkyTgr9YQ==" + }, + "morgan": { + "version": "1.9.0", + "resolved": "https://registry.npmjs.org/morgan/-/morgan-1.9.0.tgz", + "integrity": "sha1-0B+mxlhZt2/PMbPLU6OCGjEdgFE=", + "requires": { + "basic-auth": "~2.0.0", + "debug": "2.6.9", + "depd": "~1.1.1", + "on-finished": "~2.3.0", + "on-headers": "~1.0.1" + }, + "dependencies": { + "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" + } + } + } + }, + "ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=" + }, + "nan": { + "version": "2.8.0", + "resolved": "https://registry.npmjs.org/nan/-/nan-2.8.0.tgz", + "integrity": "sha1-7XFfP+neArV6XmJS2QqWZ14fCFo=" + }, + "nanomatch": { + "version": "1.2.9", + "resolved": "https://registry.npmjs.org/nanomatch/-/nanomatch-1.2.9.tgz", + "integrity": "sha512-n8R9bS8yQ6eSXaV6jHUpKzD8gLsin02w1HSFiegwrs9E098Ylhw5jdyKPaYqvHknHaSCKTPp7C8dGCQ0q9koXA==", + "dev": true, + "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-odd": "^2.0.0", + "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" + } + }, + "negotiator": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-0.6.1.tgz", + "integrity": "sha1-KzJxhOiZIQEXeyhWP7XnECrNDKk=" + }, + "nodemon": { + "version": "1.17.4", + "resolved": "https://registry.npmjs.org/nodemon/-/nodemon-1.17.4.tgz", + "integrity": "sha512-ZQcvYd8I4sWhbLoqImIiCPBTaHcq3YRRQXYwePchFK3Zk5+LT8HYQR4urf1UkSDiY/gSxjq6ao34RxJp7rRe1w==", + "dev": true, + "requires": { + "chokidar": "^2.0.2", + "debug": "^3.1.0", + "ignore-by-default": "^1.0.1", + "minimatch": "^3.0.4", + "pstree.remy": "^1.1.0", + "semver": "^5.5.0", + "supports-color": "^5.2.0", + "touch": "^3.1.0", + "undefsafe": "^2.0.2", + "update-notifier": "^2.3.0" + }, + "dependencies": { + "has-flag": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", + "integrity": "sha1-tdRU3CGZriJWmfNGfloH87lVuv0=", + "dev": true + }, + "supports-color": { + "version": "5.4.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.4.0.tgz", + "integrity": "sha512-zjaXglF5nnWpsq470jSv6P9DwPvgLkuapYmfDm3JWOm0vkNTVF2tI4UrN2r6jH1qM/uc/WtxYY1hYoA2dOKj5w==", + "dev": true, + "requires": { + "has-flag": "^3.0.0" + } + } + } + }, + "nopt": { + "version": "1.0.10", + "resolved": "https://registry.npmjs.org/nopt/-/nopt-1.0.10.tgz", + "integrity": "sha1-bd0hvSoxQXuScn3Vhfim83YI6+4=", + "dev": true, + "requires": { + "abbrev": "1" + } + }, + "normalize-path": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-2.1.1.tgz", + "integrity": "sha1-GrKLVW4Zg2Oowab35vogE3/mrtk=", + "dev": true, + "requires": { + "remove-trailing-separator": "^1.0.1" + } + }, + "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=", + "dev": true, + "requires": { + "path-key": "^2.0.0" + } + }, + "object-copy": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/object-copy/-/object-copy-0.1.0.tgz", + "integrity": "sha1-fn2Fi3gb18mRpBupde04EnVOmYw=", + "dev": true, + "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=", + "dev": true, + "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=", + "dev": true, + "requires": { + "is-buffer": "^1.1.5" + } + } + } + }, + "object-visit": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/object-visit/-/object-visit-1.0.1.tgz", + "integrity": "sha1-95xEk68MU3e1n+OdOV5BBC3QRbs=", + "dev": true, + "requires": { + "isobject": "^3.0.0" + } + }, + "object.pick": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/object.pick/-/object.pick-1.3.0.tgz", + "integrity": "sha1-h6EKxMFpS9Lhy/U1kaZhQftd10c=", + "dev": true, + "requires": { + "isobject": "^3.0.1" + } + }, + "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.1", + "resolved": "https://registry.npmjs.org/on-headers/-/on-headers-1.0.1.tgz", + "integrity": "sha1-ko9dD0cNSTQmUepnlLCFfBAGk/c=" + }, + "once": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", + "integrity": "sha1-WDsap3WWHUsROsF9nFC6753Xa9E=", + "dev": true, + "requires": { + "wrappy": "1" + } + }, + "p-finally": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/p-finally/-/p-finally-1.0.0.tgz", + "integrity": "sha1-P7z7FbiZpEEjs0ttzBi3JDNqLK4=", + "dev": true + }, + "package-json": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/package-json/-/package-json-4.0.1.tgz", + "integrity": "sha1-iGmgQBJTZhxMTKPabCEh7VVfXu0=", + "dev": true, + "requires": { + "got": "^6.7.1", + "registry-auth-token": "^3.0.1", + "registry-url": "^3.0.3", + "semver": "^5.1.0" + } + }, + "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=", + "dev": true + }, + "path-dirname": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/path-dirname/-/path-dirname-1.0.2.tgz", + "integrity": "sha1-zDPSTVJeCZpTiMAzbG4yuRYGCeA=", + "dev": true + }, + "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=", + "dev": true + }, + "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=", + "dev": true + }, + "path-key": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/path-key/-/path-key-2.0.1.tgz", + "integrity": "sha1-QRyttXTFoUDTpLGRDUDYDMn0C0A=", + "dev": true + }, + "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=" + }, + "pathval": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/pathval/-/pathval-1.1.0.tgz", + "integrity": "sha1-uULm1L3mUwBe9rcTYd74cn0GReA=", + "dev": true + }, + "pause-stream": { + "version": "0.0.11", + "resolved": "https://registry.npmjs.org/pause-stream/-/pause-stream-0.0.11.tgz", + "integrity": "sha1-/lo0sMvOErWqaitAPuLnO2AvFEU=", + "dev": true, + "requires": { + "through": "~2.3" + } + }, + "pify": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/pify/-/pify-3.0.0.tgz", + "integrity": "sha1-5aSs0sEB/fPZpNB/DbxNtJ3SgXY=", + "dev": true + }, + "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=", + "dev": true + }, + "prepend-http": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/prepend-http/-/prepend-http-1.0.4.tgz", + "integrity": "sha1-1PRWKwzjaW5BrFLQ4ALlemNdxtw=", + "dev": true + }, + "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==", + "dev": true }, "promise": { "version": "6.1.0", @@ -346,12 +2552,36 @@ } }, "proxy-addr": { - "version": "1.0.10", - "resolved": "https://registry.npmjs.org/proxy-addr/-/proxy-addr-1.0.10.tgz", - "integrity": "sha1-DUCoL4Afw1VWfS7LZe/j8HfxIcU=", + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/proxy-addr/-/proxy-addr-2.0.3.tgz", + "integrity": "sha512-jQTChiCJteusULxjBp8+jftSQE5Obdl3k4cnmLA6WXtK6XFuWRnvVL7aCiBqaLPM8c4ph0S4tKna8XvmIwEnXQ==", "requires": { - "forwarded": "~0.1.0", - "ipaddr.js": "1.0.5" + "forwarded": "~0.1.2", + "ipaddr.js": "1.6.0" + } + }, + "ps-tree": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/ps-tree/-/ps-tree-1.1.0.tgz", + "integrity": "sha1-tCGyQUDWID8e08dplrRCewjowBQ=", + "dev": true, + "requires": { + "event-stream": "~3.3.0" + } + }, + "pseudomap": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/pseudomap/-/pseudomap-1.0.2.tgz", + "integrity": "sha1-8FKijacOYYkX7wqKw0wa5aaChrM=", + "dev": true + }, + "pstree.remy": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/pstree.remy/-/pstree.remy-1.1.0.tgz", + "integrity": "sha512-q5I5vLRMVtdWa8n/3UEzZX7Lfghzrg9eG2IKk2ENLSofKRCXVqMvMUHxCKgXNaqH/8ebhBxrqftHWnyTFweJ5Q==", + "dev": true, + "requires": { + "ps-tree": "^1.1.0" } }, "punycode": { @@ -360,9 +2590,9 @@ "integrity": "sha1-llOgNvt8HuQjQvIyXM7v6jkmxI0=" }, "qs": { - "version": "2.4.2", - "resolved": "https://registry.npmjs.org/qs/-/qs-2.4.2.tgz", - "integrity": "sha1-9854jld33wtQENp/fE5zujJHD1o=" + "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", @@ -370,70 +2600,286 @@ "integrity": "sha1-sgmEkgO7Jd+CDadW50cAWHhSFiA=" }, "range-parser": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/range-parser/-/range-parser-1.0.3.tgz", - "integrity": "sha1-aHKCNTXGkuLCoBA4Jq/YLC4P8XU=" + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/range-parser/-/range-parser-1.2.0.tgz", + "integrity": "sha1-9JvmtIeJTdxA3MlKMi9hEJLgDV4=" }, "raw-body": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/raw-body/-/raw-body-2.0.2.tgz", - "integrity": "sha1-osL5jIUxzumcY9jSOLfel7tln8o=", + "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" + } + }, + "rc": { + "version": "1.2.7", + "resolved": "https://registry.npmjs.org/rc/-/rc-1.2.7.tgz", + "integrity": "sha512-LdLD8xD4zzLsAT5xyushXDNscEjB7+2ulnl8+r1pnESlYtlJtVSoCMBGr30eDRJ3+2Gq89jK9P9e4tCEH1+ywA==", + "dev": true, "requires": { - "bytes": "2.1.0", - "iconv-lite": "0.4.8" + "deep-extend": "^0.5.1", + "ini": "~1.3.0", + "minimist": "^1.2.0", + "strip-json-comments": "~2.0.1" }, "dependencies": { - "bytes": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/bytes/-/bytes-2.1.0.tgz", - "integrity": "sha1-rJPEEOL/ycx89LRks4KJBn9eR7Q=" + "minimist": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.0.tgz", + "integrity": "sha1-o1AIsg9BOD7sH7kU9M1d95omQoQ=", + "dev": true } } }, + "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==", + "dev": 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" + } + }, + "readdirp": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-2.1.0.tgz", + "integrity": "sha1-TtCtBg3zBzMAxIRANz9y0cxkLXg=", + "dev": true, + "requires": { + "graceful-fs": "^4.1.2", + "minimatch": "^3.0.2", + "readable-stream": "^2.0.2", + "set-immediate-shim": "^1.0.1" + } + }, + "regex-not": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/regex-not/-/regex-not-1.0.2.tgz", + "integrity": "sha512-J6SDjUgDxQj5NusnOtdFxDwN/+HWykR8GELwctJ7mdqhcyy1xEc4SRFHUXvxTp661YaVKAjfRLZ9cCqS6tn32A==", + "dev": true, + "requires": { + "extend-shallow": "^3.0.2", + "safe-regex": "^1.1.0" + } + }, + "registry-auth-token": { + "version": "3.3.2", + "resolved": "https://registry.npmjs.org/registry-auth-token/-/registry-auth-token-3.3.2.tgz", + "integrity": "sha512-JL39c60XlzCVgNrO+qq68FoNb56w/m7JYvGR2jT5iR1xBrUA3Mfx5Twk5rqTThPmQKMWydGmq8oFtDlxfrmxnQ==", + "dev": true, + "requires": { + "rc": "^1.1.6", + "safe-buffer": "^5.0.1" + } + }, + "registry-url": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/registry-url/-/registry-url-3.1.0.tgz", + "integrity": "sha1-PU74cPc93h138M+aOBQyRE4XSUI=", + "dev": true, + "requires": { + "rc": "^1.0.1" + } + }, + "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=", + "dev": true + }, + "repeat-element": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/repeat-element/-/repeat-element-1.1.2.tgz", + "integrity": "sha1-7wiaF40Ug7quTZPrmLT55OEdmQo=", + "dev": true + }, + "repeat-string": { + "version": "1.6.1", + "resolved": "https://registry.npmjs.org/repeat-string/-/repeat-string-1.6.1.tgz", + "integrity": "sha1-jcrkcOHIirwtYA//Sndihtp15jc=", + "dev": true + }, + "resolve-url": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/resolve-url/-/resolve-url-0.2.1.tgz", + "integrity": "sha1-LGN/53yJOv0qZj/iGqkIAGjiBSo=", + "dev": true + }, + "ret": { + "version": "0.1.15", + "resolved": "https://registry.npmjs.org/ret/-/ret-0.1.15.tgz", + "integrity": "sha512-TTlYpa+OL+vMMNG24xSlQGEJ3B/RzEfUlLct7b5G/ytav+wPrplCpVMFuwzXbkecJrb6IYo1iFb0S9v37754mg==", + "dev": true + }, + "safe-buffer": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.1.tgz", + "integrity": "sha512-kKvNJn6Mm93gAczWVJg7wH+wGYWNrDHdWvpUmHyEsgCtIwwo3bqPtV4tR5tuPaUhTOo/kvhVwd8XwwOllGYkbg==" + }, + "safe-regex": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/safe-regex/-/safe-regex-1.1.0.tgz", + "integrity": "sha1-QKNmnzsHfR6UPURinhV91IAjvy4=", + "dev": true, + "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==" + }, "sax": { "version": "1.2.1", "resolved": "https://registry.npmjs.org/sax/-/sax-1.2.1.tgz", "integrity": "sha1-e45lYZCyKOgaZq6nSEgNgozS03o=" }, + "semver": { + "version": "5.5.0", + "resolved": "https://registry.npmjs.org/semver/-/semver-5.5.0.tgz", + "integrity": "sha512-4SJ3dm0WAwWy/NVeioZh5AntkdJoWKxHxcmyP622fOkgHa4z3R0TdBJICINyaSDE6uNwVc8gZr+ZinwZAH4xIA==", + "dev": true + }, + "semver-diff": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/semver-diff/-/semver-diff-2.1.0.tgz", + "integrity": "sha1-S7uEN8jTfksM8aaP1ybsbWRdbTY=", + "dev": true, + "requires": { + "semver": "^5.0.3" + } + }, "send": { - "version": "0.12.3", - "resolved": "https://registry.npmjs.org/send/-/send-0.12.3.tgz", - "integrity": "sha1-zRLcWP3iHk+RkCs5sv2gWnptm9w=", - "requires": { - "debug": "~2.2.0", - "depd": "~1.0.1", - "destroy": "1.0.3", - "escape-html": "1.0.1", - "etag": "~1.6.0", - "fresh": "0.2.4", - "mime": "1.3.4", - "ms": "0.7.1", - "on-finished": "~2.2.1", - "range-parser": "~1.0.2" + "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" + }, + "dependencies": { + "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" + } + }, + "statuses": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/statuses/-/statuses-1.4.0.tgz", + "integrity": "sha512-zhSCtt8v2NDrRlPQpCNtw/heZLtfUDqxBM1udqikb/Hbk52LK4nQSwr10u77iopCW5LsyHpuXS0GnEc48mLeew==" + } } }, "serve-favicon": { - "version": "2.2.1", - "resolved": "https://registry.npmjs.org/serve-favicon/-/serve-favicon-2.2.1.tgz", - "integrity": "sha1-2XuswVD2b+DlzEx4qEuhW65aWEo=", + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/serve-favicon/-/serve-favicon-2.5.0.tgz", + "integrity": "sha1-k10kDN/g9YBTB/3+ln2IlCosvPA=", "requires": { - "etag": "~1.6.0", - "fresh": "0.2.4", - "ms": "0.7.1", - "parseurl": "~1.3.0" + "etag": "~1.8.1", + "fresh": "0.5.2", + "ms": "2.1.1", + "parseurl": "~1.3.2", + "safe-buffer": "5.1.1" + }, + "dependencies": { + "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==" + } } }, "serve-static": { - "version": "1.9.3", - "resolved": "https://registry.npmjs.org/serve-static/-/serve-static-1.9.3.tgz", - "integrity": "sha1-X42gcyOtOF/z3FQfGnkXsuQ261c=", + "version": "1.13.2", + "resolved": "https://registry.npmjs.org/serve-static/-/serve-static-1.13.2.tgz", + "integrity": "sha512-p/tdJrO4U387R9oMjb1oj7qSMaMfmOyd4j9hOFoxZe2baQszgHcSWjuya/CiT5kgZZKRudHNOA0pYXOl8rQ5nw==", "requires": { - "escape-html": "1.0.1", - "parseurl": "~1.3.0", - "send": "0.12.3", - "utils-merge": "1.0.0" + "encodeurl": "~1.0.2", + "escape-html": "~1.0.3", + "parseurl": "~1.3.2", + "send": "0.16.2" } }, + "set-immediate-shim": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/set-immediate-shim/-/set-immediate-shim-1.0.1.tgz", + "integrity": "sha1-SysbJ+uAip+NzEgaWOXlb1mfP2E=", + "dev": true + }, + "set-value": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/set-value/-/set-value-2.0.0.tgz", + "integrity": "sha512-hw0yxk9GT/Hr5yJEYnHNKYXkIA8mVJgd9ditYZCe16ZczcaELYYcfvaXesNACk2O8O0nTiPQcQhGUQj8JLzeeg==", + "dev": true, + "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=", + "dev": true, + "requires": { + "is-extendable": "^0.1.0" + } + } + } + }, + "setprototypeof": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.1.0.tgz", + "integrity": "sha512-BvE/TwpZX4FXExxOxZyRGQQv651MSwmWKZGqvmPcRIjDqWub67kTKuIMx43cZZrS/cBBzwBcNDWoFxt2XEFIpQ==" + }, + "shebang-command": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-1.2.0.tgz", + "integrity": "sha1-RKrGW2lbAzmJaMOfNj/uXer98eo=", + "dev": true, + "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=", + "dev": true + }, + "signal-exit": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.2.tgz", + "integrity": "sha1-tf3AjxKH6hF4Yo5BXiUTK3NkbG0=", + "dev": true + }, "sleep": { "version": "3.0.1", "resolved": "https://registry.npmjs.org/sleep/-/sleep-3.0.1.tgz", @@ -442,15 +2888,481 @@ "nan": ">=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==", + "dev": true, + "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": { + "debug": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "dev": true, + "requires": { + "ms": "2.0.0" + } + }, + "define-property": { + "version": "0.2.5", + "resolved": "https://registry.npmjs.org/define-property/-/define-property-0.2.5.tgz", + "integrity": "sha1-w1se+RjsPJkPmlvFe+BKrOxcgRY=", + "dev": true, + "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=", + "dev": true, + "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==", + "dev": true, + "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=", + "dev": true, + "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==", + "dev": true, + "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==", + "dev": true, + "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==", + "dev": true, + "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==", + "dev": true, + "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=", + "dev": true, + "requires": { + "is-buffer": "^1.1.5" + } + } + } + }, + "source-map": { + "version": "0.5.7", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.5.7.tgz", + "integrity": "sha1-igOdLRAh0i0eoUyA2OpGi6LvP8w=", + "dev": true + }, + "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==", + "dev": true, + "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-url": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/source-map-url/-/source-map-url-0.4.0.tgz", + "integrity": "sha1-PpNdfd1zYxuXZZlW1VEo6HtQhKM=", + "dev": true + }, + "split": { + "version": "0.3.3", + "resolved": "https://registry.npmjs.org/split/-/split-0.3.3.tgz", + "integrity": "sha1-zQ7qXmOiEd//frDwkcQTPi0N0o8=", + "dev": true, + "requires": { + "through": "2" + } + }, + "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==", + "dev": true, + "requires": { + "extend-shallow": "^3.0.0" + } + }, + "static-extend": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/static-extend/-/static-extend-0.1.2.tgz", + "integrity": "sha1-YICcOcv/VTNyJv1eC1IPNB8ftcY=", + "dev": true, + "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=", + "dev": true, + "requires": { + "is-descriptor": "^0.1.0" + } + } + } + }, + "statuses": { + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/statuses/-/statuses-1.5.0.tgz", + "integrity": "sha1-Fhx9rBd2Wf2YEfQ3cfqZOBR4Yow=" + }, + "stream-combiner": { + "version": "0.0.4", + "resolved": "https://registry.npmjs.org/stream-combiner/-/stream-combiner-0.0.4.tgz", + "integrity": "sha1-TV5DPBhSYd3mI8o/RMWGvPXErRQ=", + "dev": true, + "requires": { + "duplexer": "~0.1.1" + } + }, + "string-width": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-2.1.1.tgz", + "integrity": "sha512-nOqH59deCq9SRHlxq1Aw85Jnt4w6KvLKqWVik6oA9ZklXLNIOlqg4F2yrT1MVaTjAqvVwdfeZ7w7aCvJD7ugkw==", + "dev": true, + "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==", + "dev": true, + "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=", + "dev": true, + "requires": { + "ansi-regex": "^3.0.0" + } + }, + "strip-eof": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/strip-eof/-/strip-eof-1.0.0.tgz", + "integrity": "sha1-u0P/VZim6wXYm1n80SnJgzE2Br8=", + "dev": true + }, + "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=", + "dev": true + }, + "supports-color": { + "version": "4.4.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-4.4.0.tgz", + "integrity": "sha512-rKC3+DyXWgK0ZLKwmRsrkyHVZAjNkfzeehuFWdGGcqGDTZFH73+RH6S/RDAAxl9GusSjZSUWYLmT9N5pzXFOXQ==", + "dev": true, + "requires": { + "has-flag": "^2.0.0" + } + }, + "term-size": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/term-size/-/term-size-1.2.0.tgz", + "integrity": "sha1-RYuDiH8oj8Vtb/+/rSYuJmOO+mk=", + "dev": true, + "requires": { + "execa": "^0.7.0" + } + }, + "through": { + "version": "2.3.8", + "resolved": "https://registry.npmjs.org/through/-/through-2.3.8.tgz", + "integrity": "sha1-DdTJ/6q8NXlgsbckEV1+Doai4fU=", + "dev": true + }, + "timed-out": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/timed-out/-/timed-out-4.0.1.tgz", + "integrity": "sha1-8y6srFoXW+ol1/q1Zas+2HQe9W8=", + "dev": true + }, + "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=", + "dev": true, + "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=", + "dev": true, + "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==", + "dev": true, + "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=", + "dev": true, + "requires": { + "is-number": "^3.0.0", + "repeat-string": "^1.6.1" + } + }, + "touch": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/touch/-/touch-3.1.0.tgz", + "integrity": "sha512-WBx8Uy5TLtOSRtIq+M03/sKDrXCLHxwDcquSP2c43Le03/9serjQBIztjRz6FkJez9D/hleyAXTBGLwwZUw9lA==", + "dev": true, + "requires": { + "nopt": "~1.0.10" + } + }, + "type-detect": { + "version": "4.0.8", + "resolved": "https://registry.npmjs.org/type-detect/-/type-detect-4.0.8.tgz", + "integrity": "sha512-0fr/mIH1dlO+x7TlcMy+bIDqKPsw/70tVyeHW787goQjhmqaZe10uwLujubK9q9Lg6Fiho1KUKDYz0Z7k7g5/g==", + "dev": true + }, "type-is": { - "version": "1.6.15", - "resolved": "https://registry.npmjs.org/type-is/-/type-is-1.6.15.tgz", - "integrity": "sha1-yrEPtJCeRByChC6v4a1kbIGARBA=", + "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.15" + "mime-types": "~2.1.18" + } + }, + "undefsafe": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/undefsafe/-/undefsafe-2.0.2.tgz", + "integrity": "sha1-Il9rngM3Zj4Njnz9aG/Cg2zKznY=", + "dev": true, + "requires": { + "debug": "^2.2.0" + }, + "dependencies": { + "debug": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "dev": true, + "requires": { + "ms": "2.0.0" + } + } + } + }, + "union-value": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/union-value/-/union-value-1.0.0.tgz", + "integrity": "sha1-XHHDTLW61dzr4+oM0IIHulqhrqQ=", + "dev": true, + "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=", + "dev": true, + "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=", + "dev": true, + "requires": { + "extend-shallow": "^2.0.1", + "is-extendable": "^0.1.1", + "is-plain-object": "^2.0.1", + "to-object-path": "^0.3.0" + } + } + } + }, + "unique-string": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/unique-string/-/unique-string-1.0.0.tgz", + "integrity": "sha1-nhBXzKhRq7kzmPizOuGHuZyuwRo=", + "dev": true, + "requires": { + "crypto-random-string": "^1.0.0" + } + }, + "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=", + "dev": true, + "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=", + "dev": true, + "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=", + "dev": true, + "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=", + "dev": true + } + } + }, + "unzip-response": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/unzip-response/-/unzip-response-2.0.1.tgz", + "integrity": "sha1-0vD3N9FrBhXnKmk17QQhRXLVb5c=", + "dev": true + }, + "upath": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/upath/-/upath-1.1.0.tgz", + "integrity": "sha512-bzpH/oBhoS/QI/YtbkqCg6VEiPYjSZtrHQM6/QnJS6OL9pKUFLqb3aFh4Scvwm45+7iAgiMkLhSbaZxUqmrprw==", + "dev": true + }, + "update-notifier": { + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/update-notifier/-/update-notifier-2.5.0.tgz", + "integrity": "sha512-gwMdhgJHGuj/+wHJJs9e6PcCszpxR1b236igrOkUofGhqJuG+amlIKwApH1IW1WWl7ovZxsX49lMBWLxSdm5Dw==", + "dev": true, + "requires": { + "boxen": "^1.2.1", + "chalk": "^2.0.1", + "configstore": "^3.0.0", + "import-lazy": "^2.1.0", + "is-ci": "^1.0.10", + "is-installed-globally": "^0.1.0", + "is-npm": "^1.0.0", + "latest-version": "^3.0.0", + "semver-diff": "^2.0.0", + "xdg-basedir": "^3.0.0" } }, + "urix": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/urix/-/urix-0.1.0.tgz", + "integrity": "sha1-2pN/emLiH+wf0Y1Js1wpNQZ6bHI=", + "dev": true + }, "url": { "version": "0.10.3", "resolved": "https://registry.npmjs.org/url/-/url-0.10.3.tgz", @@ -460,10 +3372,34 @@ "querystring": "0.2.0" } }, - "utils-merge": { + "url-parse-lax": { "version": "1.0.0", - "resolved": "https://registry.npmjs.org/utils-merge/-/utils-merge-1.0.0.tgz", - "integrity": "sha1-ApT7kiu5N1FTVBxPcJYjHyh8ivg=" + "resolved": "https://registry.npmjs.org/url-parse-lax/-/url-parse-lax-1.0.0.tgz", + "integrity": "sha1-evjzA2Rem9eaJy56FKxovAYJ2nM=", + "dev": true, + "requires": { + "prepend-http": "^1.0.1" + } + }, + "use": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/use/-/use-3.1.0.tgz", + "integrity": "sha512-6UJEQM/L+mzC3ZJNM56Q4DFGLX/evKGRg15UJHGB9X5j5Z3AFbgZvjUh2yq/UJUY4U5dh7Fal++XbNg1uzpRAw==", + "dev": true, + "requires": { + "kind-of": "^6.0.2" + } + }, + "util-deprecate": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", + "integrity": "sha1-RQ1Nyfpw3nMnYvvS1KKJgUGaDM8=", + "dev": true + }, + "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.0.1", @@ -471,9 +3407,50 @@ "integrity": "sha1-ZUS7ot/ajBzxfmKaOjBeK7H+5sE=" }, "vary": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/vary/-/vary-1.0.1.tgz", - "integrity": "sha1-meSYFWaihhGN+yuBc1ffeZM3bRA=" + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/vary/-/vary-1.1.2.tgz", + "integrity": "sha1-IpnwLG3tMNSllhsLn3RSShj2NPw=" + }, + "which": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/which/-/which-1.3.0.tgz", + "integrity": "sha512-xcJpopdamTuY5duC/KnTTNBraPK54YwpenP4lzxU8H91GudWpFv38u0CKjclE1Wi2EH2EDz5LRcHcKbCIzqGyg==", + "dev": true, + "requires": { + "isexe": "^2.0.0" + } + }, + "widest-line": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/widest-line/-/widest-line-2.0.0.tgz", + "integrity": "sha1-AUKk6KJD+IgsAjOqDgKBqnYVInM=", + "dev": true, + "requires": { + "string-width": "^2.1.1" + } + }, + "wrappy": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", + "integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8=", + "dev": true + }, + "write-file-atomic": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/write-file-atomic/-/write-file-atomic-2.3.0.tgz", + "integrity": "sha512-xuPeK4OdjWqtfi59ylvVL0Yn35SF3zgcAcv7rBPFHVaEapaDr4GdGgm3j7ckTwH9wHL7fGmgfAnb0+THrHb8tA==", + "dev": true, + "requires": { + "graceful-fs": "^4.1.11", + "imurmurhash": "^0.1.4", + "signal-exit": "^3.0.2" + } + }, + "xdg-basedir": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/xdg-basedir/-/xdg-basedir-3.0.0.tgz", + "integrity": "sha1-SWsswQnsqNus/i3HK2A8F8WHCtQ=", + "dev": true }, "xml2js": { "version": "0.4.17", @@ -491,6 +3468,12 @@ "requires": { "lodash": "^4.0.0" } + }, + "yallist": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-2.1.2.tgz", + "integrity": "sha1-HBH5IY8HYImkfdUS+TxmmaaoHVI=", + "dev": true } } } diff --git a/package.json b/package.json index 8b82ee3..acf96d5 100644 --- a/package.json +++ b/package.json @@ -11,21 +11,21 @@ "aws-sdk": "2.77.0", "aws-sdk-promise": "0.0.2", "batch-promises": "0.0.3", - "body-parser": "1.12.4", + "body-parser": "1.18.3", "cookie-parser": "1.3.5", - "debug": "2.2.0", + "debug": "3.1.0", "ejs": "2.5.5", - "express": "4.12.4", + "express": "4.16.3", "lodash": "4.17.10", "memory-cache": "0.2.0", "moment": "2.22.1", - "morgan": "1.5.3", - "serve-favicon": "2.2.1", + "morgan": "1.9.0", + "serve-favicon": "2.5.0", "sleep": "3.0.1" }, - "dev-dependencies": { + "devDependencies": { "chai": "4.1.2", "mocha": "5.1.1", - "nodemon": "1.3.7" + "nodemon": "1.17.4" } } From 588bf81621bc40208d3f3767543f76c30b6dfacf Mon Sep 17 00:00:00 2001 From: mcallanan Date: Tue, 22 May 2018 00:09:57 +1000 Subject: [PATCH 11/16] Upgrade Dockerfile base image from node:8.5 to node:9.11.1 --- Dockerfile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Dockerfile b/Dockerfile index e721785..318ac70 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,6 +1,6 @@ # https://hub.docker.com/r/library/node/ -FROM node:8.5-alpine +FROM node:9.11.1-alpine # >> FIX: # Fixes error Ubuntu: "gyp ERR! stack Error: Can't find Python executable "python", you can set the PYTHON env variable" From f17b600cd23c987621ac563fca21a2eb10e63999 Mon Sep 17 00:00:00 2001 From: mcallanan Date: Tue, 22 May 2018 00:19:48 +1000 Subject: [PATCH 12/16] Tweak position of timestamp on UI. --- public/javascripts/graph.js | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/public/javascripts/graph.js b/public/javascripts/graph.js index 69d76f8..cf4de6f 100644 --- a/public/javascripts/graph.js +++ b/public/javascripts/graph.js @@ -180,8 +180,9 @@ function renderGraph(timestampDivId, chartDivId, legendDivId, cluster, resourceT // TODO: Move this to footer graph.append("g") - .attr("class", "fetch-timestamp") .append("text") + .attr("x", 30) + .attr("y", -5) .text(localizedClusterCacheTimestamp ? `Fetched: ${localizedClusterCacheTimestamp}` : "No fetch timestamp available"); let uniqueTaskDefs = instanceSummaries.reduce(function (acc, current) { From b2f6074c641be4e881d25db5103ab13936f12466 Mon Sep 17 00:00:00 2001 From: mcallanan Date: Tue, 22 May 2018 19:32:00 +1000 Subject: [PATCH 13/16] Updated documentation. --- ClientServerInteraction-SequenceDiagram.png | Bin 35817 -> 0 bytes ClientServerInteraction-SequenceDiagram.puml | 59 --------- README.md | 120 ++++++------------ TODO.md | 9 +- docs/AWS_SDK_CONFIGURATION.md | 74 +++++++++++ docs/CONFIGURATION.md | 39 ++++++ ...lientServerInteraction-SequenceDiagram.png | Bin 0 -> 51777 bytes ...ientServerInteraction-SequenceDiagram.puml | 61 +++++++++ .../FetchStatus-StateDiagram.puml | 0 .../FetchStatus-StateDiagram1.png | Bin .../FetchStatus-StateDiagram2.png | Bin docs/SHORT_POLLING_FETCH_STATUS.md | 49 +++++++ 12 files changed, 267 insertions(+), 144 deletions(-) delete mode 100644 ClientServerInteraction-SequenceDiagram.png delete mode 100644 ClientServerInteraction-SequenceDiagram.puml create mode 100644 docs/AWS_SDK_CONFIGURATION.md create mode 100644 docs/CONFIGURATION.md create mode 100644 docs/ClientServerInteraction-SequenceDiagram.png create mode 100644 docs/ClientServerInteraction-SequenceDiagram.puml rename FetchStatus-StateDiagram.puml => docs/FetchStatus-StateDiagram.puml (100%) rename FetchStatus-StateDiagram1.png => docs/FetchStatus-StateDiagram1.png (100%) rename FetchStatus-StateDiagram2.png => docs/FetchStatus-StateDiagram2.png (100%) create mode 100644 docs/SHORT_POLLING_FETCH_STATUS.md diff --git a/ClientServerInteraction-SequenceDiagram.png b/ClientServerInteraction-SequenceDiagram.png deleted file mode 100644 index ba7bc7ece7187208fe22a1c53546f562c62fb6a5..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 35817 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 A: Select Cluster -activate A - -A -> B: "/api/instance_summaries_with_tasks" -activate B - -B -> C: Get State -activate C -C -> D: Create -activate D -C -> D: INITIAL -C --> B: INITIAL - -B --> A: INITIAL -deactivate B - -C -> D: FETCHING - -A -> B: "/api/instance_summaries_with_tasks" -activate B -B -> C: Get State -C --> B: FETCHING -B --> A: FETCHING -deactivate B - -C -> D: FETCHED - -A -> B: "/api/instance_summaries_with_tasks" -activate B -B -> C: Get State -C --> B: FETCHED -B --> A: FETCHED -deactivate B - -D -> D : Expire -deactivate D - -A --> User: Render Graph -deactivate A - - -deactivate C -deactivate D - -@enduml \ No newline at end of file diff --git a/README.md b/README.md index d9647ab..f371e6f 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,6 @@ # c3vis - Cloud Container Cluster Visualizer -Helps visualize the resource utilisation of Amazon ECS clusters. +Helps visualize the resource reservation of Amazon ECS clusters. Deploying software as “containers” promises to solve many problems with regards to interoperability of environments, speed to deploy, and cost reduction. But understanding where our software lives now becomes more difficult both for development and operations teams. @@ -16,96 +16,72 @@ Each unique Task Definition is represented as a different color, with the legend Each Task will contain one or more containers, the task box shows accumulated reserved memory or CPU for all containers in the Task. ECS Services are not currently represented. -## Configure +## Configuration -Displaying live ECS data requires server-side AWS credentials. +See [CONFIGURATION](docs/CONFIGURATION.md) for details on server-side configurable options that affect cache entry TTL and AWS API call throttling. -### Region +## Configuring AWS SDK -Before running, add a file named ```aws_config.json``` to this project root directory. At a minimum set the default region: +See [AWS_SDK_CONFIGURATION](docs/AWS_SDK_CONFIGURATION.md) for instructions +on configuring the AWS SDK for server-side AWS connectivity. -``` -{ - "region": "" -} -``` +## Requirements -Alternatively, set the environment variable "AWS_REGION" before starting the server. +Node >= 0.12 -### Credentials +## Building and Running -AWS credentials properties "accessKeyId" and "secretAccessKey" can be added to the aws_config.json file as per https://docs.aws.amazon.com/AWSJavaScriptSDK/guide/node-configuring.html. +The c3vis server is based on ExpressJS. The client predominantly uses D3.js, +jQuery and Bootstrap. -Otherwise, the credentials will be loaded from the Shared Credentials File or Environment Variables or IAM roles if deployed to an AWS instance. +Run the following to build and run the server ("package.json" contains instructions to pre-install required node modules): + +``` +npm start +``` -When using an IAM role, ensure the role has the following access: +Now browse to the app at `http://localhost:3000`. -* ecs:listContainerInstances -* ecs:describeContainerInstances -* ecs:listTasks -* ecs:describeTasks -* ecs:describeTaskDefinition -* ecs:listClusters -* ec2:describeInstance +## Testing -Sample IAM Inline Policy: +To run the server-side unit test suite with mocha and chai: + ``` -{ - "Version": "2012-10-17", - "Statement": [ - { - "Effect": "Allow", - "Action": [ - "ecs:listContainerInstances", - "ecs:describeContainerInstances", - "ecs:listTasks", - "ecs:describeTasks", - "ecs:describeTaskDefinition", - "ecs:listClusters", - "ec2:describeInstances" - ], - "Resource": [ - "*" - ] - } - ] -} +npm run test ``` -**WARNING:** c3vis makes ECS data from the above API calls (including environment variables in task definitions) available to clients/browsers. -Ensure the c3vis server is available only to users that should have access to this information. +## Usage +### Approach -## Requirements +When a client browser first connects to the c3vis server, the Cluster dropdown will be populated with ECS cluster names for the configured region. -Node >= 0.12 +Select from the dropdown to view the resources allocated to that cluster. If no cluster names appear in the dropdown, check the server logs and ensure the correct region is configured (see below). -## Building and Running +The list of clusters and the user's current selection are stored in cookies. Use the ```[refresh list]``` dropdown entry to refresh the list of clusters. -Server is based on ExpressJS. Client uses D3.js. +The Y axis shows total memory or CPU available for the instances. Memory is the default resource type represented. Use the "resourceType" query parameter to toggle between "memory" and "cpu". E.g. ```localhost:3000/?resourceType=cpu``` -Run the following to build and run the server ("package.json" contains instructions to pre-install required node modules): +The X axis displays the Private IP Address for each EC2 instance. Right-clicking the IP address shows the context menu with links to browse the instance in the ECS and EC2 consoles. -``` -npm start -``` +### AWS API Call Throttling -This will run ```npm install``` and ```node --harmony ./bin/www``` -(NOTE: ```"--harmony"``` is required for ES6 functionality such as Array.find()) +In order to prevent AWS API Rate limiting issues for large clusters, the server: -Now browse to the app at `http://localhost:3000`. +* Introduces a delay between API calls (configurable via `aws.apiDelay` setting) +* Limits the number of items retrieved per page in `list` and `describe` API calls (configurable via `aws.*PageSize`) +* Limits the number of asynchronous API calls it makes at a time (configurable via `aws.maxSimultaneous*Calls`) -### Usage +You can increase or decrease each of these settings to suit each environment c3vis is deployed to. -When a client browser first connects to the c3vis server the Cluster dropdown will be populated with ECS cluster names for the configured region. +### Short Polling, Server-Side Caching and Fetch Status -Select from the dropdown to view the resources allocated to that cluster. If no cluster names appear in the dropdown, check the server logs and ensure the correct region is configured (see below). +For each cluster requested, the server caches cluster data in-memory while the client polls the server until the cache is populated. -The list of clusters and the user's current selection are stored in cookies. Use the ```[refresh list]``` dropdown entry to refresh the list of clusters. +For an explanation on how the client polls the server for cluster data and the applicable fetch statuses, see [SHORT_POLLING_FETCH_STATUS](docs/SHORT_POLLING_FETCH_STATUS.md). -The Y axis shows total memory or CPU available for the instances. Memory is the default resource type represented. Use the "resourceType" query parameter to toggle between "memory" and "cpu". E.g. ```localhost:3000/?resourceType=cpu``` -The X axis displays the Private IP Address for each EC2 instance. Right-clicking the IP address shows the context menu with links to browse the instance in the ECS and EC2 consoles. +## Debugging ### Sample Clusters for Testing @@ -113,7 +89,7 @@ From the browser, use a ```"?static=true"``` query parameter to have the server Browse to `http://localhost:3000/?static=true`. -### Debug Logging +### Server Debug Logging To see all debug log entries: @@ -127,21 +103,7 @@ To see just API debug log entries: DEBUG=api npm start ``` -### Debugging - -Add the following line to server-side Javascript code to add a breakpoint: - -``` -debugger; -``` - -then run the debugger with: - -``` -node debug --harmony ./bin/www -``` - -### Running with Docker +## Running with Docker Build and tag the image: @@ -168,4 +130,4 @@ Created by [Matt Callanan](https://github.com/mattcallanan) with contributions f This project is available under the [Apache 2.0 License](http://www.apache.org/licenses/LICENSE-2.0.html). -Copyright 2015 Expedia Inc. +Copyright 2018 Expedia Inc. diff --git a/TODO.md b/TODO.md index 366eac7..161924b 100644 --- a/TODO.md +++ b/TODO.md @@ -3,24 +3,21 @@ * Selectable Region * Selectable AWS account – requires ability for mutliple config files server side * Show clusters as tabbed view with one cluster per tab -* Add toggle button to switch between memory vs CPU resourceType * Show an exploded view of task with more details when hovering over tasks: * Show containers within tasks * Show memory breakdown across containers * Sliding timebar to see historical data for comparison (like google street view) * Show container actual memory utilisation vs reserved memory utilisation * Provide access to more troubleshooting information (such as docker logs, ECS logs) +* Add footer with fetched/expiry timestamp, #instances/services/tasks, Average CPU/Memory Reservation ## Server * Write a plugin system that lets adopters plugin their own statistics from favourite monitoring tool * Pluggable backend system that could support other public or private cloud providers -* Cache responses server-side to reduce AWS API calls -* Make the data transfer between client and server more efficient - Separate requests for task and instance data and populate graph asynchronously +* Return instances with FETCHED_INSTANCES FetchStatus to allow client to draw instances outline until tasks retrieved asynchronously * Arrow functions: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Functions/Arrow_functions -### Server Configuration -* Make logging levels configurable -* Make delay between AWS API calls configurable + ## Testing * Capture ECS JSON responses for testing and replay with mock AWS ECS server diff --git a/docs/AWS_SDK_CONFIGURATION.md b/docs/AWS_SDK_CONFIGURATION.md new file mode 100644 index 0000000..93af9a7 --- /dev/null +++ b/docs/AWS_SDK_CONFIGURATION.md @@ -0,0 +1,74 @@ +# Configuring AWS SDK + +The c3vis server uses the AWS JavaScript SDK to connect to AWS APIs. + +As per [Configuring the SDK for JavaScript](https://docs.aws.amazon.com/sdk-for-javascript/v2/developer-guide/configuring-the-jssdk.html), the AWS JavaScript SDK will get its configuration from the server's environment. + +## Provide Explicit AWS SDK Configuration with `aws_config.json` Configuration File + +AWS SDK configuration can be overridden by providing an `aws_config.json` file (this file location is overridable with `aws.configFile` option, see [CONFIGURATION.md](CONFIGURATION.md)). + +E.g. to set the region used by c3vis server to `us-east-1`, create an `aws_config.json` file in the root directory with the following: + +``` +{ + "region": "us-east-1" +} +``` + +The contents of this file override all other sources of AWS SDK configuration. +The settings are applied to the AWS Global Configuration Object using `AWS.config.update()` as per [Using the Global Configuration Object](https://docs.aws.amazon.com/sdk-for-javascript/v2/developer-guide/global-config-object.html) + +## AWS Region + +As per above section, AWS Region can be provided in local `aws_config.json` file. + +Otherwise the Region will be configured as per [Setting the AWS Region](https://docs.aws.amazon.com/sdk-for-javascript/v2/developer-guide/setting-region.html). + +## AWS Credentials + +If using `aws_config.json` file as per above section, you can add AWS credentials properties `accessKeyId` and `secretAccessKey` to the `aws_config.json` +See [https://docs.aws.amazon.com/sdk-for-javascript/v2/developer-guide/loading-node-credentials-json-file.html](https://docs.aws.amazon.com/sdk-for-javascript/v2/developer-guide/loading-node-credentials-json-file.html). + +*NOTE: Storing credentials in plaintext file is not recommended, especially if there is a risk this file could be committed to version control.* + +Otherwise, the credentials will be loaded as per priority listed [here](https://docs.aws.amazon.com/sdk-for-javascript/v2/developer-guide/setting-credentials-node.html). + +## IAM Role Permissions + +When using an IAM role, ensure the role has the following access: + +* `ecs:listContainerInstances` +* `ecs:describeContainerInstances` +* `ecs:listTasks` +* `ecs:describeTasks` +* `ecs:describeTaskDefinition` +* `ecs:listClusters` +* `ec2:describeInstance` + +Sample IAM Inline Policy: +``` +{ + "Version": "2012-10-17", + "Statement": [ + { + "Effect": "Allow", + "Action": [ + "ecs:listContainerInstances", + "ecs:describeContainerInstances", + "ecs:listTasks", + "ecs:describeTasks", + "ecs:describeTaskDefinition", + "ecs:listClusters", + "ec2:describeInstances" + ], + "Resource": [ + "*" + ] + } + ] +} +``` + +**WARNING:** c3vis makes ECS data from the above API calls (including environment variables in task definitions) available to clients/browsers. +Ensure the c3vis server is available only to users that should have access to this information. diff --git a/docs/CONFIGURATION.md b/docs/CONFIGURATION.md new file mode 100644 index 0000000..bf6c149 --- /dev/null +++ b/docs/CONFIGURATION.md @@ -0,0 +1,39 @@ +# c3vis Configuration + +Server-side settings are configurable via configuration files. Default settings for all environments can be found in [config/defaults.js](config/defaults.js). + +## Environment Overrides + +Different environments may require different settings (e.g. rate at which you want to make AWS API calls may be different on a laptop vs production environment). +Settings can be overridden per environment by adding entries to a config file with a name matching the `NODE_ENV` environment variable. +E.g. if `NODE_ENV` = `test`, the `config/env/test.js` file overrides will be applied and override the settings in `config/defaults.js`. + +Blank files are provided for the following configuration files: + +`NODE_ENV`|Configuration File +----------|------------------ +`null`|[config/env/dev.js](config/env/dev.js) (`dev` environment is assumed if `NODE_ENV` not set +`"dev"`|[config/env/dev.js](config/env/dev.js) +`"test"`|[config/env/test.js](config/env/test.js) +`"prod"`|[config/env/prod.js](config/env/prod.js) + +## Configuration Options + +The following configuration options are available: + +Config Key|Description|Default +------------- |-------------|----- +`port`|Server port to listen on|`3000` +`clusterStateCacheTtl`|Expiry time (in milliseconds) per cluster data entry in cluster state cache|`1800000` (30 mins) +`aws.configFile`|Configuration file containing AWS SDK configuration|`./aws_config.json` +`aws.apiDelay`|Number of milliseconds to pause between AWS API calls to prevent API rate limiting|`100` +`aws.listInstancesPageSize`|Number of Instances to retrieve per `listInstances` ECS API call (max `100`)|`100` +`aws.describeInstancesPageSize`|Number of Instances to retrieve per `describeInstances` ECS API call (max `100`)|`100` +`aws.listTasksPageSize`|Number of Tasks to retrieve per `listTasks` ECS API call (max `100`)|`100` +`aws.describeTasksPageSize`|Number of Tasks to retrieve per `describeTasks` ECS API call (max `100`)|`100` +`aws.maxSimultaneousDescribeTasksCalls`|Number of `describeTasks` ECS API calls to make in quick succession before waiting for results|`2` +`aws.maxSimultaneousDescribeTaskDefinitionCalls`|Number of `describeTaskDefinitions` ECS API calls to make in quick succession before waiting for results|`1` + +## AWS SDK Configuration + +See [AWS_SDK_CONFIGURATION](AWS_SDK_CONFIGURATION.md) diff --git a/docs/ClientServerInteraction-SequenceDiagram.png b/docs/ClientServerInteraction-SequenceDiagram.png new file mode 100644 index 0000000000000000000000000000000000000000..6065f58b213eafd45e095e99ecfb101664c721a2 GIT binary patch literal 51777 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 OCMTsN`5j{L{{H}yEa|xb literal 0 HcmV?d00001 diff --git a/docs/ClientServerInteraction-SequenceDiagram.puml b/docs/ClientServerInteraction-SequenceDiagram.puml new file mode 100644 index 0000000..2f61373 --- /dev/null +++ b/docs/ClientServerInteraction-SequenceDiagram.puml @@ -0,0 +1,61 @@ +@startuml +skinparam sequenceArrowThickness 2 +skinparam roundcorner 20 +skinparam maxmessagesize 60 +skinparam sequenceParticipant underline + +actor User +box "Client" #LightGreen + participant "Browser" as A +end box +box "Node.js Server" #LightBlue + participant "Request" as B + participant "Background Processing" as C + participant "ClusterStateCache" as D +end box + +activate D + +User -> A: Select Cluster +activate A + +A -> B: "/api/instance_summaries_with_tasks" +activate B + +B -> C: Get ClusterState +activate C +C -> D: Add ClusterState with FetchStatus:INITIAL +activate D +C --> B: FetchStatus:INITIAL + +B --> A: FetchStatus:INITIAL +deactivate B + +C -> D: FetchStatus:FETCHING + +A -> B: "/api/instance_summaries_with_tasks" +activate B +B -> C: Get ClusterState +C --> B: FetchStatus:FETCHING +B --> A: FetchStatus:FETCHING +deactivate B + +C -> D: FetchStatus:FETCHED + +A -> B: "/api/instance_summaries_with_tasks" +activate B +B -> C: Get ClusterState +C --> B: FetchStatus:FETCHED +B --> A: FetchStatus:FETCHED InstanceSummaries:[...] +deactivate B + +D --> D : Expire after 30mins +deactivate D + +A --> User: Render Graph +deactivate A + +deactivate C +deactivate D + +@enduml diff --git a/FetchStatus-StateDiagram.puml b/docs/FetchStatus-StateDiagram.puml similarity index 100% rename from FetchStatus-StateDiagram.puml rename to docs/FetchStatus-StateDiagram.puml diff --git a/FetchStatus-StateDiagram1.png b/docs/FetchStatus-StateDiagram1.png similarity index 100% rename from FetchStatus-StateDiagram1.png rename to docs/FetchStatus-StateDiagram1.png diff --git a/FetchStatus-StateDiagram2.png b/docs/FetchStatus-StateDiagram2.png similarity index 100% rename from FetchStatus-StateDiagram2.png rename to docs/FetchStatus-StateDiagram2.png diff --git a/docs/SHORT_POLLING_FETCH_STATUS.md b/docs/SHORT_POLLING_FETCH_STATUS.md new file mode 100644 index 0000000..a9c3226 --- /dev/null +++ b/docs/SHORT_POLLING_FETCH_STATUS.md @@ -0,0 +1,49 @@ +# c3vis Request Handling & Caching + +## Short Polling for `instance_summaries_with_tasks` API + +The following diagram depicts the interaction between the client and server +when retrieving cluster data. + +When a client first asks the server for instances and tasks for a cluster (or + on the first request after cluster state has expired), the server will: +1. Create a `ClusterState` object with `FetchStatus` = `INITIAL` +2. Store it in the in-memory `ClusterStateCache` +3. Return `INITIAL` to client +4. Proceed to process the request in the background ("Background Processing" swimlane) +5. Background: Populate the cached `ClusterState` object with cluster data +6. Background: Update the `ClusterState` object's `FetchStatus` +7. Return current state to client + +Client will poll the server until it finds `INITIAL` or `ERROR` state. + +![alt tag](ClientServerInteraction-SequenceDiagram.png) + +### Force Refresh + +If the user clicks "Refresh Server Cache", the client will send a +`forceRefresh=true` parameter on its first polling attempt. The server upon +seeing `forceRefresh=true` will invalidate the cached `ClusterState` entry +and begin the above process again. + +## Fetch Status State Diagram + +Fetch Status State|Description +------------------|----------- +INITIAL|State object created +FETCHING|Server has begun fetching cluster data asynchronously +FETCHED|Server has completed fetching cluster data +ERROR|Server encountered an error fetching cluster data + +![alt tag](FetchStatus-StateDiagram1.png) + +## Cluster State Cache Expiry and Fetch Status + +When a ClusterState object is added to the in-memory cache, +it is set to expire within `clusterStateCacheTtl` milliseconds. +The default being 30mins. After that time, the cache object is +set to `null` and won't be set to `INITIAL` until necessitated by +a subsequent client request. + +![alt tag](FetchStatus-StateDiagram2.png) + From 9794f1b2ba347504e421a688af26b565ee364608 Mon Sep 17 00:00:00 2001 From: mcallanan Date: Thu, 24 May 2018 10:22:12 +1000 Subject: [PATCH 14/16] Updated documentation. --- docs/AWS_SDK_CONFIGURATION.md | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/docs/AWS_SDK_CONFIGURATION.md b/docs/AWS_SDK_CONFIGURATION.md index 93af9a7..a357e10 100644 --- a/docs/AWS_SDK_CONFIGURATION.md +++ b/docs/AWS_SDK_CONFIGURATION.md @@ -36,7 +36,10 @@ Otherwise, the credentials will be loaded as per priority listed [here](https:// ## IAM Role Permissions -When using an IAM role, ensure the role has the following access: +### EC2 IAM Role Permissions + +When running c3vis on EC2 instances using an IAM role, ensure the role has the +following permissions: * `ecs:listContainerInstances` * `ecs:describeContainerInstances` @@ -70,5 +73,13 @@ Sample IAM Inline Policy: } ``` +### ECS IAM Task Role + +When running c3vis on an ECS cluster, you can use an ECS Task IAM Role, which + can be created using the process documented [here](https://docs.aws.amazon.com/AmazonECS/latest/developerguide/task-iam-roles.html#create_task_iam_policy_and_role). +Ensure the IAM Policy has the permissions listed above. + +## Security Warning + **WARNING:** c3vis makes ECS data from the above API calls (including environment variables in task definitions) available to clients/browsers. Ensure the c3vis server is available only to users that should have access to this information. From c1e12bb642dbeb50054e2152ec624a48cd60504a Mon Sep 17 00:00:00 2001 From: mcallanan Date: Sun, 27 May 2018 14:24:56 +1000 Subject: [PATCH 15/16] Refactoring. Introduce PromiseDelayer class. Rename tests from "test*.js" to "*Spec.js". --- routes/fileUtils.js | 7 +++ routes/index.js | 52 ++++++++----------- routes/promiseDelayer.js | 9 ++++ routes/staticClusterDataProvider.js | 11 ++++ ...StateCache.js => clusterStateCacheSpec.js} | 0 ...estClusterState.js => clusterStateSpec.js} | 0 test/{testConfig.js => configSpec.js} | 0 test/promiseDelayerSpec.js | 44 ++++++++++++++++ 8 files changed, 93 insertions(+), 30 deletions(-) create mode 100644 routes/fileUtils.js create mode 100644 routes/promiseDelayer.js create mode 100644 routes/staticClusterDataProvider.js rename test/{testClusterStateCache.js => clusterStateCacheSpec.js} (100%) rename test/{testClusterState.js => clusterStateSpec.js} (100%) rename test/{testConfig.js => configSpec.js} (100%) create mode 100644 test/promiseDelayerSpec.js diff --git a/routes/fileUtils.js b/routes/fileUtils.js new file mode 100644 index 0000000..634535f --- /dev/null +++ b/routes/fileUtils.js @@ -0,0 +1,7 @@ +const fs = require('fs'); + +module.exports = { + readJsonFile: (path) => { + return JSON.parse(fs.readFileSync(path, "utf8")); + }, +}; diff --git a/routes/index.js b/routes/index.js index 80f6dc3..b0b5cd8 100644 --- a/routes/index.js +++ b/routes/index.js @@ -22,6 +22,8 @@ const ClusterState = require('./clusterState'); const clusterStateCache = require('../routes/clusterStateCache'); const clusterStateCacheTtl = config.clusterStateCacheTtl; const taskDefinitionCache = require('memory-cache'); +const promiseDelayer = require('./promiseDelayer'); +const staticClusterDataProvider = require('./staticClusterDataProvider.js'); const ecs = new AWS.ECS(); const ec2 = new AWS.EC2(); @@ -31,11 +33,15 @@ const ec2 = new AWS.EC2(); router.get('/', function (req, res, next) { res.render('index', { title: 'c3vis - Cloud Container Cluster Visualizer', - useStaticData: req.query.static, + useStaticData: staticDataRequested(req), resourceType: req.query.resourceType ? req.query.resourceType : 'memory' }); }); +function staticDataRequested(req) { + return req.query.static ? (req.query.static.toLowerCase() === "true") : false; +} + /* API endpoints * ============= * Endpoints take a "?static=true" query param to enable testing with static data when AWS credentials aren't available @@ -50,7 +56,7 @@ router.get('/api/instance_summaries_with_tasks', function (req, res, next) { reject("No 'cluster' parameter provided."); } else { const clusterName = req.query.cluster; - const useStaticData = req.query.static === 'true'; + const useStaticData = staticDataRequested(req); const forceRefresh = req.query.forceRefresh === 'true'; return getInstanceSummariesWithTasks(res, clusterName, useStaticData, forceRefresh); } @@ -101,8 +107,7 @@ function populateStaticClusterStateWithInstanceSummaries(clusterName) { updateClusterState(clusterName, FetchStatus.FETCHING, {}); try { // Return some static instance details with task details - const text = fs.readFileSync("public/test_data/ecs_instance_summaries_with_tasks-" + clusterName + ".json", "utf8"); - const instanceSummaries = JSON.parse(text); + const instanceSummaries = staticClusterDataProvider.getStaticClusterData(clusterName); updateClusterState(clusterName, FetchStatus.FETCHED, instanceSummaries); } catch (err) { console.log(`${err}\n${err.stack}`); @@ -166,7 +171,7 @@ function populateClusterStateWithInstanceSummaries(cluster) { resolve(ecs.describeContainerInstances({ cluster: cluster, containerInstances: containerInstanceBatch - }).promise().then(delayPromise(config.aws.apiDelay))); + }).promise().then(promiseDelayer.delay(config.aws.apiDelay))); })); } }) @@ -221,8 +226,14 @@ function populateClusterStateWithInstanceSummaries(cluster) { } router.get('/api/cluster_names', function (req, res, next) { - const live = req.query.static !== 'true'; - if (live) { + const useStaticData = staticDataRequested(req); + getClusterNames(useStaticData, res); +}); + +function getClusterNames(useStaticData, res) { + if (useStaticData) { + res.json(["demo-cluster-8", "demo-cluster-50", "demo-cluster-75", "demo-cluster-100", "invalid"]); + } else { ecs.listClusters({}, function (err, data1) { if (err) { sendErrorResponse(err, res); @@ -232,10 +243,8 @@ router.get('/api/cluster_names', function (req, res, next) { })); } }); - } else { - res.json(["demo-cluster-8", "demo-cluster-50", "demo-cluster-75", "demo-cluster-100", "invalid"]); } -}); +} function listAllContainerInstances(cluster) { return new Promise(function (resolve, reject) { @@ -288,7 +297,7 @@ function listTasksWithToken(cluster, token, tasks) { debugLog(`\tCalling ecs.listTasks with token: ${token} ...`); // TODO: Handle errors, e.g.: (node:27333) UnhandledPromiseRejectionWarning: ClusterNotFoundException: Cluster not found. return ecs.listTasks(params).promise() - .then(delayPromise(config.aws.apiDelay)) + .then(promiseDelayer.delay(config.aws.apiDelay)) .then(function (tasksResponse) { debugLog(`\t\tReceived tasksResponse with ${tasksResponse.data.taskArns.length} Task ARNs`); const taskArns = tasks.concat(tasksResponse.data.taskArns); @@ -328,7 +337,7 @@ function getTasksWithTaskDefinitions(cluster) { // The iteratee will fire after each batch debugLog(`\tCalling ecs.describeTasks for Task batch: ${taskBatch}`); resolve(ecs.describeTasks({cluster: cluster, tasks: taskBatch}).promise() - .then(delayPromise(config.aws.apiDelay))); + .then(promiseDelayer.delay(config.aws.apiDelay))); })); }) .then(function (describeTasksResponses) { @@ -346,7 +355,7 @@ function getTasksWithTaskDefinitions(cluster) { } else { debugLog(`\tCalling ecs.describeTaskDefinition for Task Definition ARN: ${task.taskDefinitionArn}`); resolve(ecs.describeTaskDefinition({taskDefinition: task.taskDefinitionArn}).promise() - .then(delayPromise(config.aws.apiDelay)) + .then(promiseDelayer.delay(config.aws.apiDelay)) .then(function (taskDefinition) { debugLog(`\t\tReceived taskDefinition for ARN "${task.taskDefinitionArn}". Caching in memory.`); taskDefinitionCache.put(task.taskDefinitionArn, taskDefinition); @@ -394,21 +403,4 @@ function debugLog(msg) { debug(msg); } -// Introduce a delay to prevent Rate Exceeded errors -// From: https://blog.raananweber.com/2015/12/01/writing-a-promise-delayer/ -// NOTE: https://www.npmjs.com/package/promise-pause does not work as the aws-sdk-promise library does not return a Promise but a thenable object -function delayPromise(delay) { - //return a function that accepts a single variable - return function (data) { - //this function returns a promise. - return new Promise(function (resolve, reject) { - debugLog(`Pausing for ${delay}ms...`); - setTimeout(function () { - //a promise that is resolved after "delay" milliseconds with the data provided - resolve(data); - }, delay); - }); - } -} - module.exports = router; diff --git a/routes/promiseDelayer.js b/routes/promiseDelayer.js new file mode 100644 index 0000000..8d0cd4a --- /dev/null +++ b/routes/promiseDelayer.js @@ -0,0 +1,9 @@ +class PromiseDelayer { + delay(ms) { + return function(data) { + return new Promise(resolve => setTimeout(() => resolve(data), ms)); + }; + } +} + +module.exports = new PromiseDelayer(); diff --git a/routes/staticClusterDataProvider.js b/routes/staticClusterDataProvider.js new file mode 100644 index 0000000..7955ca9 --- /dev/null +++ b/routes/staticClusterDataProvider.js @@ -0,0 +1,11 @@ +fileUtils = require('./fileUtils.js'); + +const STATIC_DATA_PATH = "public/test_data"; + +function getStaticClusterData(clusterName) { + const path = `${STATIC_DATA_PATH}/ecs_instance_summaries_with_tasks-${clusterName}.json`; + return fileUtils.readJsonFile(path); +} + +module.exports.STATIC_DATA_PATH = STATIC_DATA_PATH; +module.exports.getStaticClusterData = getStaticClusterData; diff --git a/test/testClusterStateCache.js b/test/clusterStateCacheSpec.js similarity index 100% rename from test/testClusterStateCache.js rename to test/clusterStateCacheSpec.js diff --git a/test/testClusterState.js b/test/clusterStateSpec.js similarity index 100% rename from test/testClusterState.js rename to test/clusterStateSpec.js diff --git a/test/testConfig.js b/test/configSpec.js similarity index 100% rename from test/testConfig.js rename to test/configSpec.js diff --git a/test/promiseDelayerSpec.js b/test/promiseDelayerSpec.js new file mode 100644 index 0000000..df1bbac --- /dev/null +++ b/test/promiseDelayerSpec.js @@ -0,0 +1,44 @@ +const subject = require('../routes/promiseDelayer'); +const assert = require('chai').assert; + +const DELAY_MILLIS = 200; +const ACCEPTABLE_DELTA_MILLIS = 10; +const DUMMY_DATA = "dummy data"; + +function checkElapsedTimeWithinAcceptableDelta(timeBeforeDelay, timeAfterDelay) { + const elapsedTime = timeAfterDelay - timeBeforeDelay; + assert.approximately(elapsedTime, DELAY_MILLIS, ACCEPTABLE_DELTA_MILLIS, `Expected elapsed time to be roughly ${DELAY_MILLIS}ms but was ${elapsedTime}ms`) +} + +describe('PromiseDelayer', function () { + describe('#delayPromise', function () { + it('delays a promise and passes through data', function () { + let timeBeforeDelay; + let timeAfterDelay; + return Promise.resolve() + .then(() => { + timeBeforeDelay = new Date(); + return DUMMY_DATA + }) + .then(subject.delay(DELAY_MILLIS)) + .then((data) => { + timeAfterDelay = new Date(); + checkElapsedTimeWithinAcceptableDelta(timeBeforeDelay, timeAfterDelay); + assert.equal(data, DUMMY_DATA) + }); + }); + it('delays a promise with no data', function () { + let timeBeforeDelay; + let timeAfterDelay; + return Promise.resolve() + .then(() => { + timeBeforeDelay = new Date(); + }) + .then(subject.delay(DELAY_MILLIS)) + .then(() => { + timeAfterDelay = new Date(); + checkElapsedTimeWithinAcceptableDelta(timeBeforeDelay, timeAfterDelay); + }); + }); + }); +}); From 3fea419e7ca5175d407412415dfc513f26dafbd1 Mon Sep 17 00:00:00 2001 From: mcallanan Date: Sun, 27 May 2018 22:53:57 +1000 Subject: [PATCH 16/16] Use 'TARGET_ENV' as environment config loading switch (as 'NODE_ENV' is used by Express for other purposes). --- README.md | 8 +- config/config.js | 30 +- config/defaults.js | 1 + config/env/dev.js | 3 +- config/env/prod.js | 3 +- config/env/test.js | 3 +- docs/CONFIGURATION.md | 28 +- package-lock.json | 998 +++++++++++++++++++++--------------------- test/configSpec.js | 45 +- 9 files changed, 586 insertions(+), 533 deletions(-) diff --git a/README.md b/README.md index f371e6f..9bcfe19 100644 --- a/README.md +++ b/README.md @@ -117,8 +117,14 @@ Run the container: (can remove ```AWS_ACCESS_KEY_ID``` and ```AWS_SECRET_ACCESS_ docker run -e "AWS_REGION=" -e "AWS_ACCESS_KEY_ID=" -e "AWS_SECRET_ACCESS_KEY=" -p 3000:3000 c3vis ``` +E.g. To run with `prod` target environment configuration: -Browse to `:3000` (e.g. [http://192.168.99.100:3000](http://192.168.99.100:3000)) +``` +docker run -e "TARGET_ENV=prod" -p 3000:3000 c3vis +``` + + +Browse to [http://localhost:3000](http://localhost:3000) # Credits diff --git a/config/config.js b/config/config.js index ddf21b7..ace8863 100644 --- a/config/config.js +++ b/config/config.js @@ -1,11 +1,23 @@ -const _ = require('lodash'); +const lodash = require('lodash'); +const TARGET_ENV = process.env.TARGET_ENV || 'dev'; -// Assume running in 'dev' environment if NODE_ENV environment variable not defined -const targetEnvironment = (process.env.NODE_ENV || 'dev'); +function _loadDefaultConfig() { + return require('./defaults.js'); +} -// Load default configuration from defaults.js -// and extend with environment-specific configuration -module.exports = _.merge( - require('./defaults.js'), - require(`./env/${targetEnvironment}.js`) || {} -); +function _loadOverrideConfig(targetEnvironment) { + try { + // Extend configuration with environment-specific configuration + console.debug(`Overriding default configuration with '${targetEnvironment}' environment configuration from ${_overrideConfigFilename(targetEnvironment)} (TARGET_ENV=${process.env.TARGET_ENV}, NODE_ENV=${process.env.NODE_ENV})`); + return require(_overrideConfigFilename(targetEnvironment)); + } catch (err) { + console.error(`ERROR: Could not load configuration file for target environment '${targetEnvironment}'. Skipping. (${err})`); + return {} + } +} + +function _overrideConfigFilename(targetEnvironment) { + return `./env/${targetEnvironment}.js`; +} + +module.exports = lodash.merge(_loadDefaultConfig(), _loadOverrideConfig(TARGET_ENV)); diff --git a/config/defaults.js b/config/defaults.js index 4cfe495..3be0530 100644 --- a/config/defaults.js +++ b/config/defaults.js @@ -1,4 +1,5 @@ module.exports = { + environmentName: undefined, port: process.env.PORT || 3000, clusterStateCacheTtl: 30 * 60 * 1000, // Invalidate clusters in cache after 30 minutes aws: { diff --git a/config/env/dev.js b/config/env/dev.js index 0b11063..a2bf537 100644 --- a/config/env/dev.js +++ b/config/env/dev.js @@ -1,3 +1,4 @@ module.exports = { - // add dev environment config overrides here and enable at startup with NODE_ENV=dev environment variable + // Add dev environment config overrides here and enable at startup with TARGET_ENV=dev environment variable + environmentName: "Development" }; diff --git a/config/env/prod.js b/config/env/prod.js index da0b4f1..c2dc222 100644 --- a/config/env/prod.js +++ b/config/env/prod.js @@ -1,3 +1,4 @@ module.exports = { - // add production environment config overrides here and enable at startup with NODE_ENV=prod environment variable + // Add prod environment config overrides here and enable at startup with TARGET_ENV=prod environment variable + environmentName: "Production" }; diff --git a/config/env/test.js b/config/env/test.js index d6b7894..b4b5788 100644 --- a/config/env/test.js +++ b/config/env/test.js @@ -1,3 +1,4 @@ module.exports = { - // add test environment config overrides here and enable at startup with NODE_ENV=test environment variable + // Add test environment config overrides here and enable at startup with TARGET_ENV=test environment variable + environmentName: "Test" }; diff --git a/docs/CONFIGURATION.md b/docs/CONFIGURATION.md index bf6c149..765d61c 100644 --- a/docs/CONFIGURATION.md +++ b/docs/CONFIGURATION.md @@ -4,25 +4,33 @@ Server-side settings are configurable via configuration files. Default settings ## Environment Overrides -Different environments may require different settings (e.g. rate at which you want to make AWS API calls may be different on a laptop vs production environment). -Settings can be overridden per environment by adding entries to a config file with a name matching the `NODE_ENV` environment variable. -E.g. if `NODE_ENV` = `test`, the `config/env/test.js` file overrides will be applied and override the settings in `config/defaults.js`. +Different environments may require different settings (e.g. rate at which you + want to make AWS API calls may be different on a laptop vs production + environment). +Settings can be overridden per environment by adding entries to a config file + with a name matching the `TARGET_ENV` environment variable. +E.g. if `TARGET_ENV` = `test`, the `config/env/test.js` file overrides will be +applied and override the settings in `config/defaults.js`. + +Note: `TARGET_ENV` is kept distinct from `NODE_ENV` as the recommended Express +JS setting for `NODE_ENV` is `production` when running outside a development +context (see: https://expressjs.com/en/advanced/best-practice-performance.html#set-node_env-to-production) Blank files are provided for the following configuration files: -`NODE_ENV`|Configuration File -----------|------------------ -`null`|[config/env/dev.js](config/env/dev.js) (`dev` environment is assumed if `NODE_ENV` not set -`"dev"`|[config/env/dev.js](config/env/dev.js) -`"test"`|[config/env/test.js](config/env/test.js) -`"prod"`|[config/env/prod.js](config/env/prod.js) +`TARGET_ENV`|Configuration File +------------|------------------ +`undefined` |[config/env/dev.js](config/env/dev.js) (`dev` environment is assumed if `TARGET_ENV` not set) +`"dev"` |[config/env/dev.js](config/env/dev.js) +`"test"` |[config/env/test.js](config/env/test.js) +`"prod"` |[config/env/prod.js](config/env/prod.js) ## Configuration Options The following configuration options are available: Config Key|Description|Default -------------- |-------------|----- +--------- |-----------|------- `port`|Server port to listen on|`3000` `clusterStateCacheTtl`|Expiry time (in milliseconds) per cluster data entry in cluster state cache|`1800000` (30 mins) `aws.configFile`|Configuration file containing AWS SDK configuration|`./aws_config.json` diff --git a/package-lock.json b/package-lock.json index 40cfaa5..2cc2d27 100644 --- a/package-lock.json +++ b/package-lock.json @@ -15,7 +15,7 @@ "resolved": "https://registry.npmjs.org/accepts/-/accepts-1.3.5.tgz", "integrity": "sha1-63d99gEXI6OxTopywIBcjoZ0a9I=", "requires": { - "mime-types": "~2.1.18", + "mime-types": "2.1.18", "negotiator": "0.6.1" } }, @@ -25,7 +25,7 @@ "integrity": "sha1-w2rsy6VjuJzrVW82kPCx2eNUf38=", "dev": true, "requires": { - "string-width": "^2.0.0" + "string-width": "2.1.1" } }, "ansi-regex": { @@ -40,7 +40,7 @@ "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", "dev": true, "requires": { - "color-convert": "^1.9.0" + "color-convert": "1.9.1" } }, "anymatch": { @@ -49,8 +49,8 @@ "integrity": "sha512-5teOsQWABXHHBFP9y3skS5P3d/WfWXpv3FUpy+LorMrNYaT9pI4oLMQX7jzQ2KklNpGpWHzdCXTDT2Y3XGlZBw==", "dev": true, "requires": { - "micromatch": "^3.1.4", - "normalize-path": "^2.1.1" + "micromatch": "3.1.10", + "normalize-path": "2.1.1" } }, "arr-diff": { @@ -132,7 +132,7 @@ "resolved": "https://registry.npmjs.org/aws-sdk-promise/-/aws-sdk-promise-0.0.2.tgz", "integrity": "sha1-KIa9H8TEfry5r91sWXz0Jg+55eg=", "requires": { - "promise": "^6.1.0" + "promise": "6.1.0" } }, "balanced-match": { @@ -147,13 +147,13 @@ "integrity": "sha512-5T6P4xPgpp0YDFvSWwEZ4NoE3aM4QBQXDzmVbraCkFj8zHM+mba8SyqB5DbZWyR7mYHo6Y7BdQo3MoA4m0TeQg==", "dev": true, "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" + "cache-base": "1.0.1", + "class-utils": "0.3.6", + "component-emitter": "1.2.1", + "define-property": "1.0.0", + "isobject": "3.0.1", + "mixin-deep": "1.3.1", + "pascalcase": "0.1.1" }, "dependencies": { "define-property": { @@ -162,7 +162,7 @@ "integrity": "sha1-dp66rz9KY6rTr56NMEybvnm/sOY=", "dev": true, "requires": { - "is-descriptor": "^1.0.0" + "is-descriptor": "1.0.2" } }, "is-accessor-descriptor": { @@ -171,7 +171,7 @@ "integrity": "sha512-m5hnHTkcVsPfqx3AKlyttIPb7J+XykHvJP2B9bZDjlhLIoEq4XoK64Vg7boZlVWYK6LUY94dYPEE7Lh0ZkZKcQ==", "dev": true, "requires": { - "kind-of": "^6.0.0" + "kind-of": "6.0.2" } }, "is-data-descriptor": { @@ -180,7 +180,7 @@ "integrity": "sha512-jbRXy1FmtAoCjQkVmIVYwuuqDFUbaOeDjmed1tOGPrsMhtJA4rD9tkgA0F1qJ3gRFRXcHYVkdeaP50Q5rE/jLQ==", "dev": true, "requires": { - "kind-of": "^6.0.0" + "kind-of": "6.0.2" } }, "is-descriptor": { @@ -189,9 +189,9 @@ "integrity": "sha512-2eis5WqQGV7peooDyLmNEPUrps9+SXX5c9pL3xEB+4e9HnGuDa7mB7kHxHw4CbqS9k1T2hOH3miL8n8WtiYVtg==", "dev": true, "requires": { - "is-accessor-descriptor": "^1.0.0", - "is-data-descriptor": "^1.0.0", - "kind-of": "^6.0.2" + "is-accessor-descriptor": "1.0.0", + "is-data-descriptor": "1.0.0", + "kind-of": "6.0.2" } } } @@ -226,15 +226,15 @@ "integrity": "sha1-WykhmP/dVTs6DyDe0FkrlWlVyLQ=", "requires": { "bytes": "3.0.0", - "content-type": "~1.0.4", + "content-type": "1.0.4", "debug": "2.6.9", - "depd": "~1.1.2", - "http-errors": "~1.6.3", + "depd": "1.1.2", + "http-errors": "1.6.3", "iconv-lite": "0.4.23", - "on-finished": "~2.3.0", + "on-finished": "2.3.0", "qs": "6.5.2", "raw-body": "2.3.3", - "type-is": "~1.6.16" + "type-is": "1.6.16" }, "dependencies": { "debug": { @@ -253,13 +253,13 @@ "integrity": "sha512-TNPjfTr432qx7yOjQyaXm3dSR0MH9vXp7eT1BFSl/C51g+EFnOR9hTg1IreahGBmDNCehscshe45f+C1TBZbLw==", "dev": true, "requires": { - "ansi-align": "^2.0.0", - "camelcase": "^4.0.0", - "chalk": "^2.0.1", - "cli-boxes": "^1.0.0", - "string-width": "^2.0.0", - "term-size": "^1.2.0", - "widest-line": "^2.0.0" + "ansi-align": "2.0.0", + "camelcase": "4.1.0", + "chalk": "2.4.1", + "cli-boxes": "1.0.0", + "string-width": "2.1.1", + "term-size": "1.2.0", + "widest-line": "2.0.0" } }, "brace-expansion": { @@ -268,7 +268,7 @@ "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", "dev": true, "requires": { - "balanced-match": "^1.0.0", + "balanced-match": "1.0.0", "concat-map": "0.0.1" } }, @@ -278,16 +278,16 @@ "integrity": "sha512-aNdbnj9P8PjdXU4ybaWLK2IF3jc/EoDYbC7AazW6to3TRsfXxscC9UXOB5iDiEQrkyIbWp2SLQda4+QAa7nc3w==", "dev": true, "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" + "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.2", + "snapdragon-node": "2.1.1", + "split-string": "3.1.0", + "to-regex": "3.0.2" }, "dependencies": { "extend-shallow": { @@ -296,7 +296,7 @@ "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", "dev": true, "requires": { - "is-extendable": "^0.1.0" + "is-extendable": "0.1.1" } } } @@ -312,8 +312,8 @@ "resolved": "https://registry.npmjs.org/buffer/-/buffer-5.0.6.tgz", "integrity": "sha1-LqZp9+7Atu2gWwj4tf9mGyhXNYg=", "requires": { - "base64-js": "^1.0.2", - "ieee754": "^1.1.4" + "base64-js": "1.2.1", + "ieee754": "1.1.8" } }, "bytes": { @@ -327,15 +327,15 @@ "integrity": "sha512-AKcdTnFSWATd5/GCPRxr2ChwIJ85CeyrEyjRHlKxQ56d4XJMGym0uAiKn0xbLOGOl3+yRpOTi484dVCEc5AUzQ==", "dev": true, "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" + "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" } }, "camelcase": { @@ -356,12 +356,12 @@ "integrity": "sha1-D2RYS6ZC8PKs4oBiefTwbKI61zw=", "dev": true, "requires": { - "assertion-error": "^1.0.1", - "check-error": "^1.0.1", - "deep-eql": "^3.0.0", - "get-func-name": "^2.0.0", - "pathval": "^1.0.0", - "type-detect": "^4.0.0" + "assertion-error": "1.1.0", + "check-error": "1.0.2", + "deep-eql": "3.0.1", + "get-func-name": "2.0.0", + "pathval": "1.1.0", + "type-detect": "4.0.8" } }, "chalk": { @@ -370,9 +370,9 @@ "integrity": "sha512-ObN6h1v2fTJSmUXoS3nMQ92LbDK9be4TV+6G+omQlGJFdcUX5heKi1LZ1YnRMIgwTLEj3E24bT6tYni50rlCfQ==", "dev": true, "requires": { - "ansi-styles": "^3.2.1", - "escape-string-regexp": "^1.0.5", - "supports-color": "^5.3.0" + "ansi-styles": "3.2.1", + "escape-string-regexp": "1.0.5", + "supports-color": "5.4.0" }, "dependencies": { "has-flag": { @@ -387,7 +387,7 @@ "integrity": "sha512-zjaXglF5nnWpsq470jSv6P9DwPvgLkuapYmfDm3JWOm0vkNTVF2tI4UrN2r6jH1qM/uc/WtxYY1hYoA2dOKj5w==", "dev": true, "requires": { - "has-flag": "^3.0.0" + "has-flag": "3.0.0" } } } @@ -404,18 +404,18 @@ "integrity": "sha512-zW8iXYZtXMx4kux/nuZVXjkLP+CyIK5Al5FHnj1OgTKGZfp4Oy6/ymtMSKFv3GD8DviEmUPmJg9eFdJ/JzudMg==", "dev": true, "requires": { - "anymatch": "^2.0.0", - "async-each": "^1.0.0", - "braces": "^2.3.0", - "fsevents": "^1.1.2", - "glob-parent": "^3.1.0", - "inherits": "^2.0.1", - "is-binary-path": "^1.0.0", - "is-glob": "^4.0.0", - "normalize-path": "^2.1.1", - "path-is-absolute": "^1.0.0", - "readdirp": "^2.0.0", - "upath": "^1.0.0" + "anymatch": "2.0.0", + "async-each": "1.0.1", + "braces": "2.3.2", + "fsevents": "1.2.4", + "glob-parent": "3.1.0", + "inherits": "2.0.3", + "is-binary-path": "1.0.1", + "is-glob": "4.0.0", + "normalize-path": "2.1.1", + "path-is-absolute": "1.0.1", + "readdirp": "2.1.0", + "upath": "1.1.0" } }, "ci-info": { @@ -430,10 +430,10 @@ "integrity": "sha512-qOhPa/Fj7s6TY8H8esGu5QNpMMQxz79h+urzrNYN6mn+9BnxlDGf5QZ+XeCDsxSjPqsSR56XOZOJmpeurnLMeg==", "dev": true, "requires": { - "arr-union": "^3.1.0", - "define-property": "^0.2.5", - "isobject": "^3.0.0", - "static-extend": "^0.1.1" + "arr-union": "3.1.0", + "define-property": "0.2.5", + "isobject": "3.0.1", + "static-extend": "0.1.2" }, "dependencies": { "define-property": { @@ -442,7 +442,7 @@ "integrity": "sha1-w1se+RjsPJkPmlvFe+BKrOxcgRY=", "dev": true, "requires": { - "is-descriptor": "^0.1.0" + "is-descriptor": "0.1.6" } } } @@ -459,8 +459,8 @@ "integrity": "sha1-S8A3PBZLwykbTTaMgpzxqApZ3KA=", "dev": true, "requires": { - "map-visit": "^1.0.0", - "object-visit": "^1.0.0" + "map-visit": "1.0.0", + "object-visit": "1.0.1" } }, "color-convert": { @@ -469,7 +469,7 @@ "integrity": "sha512-mjGanIiwQJskCC18rPR6OmrZ6fm2Lc7PeGFYwCmy5J34wC6F1PzdGL6xeMfmgicfYcNLGuVFA3WzXtIDCQSZxQ==", "dev": true, "requires": { - "color-name": "^1.1.1" + "color-name": "1.1.3" } }, "color-name": { @@ -502,12 +502,12 @@ "integrity": "sha512-vtv5HtGjcYUgFrXc6Kx747B83MRRVS5R1VTEQoXvuP+kMI+if6uywV0nDGoiydJRy4yk7h9od5Og0kxx4zUXmw==", "dev": true, "requires": { - "dot-prop": "^4.1.0", - "graceful-fs": "^4.1.2", - "make-dir": "^1.0.0", - "unique-string": "^1.0.0", - "write-file-atomic": "^2.0.0", - "xdg-basedir": "^3.0.0" + "dot-prop": "4.2.0", + "graceful-fs": "4.1.11", + "make-dir": "1.3.0", + "unique-string": "1.0.0", + "write-file-atomic": "2.3.0", + "xdg-basedir": "3.0.0" } }, "content-disposition": { @@ -557,7 +557,7 @@ "integrity": "sha1-Br56vvlHo/FKMP1hBnHUAbyot7Y=", "dev": true, "requires": { - "capture-stack-trace": "^1.0.0" + "capture-stack-trace": "1.0.0" } }, "cross-spawn": { @@ -566,9 +566,9 @@ "integrity": "sha1-6L0O/uWPz/b4+UUQoKVUu/ojVEk=", "dev": true, "requires": { - "lru-cache": "^4.0.1", - "shebang-command": "^1.2.0", - "which": "^1.2.9" + "lru-cache": "4.1.3", + "shebang-command": "1.2.0", + "which": "1.3.0" } }, "crypto-browserify": { @@ -602,7 +602,7 @@ "integrity": "sha512-+QeIQyN5ZuO+3Uk5DYh6/1eKO0m0YmJFGNmFHGACpf1ClL1nmlV/p4gNgbl2pJGxgXb4faqo6UE+M5ACEMyVcw==", "dev": true, "requires": { - "type-detect": "^4.0.0" + "type-detect": "4.0.8" } }, "deep-extend": { @@ -617,8 +617,8 @@ "integrity": "sha512-jwK2UV4cnPpbcG7+VRARKTZPUWowwXA8bzH5NP6ud0oeAxyYPuGZUAC7hMugpCdz4BeSZl2Dl9k66CHJ/46ZYQ==", "dev": true, "requires": { - "is-descriptor": "^1.0.2", - "isobject": "^3.0.1" + "is-descriptor": "1.0.2", + "isobject": "3.0.1" }, "dependencies": { "is-accessor-descriptor": { @@ -627,7 +627,7 @@ "integrity": "sha512-m5hnHTkcVsPfqx3AKlyttIPb7J+XykHvJP2B9bZDjlhLIoEq4XoK64Vg7boZlVWYK6LUY94dYPEE7Lh0ZkZKcQ==", "dev": true, "requires": { - "kind-of": "^6.0.0" + "kind-of": "6.0.2" } }, "is-data-descriptor": { @@ -636,7 +636,7 @@ "integrity": "sha512-jbRXy1FmtAoCjQkVmIVYwuuqDFUbaOeDjmed1tOGPrsMhtJA4rD9tkgA0F1qJ3gRFRXcHYVkdeaP50Q5rE/jLQ==", "dev": true, "requires": { - "kind-of": "^6.0.0" + "kind-of": "6.0.2" } }, "is-descriptor": { @@ -645,9 +645,9 @@ "integrity": "sha512-2eis5WqQGV7peooDyLmNEPUrps9+SXX5c9pL3xEB+4e9HnGuDa7mB7kHxHw4CbqS9k1T2hOH3miL8n8WtiYVtg==", "dev": true, "requires": { - "is-accessor-descriptor": "^1.0.0", - "is-data-descriptor": "^1.0.0", - "kind-of": "^6.0.2" + "is-accessor-descriptor": "1.0.0", + "is-data-descriptor": "1.0.0", + "kind-of": "6.0.2" } } } @@ -674,7 +674,7 @@ "integrity": "sha512-tUMXrxlExSW6U2EXiiKGSBVdYgtV8qlHL+C10TsW4PURY/ic+eaysnSkwB4kA/mBlCyy/IKDJ+Lc3wbWeaXtuQ==", "dev": true, "requires": { - "is-obj": "^1.0.0" + "is-obj": "1.0.1" } }, "duplexer": { @@ -726,13 +726,13 @@ "integrity": "sha1-SrTJoPWlTbkzi0w02Gv86PSzVXE=", "dev": true, "requires": { - "duplexer": "~0.1.1", - "from": "~0", - "map-stream": "~0.1.0", + "duplexer": "0.1.1", + "from": "0.1.7", + "map-stream": "0.1.0", "pause-stream": "0.0.11", - "split": "0.3", - "stream-combiner": "~0.0.4", - "through": "~2.3.1" + "split": "0.3.3", + "stream-combiner": "0.0.4", + "through": "2.3.8" } }, "execa": { @@ -741,13 +741,13 @@ "integrity": "sha1-lEvs00zEHuMqY6n68nrVpl/Fl3c=", "dev": true, "requires": { - "cross-spawn": "^5.0.1", - "get-stream": "^3.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" + "cross-spawn": "5.1.0", + "get-stream": "3.0.0", + "is-stream": "1.1.0", + "npm-run-path": "2.0.2", + "p-finally": "1.0.0", + "signal-exit": "3.0.2", + "strip-eof": "1.0.0" } }, "expand-brackets": { @@ -756,13 +756,13 @@ "integrity": "sha1-t3c14xXOMPa27/D4OwQVGiJEliI=", "dev": true, "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" + "debug": "2.6.9", + "define-property": "0.2.5", + "extend-shallow": "2.0.1", + "posix-character-classes": "0.1.1", + "regex-not": "1.0.2", + "snapdragon": "0.8.2", + "to-regex": "3.0.2" }, "dependencies": { "debug": { @@ -780,7 +780,7 @@ "integrity": "sha1-w1se+RjsPJkPmlvFe+BKrOxcgRY=", "dev": true, "requires": { - "is-descriptor": "^0.1.0" + "is-descriptor": "0.1.6" } }, "extend-shallow": { @@ -789,7 +789,7 @@ "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", "dev": true, "requires": { - "is-extendable": "^0.1.0" + "is-extendable": "0.1.1" } } } @@ -799,36 +799,36 @@ "resolved": "https://registry.npmjs.org/express/-/express-4.16.3.tgz", "integrity": "sha1-avilAjUNsyRuzEvs9rWjTSL37VM=", "requires": { - "accepts": "~1.3.5", + "accepts": "1.3.5", "array-flatten": "1.1.1", "body-parser": "1.18.2", "content-disposition": "0.5.2", - "content-type": "~1.0.4", + "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", + "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", + "methods": "1.1.2", + "on-finished": "2.3.0", + "parseurl": "1.3.2", "path-to-regexp": "0.1.7", - "proxy-addr": "~2.0.3", + "proxy-addr": "2.0.3", "qs": "6.5.1", - "range-parser": "~1.2.0", + "range-parser": "1.2.0", "safe-buffer": "5.1.1", "send": "0.16.2", "serve-static": "1.13.2", "setprototypeof": "1.1.0", - "statuses": "~1.4.0", - "type-is": "~1.6.16", + "statuses": "1.4.0", + "type-is": "1.6.16", "utils-merge": "1.0.1", - "vary": "~1.1.2" + "vary": "1.1.2" }, "dependencies": { "body-parser": { @@ -837,15 +837,15 @@ "integrity": "sha1-h2eKGdhLR9hZuDGZvVm84iKxBFQ=", "requires": { "bytes": "3.0.0", - "content-type": "~1.0.4", + "content-type": "1.0.4", "debug": "2.6.9", - "depd": "~1.1.1", - "http-errors": "~1.6.2", + "depd": "1.1.2", + "http-errors": "1.6.3", "iconv-lite": "0.4.19", - "on-finished": "~2.3.0", + "on-finished": "2.3.0", "qs": "6.5.1", "raw-body": "2.3.2", - "type-is": "~1.6.15" + "type-is": "1.6.16" } }, "cookie": { @@ -895,7 +895,7 @@ "depd": "1.1.1", "inherits": "2.0.3", "setprototypeof": "1.0.3", - "statuses": ">= 1.3.1 < 2" + "statuses": "1.4.0" } }, "setprototypeof": { @@ -918,8 +918,8 @@ "integrity": "sha1-Jqcarwc7OfshJxcnRhMcJwQCjbg=", "dev": true, "requires": { - "assign-symbols": "^1.0.0", - "is-extendable": "^1.0.1" + "assign-symbols": "1.0.0", + "is-extendable": "1.0.1" }, "dependencies": { "is-extendable": { @@ -928,7 +928,7 @@ "integrity": "sha512-arnXMxT1hhoKo9k1LZdmlNyJdDDfy2v0fXjFlmok4+i8ul/6WlbVge9bhM74OpNPQPMGUToDtz+KXa1PneJxOA==", "dev": true, "requires": { - "is-plain-object": "^2.0.4" + "is-plain-object": "2.0.4" } } } @@ -939,14 +939,14 @@ "integrity": "sha512-Nmb6QXkELsuBr24CJSkilo6UHHgbekK5UiZgfE6UHD3Eb27YC6oD+bhcT+tJ6cl8dmsgdQxnWlcry8ksBIBLpw==", "dev": true, "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" + "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.2", + "snapdragon": "0.8.2", + "to-regex": "3.0.2" }, "dependencies": { "define-property": { @@ -955,7 +955,7 @@ "integrity": "sha1-dp66rz9KY6rTr56NMEybvnm/sOY=", "dev": true, "requires": { - "is-descriptor": "^1.0.0" + "is-descriptor": "1.0.2" } }, "extend-shallow": { @@ -964,7 +964,7 @@ "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", "dev": true, "requires": { - "is-extendable": "^0.1.0" + "is-extendable": "0.1.1" } }, "is-accessor-descriptor": { @@ -973,7 +973,7 @@ "integrity": "sha512-m5hnHTkcVsPfqx3AKlyttIPb7J+XykHvJP2B9bZDjlhLIoEq4XoK64Vg7boZlVWYK6LUY94dYPEE7Lh0ZkZKcQ==", "dev": true, "requires": { - "kind-of": "^6.0.0" + "kind-of": "6.0.2" } }, "is-data-descriptor": { @@ -982,7 +982,7 @@ "integrity": "sha512-jbRXy1FmtAoCjQkVmIVYwuuqDFUbaOeDjmed1tOGPrsMhtJA4rD9tkgA0F1qJ3gRFRXcHYVkdeaP50Q5rE/jLQ==", "dev": true, "requires": { - "kind-of": "^6.0.0" + "kind-of": "6.0.2" } }, "is-descriptor": { @@ -991,9 +991,9 @@ "integrity": "sha512-2eis5WqQGV7peooDyLmNEPUrps9+SXX5c9pL3xEB+4e9HnGuDa7mB7kHxHw4CbqS9k1T2hOH3miL8n8WtiYVtg==", "dev": true, "requires": { - "is-accessor-descriptor": "^1.0.0", - "is-data-descriptor": "^1.0.0", - "kind-of": "^6.0.2" + "is-accessor-descriptor": "1.0.0", + "is-data-descriptor": "1.0.0", + "kind-of": "6.0.2" } } } @@ -1004,10 +1004,10 @@ "integrity": "sha1-1USBHUKPmOsGpj3EAtJAPDKMOPc=", "dev": true, "requires": { - "extend-shallow": "^2.0.1", - "is-number": "^3.0.0", - "repeat-string": "^1.6.1", - "to-regex-range": "^2.1.0" + "extend-shallow": "2.0.1", + "is-number": "3.0.0", + "repeat-string": "1.6.1", + "to-regex-range": "2.1.1" }, "dependencies": { "extend-shallow": { @@ -1016,7 +1016,7 @@ "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", "dev": true, "requires": { - "is-extendable": "^0.1.0" + "is-extendable": "0.1.1" } } } @@ -1027,12 +1027,12 @@ "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" + "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" }, "dependencies": { "debug": { @@ -1067,7 +1067,7 @@ "integrity": "sha1-QpD60n8T6Jvn8zeZxrxaCr//DRk=", "dev": true, "requires": { - "map-cache": "^0.2.2" + "map-cache": "0.2.2" } }, "fresh": { @@ -1094,8 +1094,8 @@ "dev": true, "optional": true, "requires": { - "nan": "^2.9.2", - "node-pre-gyp": "^0.10.0" + "nan": "2.10.0", + "node-pre-gyp": "0.10.0" }, "dependencies": { "abbrev": { @@ -1121,8 +1121,8 @@ "dev": true, "optional": true, "requires": { - "delegates": "^1.0.0", - "readable-stream": "^2.0.6" + "delegates": "1.0.0", + "readable-stream": "2.3.6" } }, "balanced-match": { @@ -1135,7 +1135,7 @@ "bundled": true, "dev": true, "requires": { - "balanced-match": "^1.0.0", + "balanced-match": "1.0.0", "concat-map": "0.0.1" } }, @@ -1199,7 +1199,7 @@ "dev": true, "optional": true, "requires": { - "minipass": "^2.2.1" + "minipass": "2.2.4" } }, "fs.realpath": { @@ -1214,14 +1214,14 @@ "dev": 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" + "aproba": "1.2.0", + "console-control-strings": "1.1.0", + "has-unicode": "2.0.1", + "object-assign": "4.1.1", + "signal-exit": "3.0.2", + "string-width": "1.0.2", + "strip-ansi": "3.0.1", + "wide-align": "1.1.2" } }, "glob": { @@ -1230,12 +1230,12 @@ "dev": 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" + "fs.realpath": "1.0.0", + "inflight": "1.0.6", + "inherits": "2.0.3", + "minimatch": "3.0.4", + "once": "1.4.0", + "path-is-absolute": "1.0.1" } }, "has-unicode": { @@ -1250,7 +1250,7 @@ "dev": true, "optional": true, "requires": { - "safer-buffer": "^2.1.0" + "safer-buffer": "2.1.2" } }, "ignore-walk": { @@ -1259,7 +1259,7 @@ "dev": true, "optional": true, "requires": { - "minimatch": "^3.0.4" + "minimatch": "3.0.4" } }, "inflight": { @@ -1268,8 +1268,8 @@ "dev": true, "optional": true, "requires": { - "once": "^1.3.0", - "wrappy": "1" + "once": "1.4.0", + "wrappy": "1.0.2" } }, "inherits": { @@ -1288,7 +1288,7 @@ "bundled": true, "dev": true, "requires": { - "number-is-nan": "^1.0.0" + "number-is-nan": "1.0.1" } }, "isarray": { @@ -1302,7 +1302,7 @@ "bundled": true, "dev": true, "requires": { - "brace-expansion": "^1.1.7" + "brace-expansion": "1.1.11" } }, "minimist": { @@ -1315,8 +1315,8 @@ "bundled": true, "dev": true, "requires": { - "safe-buffer": "^5.1.1", - "yallist": "^3.0.0" + "safe-buffer": "5.1.1", + "yallist": "3.0.2" } }, "minizlib": { @@ -1325,7 +1325,7 @@ "dev": true, "optional": true, "requires": { - "minipass": "^2.2.1" + "minipass": "2.2.4" } }, "mkdirp": { @@ -1355,9 +1355,9 @@ "dev": true, "optional": true, "requires": { - "debug": "^2.1.2", - "iconv-lite": "^0.4.4", - "sax": "^1.2.4" + "debug": "2.6.9", + "iconv-lite": "0.4.21", + "sax": "1.2.4" } }, "node-pre-gyp": { @@ -1366,16 +1366,16 @@ "dev": true, "optional": true, "requires": { - "detect-libc": "^1.0.2", - "mkdirp": "^0.5.1", - "needle": "^2.2.0", - "nopt": "^4.0.1", - "npm-packlist": "^1.1.6", - "npmlog": "^4.0.2", - "rc": "^1.1.7", - "rimraf": "^2.6.1", - "semver": "^5.3.0", - "tar": "^4" + "detect-libc": "1.0.3", + "mkdirp": "0.5.1", + "needle": "2.2.0", + "nopt": "4.0.1", + "npm-packlist": "1.1.10", + "npmlog": "4.1.2", + "rc": "1.2.7", + "rimraf": "2.6.2", + "semver": "5.5.0", + "tar": "4.4.1" } }, "nopt": { @@ -1384,8 +1384,8 @@ "dev": true, "optional": true, "requires": { - "abbrev": "1", - "osenv": "^0.1.4" + "abbrev": "1.1.1", + "osenv": "0.1.5" } }, "npm-bundled": { @@ -1400,8 +1400,8 @@ "dev": true, "optional": true, "requires": { - "ignore-walk": "^3.0.1", - "npm-bundled": "^1.0.1" + "ignore-walk": "3.0.1", + "npm-bundled": "1.0.3" } }, "npmlog": { @@ -1410,10 +1410,10 @@ "dev": 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" + "are-we-there-yet": "1.1.4", + "console-control-strings": "1.1.0", + "gauge": "2.7.4", + "set-blocking": "2.0.0" } }, "number-is-nan": { @@ -1432,7 +1432,7 @@ "bundled": true, "dev": true, "requires": { - "wrappy": "1" + "wrappy": "1.0.2" } }, "os-homedir": { @@ -1453,8 +1453,8 @@ "dev": true, "optional": true, "requires": { - "os-homedir": "^1.0.0", - "os-tmpdir": "^1.0.0" + "os-homedir": "1.0.2", + "os-tmpdir": "1.0.2" } }, "path-is-absolute": { @@ -1475,10 +1475,10 @@ "dev": true, "optional": true, "requires": { - "deep-extend": "^0.5.1", - "ini": "~1.3.0", - "minimist": "^1.2.0", - "strip-json-comments": "~2.0.1" + "deep-extend": "0.5.1", + "ini": "1.3.5", + "minimist": "1.2.0", + "strip-json-comments": "2.0.1" }, "dependencies": { "minimist": { @@ -1495,13 +1495,13 @@ "dev": 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" + "core-util-is": "1.0.2", + "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.2" } }, "rimraf": { @@ -1510,7 +1510,7 @@ "dev": true, "optional": true, "requires": { - "glob": "^7.0.5" + "glob": "7.1.2" } }, "safe-buffer": { @@ -1553,9 +1553,9 @@ "bundled": true, "dev": true, "requires": { - "code-point-at": "^1.0.0", - "is-fullwidth-code-point": "^1.0.0", - "strip-ansi": "^3.0.0" + "code-point-at": "1.1.0", + "is-fullwidth-code-point": "1.0.0", + "strip-ansi": "3.0.1" } }, "string_decoder": { @@ -1564,7 +1564,7 @@ "dev": true, "optional": true, "requires": { - "safe-buffer": "~5.1.0" + "safe-buffer": "5.1.1" } }, "strip-ansi": { @@ -1572,7 +1572,7 @@ "bundled": true, "dev": true, "requires": { - "ansi-regex": "^2.0.0" + "ansi-regex": "2.1.1" } }, "strip-json-comments": { @@ -1587,13 +1587,13 @@ "dev": true, "optional": true, "requires": { - "chownr": "^1.0.1", - "fs-minipass": "^1.2.5", - "minipass": "^2.2.4", - "minizlib": "^1.1.0", - "mkdirp": "^0.5.0", - "safe-buffer": "^5.1.1", - "yallist": "^3.0.2" + "chownr": "1.0.1", + "fs-minipass": "1.2.5", + "minipass": "2.2.4", + "minizlib": "1.1.0", + "mkdirp": "0.5.1", + "safe-buffer": "5.1.1", + "yallist": "3.0.2" } }, "util-deprecate": { @@ -1608,7 +1608,7 @@ "dev": true, "optional": true, "requires": { - "string-width": "^1.0.2" + "string-width": "1.0.2" } }, "wrappy": { @@ -1647,12 +1647,12 @@ "integrity": "sha512-MJTUg1kjuLeQCJ+ccE4Vpa6kKVXkPYJ2mOCQyUuKLcLQsdrMCpBPUi8qVE6+YuaJkozeA9NusTAw3hLr8Xe5EQ==", "dev": 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" + "fs.realpath": "1.0.0", + "inflight": "1.0.6", + "inherits": "2.0.3", + "minimatch": "3.0.4", + "once": "1.4.0", + "path-is-absolute": "1.0.1" } }, "glob-parent": { @@ -1661,8 +1661,8 @@ "integrity": "sha1-nmr2KZ2NO9K9QEMIMr0RPfkGxa4=", "dev": true, "requires": { - "is-glob": "^3.1.0", - "path-dirname": "^1.0.0" + "is-glob": "3.1.0", + "path-dirname": "1.0.2" }, "dependencies": { "is-glob": { @@ -1671,7 +1671,7 @@ "integrity": "sha1-e6WuJCF4BKxwcHuWkiVnSGzD6Eo=", "dev": true, "requires": { - "is-extglob": "^2.1.0" + "is-extglob": "2.1.1" } } } @@ -1682,7 +1682,7 @@ "integrity": "sha1-sxnA3UYH81PzvpzKTHL8FIxJ9EU=", "dev": true, "requires": { - "ini": "^1.3.4" + "ini": "1.3.5" } }, "got": { @@ -1691,17 +1691,17 @@ "integrity": "sha1-JAzQV4WpoY5WHcG0S0HHY+8ejbA=", "dev": true, "requires": { - "create-error-class": "^3.0.0", - "duplexer3": "^0.1.4", - "get-stream": "^3.0.0", - "is-redirect": "^1.0.0", - "is-retry-allowed": "^1.0.0", - "is-stream": "^1.0.0", - "lowercase-keys": "^1.0.0", - "safe-buffer": "^5.0.1", - "timed-out": "^4.0.0", - "unzip-response": "^2.0.1", - "url-parse-lax": "^1.0.0" + "create-error-class": "3.0.2", + "duplexer3": "0.1.4", + "get-stream": "3.0.0", + "is-redirect": "1.0.0", + "is-retry-allowed": "1.1.0", + "is-stream": "1.1.0", + "lowercase-keys": "1.0.1", + "safe-buffer": "5.1.1", + "timed-out": "4.0.1", + "unzip-response": "2.0.1", + "url-parse-lax": "1.0.0" } }, "graceful-fs": { @@ -1728,9 +1728,9 @@ "integrity": "sha1-GLKB2lhbHFxR3vJMkw7SmgvmsXc=", "dev": true, "requires": { - "get-value": "^2.0.6", - "has-values": "^1.0.0", - "isobject": "^3.0.0" + "get-value": "2.0.6", + "has-values": "1.0.0", + "isobject": "3.0.1" } }, "has-values": { @@ -1739,8 +1739,8 @@ "integrity": "sha1-lbC2P+whRmGab+V/51Yo1aOe/k8=", "dev": true, "requires": { - "is-number": "^3.0.0", - "kind-of": "^4.0.0" + "is-number": "3.0.0", + "kind-of": "4.0.0" }, "dependencies": { "kind-of": { @@ -1749,7 +1749,7 @@ "integrity": "sha1-IIE989cSkosgc3hpGkUGb65y3Vc=", "dev": true, "requires": { - "is-buffer": "^1.1.5" + "is-buffer": "1.1.6" } } } @@ -1765,10 +1765,10 @@ "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-1.6.3.tgz", "integrity": "sha1-i1VoC7S+KDoLW/TqLjhYC+HZMg0=", "requires": { - "depd": "~1.1.2", + "depd": "1.1.2", "inherits": "2.0.3", "setprototypeof": "1.1.0", - "statuses": ">= 1.4.0 < 2" + "statuses": "1.5.0" } }, "iconv-lite": { @@ -1776,7 +1776,7 @@ "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" + "safer-buffer": "2.1.2" } }, "ieee754": { @@ -1808,8 +1808,8 @@ "integrity": "sha1-Sb1jMdfQLQwJvJEKEHW6gWW1bfk=", "dev": true, "requires": { - "once": "^1.3.0", - "wrappy": "1" + "once": "1.4.0", + "wrappy": "1.0.2" } }, "inherits": { @@ -1834,7 +1834,7 @@ "integrity": "sha1-qeEss66Nh2cn7u84Q/igiXtcmNY=", "dev": true, "requires": { - "kind-of": "^3.0.2" + "kind-of": "3.2.2" }, "dependencies": { "kind-of": { @@ -1843,7 +1843,7 @@ "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", "dev": true, "requires": { - "is-buffer": "^1.1.5" + "is-buffer": "1.1.6" } } } @@ -1854,7 +1854,7 @@ "integrity": "sha1-dfFmQrSA8YenEcgUFh/TpKdlWJg=", "dev": true, "requires": { - "binary-extensions": "^1.0.0" + "binary-extensions": "1.11.0" } }, "is-buffer": { @@ -1869,7 +1869,7 @@ "integrity": "sha512-c7TnwxLePuqIlxHgr7xtxzycJPegNHFuIrBkwbf8hc58//+Op1CqFkyS+xnIMkwn9UsJIwc174BIjkyBmSpjKg==", "dev": true, "requires": { - "ci-info": "^1.0.0" + "ci-info": "1.1.3" } }, "is-data-descriptor": { @@ -1878,7 +1878,7 @@ "integrity": "sha1-C17mSDiOLIYCgueT8YVv7D8wG1Y=", "dev": true, "requires": { - "kind-of": "^3.0.2" + "kind-of": "3.2.2" }, "dependencies": { "kind-of": { @@ -1887,7 +1887,7 @@ "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", "dev": true, "requires": { - "is-buffer": "^1.1.5" + "is-buffer": "1.1.6" } } } @@ -1898,9 +1898,9 @@ "integrity": "sha512-avDYr0SB3DwO9zsMov0gKCESFYqCnE4hq/4z3TdUlukEy5t9C0YRq7HLrsN52NAcqXKaepeCD0n+B0arnVG3Hg==", "dev": true, "requires": { - "is-accessor-descriptor": "^0.1.6", - "is-data-descriptor": "^0.1.4", - "kind-of": "^5.0.0" + "is-accessor-descriptor": "0.1.6", + "is-data-descriptor": "0.1.4", + "kind-of": "5.1.0" }, "dependencies": { "kind-of": { @@ -1935,7 +1935,7 @@ "integrity": "sha1-lSHHaEXMJhCoUgPd8ICpWML/q8A=", "dev": true, "requires": { - "is-extglob": "^2.1.1" + "is-extglob": "2.1.1" } }, "is-installed-globally": { @@ -1944,8 +1944,8 @@ "integrity": "sha1-Df2Y9akRFxbdU13aZJL2e/PSWoA=", "dev": true, "requires": { - "global-dirs": "^0.1.0", - "is-path-inside": "^1.0.0" + "global-dirs": "0.1.1", + "is-path-inside": "1.0.1" } }, "is-npm": { @@ -1960,7 +1960,7 @@ "integrity": "sha1-JP1iAaR4LPUFYcgQJ2r8fRLXEZU=", "dev": true, "requires": { - "kind-of": "^3.0.2" + "kind-of": "3.2.2" }, "dependencies": { "kind-of": { @@ -1969,7 +1969,7 @@ "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", "dev": true, "requires": { - "is-buffer": "^1.1.5" + "is-buffer": "1.1.6" } } } @@ -1986,7 +1986,7 @@ "integrity": "sha512-OTiixgpZAT1M4NHgS5IguFp/Vz2VI3U7Goh4/HA1adtwyLtSBrxYlcSYkhpAE07s4fKEcjrFxyvtQBND4vFQyQ==", "dev": true, "requires": { - "is-number": "^4.0.0" + "is-number": "4.0.0" }, "dependencies": { "is-number": { @@ -2003,7 +2003,7 @@ "integrity": "sha1-jvW33lBDej/cprToZe96pVy0gDY=", "dev": true, "requires": { - "path-is-inside": "^1.0.1" + "path-is-inside": "1.0.2" } }, "is-plain-object": { @@ -2012,7 +2012,7 @@ "integrity": "sha512-h5PpgXkWitc38BBMYawTYMWJHFZJVnBquFE57xFpjB8pJFiF6gZ+bU+WyI/yqXiFR5mdLsgYNaPe8uao6Uv9Og==", "dev": true, "requires": { - "isobject": "^3.0.1" + "isobject": "3.0.1" } }, "is-redirect": { @@ -2074,7 +2074,7 @@ "integrity": "sha1-ogU4P+oyKzO1rjsYq+4NwvNW7hU=", "dev": true, "requires": { - "package-json": "^4.0.0" + "package-json": "4.0.1" } }, "lodash": { @@ -2094,8 +2094,8 @@ "integrity": "sha512-fFEhvcgzuIoJVUF8fYr5KR0YqxD238zgObTps31YdADwPPAp82a4M8TrckkWyx7ekNlf9aBcVn81cFwwXngrJA==", "dev": true, "requires": { - "pseudomap": "^1.0.2", - "yallist": "^2.1.2" + "pseudomap": "1.0.2", + "yallist": "2.1.2" } }, "make-dir": { @@ -2104,7 +2104,7 @@ "integrity": "sha512-2w31R7SJtieJJnQtGc7RVL2StM2vGYVfqUOvUDxH6bC6aJTxPxTF0GnIgCyu7tjockiUWAYQRbxa7vKn34s5sQ==", "dev": true, "requires": { - "pify": "^3.0.0" + "pify": "3.0.0" } }, "map-cache": { @@ -2125,7 +2125,7 @@ "integrity": "sha1-7Nyo8TFE5mDxtb1B8S80edmN+48=", "dev": true, "requires": { - "object-visit": "^1.0.0" + "object-visit": "1.0.1" } }, "media-typer": { @@ -2154,19 +2154,19 @@ "integrity": "sha512-MWikgl9n9M3w+bpsY3He8L+w9eF9338xRl8IAO5viDizwSzziFEyUzo2xrrloB64ADbTf8uA8vRqqttDTOmccg==", "dev": true, "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" + "arr-diff": "4.0.0", + "array-unique": "0.3.2", + "braces": "2.3.2", + "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.2", + "snapdragon": "0.8.2", + "to-regex": "3.0.2" } }, "mime": { @@ -2184,7 +2184,7 @@ "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.18.tgz", "integrity": "sha512-lc/aahn+t4/SWV/qcmumYjymLsWfN3ELhpmVuUFjgsORruuZPVSwAQryq+HHGvO/SI2KVX26bx+En+zhM8g8hQ==", "requires": { - "mime-db": "~1.33.0" + "mime-db": "1.33.0" } }, "minimatch": { @@ -2193,7 +2193,7 @@ "integrity": "sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA==", "dev": true, "requires": { - "brace-expansion": "^1.1.7" + "brace-expansion": "1.1.11" } }, "minimist": { @@ -2208,8 +2208,8 @@ "integrity": "sha512-8ZItLHeEgaqEvd5lYBXfm4EZSFCX29Jb9K+lAHhDKzReKBQKj3R+7NOF6tjqYi9t4oI8VUfaWITJQm86wnXGNQ==", "dev": true, "requires": { - "for-in": "^1.0.2", - "is-extendable": "^1.0.1" + "for-in": "1.0.2", + "is-extendable": "1.0.1" }, "dependencies": { "is-extendable": { @@ -2218,7 +2218,7 @@ "integrity": "sha512-arnXMxT1hhoKo9k1LZdmlNyJdDDfy2v0fXjFlmok4+i8ul/6WlbVge9bhM74OpNPQPMGUToDtz+KXa1PneJxOA==", "dev": true, "requires": { - "is-plain-object": "^2.0.4" + "is-plain-object": "2.0.4" } } } @@ -2261,11 +2261,11 @@ "resolved": "https://registry.npmjs.org/morgan/-/morgan-1.9.0.tgz", "integrity": "sha1-0B+mxlhZt2/PMbPLU6OCGjEdgFE=", "requires": { - "basic-auth": "~2.0.0", + "basic-auth": "2.0.0", "debug": "2.6.9", - "depd": "~1.1.1", - "on-finished": "~2.3.0", - "on-headers": "~1.0.1" + "depd": "1.1.2", + "on-finished": "2.3.0", + "on-headers": "1.0.1" }, "dependencies": { "debug": { @@ -2294,18 +2294,18 @@ "integrity": "sha512-n8R9bS8yQ6eSXaV6jHUpKzD8gLsin02w1HSFiegwrs9E098Ylhw5jdyKPaYqvHknHaSCKTPp7C8dGCQ0q9koXA==", "dev": true, "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-odd": "^2.0.0", - "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" + "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-odd": "2.0.0", + "is-windows": "1.0.2", + "kind-of": "6.0.2", + "object.pick": "1.3.0", + "regex-not": "1.0.2", + "snapdragon": "0.8.2", + "to-regex": "3.0.2" } }, "negotiator": { @@ -2319,16 +2319,16 @@ "integrity": "sha512-ZQcvYd8I4sWhbLoqImIiCPBTaHcq3YRRQXYwePchFK3Zk5+LT8HYQR4urf1UkSDiY/gSxjq6ao34RxJp7rRe1w==", "dev": true, "requires": { - "chokidar": "^2.0.2", - "debug": "^3.1.0", - "ignore-by-default": "^1.0.1", - "minimatch": "^3.0.4", - "pstree.remy": "^1.1.0", - "semver": "^5.5.0", - "supports-color": "^5.2.0", - "touch": "^3.1.0", - "undefsafe": "^2.0.2", - "update-notifier": "^2.3.0" + "chokidar": "2.0.3", + "debug": "3.1.0", + "ignore-by-default": "1.0.1", + "minimatch": "3.0.4", + "pstree.remy": "1.1.0", + "semver": "5.5.0", + "supports-color": "5.4.0", + "touch": "3.1.0", + "undefsafe": "2.0.2", + "update-notifier": "2.5.0" }, "dependencies": { "has-flag": { @@ -2343,7 +2343,7 @@ "integrity": "sha512-zjaXglF5nnWpsq470jSv6P9DwPvgLkuapYmfDm3JWOm0vkNTVF2tI4UrN2r6jH1qM/uc/WtxYY1hYoA2dOKj5w==", "dev": true, "requires": { - "has-flag": "^3.0.0" + "has-flag": "3.0.0" } } } @@ -2354,7 +2354,7 @@ "integrity": "sha1-bd0hvSoxQXuScn3Vhfim83YI6+4=", "dev": true, "requires": { - "abbrev": "1" + "abbrev": "1.1.1" } }, "normalize-path": { @@ -2363,7 +2363,7 @@ "integrity": "sha1-GrKLVW4Zg2Oowab35vogE3/mrtk=", "dev": true, "requires": { - "remove-trailing-separator": "^1.0.1" + "remove-trailing-separator": "1.1.0" } }, "npm-run-path": { @@ -2372,7 +2372,7 @@ "integrity": "sha1-NakjLfo11wZ7TLLd8jV7GHFTbF8=", "dev": true, "requires": { - "path-key": "^2.0.0" + "path-key": "2.0.1" } }, "object-copy": { @@ -2381,9 +2381,9 @@ "integrity": "sha1-fn2Fi3gb18mRpBupde04EnVOmYw=", "dev": true, "requires": { - "copy-descriptor": "^0.1.0", - "define-property": "^0.2.5", - "kind-of": "^3.0.3" + "copy-descriptor": "0.1.1", + "define-property": "0.2.5", + "kind-of": "3.2.2" }, "dependencies": { "define-property": { @@ -2392,7 +2392,7 @@ "integrity": "sha1-w1se+RjsPJkPmlvFe+BKrOxcgRY=", "dev": true, "requires": { - "is-descriptor": "^0.1.0" + "is-descriptor": "0.1.6" } }, "kind-of": { @@ -2401,7 +2401,7 @@ "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", "dev": true, "requires": { - "is-buffer": "^1.1.5" + "is-buffer": "1.1.6" } } } @@ -2412,7 +2412,7 @@ "integrity": "sha1-95xEk68MU3e1n+OdOV5BBC3QRbs=", "dev": true, "requires": { - "isobject": "^3.0.0" + "isobject": "3.0.1" } }, "object.pick": { @@ -2421,7 +2421,7 @@ "integrity": "sha1-h6EKxMFpS9Lhy/U1kaZhQftd10c=", "dev": true, "requires": { - "isobject": "^3.0.1" + "isobject": "3.0.1" } }, "on-finished": { @@ -2443,7 +2443,7 @@ "integrity": "sha1-WDsap3WWHUsROsF9nFC6753Xa9E=", "dev": true, "requires": { - "wrappy": "1" + "wrappy": "1.0.2" } }, "p-finally": { @@ -2458,10 +2458,10 @@ "integrity": "sha1-iGmgQBJTZhxMTKPabCEh7VVfXu0=", "dev": true, "requires": { - "got": "^6.7.1", - "registry-auth-token": "^3.0.1", - "registry-url": "^3.0.3", - "semver": "^5.1.0" + "got": "6.7.1", + "registry-auth-token": "3.3.2", + "registry-url": "3.1.0", + "semver": "5.5.0" } }, "parseurl": { @@ -2516,7 +2516,7 @@ "integrity": "sha1-/lo0sMvOErWqaitAPuLnO2AvFEU=", "dev": true, "requires": { - "through": "~2.3" + "through": "2.3.8" } }, "pify": { @@ -2548,7 +2548,7 @@ "resolved": "https://registry.npmjs.org/promise/-/promise-6.1.0.tgz", "integrity": "sha1-LOcp9rlLRcJoka0GAsXJDgTG7vY=", "requires": { - "asap": "~1.0.0" + "asap": "1.0.0" } }, "proxy-addr": { @@ -2556,7 +2556,7 @@ "resolved": "https://registry.npmjs.org/proxy-addr/-/proxy-addr-2.0.3.tgz", "integrity": "sha512-jQTChiCJteusULxjBp8+jftSQE5Obdl3k4cnmLA6WXtK6XFuWRnvVL7aCiBqaLPM8c4ph0S4tKna8XvmIwEnXQ==", "requires": { - "forwarded": "~0.1.2", + "forwarded": "0.1.2", "ipaddr.js": "1.6.0" } }, @@ -2566,7 +2566,7 @@ "integrity": "sha1-tCGyQUDWID8e08dplrRCewjowBQ=", "dev": true, "requires": { - "event-stream": "~3.3.0" + "event-stream": "3.3.4" } }, "pseudomap": { @@ -2581,7 +2581,7 @@ "integrity": "sha512-q5I5vLRMVtdWa8n/3UEzZX7Lfghzrg9eG2IKk2ENLSofKRCXVqMvMUHxCKgXNaqH/8ebhBxrqftHWnyTFweJ5Q==", "dev": true, "requires": { - "ps-tree": "^1.1.0" + "ps-tree": "1.1.0" } }, "punycode": { @@ -2621,10 +2621,10 @@ "integrity": "sha512-LdLD8xD4zzLsAT5xyushXDNscEjB7+2ulnl8+r1pnESlYtlJtVSoCMBGr30eDRJ3+2Gq89jK9P9e4tCEH1+ywA==", "dev": true, "requires": { - "deep-extend": "^0.5.1", - "ini": "~1.3.0", - "minimist": "^1.2.0", - "strip-json-comments": "~2.0.1" + "deep-extend": "0.5.1", + "ini": "1.3.5", + "minimist": "1.2.0", + "strip-json-comments": "2.0.1" }, "dependencies": { "minimist": { @@ -2641,13 +2641,13 @@ "integrity": "sha512-tQtKA9WIAhBF3+VLAseyMqZeBjW0AHJoxOtYqSUZNJxauErmLbVm2FW1y+J/YA9dUrAC39ITejlZWhVIwawkKw==", "dev": 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" + "core-util-is": "1.0.2", + "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.2" } }, "readdirp": { @@ -2656,10 +2656,10 @@ "integrity": "sha1-TtCtBg3zBzMAxIRANz9y0cxkLXg=", "dev": true, "requires": { - "graceful-fs": "^4.1.2", - "minimatch": "^3.0.2", - "readable-stream": "^2.0.2", - "set-immediate-shim": "^1.0.1" + "graceful-fs": "4.1.11", + "minimatch": "3.0.4", + "readable-stream": "2.3.6", + "set-immediate-shim": "1.0.1" } }, "regex-not": { @@ -2668,8 +2668,8 @@ "integrity": "sha512-J6SDjUgDxQj5NusnOtdFxDwN/+HWykR8GELwctJ7mdqhcyy1xEc4SRFHUXvxTp661YaVKAjfRLZ9cCqS6tn32A==", "dev": true, "requires": { - "extend-shallow": "^3.0.2", - "safe-regex": "^1.1.0" + "extend-shallow": "3.0.2", + "safe-regex": "1.1.0" } }, "registry-auth-token": { @@ -2678,8 +2678,8 @@ "integrity": "sha512-JL39c60XlzCVgNrO+qq68FoNb56w/m7JYvGR2jT5iR1xBrUA3Mfx5Twk5rqTThPmQKMWydGmq8oFtDlxfrmxnQ==", "dev": true, "requires": { - "rc": "^1.1.6", - "safe-buffer": "^5.0.1" + "rc": "1.2.7", + "safe-buffer": "5.1.1" } }, "registry-url": { @@ -2688,7 +2688,7 @@ "integrity": "sha1-PU74cPc93h138M+aOBQyRE4XSUI=", "dev": true, "requires": { - "rc": "^1.0.1" + "rc": "1.2.7" } }, "remove-trailing-separator": { @@ -2732,7 +2732,7 @@ "integrity": "sha1-QKNmnzsHfR6UPURinhV91IAjvy4=", "dev": true, "requires": { - "ret": "~0.1.10" + "ret": "0.1.15" } }, "safer-buffer": { @@ -2757,7 +2757,7 @@ "integrity": "sha1-S7uEN8jTfksM8aaP1ybsbWRdbTY=", "dev": true, "requires": { - "semver": "^5.0.3" + "semver": "5.5.0" } }, "send": { @@ -2766,18 +2766,18 @@ "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", + "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", + "http-errors": "1.6.3", "mime": "1.4.1", "ms": "2.0.0", - "on-finished": "~2.3.0", - "range-parser": "~1.2.0", - "statuses": "~1.4.0" + "on-finished": "2.3.0", + "range-parser": "1.2.0", + "statuses": "1.4.0" }, "dependencies": { "debug": { @@ -2800,10 +2800,10 @@ "resolved": "https://registry.npmjs.org/serve-favicon/-/serve-favicon-2.5.0.tgz", "integrity": "sha1-k10kDN/g9YBTB/3+ln2IlCosvPA=", "requires": { - "etag": "~1.8.1", + "etag": "1.8.1", "fresh": "0.5.2", "ms": "2.1.1", - "parseurl": "~1.3.2", + "parseurl": "1.3.2", "safe-buffer": "5.1.1" }, "dependencies": { @@ -2819,9 +2819,9 @@ "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", + "encodeurl": "1.0.2", + "escape-html": "1.0.3", + "parseurl": "1.3.2", "send": "0.16.2" } }, @@ -2837,10 +2837,10 @@ "integrity": "sha512-hw0yxk9GT/Hr5yJEYnHNKYXkIA8mVJgd9ditYZCe16ZczcaELYYcfvaXesNACk2O8O0nTiPQcQhGUQj8JLzeeg==", "dev": true, "requires": { - "extend-shallow": "^2.0.1", - "is-extendable": "^0.1.1", - "is-plain-object": "^2.0.3", - "split-string": "^3.0.1" + "extend-shallow": "2.0.1", + "is-extendable": "0.1.1", + "is-plain-object": "2.0.4", + "split-string": "3.1.0" }, "dependencies": { "extend-shallow": { @@ -2849,7 +2849,7 @@ "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", "dev": true, "requires": { - "is-extendable": "^0.1.0" + "is-extendable": "0.1.1" } } } @@ -2865,7 +2865,7 @@ "integrity": "sha1-RKrGW2lbAzmJaMOfNj/uXer98eo=", "dev": true, "requires": { - "shebang-regex": "^1.0.0" + "shebang-regex": "1.0.0" } }, "shebang-regex": { @@ -2885,7 +2885,7 @@ "resolved": "https://registry.npmjs.org/sleep/-/sleep-3.0.1.tgz", "integrity": "sha1-vk0XxXk2DgfgTtgXK6KxCmkFTfM=", "requires": { - "nan": ">=2.0.0" + "nan": "2.8.0" } }, "snapdragon": { @@ -2894,14 +2894,14 @@ "integrity": "sha512-FtyOnWN/wCHTVXOMwvSv26d+ko5vWlIDD6zoUJ7LW8vh+ZBC8QdljveRP+crNrtBwioEUWy/4dMtbBjA4ioNlg==", "dev": true, "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" + "base": "0.11.2", + "debug": "2.6.9", + "define-property": "0.2.5", + "extend-shallow": "2.0.1", + "map-cache": "0.2.2", + "source-map": "0.5.7", + "source-map-resolve": "0.5.2", + "use": "3.1.0" }, "dependencies": { "debug": { @@ -2919,7 +2919,7 @@ "integrity": "sha1-w1se+RjsPJkPmlvFe+BKrOxcgRY=", "dev": true, "requires": { - "is-descriptor": "^0.1.0" + "is-descriptor": "0.1.6" } }, "extend-shallow": { @@ -2928,7 +2928,7 @@ "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", "dev": true, "requires": { - "is-extendable": "^0.1.0" + "is-extendable": "0.1.1" } } } @@ -2939,9 +2939,9 @@ "integrity": "sha512-O27l4xaMYt/RSQ5TR3vpWCAB5Kb/czIcqUFOM/C4fYcLnbZUc1PkjTAMjof2pBWaSTwOUd6qUHcFGVGj7aIwnw==", "dev": true, "requires": { - "define-property": "^1.0.0", - "isobject": "^3.0.0", - "snapdragon-util": "^3.0.1" + "define-property": "1.0.0", + "isobject": "3.0.1", + "snapdragon-util": "3.0.1" }, "dependencies": { "define-property": { @@ -2950,7 +2950,7 @@ "integrity": "sha1-dp66rz9KY6rTr56NMEybvnm/sOY=", "dev": true, "requires": { - "is-descriptor": "^1.0.0" + "is-descriptor": "1.0.2" } }, "is-accessor-descriptor": { @@ -2959,7 +2959,7 @@ "integrity": "sha512-m5hnHTkcVsPfqx3AKlyttIPb7J+XykHvJP2B9bZDjlhLIoEq4XoK64Vg7boZlVWYK6LUY94dYPEE7Lh0ZkZKcQ==", "dev": true, "requires": { - "kind-of": "^6.0.0" + "kind-of": "6.0.2" } }, "is-data-descriptor": { @@ -2968,7 +2968,7 @@ "integrity": "sha512-jbRXy1FmtAoCjQkVmIVYwuuqDFUbaOeDjmed1tOGPrsMhtJA4rD9tkgA0F1qJ3gRFRXcHYVkdeaP50Q5rE/jLQ==", "dev": true, "requires": { - "kind-of": "^6.0.0" + "kind-of": "6.0.2" } }, "is-descriptor": { @@ -2977,9 +2977,9 @@ "integrity": "sha512-2eis5WqQGV7peooDyLmNEPUrps9+SXX5c9pL3xEB+4e9HnGuDa7mB7kHxHw4CbqS9k1T2hOH3miL8n8WtiYVtg==", "dev": true, "requires": { - "is-accessor-descriptor": "^1.0.0", - "is-data-descriptor": "^1.0.0", - "kind-of": "^6.0.2" + "is-accessor-descriptor": "1.0.0", + "is-data-descriptor": "1.0.0", + "kind-of": "6.0.2" } } } @@ -2990,7 +2990,7 @@ "integrity": "sha512-mbKkMdQKsjX4BAL4bRYTj21edOf8cN7XHdYUJEe+Zn99hVEYcMvKPct1IqNe7+AZPirn8BCDOQBHQZknqmKlZQ==", "dev": true, "requires": { - "kind-of": "^3.2.0" + "kind-of": "3.2.2" }, "dependencies": { "kind-of": { @@ -2999,7 +2999,7 @@ "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", "dev": true, "requires": { - "is-buffer": "^1.1.5" + "is-buffer": "1.1.6" } } } @@ -3016,11 +3016,11 @@ "integrity": "sha512-MjqsvNwyz1s0k81Goz/9vRBe9SZdB09Bdw+/zYyO+3CuPk6fouTaxscHkgtE8jKvf01kVfl8riHzERQ/kefaSA==", "dev": true, "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" + "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-url": { @@ -3035,7 +3035,7 @@ "integrity": "sha1-zQ7qXmOiEd//frDwkcQTPi0N0o8=", "dev": true, "requires": { - "through": "2" + "through": "2.3.8" } }, "split-string": { @@ -3044,7 +3044,7 @@ "integrity": "sha512-NzNVhJDYpwceVVii8/Hu6DKfD2G+NrQHlS/V/qgv763EYudVwEcMQNxd2lh+0VrUByXN/oJkl5grOhYWvQUYiw==", "dev": true, "requires": { - "extend-shallow": "^3.0.0" + "extend-shallow": "3.0.2" } }, "static-extend": { @@ -3053,8 +3053,8 @@ "integrity": "sha1-YICcOcv/VTNyJv1eC1IPNB8ftcY=", "dev": true, "requires": { - "define-property": "^0.2.5", - "object-copy": "^0.1.0" + "define-property": "0.2.5", + "object-copy": "0.1.0" }, "dependencies": { "define-property": { @@ -3063,7 +3063,7 @@ "integrity": "sha1-w1se+RjsPJkPmlvFe+BKrOxcgRY=", "dev": true, "requires": { - "is-descriptor": "^0.1.0" + "is-descriptor": "0.1.6" } } } @@ -3079,7 +3079,7 @@ "integrity": "sha1-TV5DPBhSYd3mI8o/RMWGvPXErRQ=", "dev": true, "requires": { - "duplexer": "~0.1.1" + "duplexer": "0.1.1" } }, "string-width": { @@ -3088,8 +3088,8 @@ "integrity": "sha512-nOqH59deCq9SRHlxq1Aw85Jnt4w6KvLKqWVik6oA9ZklXLNIOlqg4F2yrT1MVaTjAqvVwdfeZ7w7aCvJD7ugkw==", "dev": true, "requires": { - "is-fullwidth-code-point": "^2.0.0", - "strip-ansi": "^4.0.0" + "is-fullwidth-code-point": "2.0.0", + "strip-ansi": "4.0.0" } }, "string_decoder": { @@ -3098,7 +3098,7 @@ "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", "dev": true, "requires": { - "safe-buffer": "~5.1.0" + "safe-buffer": "5.1.1" } }, "strip-ansi": { @@ -3107,7 +3107,7 @@ "integrity": "sha1-qEeQIusaw2iocTibY1JixQXuNo8=", "dev": true, "requires": { - "ansi-regex": "^3.0.0" + "ansi-regex": "3.0.0" } }, "strip-eof": { @@ -3128,7 +3128,7 @@ "integrity": "sha512-rKC3+DyXWgK0ZLKwmRsrkyHVZAjNkfzeehuFWdGGcqGDTZFH73+RH6S/RDAAxl9GusSjZSUWYLmT9N5pzXFOXQ==", "dev": true, "requires": { - "has-flag": "^2.0.0" + "has-flag": "2.0.0" } }, "term-size": { @@ -3137,7 +3137,7 @@ "integrity": "sha1-RYuDiH8oj8Vtb/+/rSYuJmOO+mk=", "dev": true, "requires": { - "execa": "^0.7.0" + "execa": "0.7.0" } }, "through": { @@ -3158,7 +3158,7 @@ "integrity": "sha1-KXWIt7Dn4KwI4E5nL4XB9JmeF68=", "dev": true, "requires": { - "kind-of": "^3.0.2" + "kind-of": "3.2.2" }, "dependencies": { "kind-of": { @@ -3167,7 +3167,7 @@ "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", "dev": true, "requires": { - "is-buffer": "^1.1.5" + "is-buffer": "1.1.6" } } } @@ -3178,10 +3178,10 @@ "integrity": "sha512-FWtleNAtZ/Ki2qtqej2CXTOayOH9bHDQF+Q48VpWyDXjbYxA4Yz8iDB31zXOBUlOHHKidDbqGVrTUvQMPmBGBw==", "dev": true, "requires": { - "define-property": "^2.0.2", - "extend-shallow": "^3.0.2", - "regex-not": "^1.0.2", - "safe-regex": "^1.1.0" + "define-property": "2.0.2", + "extend-shallow": "3.0.2", + "regex-not": "1.0.2", + "safe-regex": "1.1.0" } }, "to-regex-range": { @@ -3190,8 +3190,8 @@ "integrity": "sha1-fIDBe53+vlmeJzZ+DU3VWQFB2zg=", "dev": true, "requires": { - "is-number": "^3.0.0", - "repeat-string": "^1.6.1" + "is-number": "3.0.0", + "repeat-string": "1.6.1" } }, "touch": { @@ -3200,7 +3200,7 @@ "integrity": "sha512-WBx8Uy5TLtOSRtIq+M03/sKDrXCLHxwDcquSP2c43Le03/9serjQBIztjRz6FkJez9D/hleyAXTBGLwwZUw9lA==", "dev": true, "requires": { - "nopt": "~1.0.10" + "nopt": "1.0.10" } }, "type-detect": { @@ -3215,7 +3215,7 @@ "integrity": "sha512-HRkVv/5qY2G6I8iab9cI7v1bOIdhm94dVjQCPFElW9W+3GeDOSHmy2EBYe4VTApuzolPcmgFTN3ftVJRKR2J9Q==", "requires": { "media-typer": "0.3.0", - "mime-types": "~2.1.18" + "mime-types": "2.1.18" } }, "undefsafe": { @@ -3224,7 +3224,7 @@ "integrity": "sha1-Il9rngM3Zj4Njnz9aG/Cg2zKznY=", "dev": true, "requires": { - "debug": "^2.2.0" + "debug": "2.6.9" }, "dependencies": { "debug": { @@ -3244,10 +3244,10 @@ "integrity": "sha1-XHHDTLW61dzr4+oM0IIHulqhrqQ=", "dev": true, "requires": { - "arr-union": "^3.1.0", - "get-value": "^2.0.6", - "is-extendable": "^0.1.1", - "set-value": "^0.4.3" + "arr-union": "3.1.0", + "get-value": "2.0.6", + "is-extendable": "0.1.1", + "set-value": "0.4.3" }, "dependencies": { "extend-shallow": { @@ -3256,7 +3256,7 @@ "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", "dev": true, "requires": { - "is-extendable": "^0.1.0" + "is-extendable": "0.1.1" } }, "set-value": { @@ -3265,10 +3265,10 @@ "integrity": "sha1-fbCPnT0i3H945Trzw79GZuzfzPE=", "dev": true, "requires": { - "extend-shallow": "^2.0.1", - "is-extendable": "^0.1.1", - "is-plain-object": "^2.0.1", - "to-object-path": "^0.3.0" + "extend-shallow": "2.0.1", + "is-extendable": "0.1.1", + "is-plain-object": "2.0.4", + "to-object-path": "0.3.0" } } } @@ -3279,7 +3279,7 @@ "integrity": "sha1-nhBXzKhRq7kzmPizOuGHuZyuwRo=", "dev": true, "requires": { - "crypto-random-string": "^1.0.0" + "crypto-random-string": "1.0.0" } }, "unpipe": { @@ -3293,8 +3293,8 @@ "integrity": "sha1-g3aHP30jNRef+x5vw6jtDfyKtVk=", "dev": true, "requires": { - "has-value": "^0.3.1", - "isobject": "^3.0.0" + "has-value": "0.3.1", + "isobject": "3.0.1" }, "dependencies": { "has-value": { @@ -3303,9 +3303,9 @@ "integrity": "sha1-ex9YutpiyoJ+wKIHgCVlSEWZXh8=", "dev": true, "requires": { - "get-value": "^2.0.3", - "has-values": "^0.1.4", - "isobject": "^2.0.0" + "get-value": "2.0.6", + "has-values": "0.1.4", + "isobject": "2.1.0" }, "dependencies": { "isobject": { @@ -3345,16 +3345,16 @@ "integrity": "sha512-gwMdhgJHGuj/+wHJJs9e6PcCszpxR1b236igrOkUofGhqJuG+amlIKwApH1IW1WWl7ovZxsX49lMBWLxSdm5Dw==", "dev": true, "requires": { - "boxen": "^1.2.1", - "chalk": "^2.0.1", - "configstore": "^3.0.0", - "import-lazy": "^2.1.0", - "is-ci": "^1.0.10", - "is-installed-globally": "^0.1.0", - "is-npm": "^1.0.0", - "latest-version": "^3.0.0", - "semver-diff": "^2.0.0", - "xdg-basedir": "^3.0.0" + "boxen": "1.3.0", + "chalk": "2.4.1", + "configstore": "3.1.2", + "import-lazy": "2.1.0", + "is-ci": "1.1.0", + "is-installed-globally": "0.1.0", + "is-npm": "1.0.0", + "latest-version": "3.1.0", + "semver-diff": "2.1.0", + "xdg-basedir": "3.0.0" } }, "urix": { @@ -3378,7 +3378,7 @@ "integrity": "sha1-evjzA2Rem9eaJy56FKxovAYJ2nM=", "dev": true, "requires": { - "prepend-http": "^1.0.1" + "prepend-http": "1.0.4" } }, "use": { @@ -3387,7 +3387,7 @@ "integrity": "sha512-6UJEQM/L+mzC3ZJNM56Q4DFGLX/evKGRg15UJHGB9X5j5Z3AFbgZvjUh2yq/UJUY4U5dh7Fal++XbNg1uzpRAw==", "dev": true, "requires": { - "kind-of": "^6.0.2" + "kind-of": "6.0.2" } }, "util-deprecate": { @@ -3417,7 +3417,7 @@ "integrity": "sha512-xcJpopdamTuY5duC/KnTTNBraPK54YwpenP4lzxU8H91GudWpFv38u0CKjclE1Wi2EH2EDz5LRcHcKbCIzqGyg==", "dev": true, "requires": { - "isexe": "^2.0.0" + "isexe": "2.0.0" } }, "widest-line": { @@ -3426,7 +3426,7 @@ "integrity": "sha1-AUKk6KJD+IgsAjOqDgKBqnYVInM=", "dev": true, "requires": { - "string-width": "^2.1.1" + "string-width": "2.1.1" } }, "wrappy": { @@ -3441,9 +3441,9 @@ "integrity": "sha512-xuPeK4OdjWqtfi59ylvVL0Yn35SF3zgcAcv7rBPFHVaEapaDr4GdGgm3j7ckTwH9wHL7fGmgfAnb0+THrHb8tA==", "dev": true, "requires": { - "graceful-fs": "^4.1.11", - "imurmurhash": "^0.1.4", - "signal-exit": "^3.0.2" + "graceful-fs": "4.1.11", + "imurmurhash": "0.1.4", + "signal-exit": "3.0.2" } }, "xdg-basedir": { @@ -3457,8 +3457,8 @@ "resolved": "https://registry.npmjs.org/xml2js/-/xml2js-0.4.17.tgz", "integrity": "sha1-F76T6q4/O3eTWceVtBlwWogX6Gg=", "requires": { - "sax": ">=0.6.0", - "xmlbuilder": "^4.1.0" + "sax": "1.2.1", + "xmlbuilder": "4.2.1" } }, "xmlbuilder": { @@ -3466,7 +3466,7 @@ "resolved": "https://registry.npmjs.org/xmlbuilder/-/xmlbuilder-4.2.1.tgz", "integrity": "sha1-qlijBBoGb5DqoWwvU4n/GfP0YaU=", "requires": { - "lodash": "^4.0.0" + "lodash": "4.17.10" } }, "yallist": { diff --git a/test/configSpec.js b/test/configSpec.js index 9244ccb..6019873 100644 --- a/test/configSpec.js +++ b/test/configSpec.js @@ -1,17 +1,40 @@ -const subject = require('../config/config'); const assert = require('chai').assert; +const CONFIG_MODULE_FILE_NAME = '../config/config'; +const DEFAULT_ENVIRONMENT_NAME = "Development"; describe('config', function() { it('should have default values', function() { - assert.equal(subject.port, 3000); - assert.equal(subject.clusterStateCacheTtl, 30 * 60 * 1000); - assert.equal(subject.aws.apiDelay, 100); - assert.equal(subject.aws.configFile, './aws_config.json'); - assert.equal(subject.aws.listInstancesPageSize, 100); - assert.equal(subject.aws.describeInstancesPageSize, 100); - assert.equal(subject.aws.listTasksPageSize, 100); - assert.equal(subject.aws.describeTasksPageSize, 100); - assert.equal(subject.aws.maxSimultaneousDescribeTasksCalls, 2); - assert.equal(subject.aws.maxSimultaneousDescribeTaskDefinitionCalls, 1); + const subject = require(CONFIG_MODULE_FILE_NAME); + checkDefaults(subject); + assert.equal(subject.environmentName, DEFAULT_ENVIRONMENT_NAME); + }); + it('should have default values when TARGET_ENV file is missing', function() { + process.env.TARGET_ENV = "missing_env"; + // Invalidate cached config module so we can load it again with new TARGET_ENV + delete require.cache[require.resolve(CONFIG_MODULE_FILE_NAME)]; + const subject = require(CONFIG_MODULE_FILE_NAME); + checkDefaults(subject); + assert.equal(subject.environmentName, DEFAULT_ENVIRONMENT_NAME); + }); + it('should override with environment values', function() { + process.env.TARGET_ENV = "test"; + // Invalidate cached config module so we can load it again with new TARGET_ENV + delete require.cache[require.resolve(CONFIG_MODULE_FILE_NAME)]; + const subject = require(CONFIG_MODULE_FILE_NAME); + checkDefaults(subject); + assert.equal(subject.environmentName, "Test"); }); }); + +function checkDefaults(subject) { + assert.equal(subject.port, 3000); + assert.equal(subject.clusterStateCacheTtl, 30 * 60 * 1000); + assert.equal(subject.aws.apiDelay, 100); + assert.equal(subject.aws.configFile, './aws_config.json'); + assert.equal(subject.aws.listInstancesPageSize, 100); + assert.equal(subject.aws.describeInstancesPageSize, 100); + assert.equal(subject.aws.listTasksPageSize, 100); + assert.equal(subject.aws.describeTasksPageSize, 100); + assert.equal(subject.aws.maxSimultaneousDescribeTasksCalls, 2); + assert.equal(subject.aws.maxSimultaneousDescribeTaskDefinitionCalls, 1); +}