diff --git a/Makefile b/Makefile
index c5dcc48cb..a3d17c501 100644
--- a/Makefile
+++ b/Makefile
@@ -51,6 +51,11 @@ client-test: client/test/*
-v $(shell pwd)/client/test:/home/weave/test \
$(SCOPE_UI_BUILD_IMAGE) npm test
+client-lint:
+ docker run -ti -v $(shell pwd)/client/app:/home/weave/app \
+ -v $(shell pwd)/client/test:/home/weave/test \
+ $(SCOPE_UI_BUILD_IMAGE) npm run lint
+
$(SCOPE_UI_BUILD_EXPORT): client/Dockerfile client/gulpfile.js client/package.json
docker build -t $(SCOPE_UI_BUILD_IMAGE) client
docker save $(SCOPE_UI_BUILD_IMAGE):latest > $@
diff --git a/client/.editorconfig b/client/.editorconfig
index 561df8158..62d19a839 100644
--- a/client/.editorconfig
+++ b/client/.editorconfig
@@ -9,7 +9,7 @@ root = true
# Change these settings to your own preference
indent_style = space
-indent_size = 4
+indent_size = 2
# We recommend you to keep these unchanged
end_of_line = lf
@@ -20,10 +20,3 @@ insert_final_newline = true
[*.md]
trim_trailing_whitespace = false
-[package.json]
-indent_style = space
-indent_size = 2
-
-[bower.json]
-indent_style = space
-indent_size = 2
diff --git a/client/.eslintignore b/client/.eslintignore
new file mode 100644
index 000000000..a2e2b2a31
--- /dev/null
+++ b/client/.eslintignore
@@ -0,0 +1 @@
+app/**/__tests__/*.js
diff --git a/client/.eslintrc b/client/.eslintrc
new file mode 100644
index 000000000..4dce480af
--- /dev/null
+++ b/client/.eslintrc
@@ -0,0 +1,213 @@
+{
+ "parser": "babel-eslint",
+ "plugins": [
+ "react",
+ "jasmine"
+ ],
+ "env": {
+ "browser": true,
+ "node": true
+ },
+ "ecmaFeatures": {
+ "arrowFunctions": true,
+ "blockBindings": true,
+ "classes": true,
+ "defaultParams": true,
+ "destructuring": true,
+ "forOf": true,
+ "generators": false,
+ "modules": true,
+ "objectLiteralComputedProperties": true,
+ "objectLiteralDuplicateProperties": false,
+ "objectLiteralShorthandMethods": true,
+ "objectLiteralShorthandProperties": true,
+ "spread": true,
+ "superInFunctions": true,
+ "templateStrings": true,
+ "jsx": true
+ },
+ "rules": {
+/**
+ * Strict mode
+ */
+ // babel inserts "use strict"; for us
+ // http://eslint.org/docs/rules/strict
+ "strict": [2, "never"],
+
+/**
+ * ES6
+ */
+ "no-var": 2, // http://eslint.org/docs/rules/no-var
+
+/**
+ * Variables
+ */
+ "no-shadow": 2, // http://eslint.org/docs/rules/no-shadow
+ "no-shadow-restricted-names": 2, // http://eslint.org/docs/rules/no-shadow-restricted-names
+ "no-unused-vars": [2, { // http://eslint.org/docs/rules/no-unused-vars
+ "vars": "local",
+ "args": "after-used"
+ }],
+ "no-use-before-define": 2, // http://eslint.org/docs/rules/no-use-before-define
+
+/**
+ * Possible errors
+ */
+ "comma-dangle": [2, "never"], // http://eslint.org/docs/rules/comma-dangle
+ "no-cond-assign": [2, "always"], // http://eslint.org/docs/rules/no-cond-assign
+ "no-console": 1, // http://eslint.org/docs/rules/no-console
+ "no-debugger": 1, // http://eslint.org/docs/rules/no-debugger
+ "no-alert": 1, // http://eslint.org/docs/rules/no-alert
+ "no-constant-condition": 1, // http://eslint.org/docs/rules/no-constant-condition
+ "no-dupe-keys": 2, // http://eslint.org/docs/rules/no-dupe-keys
+ "no-duplicate-case": 2, // http://eslint.org/docs/rules/no-duplicate-case
+ "no-empty": 2, // http://eslint.org/docs/rules/no-empty
+ "no-ex-assign": 2, // http://eslint.org/docs/rules/no-ex-assign
+ "no-extra-boolean-cast": 0, // http://eslint.org/docs/rules/no-extra-boolean-cast
+ "no-extra-semi": 2, // http://eslint.org/docs/rules/no-extra-semi
+ "no-func-assign": 2, // http://eslint.org/docs/rules/no-func-assign
+ "no-inner-declarations": 2, // http://eslint.org/docs/rules/no-inner-declarations
+ "no-invalid-regexp": 2, // http://eslint.org/docs/rules/no-invalid-regexp
+ "no-irregular-whitespace": 2, // http://eslint.org/docs/rules/no-irregular-whitespace
+ "no-obj-calls": 2, // http://eslint.org/docs/rules/no-obj-calls
+ "no-reserved-keys": 2, // http://eslint.org/docs/rules/no-reserved-keys
+ "no-sparse-arrays": 2, // http://eslint.org/docs/rules/no-sparse-arrays
+ "no-unreachable": 2, // http://eslint.org/docs/rules/no-unreachable
+ "use-isnan": 2, // http://eslint.org/docs/rules/use-isnan
+ "block-scoped-var": 2, // http://eslint.org/docs/rules/block-scoped-var
+
+/**
+ * Best practices
+ */
+ "consistent-return": 2, // http://eslint.org/docs/rules/consistent-return
+ "curly": [2, "multi-line"], // http://eslint.org/docs/rules/curly
+ "default-case": 2, // http://eslint.org/docs/rules/default-case
+ "dot-notation": [2, { // http://eslint.org/docs/rules/dot-notation
+ "allowKeywords": true
+ }],
+ "eqeqeq": 2, // http://eslint.org/docs/rules/eqeqeq
+ "guard-for-in": 2, // http://eslint.org/docs/rules/guard-for-in
+ "no-caller": 2, // http://eslint.org/docs/rules/no-caller
+ "no-else-return": 2, // http://eslint.org/docs/rules/no-else-return
+ "no-eq-null": 2, // http://eslint.org/docs/rules/no-eq-null
+ "no-eval": 2, // http://eslint.org/docs/rules/no-eval
+ "no-extend-native": 2, // http://eslint.org/docs/rules/no-extend-native
+ "no-extra-bind": 2, // http://eslint.org/docs/rules/no-extra-bind
+ "no-fallthrough": 2, // http://eslint.org/docs/rules/no-fallthrough
+ "no-floating-decimal": 2, // http://eslint.org/docs/rules/no-floating-decimal
+ "no-implied-eval": 2, // http://eslint.org/docs/rules/no-implied-eval
+ "no-lone-blocks": 2, // http://eslint.org/docs/rules/no-lone-blocks
+ "no-loop-func": 2, // http://eslint.org/docs/rules/no-loop-func
+ "no-multi-str": 2, // http://eslint.org/docs/rules/no-multi-str
+ "no-native-reassign": 2, // http://eslint.org/docs/rules/no-native-reassign
+ "no-new": 2, // http://eslint.org/docs/rules/no-new
+ "no-new-func": 2, // http://eslint.org/docs/rules/no-new-func
+ "no-new-wrappers": 2, // http://eslint.org/docs/rules/no-new-wrappers
+ "no-octal": 2, // http://eslint.org/docs/rules/no-octal
+ "no-octal-escape": 2, // http://eslint.org/docs/rules/no-octal-escape
+ "no-param-reassign": 2, // http://eslint.org/docs/rules/no-param-reassign
+ "no-proto": 2, // http://eslint.org/docs/rules/no-proto
+ "no-redeclare": 2, // http://eslint.org/docs/rules/no-redeclare
+ "no-return-assign": 2, // http://eslint.org/docs/rules/no-return-assign
+ "no-script-url": 2, // http://eslint.org/docs/rules/no-script-url
+ "no-self-compare": 2, // http://eslint.org/docs/rules/no-self-compare
+ "no-sequences": 2, // http://eslint.org/docs/rules/no-sequences
+ "no-throw-literal": 2, // http://eslint.org/docs/rules/no-throw-literal
+ "no-with": 2, // http://eslint.org/docs/rules/no-with
+ "radix": 2, // http://eslint.org/docs/rules/radix
+ "vars-on-top": 2, // http://eslint.org/docs/rules/vars-on-top
+ "wrap-iife": [2, "any"], // http://eslint.org/docs/rules/wrap-iife
+ "yoda": 2, // http://eslint.org/docs/rules/yoda
+
+/**
+ * Style
+ */
+ "indent": [2, 2], // http://eslint.org/docs/rules/
+ "brace-style": [2, // http://eslint.org/docs/rules/brace-style
+ "1tbs", {
+ "allowSingleLine": true
+ }],
+ "quotes": [
+ 2, "single", "avoid-escape" // http://eslint.org/docs/rules/quotes
+ ],
+ "camelcase": [2, { // http://eslint.org/docs/rules/camelcase
+ "properties": "never"
+ }],
+ "comma-spacing": [2, { // http://eslint.org/docs/rules/comma-spacing
+ "before": false,
+ "after": true
+ }],
+ "comma-style": [2, "last"], // http://eslint.org/docs/rules/comma-style
+ "eol-last": 2, // http://eslint.org/docs/rules/eol-last
+ "func-names": 0, // http://eslint.org/docs/rules/func-names
+ "key-spacing": [2, { // http://eslint.org/docs/rules/key-spacing
+ "beforeColon": false,
+ "afterColon": true
+ }],
+ "new-cap": [2, { // http://eslint.org/docs/rules/new-cap
+ "newIsCap": true
+ }],
+ "no-multiple-empty-lines": [2, { // http://eslint.org/docs/rules/no-multiple-empty-lines
+ "max": 2
+ }],
+ "no-nested-ternary": 2, // http://eslint.org/docs/rules/no-nested-ternary
+ "no-new-object": 2, // http://eslint.org/docs/rules/no-new-object
+ "no-spaced-func": 2, // http://eslint.org/docs/rules/no-spaced-func
+ "no-trailing-spaces": 2, // http://eslint.org/docs/rules/no-trailing-spaces
+ "no-wrap-func": 2, // http://eslint.org/docs/rules/no-wrap-func
+ "no-underscore-dangle": 0, // http://eslint.org/docs/rules/no-underscore-dangle
+ "one-var": [2, "never"], // http://eslint.org/docs/rules/one-var
+ "padded-blocks": [2, "never"], // http://eslint.org/docs/rules/padded-blocks
+ "semi": [2, "always"], // http://eslint.org/docs/rules/semi
+ "semi-spacing": [2, { // http://eslint.org/docs/rules/semi-spacing
+ "before": false,
+ "after": true
+ }],
+ "space-after-keywords": 2, // http://eslint.org/docs/rules/space-after-keywords
+ "space-before-blocks": 2, // http://eslint.org/docs/rules/space-before-blocks
+ "space-before-function-paren": [2, "never"], // http://eslint.org/docs/rules/space-before-function-paren
+ "space-infix-ops": 2, // http://eslint.org/docs/rules/space-infix-ops
+ "space-return-throw-case": 2, // http://eslint.org/docs/rules/space-return-throw-case
+ "spaced-line-comment": 2, // http://eslint.org/docs/rules/spaced-line-comment
+
+/**
+ * JSX style
+ */
+ "react/display-name": 0,
+ "react/jsx-boolean-value": 2,
+ "react/jsx-quotes": [2, "double"],
+ "react/jsx-no-undef": 2,
+ "react/jsx-sort-props": 0,
+ "react/jsx-sort-prop-types": 0,
+ "react/jsx-uses-react": 2,
+ "react/jsx-uses-vars": 2,
+ "react/no-did-mount-set-state": [2, "allow-in-func"],
+ "react/no-did-update-set-state": 2,
+ "react/no-multi-comp": 2,
+ "react/no-unknown-property": 2,
+ "react/prop-types": 0,
+ "react/react-in-jsx-scope": 2,
+ "react/self-closing-comp": 2,
+ "react/wrap-multilines": 2,
+ "react/sort-comp": [2, {
+ "order": [
+ "displayName",
+ "mixins",
+ "statics",
+ "propTypes",
+ "getDefaultProps",
+ "getInitialState",
+ "componentWillMount",
+ "componentDidMount",
+ "componentWillReceiveProps",
+ "shouldComponentUpdate",
+ "componentWillUpdate",
+ "componentWillUnmount",
+ "/^on.+$/",
+ "/^get.+$/",
+ "/^render.+$/",
+ "render"
+ ]
+ }]
+ }
+}
diff --git a/client/.jshintrc b/client/.jshintrc
deleted file mode 100644
index 287bc43d2..000000000
--- a/client/.jshintrc
+++ /dev/null
@@ -1,21 +0,0 @@
-{
- "node": true,
- "browser": true,
- "esnext": true,
- "bitwise": true,
- "camelcase": true,
- "curly": true,
- "eqeqeq": true,
- "immed": true,
- "indent": 4,
- "latedef": true,
- "newcap": true,
- "noarg": true,
- "quotmark": "single",
- "undef": true,
- "unused": true,
- "strict": true,
- "trailing": true,
- "smarttabs": true,
- "jquery": true
-}
diff --git a/client/.yo-rc.json b/client/.yo-rc.json
deleted file mode 100644
index 7a2135249..000000000
--- a/client/.yo-rc.json
+++ /dev/null
@@ -1,3 +0,0 @@
-{
- "generator-mocha": {}
-}
\ No newline at end of file
diff --git a/client/app/index.html b/client/app/index.html
index 82ed22d65..3688c7914 100644
--- a/client/app/index.html
+++ b/client/app/index.html
@@ -1,33 +1,33 @@
-
-
- Weave Scope
-
-
+
+
+ Weave Scope
+
+
-
-
+
+
-
-
-
+
+
+
-
-
-
-
+
+
+
+
-
-
-
+
+
+
-
-
-
-
+
+
+
+
diff --git a/client/app/scripts/actions/app-actions.js b/client/app/scripts/actions/app-actions.js
index 8f493e189..2aa991628 100644
--- a/client/app/scripts/actions/app-actions.js
+++ b/client/app/scripts/actions/app-actions.js
@@ -1,97 +1,103 @@
-var AppDispatcher = require('../dispatcher/app-dispatcher');
-var ActionTypes = require('../constants/action-types');
+let ActionTypes;
+let AppDispatcher;
+let AppStore;
+let RouterUtils;
+let WebapiUtils;
module.exports = {
- clickCloseDetails: function() {
- AppDispatcher.dispatch({
- type: ActionTypes.CLICK_CLOSE_DETAILS
- });
- RouterUtils.updateRoute();
- },
+ clickCloseDetails: function() {
+ AppDispatcher.dispatch({
+ type: ActionTypes.CLICK_CLOSE_DETAILS
+ });
+ RouterUtils.updateRoute();
+ },
- clickGrouping: function(grouping) {
- AppDispatcher.dispatch({
- type: ActionTypes.CLICK_GROUPING,
- grouping: grouping
- });
- RouterUtils.updateRoute();
- WebapiUtils.getNodesDelta(AppStore.getCurrentTopologyUrl());
- },
+ clickGrouping: function(grouping) {
+ AppDispatcher.dispatch({
+ type: ActionTypes.CLICK_GROUPING,
+ grouping: grouping
+ });
+ RouterUtils.updateRoute();
+ WebapiUtils.getNodesDelta(AppStore.getCurrentTopologyUrl());
+ },
- clickNode: function(nodeId) {
- AppDispatcher.dispatch({
- type: ActionTypes.CLICK_NODE,
- nodeId: nodeId
- });
- RouterUtils.updateRoute();
- WebapiUtils.getNodeDetails(AppStore.getCurrentTopologyUrl(), AppStore.getSelectedNodeId());
- },
+ clickNode: function(nodeId) {
+ AppDispatcher.dispatch({
+ type: ActionTypes.CLICK_NODE,
+ nodeId: nodeId
+ });
+ RouterUtils.updateRoute();
+ WebapiUtils.getNodeDetails(AppStore.getCurrentTopologyUrl(), AppStore.getSelectedNodeId());
+ },
- clickTopology: function(topologyId) {
- AppDispatcher.dispatch({
- type: ActionTypes.CLICK_TOPOLOGY,
- topologyId: topologyId
- });
- RouterUtils.updateRoute();
- WebapiUtils.getNodesDelta(AppStore.getCurrentTopologyUrl());
- },
+ clickTopology: function(topologyId) {
+ AppDispatcher.dispatch({
+ type: ActionTypes.CLICK_TOPOLOGY,
+ topologyId: topologyId
+ });
+ RouterUtils.updateRoute();
+ WebapiUtils.getNodesDelta(AppStore.getCurrentTopologyUrl());
+ },
- enterNode: function(nodeId) {
- AppDispatcher.dispatch({
- type: ActionTypes.ENTER_NODE,
- nodeId: nodeId
- });
- },
+ enterNode: function(nodeId) {
+ AppDispatcher.dispatch({
+ type: ActionTypes.ENTER_NODE,
+ nodeId: nodeId
+ });
+ },
- hitEsc: function() {
- AppDispatcher.dispatch({
- type: ActionTypes.HIT_ESC_KEY
- });
- RouterUtils.updateRoute();
- },
+ hitEsc: function() {
+ AppDispatcher.dispatch({
+ type: ActionTypes.HIT_ESC_KEY
+ });
+ RouterUtils.updateRoute();
+ },
- leaveNode: function(nodeId) {
- AppDispatcher.dispatch({
- type: ActionTypes.LEAVE_NODE,
- nodeId: nodeId
- });
- },
+ leaveNode: function(nodeId) {
+ AppDispatcher.dispatch({
+ type: ActionTypes.LEAVE_NODE,
+ nodeId: nodeId
+ });
+ },
- receiveNodeDetails: function(details) {
- AppDispatcher.dispatch({
- type: ActionTypes.RECEIVE_NODE_DETAILS,
- details: details
- });
- },
+ receiveNodeDetails: function(details) {
+ AppDispatcher.dispatch({
+ type: ActionTypes.RECEIVE_NODE_DETAILS,
+ details: details
+ });
+ },
- receiveNodesDelta: function(delta) {
- AppDispatcher.dispatch({
- type: ActionTypes.RECEIVE_NODES_DELTA,
- delta: delta
- });
- },
+ receiveNodesDelta: function(delta) {
+ AppDispatcher.dispatch({
+ type: ActionTypes.RECEIVE_NODES_DELTA,
+ delta: delta
+ });
+ },
- receiveTopologies: function(topologies) {
- AppDispatcher.dispatch({
- type: ActionTypes.RECEIVE_TOPOLOGIES,
- topologies: topologies
- });
- WebapiUtils.getNodesDelta(AppStore.getCurrentTopologyUrl());
- WebapiUtils.getNodeDetails(AppStore.getCurrentTopologyUrl(), AppStore.getSelectedNodeId());
- },
+ receiveTopologies: function(topologies) {
+ AppDispatcher.dispatch({
+ type: ActionTypes.RECEIVE_TOPOLOGIES,
+ topologies: topologies
+ });
+ WebapiUtils.getNodesDelta(AppStore.getCurrentTopologyUrl());
+ WebapiUtils.getNodeDetails(AppStore.getCurrentTopologyUrl(), AppStore.getSelectedNodeId());
+ },
- route: function(state) {
- AppDispatcher.dispatch({
- state: state,
- type: ActionTypes.ROUTE_TOPOLOGY
- });
- WebapiUtils.getNodesDelta(AppStore.getCurrentTopologyUrl());
- WebapiUtils.getNodeDetails(AppStore.getCurrentTopologyUrl(), AppStore.getSelectedNodeId());
- }
+ route: function(state) {
+ AppDispatcher.dispatch({
+ state: state,
+ type: ActionTypes.ROUTE_TOPOLOGY
+ });
+ WebapiUtils.getNodesDelta(AppStore.getCurrentTopologyUrl());
+ WebapiUtils.getNodeDetails(AppStore.getCurrentTopologyUrl(), AppStore.getSelectedNodeId());
+ }
};
-// breaking circular deps
+// require below export to break circular dep
-var RouterUtils = require('../utils/router-utils');
-var WebapiUtils = require('../utils/web-api-utils');
-var AppStore = require('../stores/app-store');
\ No newline at end of file
+AppDispatcher = require('../dispatcher/app-dispatcher');
+ActionTypes = require('../constants/action-types');
+
+RouterUtils = require('../utils/router-utils');
+WebapiUtils = require('../utils/web-api-utils');
+AppStore = require('../stores/app-store');
diff --git a/client/app/scripts/charts/node.js b/client/app/scripts/charts/node.js
index acfc42b69..fa8e46efa 100644
--- a/client/app/scripts/charts/node.js
+++ b/client/app/scripts/charts/node.js
@@ -1,63 +1,61 @@
-var _ = require('lodash');
-var React = require('react');
-var tweenState = require('react-tween-state');
+const React = require('react');
+const tweenState = require('react-tween-state');
-var NodeColorMixin = require('../mixins/node-color-mixin');
+const NodeColorMixin = require('../mixins/node-color-mixin');
-var Node = React.createClass({
- mixins: [
- NodeColorMixin,
- tweenState.Mixin
- ],
+const Node = React.createClass({
+ mixins: [
+ NodeColorMixin,
+ tweenState.Mixin
+ ],
- getInitialState: function() {
- return {
- x: 0,
- y: 0
- };
- },
+ getInitialState: function() {
+ return {
+ x: 0,
+ y: 0
+ };
+ },
- componentWillMount: function() {
- // initial node position when rendered the first time
- this.setState({
- x: this.props.dx,
- y: this.props.dy
- });
- },
+ componentWillMount: function() {
+ // initial node position when rendered the first time
+ this.setState({
+ x: this.props.dx,
+ y: this.props.dy
+ });
+ },
- componentWillReceiveProps: function(nextProps) {
- // animate node transition to next position
- this.tweenState('x', {
- easing: tweenState.easingTypes.easeInOutQuad,
- duration: 500,
- endValue: nextProps.dx
- });
- this.tweenState('y', {
- easing: tweenState.easingTypes.easeInOutQuad,
- duration: 500,
- endValue: nextProps.dy
- });
- },
+ componentWillReceiveProps: function(nextProps) {
+ // animate node transition to next position
+ this.tweenState('x', {
+ easing: tweenState.easingTypes.easeInOutQuad,
+ duration: 500,
+ endValue: nextProps.dx
+ });
+ this.tweenState('y', {
+ easing: tweenState.easingTypes.easeInOutQuad,
+ duration: 500,
+ endValue: nextProps.dy
+ });
+ },
- render: function() {
- var transform = "translate(" + this.getTweeningValue('x') + "," + this.getTweeningValue('y') + ")";
- var scale = this.props.scale;
- var textOffsetX = 0;
- var textOffsetY = scale(0.5) + 18;
- var textAngle = _.isUndefined(this.props.angle) ? 0 : -1 * (this.props.angle * 180 / Math.PI - 90);
- var color = this.getNodeColor(this.props.label);
- var className = this.props.highlighted ? "node highlighted" : "node";
+ render: function() {
+ const transform = 'translate(' + this.getTweeningValue('x') + ',' + this.getTweeningValue('y') + ')';
+ const scale = this.props.scale;
+ const textOffsetX = 0;
+ const textOffsetY = scale(0.5) + 18;
+ const color = this.getNodeColor(this.props.label);
+ const className = this.props.highlighted ? 'node highlighted' : 'node';
- return (
-
-
-
-
- {this.props.label}
- {this.props.subLabel}
-
- );
- }
+ return (
+
+
+
+
+ {this.props.label}
+ {this.props.subLabel}
+
+ );
+ }
});
module.exports = Node;
diff --git a/client/app/scripts/charts/nodes-chart.js b/client/app/scripts/charts/nodes-chart.js
index 8b43528da..91b1034dd 100644
--- a/client/app/scripts/charts/nodes-chart.js
+++ b/client/app/scripts/charts/nodes-chart.js
@@ -1,234 +1,231 @@
-var _ = require('lodash');
-var d3 = require('d3');
-var React = require('react');
+const _ = require('lodash');
+const d3 = require('d3');
+const React = require('react');
-var NodesLayout = require('./nodes-layout');
-var Node = require('./node');
+const NodesLayout = require('./nodes-layout');
+const Node = require('./node');
-var MAX_NODES = 100;
-var MARGINS = {
- top: 130,
- left: 40,
- right: 40,
- bottom: 0
+const MARGINS = {
+ top: 130,
+ left: 40,
+ right: 40,
+ bottom: 0
};
-var line = d3.svg.line()
- .interpolate("basis")
- .x(function(d) { return d.x; })
- .y(function(d) { return d.y; });
+const line = d3.svg.line()
+ .interpolate('basis')
+ .x(function(d) { return d.x; })
+ .y(function(d) { return d.y; });
-var NodesChart = React.createClass({
+const NodesChart = React.createClass({
- getInitialState: function() {
- return {
- nodes: {},
- edges: {},
- nodeScale: 1,
- translate: "0,0",
- scale: 1,
- hasZoomed: false
- };
- },
+ getInitialState: function() {
+ return {
+ nodes: {},
+ edges: {},
+ nodeScale: 1,
+ translate: '0,0',
+ scale: 1,
+ hasZoomed: false
+ };
+ },
- initNodes: function(topology, prevNodes) {
- var centerX = this.props.width / 2;
- var centerY = this.props.height / 2;
- var nodes = {};
+ componentWillMount: function() {
+ this.updateGraphState(this.props);
+ },
- _.each(topology, function(node, id) {
- nodes[id] = prevNodes[id] || {};
- _.defaults(nodes[id], {
- x: centerX,
- y: centerY,
- textAnchor: 'start'
- });
- _.assign(nodes[id], {
- id: id,
- label: node.label_major,
- subLabel: node.label_minor,
- degree: _.size(node.adjacency)
- });
- }, this);
+ componentDidMount: function() {
+ this.zoom = d3.behavior.zoom()
+ .scaleExtent([0.1, 2])
+ .on('zoom', this.zoomed);
- return nodes;
- },
+ d3.select('.nodes-chart')
+ .call(this.zoom);
+ },
- initEdges: function(topology, nodes) {
- var edges = {};
-
- _.each(topology, function(node) {
- _.each(node.adjacency, function(adjacent) {
- var edge = [node.id, adjacent],
- edgeId = edge.join('-');
-
- if (!edges[edgeId]) {
- var source = nodes[edge[0]];
- var target = nodes[edge[1]];
-
- if(!source || !target) {
- console.error("Missing edge node", edge[0], source, edge[1], target);
- }
-
- edges[edgeId] = {
- id: edgeId,
- value: 1,
- source: source,
- target: target
- };
- }
- });
- }, this);
-
- return edges;
- },
-
- getNodes: function(nodes, scale) {
- return _.map(nodes, function (node) {
- var highlighted = _.includes(this.props.highlightedNodes, node.id);
- return (
-
- );
- }, this);
- },
-
- getEdges: function(edges, scale) {
- return _.map(edges, function(edge) {
- return (
-
- );
- });
- },
-
- getTopologyFingerprint: function(topology) {
- var nodes = _.keys(topology).sort();
- var fingerprint = [];
-
- _.each(topology, function(node) {
- fingerprint.push(node.id);
- if (node.adjacency) {
- fingerprint.push(node.adjacency.join(','));
- }
- });
- return fingerprint.join(';');
- },
-
- updateGraphState: function(props) {
- var nodes = this.initNodes(props.nodes, this.state.nodes);
- var edges = this.initEdges(props.nodes, nodes);
-
- var expanse = Math.min(props.height, props.width);
- var nodeSize = expanse / 2;
- var n = _.size(props.nodes);
- var nodeScale = d3.scale.linear().range([0, nodeSize/Math.pow(n, 0.7)]);
-
- var layoutId = 'layered node chart';
- console.time(layoutId);
- var graph = NodesLayout.doLayout(
- nodes,
- edges,
- props.width,
- props.height,
- nodeScale,
- MARGINS
- );
- console.timeEnd(layoutId);
-
- // adjust layout based on viewport
-
- var xFactor = (props.width - MARGINS.left - MARGINS.right) / graph.width;
- var yFactor = props.height / graph.height;
- var zoomFactor = Math.min(xFactor, yFactor);
- var zoomScale = this.state.scale;
-
- if(this.zoom && !this.state.hasZoomed && zoomFactor > 0 && zoomFactor < 1) {
- zoomScale = zoomFactor;
- // saving in d3's behavior cache
- this.zoom.scale(zoomFactor);
- }
-
- this.setState({
- nodes: nodes,
- edges: edges,
- nodeScale: nodeScale,
- scale: zoomScale
- });
- },
-
- componentWillMount: function() {
- this.updateGraphState(this.props);
- },
-
- componentDidMount: function() {
- this.zoom = d3.behavior.zoom()
- .scaleExtent([0.1, 2])
- .on('zoom', this.zoomed);
-
- d3.select('.nodes-chart')
- .call(this.zoom);
- },
-
- componentWillUnmount: function() {
-
- // undoing .call(zoom)
-
- d3.select('.nodes-chart')
- .on("mousedown.zoom", null)
- .on("onwheel", null)
- .on("onmousewheel", null)
- .on("dblclick.zoom", null)
- .on("touchstart.zoom", null);
- },
-
- componentWillReceiveProps: function(nextProps) {
- if (this.getTopologyFingerprint(nextProps.nodes) !== this.getTopologyFingerprint(this.props.nodes)) {
- this.setState({
- nodes: {},
- edges: {}
- });
- }
-
- this.updateGraphState(nextProps);
- },
-
- zoomed: function() {
- this.setState({
- hasZoomed: true,
- translate: d3.event.translate,
- scale: d3.event.scale
- });
- },
-
- render: function() {
- var nodeElements = this.getNodes(this.state.nodes, this.state.nodeScale);
- var edgeElements = this.getEdges(this.state.edges, this.state.nodeScale);
- var transform = 'translate(' + this.state.translate + ')' +
- ' scale(' + this.state.scale + ')';
-
- return (
-
- );
+ componentWillReceiveProps: function(nextProps) {
+ if (this.getTopologyFingerprint(nextProps.nodes) !== this.getTopologyFingerprint(this.props.nodes)) {
+ this.setState({
+ nodes: {},
+ edges: {}
+ });
}
+ this.updateGraphState(nextProps);
+ },
+
+ componentWillUnmount: function() {
+ // undoing .call(zoom)
+
+ d3.select('.nodes-chart')
+ .on('mousedown.zoom', null)
+ .on('onwheel', null)
+ .on('onmousewheel', null)
+ .on('dblclick.zoom', null)
+ .on('touchstart.zoom', null);
+ },
+
+ getTopologyFingerprint: function(topology) {
+ const fingerprint = [];
+
+ _.each(topology, function(node) {
+ fingerprint.push(node.id);
+ if (node.adjacency) {
+ fingerprint.push(node.adjacency.join(','));
+ }
+ });
+ return fingerprint.join(';');
+ },
+
+ getGraphNodes: function(nodes, scale) {
+ return _.map(nodes, function(node) {
+ const highlighted = _.includes(this.props.highlightedNodes, node.id);
+ return (
+
+ );
+ }, this);
+ },
+
+ getGraphEdges: function(edges) {
+ return _.map(edges, function(edge) {
+ return (
+
+ );
+ });
+ },
+
+ render: function() {
+ const nodeElements = this.getGraphNodes(this.state.nodes, this.state.nodeScale);
+ const edgeElements = this.getGraphEdges(this.state.edges, this.state.nodeScale);
+ const transform = 'translate(' + this.state.translate + ')' +
+ ' scale(' + this.state.scale + ')';
+
+ return (
+
+ );
+ },
+
+ initNodes: function(topology, prevNodes) {
+ const centerX = this.props.width / 2;
+ const centerY = this.props.height / 2;
+ const nodes = {};
+
+ _.each(topology, function(node, id) {
+ nodes[id] = prevNodes[id] || {};
+ _.defaults(nodes[id], {
+ x: centerX,
+ y: centerY,
+ textAnchor: 'start'
+ });
+ _.assign(nodes[id], {
+ id: id,
+ label: node.label_major,
+ subLabel: node.label_minor,
+ degree: _.size(node.adjacency)
+ });
+ }, this);
+
+ return nodes;
+ },
+
+ initEdges: function(topology, nodes) {
+ const edges = {};
+
+ _.each(topology, function(node) {
+ _.each(node.adjacency, function(adjacent) {
+ const edge = [node.id, adjacent];
+ const edgeId = edge.join('-');
+
+ if (!edges[edgeId]) {
+ const source = nodes[edge[0]];
+ const target = nodes[edge[1]];
+
+ if (!source || !target) {
+ console.error('Missing edge node', edge[0], source, edge[1], target);
+ }
+
+ edges[edgeId] = {
+ id: edgeId,
+ value: 1,
+ source: source,
+ target: target
+ };
+ }
+ });
+ }, this);
+
+ return edges;
+ },
+
+ updateGraphState: function(props) {
+ const nodes = this.initNodes(props.nodes, this.state.nodes);
+ const edges = this.initEdges(props.nodes, nodes);
+
+ const expanse = Math.min(props.height, props.width);
+ const nodeSize = expanse / 2;
+ const n = _.size(props.nodes);
+ const nodeScale = d3.scale.linear().range([0, nodeSize / Math.pow(n, 0.7)]);
+
+ const layoutId = 'layered node chart';
+ console.time(layoutId);
+ const graph = NodesLayout.doLayout(
+ nodes,
+ edges,
+ props.width,
+ props.height,
+ nodeScale,
+ MARGINS
+ );
+ console.timeEnd(layoutId);
+
+ // adjust layout based on viewport
+
+ const xFactor = (props.width - MARGINS.left - MARGINS.right) / graph.width;
+ const yFactor = props.height / graph.height;
+ const zoomFactor = Math.min(xFactor, yFactor);
+ let zoomScale = this.state.scale;
+
+ if (this.zoom && !this.state.hasZoomed && zoomFactor > 0 && zoomFactor < 1) {
+ zoomScale = zoomFactor;
+ // saving in d3's behavior cache
+ this.zoom.scale(zoomFactor);
+ }
+
+ this.setState({
+ nodes: nodes,
+ edges: edges,
+ nodeScale: nodeScale,
+ scale: zoomScale
+ });
+ },
+
+ zoomed: function() {
+ this.setState({
+ hasZoomed: true,
+ translate: d3.event.translate,
+ scale: d3.event.scale
+ });
+ }
+
});
module.exports = NodesChart;
diff --git a/client/app/scripts/charts/nodes-layout.js b/client/app/scripts/charts/nodes-layout.js
index 0f571532c..d21006894 100644
--- a/client/app/scripts/charts/nodes-layout.js
+++ b/client/app/scripts/charts/nodes-layout.js
@@ -1,73 +1,73 @@
-var dagre = require('dagre');
-var _ = require('lodash');
+const dagre = require('dagre');
+const _ = require('lodash');
-var MAX_NODES = 100;
+const MAX_NODES = 100;
-var doLayout = function(nodes, edges, width, height, scale, margins) {
- var offsetX = 0 + margins.left;
- var offsetY = 0 + margins.top;
- var g = new dagre.graphlib.Graph({});
+const doLayout = function(nodes, edges, width, height, scale, margins) {
+ let offsetX = 0 + margins.left;
+ let offsetY = 0 + margins.top;
+ const g = new dagre.graphlib.Graph({});
- if (_.size(nodes) > MAX_NODES) {
- console.error('Too many nodes for graph layout engine. Limit: ' + MAX_NODES);
- return;
- }
+ if (_.size(nodes) > MAX_NODES) {
+ console.error('Too many nodes for graph layout engine. Limit: ' + MAX_NODES);
+ return null;
+ }
- // configure node margins
+ // configure node margins
- g.setGraph({
- nodesep: scale(2.5),
- ranksep: scale(2.5)
+ g.setGraph({
+ nodesep: scale(2.5),
+ ranksep: scale(2.5)
+ });
+
+ // add nodes and edges to layout engine
+
+ _.each(nodes, function(node) {
+ g.setNode(node.id, {id: node.id, width: scale(0.75), height: scale(0.75)});
+ });
+
+ _.each(edges, function(edge) {
+ const virtualNodes = edge.source.id === edge.target.id ? 1 : 0;
+ g.setEdge(edge.source.id, edge.target.id, {id: edge.id, minlen: virtualNodes});
+ });
+
+ dagre.layout(g);
+
+ const graph = g.graph();
+
+ // shifting graph coordinates to center
+
+ if (graph.width < width) {
+ offsetX = (width - graph.width) / 2 + margins.left;
+ }
+ if (graph.height < height) {
+ offsetY = (height - graph.height) / 2 + margins.top;
+ }
+
+ // apply coordinates to nodes and edges
+
+ g.nodes().forEach(function(id) {
+ const node = nodes[id];
+ const graphNode = g.node(id);
+ node.x = graphNode.x + offsetX;
+ node.y = graphNode.y + offsetY;
+ });
+
+ g.edges().forEach(function(id) {
+ const graphEdge = g.edge(id);
+ const edge = edges[graphEdge.id];
+ _.each(graphEdge.points, function(point) {
+ point.x += offsetX;
+ point.y += offsetY;
});
+ edge.points = graphEdge.points;
+ });
- // add nodes and edges to layout engine
+ // return object with width and height of layout
- _.each(nodes, function(node) {
- g.setNode(node.id, {id: node.id, width: scale(0.75), height: scale(0.75)});
- });
-
- _.each(edges, function(edge) {
- var virtualNodes = edge.source.id === edge.target.id ? 1 : 0;
- g.setEdge(edge.source.id, edge.target.id, {id: edge.id, minlen: virtualNodes});
- });
-
- dagre.layout(g);
-
- var graph = g.graph();
-
- // shifting graph coordinates to center
-
- if (graph.width < width) {
- offsetX = (width - graph.width) / 2 + margins.left;
- }
- if (graph.height < height) {
- offsetY = (height - graph.height) / 2 + margins.top;
- }
-
- // apply coordinates to nodes and edges
-
- g.nodes().forEach(function(id) {
- var node = nodes[id];
- var graphNode = g.node(id);
- node.x = graphNode.x + offsetX;
- node.y = graphNode.y + offsetY;
- });
-
- g.edges().forEach(function(id) {
- var graphEdge = g.edge(id);
- var edge = edges[graphEdge.id];
- _.each(graphEdge.points, function(point) {
- point.x += offsetX;
- point.y += offsetY;
- });
- edge.points = graphEdge.points;
- });
-
- // return object with width and height of layout
-
- return graph;
+ return graph;
};
module.exports = {
- doLayout: doLayout
+ doLayout: doLayout
};
diff --git a/client/app/scripts/components/app.js b/client/app/scripts/components/app.js
index 326fd891d..97cdc112e 100644
--- a/client/app/scripts/components/app.js
+++ b/client/app/scripts/components/app.js
@@ -1,79 +1,76 @@
-/** @jsx React.DOM */
+const React = require('react');
-var React = require('react');
-var _ = require('lodash');
-
-var Logo = require('./logo');
-var AppStore = require('../stores/app-store');
-var Groupings = require('./groupings.js');
-var Status = require('./status.js');
-var Topologies = require('./topologies.js');
-var WebapiUtils = require('../utils/web-api-utils');
-var AppActions = require('../actions/app-actions');
-var Details = require('./details');
-var Nodes = require('./nodes');
-var RouterUtils = require('../utils/router-utils');
+const Logo = require('./logo');
+const AppStore = require('../stores/app-store');
+const Groupings = require('./groupings.js');
+const Status = require('./status.js');
+const Topologies = require('./topologies.js');
+const WebapiUtils = require('../utils/web-api-utils');
+const AppActions = require('../actions/app-actions');
+const Details = require('./details');
+const Nodes = require('./nodes');
+const RouterUtils = require('../utils/router-utils');
-var ESC_KEY_CODE = 27;
+const ESC_KEY_CODE = 27;
function getStateFromStores() {
- return {
- currentTopology: AppStore.getCurrentTopology(),
- connectionState: AppStore.getConnectionState(),
- currentGrouping: AppStore.getCurrentGrouping(),
- selectedNodeId: AppStore.getSelectedNodeId(),
- nodeDetails: AppStore.getNodeDetails(),
- nodes: AppStore.getNodes(),
- topologies: AppStore.getTopologies()
- }
+ return {
+ currentTopology: AppStore.getCurrentTopology(),
+ connectionState: AppStore.getConnectionState(),
+ currentGrouping: AppStore.getCurrentGrouping(),
+ selectedNodeId: AppStore.getSelectedNodeId(),
+ nodeDetails: AppStore.getNodeDetails(),
+ nodes: AppStore.getNodes(),
+ topologies: AppStore.getTopologies()
+ };
}
-var App = React.createClass({
+const App = React.createClass({
- getInitialState: function() {
- return getStateFromStores();
- },
+ getInitialState: function() {
+ return getStateFromStores();
+ },
- componentDidMount: function() {
- AppStore.on(AppStore.CHANGE_EVENT, this.onChange);
- window.addEventListener('keyup', this.onKeyPress);
+ componentDidMount: function() {
+ AppStore.on(AppStore.CHANGE_EVENT, this.onChange);
+ window.addEventListener('keyup', this.onKeyPress);
- RouterUtils.getRouter().start({hashbang: true});
- WebapiUtils.getTopologies();
- },
+ RouterUtils.getRouter().start({hashbang: true});
+ WebapiUtils.getTopologies();
+ },
- onChange: function() {
- this.setState(getStateFromStores());
- },
+ onChange: function() {
+ this.setState(getStateFromStores());
+ },
- onKeyPress: function(ev) {
- if (ev.keyCode === ESC_KEY_CODE) {
- AppActions.hitEsc();
- }
- },
+ onKeyPress: function(ev) {
+ if (ev.keyCode === ESC_KEY_CODE) {
+ AppActions.hitEsc();
+ }
+ },
- render: function() {
- var showingDetails = this.state.selectedNodeId;
+ render: function() {
+ const showingDetails = this.state.selectedNodeId;
- return (
-
- {showingDetails &&
}
+ return (
+
+ {showingDetails &&
}
-
-
-
-
-
-
+
+
+
+
+
+
-
-
- );
- }
+
+
+ );
+ }
});
diff --git a/client/app/scripts/components/details.js b/client/app/scripts/components/details.js
index c9092f5a3..e22b815a2 100644
--- a/client/app/scripts/components/details.js
+++ b/client/app/scripts/components/details.js
@@ -1,34 +1,31 @@
-/** @jsx React.DOM */
+const React = require('react');
+const mui = require('material-ui');
+const Paper = mui.Paper;
-var React = require('react');
-var _ = require('lodash');
-var mui = require('material-ui');
-var Paper = mui.Paper;
-var IconButton = mui.IconButton;
+const AppActions = require('../actions/app-actions');
+const NodeDetails = require('./node-details');
-var AppActions = require('../actions/app-actions');
-var NodeDetails = require('./node-details');
+const Details = React.createClass({
-var Details = React.createClass({
+ render: function() {
+ return (
+
+ );
+ },
- handleClickClose: function(ev) {
- ev.preventDefault();
- AppActions.clickCloseDetails();
- },
+ handleClickClose: function(ev) {
+ ev.preventDefault();
+ AppActions.clickCloseDetails();
+ }
- render: function() {
- return (
-
- );
- }
});
-module.exports = Details;
\ No newline at end of file
+module.exports = Details;
diff --git a/client/app/scripts/components/explorer.js b/client/app/scripts/components/explorer.js
index f771b4b3a..606ea413e 100644
--- a/client/app/scripts/components/explorer.js
+++ b/client/app/scripts/components/explorer.js
@@ -1,95 +1,87 @@
-/** @jsx React.DOM */
+const React = require('react');
+const _ = require('lodash');
-var React = require('react');
-var _ = require('lodash');
+const NodesChart = require('../charts/nodes-chart');
+const NodeDetails = require('./node-details');
-var NodesChart = require('../charts/nodes-chart');
-var NodeDetails = require('./node-details');
+const marginBottom = 64;
+const marginTop = 64;
+const marginLeft = 36;
+const marginRight = 36;
-var marginBottom = 64;
-var marginTop = 64;
-var marginLeft = 36;
-var marginRight = 36;
+const Explorer = React.createClass({
-var Explorer = React.createClass({
+ getInitialState: function() {
+ return {
+ layout: 'solar',
+ width: window.innerWidth - marginLeft - marginRight,
+ height: window.innerHeight - marginBottom - marginTop
+ };
+ },
- getInitialState: function() {
- return {
- layout: 'solar',
- width: window.innerWidth - marginLeft - marginRight,
- height: window.innerHeight - marginBottom - marginTop
- };
- },
+ componentDidMount: function() {
+ window.addEventListener('resize', this.handleResize);
+ },
- componentDidMount: function() {
- window.addEventListener('resize', this.handleResize);
- },
+ componentWillUnmount: function() {
+ window.removeEventListener('resize', this.handleResize);
+ },
- componentWillUnmount: function() {
- window.removeEventListener('resize', this.handleResize);
- },
+ getSubTopology: function(topology) {
+ const subTopology = {};
+ const nodeSet = [];
- setDimensions: function() {
- this.setState({
- height: window.innerHeight - marginBottom - marginTop,
- width: window.innerWidth - marginLeft - marginRight
- });
- },
+ _.each(this.props.expandedNodes, function(nodeId) {
+ if (topology[nodeId]) {
+ subTopology[nodeId] = topology[nodeId];
+ nodeSet = _.union(subTopology[nodeId].adjacency, nodeSet);
+ _.each(subTopology[nodeId].adjacency, function(adjacentId) {
+ const node = _.assign({}, topology[adjacentId]);
- handleResize: function() {
- this.setDimensions();
- },
+ subTopology[adjacentId] = node;
+ });
+ }
+ });
- getSubTopology: function(topology) {
- var subTopology = {};
- var nodeSet = [];
+ // weed out edges
+ _.each(subTopology, function(node) {
+ node.adjacency = _.intersection(node.adjacency, nodeSet);
+ });
- _.each(this.props.expandedNodes, function(nodeId) {
- if (topology[nodeId]) {
- subTopology[nodeId] = topology[nodeId];
- nodeSet = _.union(subTopology[nodeId].adjacency, nodeSet);
- _.each(subTopology[nodeId].adjacency, function(adjacentId) {
- var node = _.assign({}, topology[adjacentId]);
+ return subTopology;
+ },
- subTopology[adjacentId] = node;
- });
- }
- });
+ render: function() {
+ const subTopology = this.getSubTopology(this.props.nodes);
- // weed out edges
- _.each(subTopology, function(node) {
- node.adjacency = _.intersection(node.adjacency, nodeSet);
- });
+ return (
+
+ );
+ },
- return subTopology;
- },
+ setDimensions: function() {
+ this.setState({
+ height: window.innerHeight - marginBottom - marginTop,
+ width: window.innerWidth - marginLeft - marginRight
+ });
+ },
- onNodeClick: function(ev) {
- var nodeId = ev.currentTarget.id;
- AppActions.clickNode(nodeId);
- },
-
- render: function() {
- var subTopology = this.getSubTopology(this.props.nodes);
-
- return (
-
- );
- }
+ handleResize: function() {
+ this.setDimensions();
+ }
});
-module.exports = Explorer;
\ No newline at end of file
+module.exports = Explorer;
diff --git a/client/app/scripts/components/groupings.js b/client/app/scripts/components/groupings.js
index d99457143..66809a28c 100644
--- a/client/app/scripts/components/groupings.js
+++ b/client/app/scripts/components/groupings.js
@@ -1,67 +1,64 @@
-/** @jsx React.DOM */
+const React = require('react');
+const _ = require('lodash');
-var React = require('react');
-var _ = require('lodash');
+const AppActions = require('../actions/app-actions');
-var AppActions = require('../actions/app-actions');
-var AppStore = require('../stores/app-store');
-
-var GROUPINGS = [{
- id: 'none',
- iconClass: 'fa fa-th',
- needsTopology: false
+const GROUPINGS = [{
+ id: 'none',
+ iconClass: 'fa fa-th',
+ needsTopology: false
}, {
- id: 'grouped',
- iconClass: 'fa fa-th-large',
- needsTopology: 'grouped_url'
+ id: 'grouped',
+ iconClass: 'fa fa-th-large',
+ needsTopology: 'grouped_url'
}];
-var Groupings = React.createClass({
+const Groupings = React.createClass({
- onGroupingClick: function(ev) {
- ev.preventDefault();
- AppActions.clickGrouping(ev.currentTarget.getAttribute('rel'));
- },
+ onGroupingClick: function(ev) {
+ ev.preventDefault();
+ AppActions.clickGrouping(ev.currentTarget.getAttribute('rel'));
+ },
- isGroupingSupportedByTopology: function(topology, grouping) {
- return !grouping.needsTopology || topology && topology[grouping.needsTopology];
- },
+ getGroupingsSupportedByTopology: function(topology) {
+ return _.filter(GROUPINGS, _.partial(this.isGroupingSupportedByTopology, topology));
+ },
- getGroupingsSupportedByTopology: function(topology) {
- return _.filter(GROUPINGS, _.partial(this.isGroupingSupportedByTopology, topology));
- },
+ renderGrouping: function(grouping, activeGroupingId) {
+ let className = 'groupings-item';
+ const isSupportedByTopology = this.isGroupingSupportedByTopology(this.props.currentTopology, grouping);
- renderGrouping: function(grouping, activeGroupingId) {
- var className = "groupings-item",
- isSupportedByTopology = this.isGroupingSupportedByTopology(this.props.currentTopology, grouping);
+ if (grouping.id === activeGroupingId) {
+ className += ' groupings-item-active';
+ } else if (!isSupportedByTopology) {
+ className += ' groupings-item-disabled';
+ } else {
+ className += ' groupings-item-default';
+ }
- if (grouping.id === activeGroupingId) {
- className += " groupings-item-active";
- } else if (!isSupportedByTopology) {
- className += " groupings-item-disabled";
- } else {
- className += " groupings-item-default";
- }
+ return (
+
+
+
+ );
+ },
- return (
-
-
-
- );
- },
+ render: function() {
+ const activeGrouping = this.props.active;
+ const isGroupingSupported = _.size(this.getGroupingsSupportedByTopology(this.props.currentTopology)) > 1;
- render: function() {
- var activeGrouping = this.props.active,
- isGroupingSupported = _.size(this.getGroupingsSupportedByTopology(this.props.currentTopology)) > 1;
+ return (
+
+ {isGroupingSupported && GROUPINGS.map(function(grouping) {
+ return this.renderGrouping(grouping, activeGrouping);
+ }, this)}
+
+ );
+ },
- return (
-
- {isGroupingSupported && GROUPINGS.map(function(grouping) {
- return this.renderGrouping(grouping, activeGrouping);
- }, this)}
-
- );
- }
+ isGroupingSupportedByTopology: function(topology, grouping) {
+ return !grouping.needsTopology || topology && topology[grouping.needsTopology];
+ }
});
diff --git a/client/app/scripts/components/logo.js b/client/app/scripts/components/logo.js
index 45eb63200..003e40c79 100644
--- a/client/app/scripts/components/logo.js
+++ b/client/app/scripts/components/logo.js
@@ -1,67 +1,65 @@
-/** @jsx React.DOM */
+const React = require('react');
-var React = require('react');
+const Logo = React.createClass({
-var Logo = React.createClass({
-
- render: function() {
- return (
-
-
-
- );
- }
+ render: function() {
+ return (
+
+
+
+ );
+ }
});
-module.exports = Logo;
\ No newline at end of file
+module.exports = Logo;
diff --git a/client/app/scripts/components/node-details-table.js b/client/app/scripts/components/node-details-table.js
index aab2febe6..7363cf8cb 100644
--- a/client/app/scripts/components/node-details-table.js
+++ b/client/app/scripts/components/node-details-table.js
@@ -1,34 +1,31 @@
-/** @jsx React.DOM */
+const React = require('react');
-var React = require('react');
-var _ = require('lodash');
+const NodeDetailsTable = React.createClass({
-var NodeDetailsTable = React.createClass({
+ render: function() {
+ const isNumeric = this.props.isNumeric;
- render: function() {
- var isNumeric = this.props.isNumeric;
+ return (
+
+
+ {this.props.title}
+
- return (
-
-
- {this.props.title}
-
-
- {this.props.rows.map(function(row) {
- return (
-
-
{row.key}
- {isNumeric &&
{row.value_major}
}
- {isNumeric &&
{row.value_minor}
}
- {!isNumeric &&
{row.value_major}
}
- {!isNumeric && row.value_minor &&
{row.value_minor}
}
-
- );
- })}
-
- );
- }
+ {this.props.rows.map(function(row) {
+ return (
+
+
{row.key}
+ {isNumeric &&
{row.value_major}
}
+ {isNumeric &&
{row.value_minor}
}
+ {!isNumeric &&
{row.value_major}
}
+ {!isNumeric && row.value_minor &&
{row.value_minor}
}
+
+ );
+ })}
+
+ );
+ }
});
-module.exports = NodeDetailsTable;
\ No newline at end of file
+module.exports = NodeDetailsTable;
diff --git a/client/app/scripts/components/node-details.js b/client/app/scripts/components/node-details.js
index 45e22ec19..0f49fbe53 100644
--- a/client/app/scripts/components/node-details.js
+++ b/client/app/scripts/components/node-details.js
@@ -1,46 +1,43 @@
-/** @jsx React.DOM */
+const React = require('react');
-var React = require('react');
-var _ = require('lodash');
+const NodeDetailsTable = require('./node-details-table');
+const NodeColorMixin = require('../mixins/node-color-mixin');
-var NodeDetailsTable = require('./node-details-table');
-var NodeColorMixin = require('../mixins/node-color-mixin');
+const NodeDetails = React.createClass({
-var NodeDetails = React.createClass({
+ mixins: [
+ NodeColorMixin
+ ],
- mixins: [
- NodeColorMixin
- ],
+ render: function() {
+ const node = this.props.details;
- render: function() {
- var node = this.props.details;
+ if (!node) {
+ return ;
+ }
- if (!node) {
- return ;
- }
+ const style = {
+ 'background-color': this.getNodeColorDark(node.label_major)
+ };
- var style = {
- "background-color": this.getNodeColorDark(node.label_major)
- };
+ return (
+
+
+
+ {node.label_major}
+
+
{node.label_minor}
+
- return (
-
-
-
- {node.label_major}
-
-
{node.label_minor}
-
-
-
- {this.props.details.tables.map(function(table) {
- return ;
- })}
-
-
- );
- }
+
+ {this.props.details.tables.map(function(table) {
+ return ;
+ })}
+
+
+ );
+ }
});
-module.exports = NodeDetails;
\ No newline at end of file
+module.exports = NodeDetails;
diff --git a/client/app/scripts/components/nodes.js b/client/app/scripts/components/nodes.js
index 34c6f7f6d..945428b6a 100644
--- a/client/app/scripts/components/nodes.js
+++ b/client/app/scripts/components/nodes.js
@@ -1,60 +1,57 @@
-/** @jsx React.DOM */
+const React = require('react');
-var React = require('react');
+const NodesChart = require('../charts/nodes-chart');
+const AppActions = require('../actions/app-actions');
-var NodesChart = require('../charts/nodes-chart');
-var AppActions = require('../actions/app-actions');
+const navbarHeight = 160;
+const marginTop = 0;
-var navbarHeight = 160;
-var marginTop = 0;
-var marginLeft = 0;
+const Nodes = React.createClass({
-var Nodes = React.createClass({
+ getInitialState: function() {
+ return {
+ width: window.innerWidth,
+ height: window.innerHeight - navbarHeight - marginTop
+ };
+ },
- getInitialState: function() {
- return {
- width: window.innerWidth,
- height: window.innerHeight - navbarHeight - marginTop
- };
- },
+ componentDidMount: function() {
+ window.addEventListener('resize', this.handleResize);
+ },
- onNodeClick: function(ev) {
- AppActions.clickNode(ev.currentTarget.id);
- },
+ componentWillUnmount: function() {
+ window.removeEventListener('resize', this.handleResize);
+ },
- componentDidMount: function() {
- window.addEventListener('resize', this.handleResize);
- },
+ onNodeClick: function(ev) {
+ AppActions.clickNode(ev.currentTarget.id);
+ },
- componentWillUnmount: function() {
- window.removeEventListener('resize', this.handleResize);
- },
+ render: function() {
+ return (
+
+
+
+ );
+ },
- setDimensions: function() {
- this.setState({
- height: window.innerHeight - navbarHeight - marginTop,
- width: window.innerWidth
- });
- },
+ handleResize: function() {
+ this.setDimensions();
+ },
- handleResize: function() {
- this.setDimensions();
- },
-
- render: function() {
- return (
-
-
-
- );
- }
+ setDimensions: function() {
+ this.setState({
+ height: window.innerHeight - navbarHeight - marginTop,
+ width: window.innerWidth
+ });
+ }
});
-module.exports = Nodes;
\ No newline at end of file
+module.exports = Nodes;
diff --git a/client/app/scripts/components/status.js b/client/app/scripts/components/status.js
index bd3b023cd..d07d13f4f 100644
--- a/client/app/scripts/components/status.js
+++ b/client/app/scripts/components/status.js
@@ -1,27 +1,25 @@
-/** @jsx React.DOM */
+const React = require('react');
-var React = require('react');
+const Status = React.createClass({
-var Status = React.createClass({
+ renderConnectionState: function() {
+ return (
+
+
+ Scope is disconnected
+
+ );
+ },
- renderConnectionState: function() {
- return (
-
-
- Scope is disconnected
-
- );
- },
+ render: function() {
+ const isDisconnected = this.props.connectionState === 'disconnected';
- render: function() {
- var isDisconnected = this.props.connectionState === 'disconnected';
-
- return (
-
- {isDisconnected && this.renderConnectionState()}
-
- );
- }
+ return (
+
+ {isDisconnected && this.renderConnectionState()}
+
+ );
+ }
});
diff --git a/client/app/scripts/components/topologies.js b/client/app/scripts/components/topologies.js
index b5347e406..152e4245c 100644
--- a/client/app/scripts/components/topologies.js
+++ b/client/app/scripts/components/topologies.js
@@ -1,50 +1,48 @@
-/** @jsx React.DOM */
+const React = require('react');
+const _ = require('lodash');
-var React = require('react');
-var _ = require('lodash');
+const AppActions = require('../actions/app-actions');
+const AppStore = require('../stores/app-store');
-var AppActions = require('../actions/app-actions');
-var AppStore = require('../stores/app-store');
+const Topologies = React.createClass({
-var Topologies = React.createClass({
+ onTopologyClick: function(ev) {
+ ev.preventDefault();
+ AppActions.clickTopology(ev.currentTarget.getAttribute('rel'));
+ },
- onTopologyClick: function(ev) {
- ev.preventDefault();
- AppActions.clickTopology(ev.currentTarget.getAttribute('rel'));
- },
+ renderTopology: function(topology) {
+ const isActive = topology.name === this.props.currentTopology.name;
+ const className = isActive ? 'topologies-item topologies-item-active' : 'topologies-item';
+ const topologyId = AppStore.getTopologyIdForUrl(topology.url);
+ const title = ['Topology: ' + topology.name,
+ 'Nodes: ' + topology.stats.node_count,
+ 'Connections: ' + topology.stats.node_count].join('\n');
- renderTopology: function(topology, active) {
- var isActive = topology.name === this.props.currentTopology.name,
- className = isActive ? "topologies-item topologies-item-active" : "topologies-item",
- topologyId = AppStore.getTopologyIdForUrl(topology.url),
- title = ['Topology: ' + topology.name,
- 'Nodes: ' + topology.stats.node_count,
- 'Connections: ' + topology.stats.node_count].join('\n');
+ return (
+
+ );
+ },
- return (
-
- );
- },
+ render: function() {
+ const topologies = _.sortBy(this.props.topologies, function(topology) {
+ return topology.name;
+ });
- render: function() {
- var topologies = _.sortBy(this.props.topologies, function(topology) {
- return topology.name;
- });
-
- return (
-
- {this.props.currentTopology && topologies.map(function(topology) {
- return this.renderTopology(topology);
- }, this)}
-
- );
- }
+ return (
+
+ {this.props.currentTopology && topologies.map(function(topology) {
+ return this.renderTopology(topology);
+ }, this)}
+
+ );
+ }
});
diff --git a/client/app/scripts/constants/action-types.js b/client/app/scripts/constants/action-types.js
index e25f6e99c..91a3772c5 100644
--- a/client/app/scripts/constants/action-types.js
+++ b/client/app/scripts/constants/action-types.js
@@ -1,16 +1,16 @@
-var keymirror = require('keymirror');
+const keymirror = require('keymirror');
module.exports = keymirror({
- CLICK_CLOSE_DETAILS: null,
- CLICK_GROUPING: null,
- CLICK_NODE: null,
- CLICK_TOPOLOGY: null,
- ENTER_NODE: null,
- HIT_ESC_KEY: null,
- LEAVE_NODE: null,
- RECEIVE_NODE_DETAILS: null,
- RECEIVE_NODES: null,
- RECEIVE_NODES_DELTA: null,
- RECEIVE_TOPOLOGIES: null,
- ROUTE_TOPOLOGY: null
+ CLICK_CLOSE_DETAILS: null,
+ CLICK_GROUPING: null,
+ CLICK_NODE: null,
+ CLICK_TOPOLOGY: null,
+ ENTER_NODE: null,
+ HIT_ESC_KEY: null,
+ LEAVE_NODE: null,
+ RECEIVE_NODE_DETAILS: null,
+ RECEIVE_NODES: null,
+ RECEIVE_NODES_DELTA: null,
+ RECEIVE_TOPOLOGIES: null,
+ ROUTE_TOPOLOGY: null
});
diff --git a/client/app/scripts/constants/topologies.js b/client/app/scripts/constants/topologies.js
index 395da8edf..b8180ef16 100644
--- a/client/app/scripts/constants/topologies.js
+++ b/client/app/scripts/constants/topologies.js
@@ -1,13 +1,12 @@
-
module.exports = [
- {
- url: '/api/topology/applications',
- name: 'Applications',
- stats: {}
- },
- {
- url: '/api/topology/hosts',
- name: 'Hosts',
- stats: {}
- }
+ {
+ url: '/api/topology/applications',
+ name: 'Applications',
+ stats: {}
+ },
+ {
+ url: '/api/topology/hosts',
+ name: 'Hosts',
+ stats: {}
+ }
];
diff --git a/client/app/scripts/dispatcher/app-dispatcher.js b/client/app/scripts/dispatcher/app-dispatcher.js
index 4c5b1ebcb..1f24e13fc 100644
--- a/client/app/scripts/dispatcher/app-dispatcher.js
+++ b/client/app/scripts/dispatcher/app-dispatcher.js
@@ -1,12 +1,12 @@
-var flux = require('flux');
-var _ = require('lodash');
+const flux = require('flux');
+const _ = require('lodash');
-var AppDispatcher = new flux.Dispatcher();
+const AppDispatcher = new flux.Dispatcher();
AppDispatcher.dispatch = _.wrap(flux.Dispatcher.prototype.dispatch, function(func) {
- var args = Array.prototype.slice.call(arguments, 1);
- // console.log(args[0]);
- func.apply(this, args);
+ const args = Array.prototype.slice.call(arguments, 1);
+ // console.log(args[0]);
+ func.apply(this, args);
});
module.exports = AppDispatcher;
diff --git a/client/app/scripts/main.js b/client/app/scripts/main.js
index 91bf8528a..330003e05 100644
--- a/client/app/scripts/main.js
+++ b/client/app/scripts/main.js
@@ -1,11 +1,7 @@
-/**
- * @jsx React.DOM
- */
+const React = require('react');
-var React = require('react');
-
-var App = require('./components/app.js');
+const App = require('./components/app.js');
React.render(
- ,
- document.getElementById('app'));
+ ,
+ document.getElementById('app'));
diff --git a/client/app/scripts/mixins/node-color-mixin.js b/client/app/scripts/mixins/node-color-mixin.js
index b1c986f66..c96b3468a 100644
--- a/client/app/scripts/mixins/node-color-mixin.js
+++ b/client/app/scripts/mixins/node-color-mixin.js
@@ -1,27 +1,27 @@
-var d3 = require('d3');
+const d3 = require('d3');
-var colors = d3.scale.category20();
+const colors = d3.scale.category20();
// make sure the internet always gets the same color
-var internetLabel = "the Internet";
+const internetLabel = 'the Internet';
colors(internetLabel);
-var NodeColorMixin = {
- getNodeColor: function(text) {
- return colors(text);
- },
- getNodeColorDark: function(text) {
- var color = d3.rgb(colors(text));
- var hsl = color.hsl();
+const NodeColorMixin = {
+ getNodeColor: function(text) {
+ return colors(text);
+ },
+ getNodeColorDark: function(text) {
+ const color = d3.rgb(colors(text));
+ let hsl = color.hsl();
- // ensure darkness
- // if (hsl.l > 0.5) {
- hsl = hsl.darker();
- // }
+ // ensure darkness
+ // if (hsl.l > 0.5) {
+ hsl = hsl.darker();
+ // }
- return hsl.toString();
- }
+ return hsl.toString();
+ }
};
-module.exports = NodeColorMixin;
\ No newline at end of file
+module.exports = NodeColorMixin;
diff --git a/client/app/scripts/stores/__tests__/app-store-test.js b/client/app/scripts/stores/__tests__/app-store-test.js
index 4c7159d0a..acc7065c0 100644
--- a/client/app/scripts/stores/__tests__/app-store-test.js
+++ b/client/app/scripts/stores/__tests__/app-store-test.js
@@ -1,61 +1,61 @@
+
describe('AppStore', function() {
+ const ActionTypes = require('../../constants/action-types');
+ let AppStore;
+ let registeredCallback;
- var ActionTypes = require('../../constants/action-types');
- var AppStore, registeredCallback;
+ // actions
- // actions
+ const ClickTopologyAction = {
+ type: ActionTypes.CLICK_TOPOLOGY,
+ topologyId: 'topo1'
+ };
- var ClickTopologyAction = {
- type: ActionTypes.CLICK_TOPOLOGY,
- topologyId: 'topo1'
- };
+ const ClickGroupingAction = {
+ type: ActionTypes.CLICK_GROUPING,
+ grouping: 'grouped'
+ };
- var ClickGroupingAction = {
- type: ActionTypes.CLICK_GROUPING,
- grouping: 'grouped'
- };
+ const ReceiveTopologiesAction = {
+ type: ActionTypes.RECEIVE_TOPOLOGIES,
+ topologies: [{
+ url: '/topo1',
+ grouped_url: '/topo1grouped',
+ name: 'Topo1'
+ }]
+ };
- var ReceiveTopologiesAction = {
- type: ActionTypes.RECEIVE_TOPOLOGIES,
- topologies: [{
- url: '/topo1',
- grouped_url: '/topo1grouped',
- name: 'Topo1'
- }]
- };
+ beforeEach(function() {
+ AppStore = require('../app-store');
+ registeredCallback = AppStore.registeredCallback;
+ });
- beforeEach(function() {
- AppStore = require('../app-store');
- registeredCallback = AppStore.registeredCallback;
- });
+ // topology tests
- // topology tests
+ it('init with no topologies', function() {
+ const topos = AppStore.getTopologies();
+ expect(topos.length).toBe(0);
+ expect(AppStore.getCurrentTopology()).toBeUndefined();
+ });
- it('init with no topologies', function() {
- var topos = AppStore.getTopologies();
- expect(topos.length).toBe(0);
- expect(AppStore.getCurrentTopology()).toBeUndefined();
- });
+ it('get current topology', function() {
+ registeredCallback(ClickTopologyAction);
+ registeredCallback(ReceiveTopologiesAction);
- it('get current topology', function() {
- registeredCallback(ClickTopologyAction);
- registeredCallback(ReceiveTopologiesAction);
+ expect(AppStore.getTopologies().length).toBe(1);
+ expect(AppStore.getCurrentTopology().name).toBe('Topo1');
+ expect(AppStore.getCurrentTopologyUrl()).toBe('/topo1');
+ });
- expect(AppStore.getTopologies().length).toBe(1);
- expect(AppStore.getCurrentTopology().name).toBe('Topo1');
- expect(AppStore.getCurrentTopologyUrl()).toBe('/topo1');
- });
-
- it('get grouped topology', function() {
- registeredCallback(ClickTopologyAction);
- registeredCallback(ReceiveTopologiesAction);
- registeredCallback(ClickGroupingAction);
-
- expect(AppStore.getTopologies().length).toBe(1);
- expect(AppStore.getCurrentTopology().name).toBe('Topo1');
- expect(AppStore.getCurrentTopologyUrl()).toBe('/topo1grouped');
- });
+ it('get grouped topology', function() {
+ registeredCallback(ClickTopologyAction);
+ registeredCallback(ReceiveTopologiesAction);
+ registeredCallback(ClickGroupingAction);
+ expect(AppStore.getTopologies().length).toBe(1);
+ expect(AppStore.getCurrentTopology().name).toBe('Topo1');
+ expect(AppStore.getCurrentTopologyUrl()).toBe('/topo1grouped');
+ });
});
\ No newline at end of file
diff --git a/client/app/scripts/stores/app-store.js b/client/app/scripts/stores/app-store.js
index e670b6170..1763c0321 100644
--- a/client/app/scripts/stores/app-store.js
+++ b/client/app/scripts/stores/app-store.js
@@ -1,187 +1,183 @@
+const EventEmitter = require('events').EventEmitter;
+const _ = require('lodash');
+const assign = require('object-assign');
-var EventEmitter = require('events').EventEmitter;
-var _ = require('lodash');
-var assign = require('object-assign');
-
-var AppDispatcher = require('../dispatcher/app-dispatcher');
-var ActionTypes = require('../constants/action-types');
-
-
-// Initial values
-
-var connectionState = 'disconnected';
-var currentGrouping = 'none';
-var currentTopologyId = 'applications';
-var mouseOverNode = null;
-var nodes = {};
-var nodeDetails = null;
-var selectedNodeId = null;
-var topologies = [];
-
-// Store API
-
-var AppStore = assign({}, EventEmitter.prototype, {
-
- CHANGE_EVENT: 'change',
-
- getAppState: function() {
- return {
- topologyId: currentTopologyId,
- grouping: this.getCurrentGrouping(),
- selectedNodeId: this.getSelectedNodeId()
- };
- },
-
- getConnectionState: function() {
- return connectionState;
- },
-
- getCurrentTopology: function() {
- return _.find(topologies, function(topology) {
- return isUrlForTopologyId(topology.url, currentTopologyId);
- });
- },
-
- getCurrentTopologyUrl: function() {
- var topology = this.getCurrentTopology();
-
- if (topology) {
- return topology.grouped_url && currentGrouping == 'grouped' ? topology.grouped_url : topology.url;
- }
- },
-
- getCurrentGrouping: function() {
- return currentGrouping;
- },
-
- getNodeDetails: function() {
- return nodeDetails;
- },
-
- getNodes: function() {
- return nodes;
- },
-
- getSelectedNodeId: function() {
- return selectedNodeId;
- },
-
- getTopologies: function() {
- return topologies;
- },
-
- getTopologyIdForUrl: function(url) {
- return url.split('/').pop();
- }
-});
-
+const AppDispatcher = require('../dispatcher/app-dispatcher');
+const ActionTypes = require('../constants/action-types');
// Helpers
function isUrlForTopologyId(url, topologyId) {
- return _.endsWith(url, topologyId);
+ return _.endsWith(url, topologyId);
}
+// Initial values
+
+let connectionState = 'disconnected';
+let currentGrouping = 'none';
+let currentTopologyId = 'applications';
+let mouseOverNode = null;
+let nodes = {};
+let nodeDetails = null;
+let selectedNodeId = null;
+let topologies = [];
+
+// Store API
+
+const AppStore = assign({}, EventEmitter.prototype, {
+
+ CHANGE_EVENT: 'change',
+
+ getAppState: function() {
+ return {
+ topologyId: currentTopologyId,
+ grouping: this.getCurrentGrouping(),
+ selectedNodeId: this.getSelectedNodeId()
+ };
+ },
+
+ getConnectionState: function() {
+ return connectionState;
+ },
+
+ getCurrentTopology: function() {
+ return _.find(topologies, function(topology) {
+ return isUrlForTopologyId(topology.url, currentTopologyId);
+ });
+ },
+
+ getCurrentTopologyUrl: function() {
+ const topology = this.getCurrentTopology();
+
+ if (topology) {
+ return topology.grouped_url && currentGrouping === 'grouped' ? topology.grouped_url : topology.url;
+ }
+ },
+
+ getCurrentGrouping: function() {
+ return currentGrouping;
+ },
+
+ getNodeDetails: function() {
+ return nodeDetails;
+ },
+
+ getNodes: function() {
+ return nodes;
+ },
+
+ getSelectedNodeId: function() {
+ return selectedNodeId;
+ },
+
+ getTopologies: function() {
+ return topologies;
+ },
+
+ getTopologyIdForUrl: function(url) {
+ return url.split('/').pop();
+ }
+});
// Store Dispatch Hooks
AppStore.registeredCallback = function(payload) {
- switch (payload.type) {
+ switch (payload.type) {
- case ActionTypes.CLICK_CLOSE_DETAILS:
- selectedNodeId = null;
- AppStore.emit(AppStore.CHANGE_EVENT);
- break;
+ case ActionTypes.CLICK_CLOSE_DETAILS:
+ selectedNodeId = null;
+ AppStore.emit(AppStore.CHANGE_EVENT);
+ break;
- case ActionTypes.CLICK_GROUPING:
- if (payload.grouping !== currentGrouping) {
- currentGrouping = payload.grouping;
- nodes = {};
- AppStore.emit(AppStore.CHANGE_EVENT);
- }
- break;
+ case ActionTypes.CLICK_GROUPING:
+ if (payload.grouping !== currentGrouping) {
+ currentGrouping = payload.grouping;
+ nodes = {};
+ AppStore.emit(AppStore.CHANGE_EVENT);
+ }
+ break;
- case ActionTypes.CLICK_NODE:
- selectedNodeId = payload.nodeId;
- AppStore.emit(AppStore.CHANGE_EVENT);
- break;
+ case ActionTypes.CLICK_NODE:
+ selectedNodeId = payload.nodeId;
+ AppStore.emit(AppStore.CHANGE_EVENT);
+ break;
- case ActionTypes.CLICK_TOPOLOGY:
- if (payload.topologyId !== currentTopologyId) {
- currentTopologyId = payload.topologyId;
- nodes = {};
- }
- AppStore.emit(AppStore.CHANGE_EVENT);
- break;
+ case ActionTypes.CLICK_TOPOLOGY:
+ if (payload.topologyId !== currentTopologyId) {
+ currentTopologyId = payload.topologyId;
+ nodes = {};
+ }
+ AppStore.emit(AppStore.CHANGE_EVENT);
+ break;
- case ActionTypes.ENTER_NODE:
- mouseOverNode = payload.nodeId;
- AppStore.emit(AppStore.CHANGE_EVENT);
- break;
+ case ActionTypes.ENTER_NODE:
+ mouseOverNode = payload.nodeId;
+ AppStore.emit(AppStore.CHANGE_EVENT);
+ break;
- case ActionTypes.HIT_ESC_KEY:
- nodeDetails = null;
- selectedNodeId = null;
- AppStore.emit(AppStore.CHANGE_EVENT);
- break;
+ case ActionTypes.HIT_ESC_KEY:
+ nodeDetails = null;
+ selectedNodeId = null;
+ AppStore.emit(AppStore.CHANGE_EVENT);
+ break;
- case ActionTypes.LEAVE_NODE:
- mouseOverNode = null;
- AppStore.emit(AppStore.CHANGE_EVENT);
- break;
+ case ActionTypes.LEAVE_NODE:
+ mouseOverNode = null;
+ AppStore.emit(AppStore.CHANGE_EVENT);
+ break;
- case ActionTypes.RECEIVE_NODE_DETAILS:
- nodeDetails = payload.details;
- AppStore.emit(AppStore.CHANGE_EVENT);
- break;
+ case ActionTypes.RECEIVE_NODE_DETAILS:
+ nodeDetails = payload.details;
+ AppStore.emit(AppStore.CHANGE_EVENT);
+ break;
- case ActionTypes.RECEIVE_NODES_DELTA:
- console.log('RECEIVE_NODES_DELTA',
- 'remove', _.size(payload.delta.remove),
- 'update', _.size(payload.delta.update),
- 'add', _.size(payload.delta.add));
+ case ActionTypes.RECEIVE_NODES_DELTA:
+ console.log('RECEIVE_NODES_DELTA',
+ 'remove', _.size(payload.delta.remove),
+ 'update', _.size(payload.delta.update),
+ 'add', _.size(payload.delta.add));
- connectionState = "connected";
+ connectionState = 'connected';
- // nodes that no longer exist
- _.each(payload.delta.remove, function(nodeId) {
- // in case node disappears before mouseleave event
- if (mouseOverNode === nodeId) {
- mouseOverNode = null;
- }
- delete nodes[nodeId];
- });
+ // nodes that no longer exist
+ _.each(payload.delta.remove, function(nodeId) {
+ // in case node disappears before mouseleave event
+ if (mouseOverNode === nodeId) {
+ mouseOverNode = null;
+ }
+ delete nodes[nodeId];
+ });
- // update existing nodes
- _.each(payload.delta.update, function(node) {
- nodes[node.id] = node;
- });
+ // update existing nodes
+ _.each(payload.delta.update, function(node) {
+ nodes[node.id] = node;
+ });
- // add new nodes
- _.each(payload.delta.add, function(node) {
- nodes[node.id] = node;
- });
+ // add new nodes
+ _.each(payload.delta.add, function(node) {
+ nodes[node.id] = node;
+ });
- AppStore.emit(AppStore.CHANGE_EVENT);
- break;
+ AppStore.emit(AppStore.CHANGE_EVENT);
+ break;
- case ActionTypes.RECEIVE_TOPOLOGIES:
- topologies = payload.topologies;
- AppStore.emit(AppStore.CHANGE_EVENT);
- break;
+ case ActionTypes.RECEIVE_TOPOLOGIES:
+ topologies = payload.topologies;
+ AppStore.emit(AppStore.CHANGE_EVENT);
+ break;
- case ActionTypes.ROUTE_TOPOLOGY:
- nodes = {};
- currentTopologyId = payload.state.topologyId;
- currentGrouping = payload.state.grouping;
- selectedNodeId = payload.state.selectedNodeId;
- AppStore.emit(AppStore.CHANGE_EVENT);
- break;
+ case ActionTypes.ROUTE_TOPOLOGY:
+ nodes = {};
+ currentTopologyId = payload.state.topologyId;
+ currentGrouping = payload.state.grouping;
+ selectedNodeId = payload.state.selectedNodeId;
+ AppStore.emit(AppStore.CHANGE_EVENT);
+ break;
- default:
- break;
+ default:
+ break;
- }
+ }
};
AppStore.dispatchToken = AppDispatcher.register(AppStore.registeredCallback);
diff --git a/client/app/scripts/utils/router-utils.js b/client/app/scripts/utils/router-utils.js
index 4493cde30..bd484a260 100644
--- a/client/app/scripts/utils/router-utils.js
+++ b/client/app/scripts/utils/router-utils.js
@@ -1,31 +1,29 @@
+const page = require('page');
-var page = require('page');
+const AppActions = require('../actions/app-actions');
+const AppStore = require('../stores/app-store');
-var AppActions = require('../actions/app-actions');
-var AppStore = require('../stores/app-store');
+function updateRoute() {
+ const state = AppStore.getAppState();
+ const stateUrl = JSON.stringify(state);
+ const dispatch = false;
-page('/', function(ctx) {
- updateRoute();
+ page.show('/state/' + stateUrl, state, dispatch);
+}
+
+page('/', function() {
+ updateRoute();
});
page('/state/:state', function(ctx) {
- var state = JSON.parse(ctx.params.state);
- AppActions.route(state);
+ const state = JSON.parse(ctx.params.state);
+ AppActions.route(state);
});
-function updateRoute() {
- var state = AppStore.getAppState();
- var stateUrl = JSON.stringify(state);
- var dispatch = false;
-
- page.show('/state/' + stateUrl, state, dispatch);
-}
-
-
module.exports = {
- getRouter: function() {
- return page;
- },
+ getRouter: function() {
+ return page;
+ },
- updateRoute: updateRoute
-};
\ No newline at end of file
+ updateRoute: updateRoute
+};
diff --git a/client/app/scripts/utils/web-api-utils.js b/client/app/scripts/utils/web-api-utils.js
index cd391ffdb..e1ae0b234 100644
--- a/client/app/scripts/utils/web-api-utils.js
+++ b/client/app/scripts/utils/web-api-utils.js
@@ -1,68 +1,69 @@
-var reqwest = require('reqwest');
+const reqwest = require('reqwest');
-var AppActions = require('../actions/app-actions');
+const AppActions = require('../actions/app-actions');
-var WS_URL = window.WS_URL || 'ws://' + location.host;
+const WS_URL = window.WS_URL || 'ws://' + location.host;
-var socket;
-var reconnectTimer = 0;
-var currentUrl = null;
-var updateFrequency = '5s';
-var topologyTimer = 0;
+let socket;
+let reconnectTimer = 0;
+let currentUrl = null;
+let updateFrequency = '5s';
+let topologyTimer = 0;
function createWebsocket(topologyUrl) {
- if (socket) {
- socket.onclose = null;
- socket.close();
- }
+ if (socket) {
+ socket.onclose = null;
+ socket.close();
+ }
- socket = new WebSocket(WS_URL + topologyUrl + '/ws?t=' + updateFrequency);
+ socket = new WebSocket(WS_URL + topologyUrl + '/ws?t=' + updateFrequency);
- socket.onclose = function() {
- clearTimeout(reconnectTimer);
- socket = null;
+ socket.onclose = function() {
+ clearTimeout(reconnectTimer);
+ socket = null;
- reconnectTimer = setTimeout(function() {
- createWebsocket(topologyUrl);
- }, 5000);
- }
+ reconnectTimer = setTimeout(function() {
+ createWebsocket(topologyUrl);
+ }, 5000);
+ };
- socket.onmessage = function(event) {
- var msg = JSON.parse(event.data);
- if (msg.add || msg.remove || msg.update) {
- AppActions.receiveNodesDelta(msg);
- }
- };
+ socket.onmessage = function(event) {
+ const msg = JSON.parse(event.data);
+ if (msg.add || msg.remove || msg.update) {
+ AppActions.receiveNodesDelta(msg);
+ }
+ };
- currentUrl = topologyUrl;
+ currentUrl = topologyUrl;
}
function getTopologies() {
- clearTimeout(topologyTimer);
- reqwest('/api/topology', function(res) {
- AppActions.receiveTopologies(res);
- topologyTimer = setTimeout(getTopologies, 10000);
- });
+ clearTimeout(topologyTimer);
+ reqwest('/api/topology', function(res) {
+ AppActions.receiveTopologies(res);
+ topologyTimer = setTimeout(getTopologies, 10000);
+ });
}
function getNodeDetails(topologyUrl, nodeId) {
- if (topologyUrl && nodeId) {
- var url = [topologyUrl, nodeId].join('/');
- reqwest(url, function(res) {
- AppActions.receiveNodeDetails(res.node);
- });
- }
+ if (topologyUrl && nodeId) {
+ const url = [topologyUrl, nodeId].join('/');
+ reqwest(url, function(res) {
+ AppActions.receiveNodeDetails(res.node);
+ });
+ }
}
module.exports = {
- getNodeDetails: getNodeDetails,
+ getNodeDetails: getNodeDetails,
- getTopologies: getTopologies,
+ getTopologies: getTopologies,
+
+ getNodesDelta: function(topologyUrl) {
+ if (topologyUrl && topologyUrl !== currentUrl) {
+ createWebsocket(topologyUrl);
+ }
+ }
+};
- getNodesDelta: function(topologyUrl) {
- if (topologyUrl && topologyUrl !== currentUrl) {
- createWebsocket(topologyUrl);
- }
- }
-}
diff --git a/client/app/styles/main.less b/client/app/styles/main.less
index 9881388bc..4a442f3e5 100644
--- a/client/app/styles/main.less
+++ b/client/app/styles/main.less
@@ -5,10 +5,10 @@
@import url(http://fonts.googleapis.com/css?family=Roboto:300,400);
.browsehappy {
- margin: 0.2em 0;
- background: #ccc;
- color: #000;
- padding: 0.2em 0;
+ margin: 0.2em 0;
+ background: #ccc;
+ color: #000;
+ padding: 0.2em 0;
}
/* weave company colours */
@@ -35,250 +35,248 @@ html, body {
/* Space out content a bit */
body {
- background: linear-gradient(30deg, @background-color 0%, @background-secondary-color 100%);
- color: @text-color;
- line-height: 150%;
+ background: linear-gradient(30deg, @background-color 0%, @background-secondary-color 100%);
+ color: @text-color;
+ line-height: 150%;
}
#app {
}
.header {
- position: absolute;
- top: 32px;
- width: 100%;
- height: 80px;
- z-index: 20;
+ position: absolute;
+ top: 32px;
+ width: 100%;
+ height: 80px;
+ z-index: 20;
}
.logo {
- margin: -8px 0 0 64px;
- height: 64px;
- width: 250px;
- float: left;
+ margin: -8px 0 0 64px;
+ height: 64px;
+ width: 250px;
+ float: left;
}
.topologies,
.groupings {
- float: left;
- margin-top: 7px;
- margin-left: 48px;
+ float: left;
+ margin-top: 7px;
+ margin-left: 48px;
}
.topologies {
- &-icon {
- font-size: 12px;
- color: @text-secondary-color;
- margin-right: 16px;
- position: relative;
- top: -1px;
+ &-icon {
+ font-size: 12px;
+ color: @text-secondary-color;
+ margin-right: 16px;
+ position: relative;
+ top: -1px;
+ }
+
+ .topologies-item {
+ margin: 8px 16px 6px 0;
+ cursor: pointer;
+ display: inline-block;
+
+ &-label {
+ color: @text-secondary-color;
+ font-size: 15px;
+ text-transform: uppercase;
}
- .topologies-item {
- margin: 8px 16px 6px 0;
- cursor: pointer;
- display: inline-block;
-
- &-label {
- color: @text-secondary-color;
- font-size: 15px;
- text-transform: uppercase;
- }
-
- &-active, &:hover {
- .topologies-item-label {
- color: @text-color;
- }
- }
+ &-active, &:hover {
+ .topologies-item-label {
+ color: @text-color;
+ }
}
+ }
}
.groupings {
- &-item {
- font-size: 15px;
- margin: 8px 12px 6px 0;
- cursor: pointer;
- display: inline-block;
- color: @text-tertiary-color;
+ &-item {
+ font-size: 15px;
+ margin: 8px 12px 6px 0;
+ cursor: pointer;
+ display: inline-block;
+ color: @text-tertiary-color;
- &-disabled {
- color: @text-tertiary-color;
- cursor: default;
- }
-
- &-default:hover,
- &-active {
- color: @text-color;
- }
+ &-disabled {
+ color: @text-tertiary-color;
+ cursor: default;
}
+
+ &-default:hover,
+ &-active {
+ color: @text-color;
+ }
+ }
}
.status {
- float: right;
- margin-top: 14px;
- margin-right: 64px;
+ float: right;
+ margin-top: 14px;
+ margin-right: 64px;
- &-icon {
- font-size: 16px;
- position: relative;
- top: 1px;
- color: @text-secondary-color;
- }
+ &-icon {
+ font-size: 16px;
+ position: relative;
+ top: 1px;
+ color: @text-secondary-color;
+ }
- &-label {
- margin-left: 0.5em;
- }
+ &-label {
+ margin-left: 0.5em;
+ }
}
#nodes {
- svg {
- position: absolute;
- top: 0px;
- }
+ svg {
+ position: absolute;
+ top: 0px;
+ }
- text {
- font-size: 14px;
- font-family: Roboto;
- fill: @text-secondary-color;
- text-shadow: 0 2px 0 @white, 2px 0 0 @white, 0 -2px 0 @white, -2px 0 0 @white;
+ text {
+ font-size: 14px;
+ font-family: Roboto;
+ fill: @text-secondary-color;
+ text-shadow: 0 2px 0 @white, 2px 0 0 @white, 0 -2px 0 @white, -2px 0 0 @white;
- &.node-label {
- fill: @text-color;
- }
-
- &.node-sublabel {
- font-size: 12px;
- fill: @text-secondary-color;
- }
- }
-
- g.node {
- cursor: pointer;
- }
-
- .link {
- stroke: @text-secondary-color;
- stroke-width: 1.5px;
- fill: none;
- opacity: 0.5;
- }
-
- circle.border {
- stroke-width: 3px;
- fill: none;
- }
-
- circle.shadow {
- stroke: none;
- fill: @background-secondary-color;
- }
-
- circle.node {
+ &.node-label {
fill: @text-color;
}
+
+ &.node-sublabel {
+ font-size: 12px;
+ fill: @text-secondary-color;
+ }
+ }
+
+ g.node {
+ cursor: pointer;
+ }
+
+ .link {
+ stroke: @text-secondary-color;
+ stroke-width: 1.5px;
+ fill: none;
+ opacity: 0.5;
+ }
+
+ circle.border {
+ stroke-width: 3px;
+ fill: none;
+ }
+
+ circle.shadow {
+ stroke: none;
+ fill: @background-secondary-color;
+ }
+
+ circle.node {
+ fill: @text-color;
+ }
}
#details {
- position: absolute;
- z-index: 1024;
- display: block;
- right: 36px;
- top: 24px;
- bottom: 48px;
- width: 32em;
+ position: absolute;
+ z-index: 1024;
+ display: block;
+ right: 36px;
+ top: 24px;
+ bottom: 48px;
+ width: 32em;
- .details-tools {
- float: right;
- padding: 16px 24px;
- color: @white;
+ .details-tools {
+ float: right;
+ padding: 16px 24px;
+ color: @white;
- span {
- cursor: pointer;
- &:hover {
- color: white;
- }
- }
+ span {
+ cursor: pointer;
+ &:hover {
+ color: white;
+ }
}
+ }
}
.mui-paper, .mui-paper-container {
- height: 100%;
+ height: 100%;
}
.node-details {
- height: 100%;
+ height: 100%;
+ width: 100%;
+ background-color: rgba(255, 255, 255, 0.86);
+
+ &-header {
+
+ padding: 24px 36px 24px 36px;
+
+ &-label {
+ color: white;
+ margin-bottom: 0;
+
+ &-minor {
+ font-size: 120%;
+ color: @white;
+ }
+
+ }
+ }
+
+ &-content {
+ position: absolute;
+ top: 115px;
+ bottom: 0;
width: 100%;
- background-color: rgba(255, 255, 255, 0.86);
+ padding: 0 36px 0 36px;
+ overflow-y: scroll;
+ }
- &-header {
+ &-table {
- padding: 24px 36px 24px 36px;
+ margin-bottom: 8px;
- &-label {
- color: white;
- margin-bottom: 0;
+ &-title {
+ text-transform: uppercase;
+ margin-bottom: 0;
+ color: @text-secondary-color;
+ font-size: 100%;
+ }
- &-minor {
- font-size: 120%;
- color: @white;
- }
+ &-row {
+ white-space: nowrap;
+ clear: left;
- }
+ &-key {
+ width: 9em;
+ float: left;
+ }
+
+ &-value-major {
+ margin-right: 0.5em;
+ overflow: hidden;
+ text-overflow: ellipsis;
+ }
+
+ &-value-scalar {
+ width: 2em;
+ text-align: right;
+ margin-right: 0.5em;
+ }
+
+ &-value-minor,
+ &-value-unit {
+ font-size: 95%;
+ color: @text-secondary-color;
+ }
}
- &-content {
- position: absolute;
- top: 115px;
- bottom: 0;
- width: 100%;
- padding: 0 36px 0 36px;
- overflow-y: scroll;
- }
-
- &-table {
-
- margin-bottom: 8px;
-
- &-title {
- text-transform: uppercase;
- margin-bottom: 0;
- color: @text-secondary-color;
- font-size: 100%;
- }
-
- &-row {
- white-space: nowrap;
- clear: left;
-
- &-key {
- float: left;
- width: 9em;
- }
-
- &-value-major {
- margin-right: 0.5em;
-
- overflow: hidden;
- text-overflow: ellipsis;
- }
-
- &-value-scalar {
- width: 2em;
- text-align: right;
- margin-right: 0.5em;
- }
-
- &-value-minor,
- &-value-unit {
- font-size: 95%;
- color: @text-secondary-color;
- }
-
- }
-
- }
+ }
}
diff --git a/client/gulpfile.js b/client/gulpfile.js
index c443f086e..c8a192958 100644
--- a/client/gulpfile.js
+++ b/client/gulpfile.js
@@ -1,115 +1,143 @@
-'use strict';
-var gulp = require('gulp');
-var connect = require('gulp-connect');
-var livereload = require('gulp-livereload');
-var browserify = require('browserify');
-var del = require('del');
-var source = require('vinyl-source-stream');
-var buffer = require('vinyl-buffer');
-var reactify = require('reactify');
+const gulp = require('gulp');
+const connect = require('gulp-connect');
+const livereload = require('gulp-livereload');
+const babelify = require('babelify');
+const browserify = require('browserify');
+const del = require('del');
+const source = require('vinyl-source-stream');
+const vbuffer = require('vinyl-buffer');
+const reactify = require('reactify');
// load plugins
-var $ = require('gulp-load-plugins')();
+const $ = require('gulp-load-plugins')();
var isDev = true;
var isProd = false;
-gulp.task('styles', function () {
- return gulp.src('app/styles/main.less')
- .pipe($.if(isDev, $.sourcemaps.init()))
- .pipe($.less())
- .pipe($.autoprefixer('last 1 version'))
- .pipe($.if(isDev, $.sourcemaps.write()))
- .pipe($.if(isDev, gulp.dest('.tmp/styles')))
- .pipe($.if(isProd, $.csso()))
- .pipe($.if(isProd, gulp.dest('dist/styles')))
- .pipe($.size())
- .pipe(livereload());
+gulp.task('styles', function() {
+ return gulp.src('app/styles/main.less')
+ .pipe($.if(isDev, $.sourcemaps.init()))
+ .pipe($.less())
+ .pipe($.autoprefixer('last 1 version'))
+ .pipe($.if(isDev, $.sourcemaps.write()))
+ .pipe($.if(isDev, gulp.dest('.tmp/styles')))
+ .pipe($.if(isProd, $.csso()))
+ .pipe($.if(isProd, gulp.dest('dist/styles')))
+ .pipe($.size())
+ .pipe(livereload());
});
gulp.task('scripts', function() {
- var bundler = browserify('./app/scripts/main.js', {debug: isDev});
- bundler.transform(reactify);
+ const bundler = browserify('./app/scripts/main.js', {debug: isDev});
+ bundler.transform(reactify);
+ bundler.transform(babelify);
- var stream = bundler.bundle();
+ const stream = bundler.bundle();
- return stream
- .pipe(source('bundle.js'))
- .pipe($.if(isDev, gulp.dest('.tmp/scripts')))
- .pipe($.if(isProd, buffer()))
- .pipe($.if(isProd, $.uglify()))
- .pipe($.if(isProd, gulp.dest('dist/scripts')))
- .pipe(livereload())
- .on('error', $.util.log);
+ return stream
+ .pipe(source('bundle.js'))
+ .pipe($.if(isDev, gulp.dest('.tmp/scripts')))
+ .pipe($.if(isProd, vbuffer()))
+ .pipe($.if(isProd, $.uglify()))
+ .on('error', $.util.log)
+ .pipe($.if(isProd, gulp.dest('dist/scripts')))
+ .pipe(livereload());
});
-gulp.task('html', ['styles', 'scripts'], function () {
- return gulp.src('app/*.html')
- .pipe($.preprocess())
- .pipe(gulp.dest('dist'))
- .pipe($.size())
- .pipe(livereload());
+gulp.task('html', ['styles', 'scripts'], function() {
+ return gulp.src('app/*.html')
+ .pipe($.preprocess())
+ .pipe(gulp.dest('dist'))
+ .pipe($.size())
+ .pipe(livereload());
});
-gulp.task('images', function () {
- return gulp.src('app/images/**/*')
- .pipe(gulp.dest('dist/images'))
- .pipe($.size());
+gulp.task('images', function() {
+ return gulp.src('app/images/**/*')
+ .pipe(gulp.dest('dist/images'))
+ .pipe($.size());
});
-gulp.task('fonts', function () {
- return gulp.src('node_modules/font-awesome/fonts/*')
- .pipe($.filter('**/*.{eot,svg,ttf,woff,woff2}'))
- .pipe($.flatten())
- .pipe($.if(isDev, gulp.dest('.tmp/fonts')))
- .pipe($.if(isProd, gulp.dest('dist/fonts')))
- .pipe($.size());
+gulp.task('fonts', function() {
+ return gulp.src('node_modules/font-awesome/fonts/*')
+ .pipe($.filter('**/*.{eot,svg,ttf,woff,woff2}'))
+ .pipe($.flatten())
+ .pipe($.if(isDev, gulp.dest('.tmp/fonts')))
+ .pipe($.if(isProd, gulp.dest('dist/fonts')))
+ .pipe($.size());
});
-gulp.task('extras', function () {
- return gulp.src(['app/*.*', '!app/*.html'], { dot: true })
- .pipe(gulp.dest('dist'));
+gulp.task('extras', function() {
+ return gulp.src(['app/*.*', '!app/*.html'], { dot: true })
+ .pipe(gulp.dest('dist'));
});
-gulp.task('clean', function () {
- return del(['.tmp', 'dist']);
+gulp.task('clean', function() {
+ return del(['.tmp', 'dist']);
+});
+
+
+gulp.task('lint', function() {
+ return gulp.src(['app/**/*.js'])
+ // eslint() attaches the lint output to the eslint property
+ // of the file object so it can be used by other modules.
+ .pipe($.eslint())
+ // eslint.format() outputs the lint results to the console.
+ // Alternatively use eslint.formatEach() (see Docs).
+ .pipe($.eslint.format())
+ // To have the process exit with an error code (1) on
+ // lint error, return the stream and pipe to failOnError last.
+ .pipe($.eslint.failOnError());
});
gulp.task('production', ['html', 'images', 'fonts', 'extras']);
-gulp.task('build', function () {
- isDev = false;
- isProd = true;
- gulp.start('production');
+gulp.task('build', function() {
+ isDev = false;
+ isProd = true;
+ gulp.start('production');
});
-gulp.task('default', ['clean'], function () {
- gulp.start('build');
+gulp.task('default', ['clean'], function() {
+ gulp.start('build');
});
-gulp.task('connect', function () {
- connect.server({
- root: ['.tmp', 'app'],
- port: 4041,
- middleware: function(connect, o) {
- return [(function() {
- var url = require('url');
- var proxy = require('proxy-middleware');
- var options = url.parse('http://localhost:4040/api');
- options.route = '/api';
- return proxy(options);
- })()];
- },
- livereload: false
- });
+gulp.task('connect', function() {
+ const root = isProd ? ['dist'] : ['.tmp', 'app'];
+ connect.server({
+ root: root,
+ port: 4041,
+ middleware: function() {
+ return [(function() {
+ const url = require('url');
+ const proxy = require('proxy-middleware');
+ const options = url.parse('http://localhost:4040/api');
+ options.route = '/api';
+ return proxy(options);
+ })()];
+ },
+ livereload: false
+ });
});
gulp.task('serve', ['connect', 'styles', 'scripts', 'fonts']);
-gulp.task('watch', ['serve'], function () {
- livereload.listen();
- gulp.watch('app/styles/**/*.less', ['styles']);
- gulp.watch('app/scripts/**/*.js', ['scripts']);
- gulp.watch('app/images/**/*', ['images']);
+gulp.task('serve-build', function() {
+ isDev = false;
+ isProd = true;
+
+ // use local WS api
+ gulp.src('app/*.html')
+ .pipe($.preprocess({context: {DEBUG: true} }))
+ .pipe(gulp.dest('dist'));
+
+ gulp.start('connect');
+});
+
+gulp.task('watch', ['serve'], function() {
+ livereload.listen();
+ gulp.watch('app/styles/**/*.less', ['styles']);
+ gulp.watch('app/scripts/**/*.js', ['scripts']);
+ gulp.watch('app/images/**/*', ['images']);
});
diff --git a/client/package.json b/client/package.json
index 7b14a29f5..e45f17f09 100644
--- a/client/package.json
+++ b/client/package.json
@@ -21,16 +21,21 @@
"reqwest": "~1.1.5"
},
"devDependencies": {
+ "babel-eslint": "^3.1.9",
+ "babelify": "^6.1.2",
"browserify": "^10.2.0",
"del": "^1.1.1",
+ "eslint": "^0.21.2",
+ "eslint-plugin-jasmine": "^1.0.0",
+ "eslint-plugin-react": "^2.3.0",
"gulp": "^3.8.11",
"gulp-autoprefixer": "^2.3.0",
"gulp-connect": "^2.2.0",
"gulp-csso": "^1.0.0",
+ "gulp-eslint": "^0.12.0",
"gulp-filter": "^2.0.2",
"gulp-flatten": "^0.0.4",
"gulp-if": "^1.2.5",
- "gulp-jshint": "^1.10.0",
"gulp-less": "^3.0.3",
"gulp-livereload": "^3.8.0",
"gulp-load-plugins": "^0.10.0",
@@ -55,6 +60,7 @@
"vinyl-source-stream": "^1.1.0"
},
"scripts": {
+ "lint": "gulp lint",
"start": "gulp",
"test": "karma start test/karma.conf.js --single-run"
},
diff --git a/client/test/karma.conf.js b/client/test/karma.conf.js
index 1d50e1f7b..068b09e99 100644
--- a/client/test/karma.conf.js
+++ b/client/test/karma.conf.js
@@ -14,7 +14,7 @@ module.exports = function(config) {
},
browserify: {
debug: true,
- transform: ['reactify']
+ transform: ['reactify', 'babelify']
},
reporters: [
'dots'