aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--mitmproxy/web/__init__.py2
-rw-r--r--mitmproxy/web/static/app.js614
-rw-r--r--web/src/js/components/flowtable.js64
-rw-r--r--web/src/js/components/mainview.js149
-rw-r--r--web/src/js/components/proxyapp.js8
-rw-r--r--web/src/js/ducks/eventLog.js2
-rw-r--r--web/src/js/ducks/flows.js51
-rw-r--r--web/src/js/ducks/utils/view.js77
-rw-r--r--web/src/js/store/store.js59
-rw-r--r--web/src/js/store/view.js111
10 files changed, 434 insertions, 703 deletions
diff --git a/mitmproxy/web/__init__.py b/mitmproxy/web/__init__.py
index e69de29b..cd497ce2 100644
--- a/mitmproxy/web/__init__.py
+++ b/mitmproxy/web/__init__.py
@@ -0,0 +1,2 @@
+from mitmproxy.web import master
+__all__ = ["master"] \ No newline at end of file
diff --git a/mitmproxy/web/static/app.js b/mitmproxy/web/static/app.js
index d1c9dfa8..88b5e85a 100644
--- a/mitmproxy/web/static/app.js
+++ b/mitmproxy/web/static/app.js
@@ -437,7 +437,7 @@ var Query = exports.Query = {
SHOW_EVENTLOG: "e"
};
-},{"./dispatcher.js":22,"./utils.js":33,"jquery":"jquery"}],3:[function(require,module,exports){
+},{"./dispatcher.js":22,"./utils.js":32,"jquery":"jquery"}],3:[function(require,module,exports){
'use strict';
var _react = require('react');
@@ -709,7 +709,7 @@ ToggleInputButton.propTypes = {
onToggleChanged: _react2.default.PropTypes.func.isRequired
};
-},{"../utils.js":33,"lodash":"lodash","react":"react","react-dom":"react-dom"}],5:[function(require,module,exports){
+},{"../utils.js":32,"lodash":"lodash","react":"react","react-dom":"react-dom"}],5:[function(require,module,exports){
"use strict";
Object.defineProperty(exports, "__esModule", {
@@ -967,7 +967,7 @@ var ValueEditor = exports.ValueEditor = _react2.default.createClass({
}
});
-},{"../utils.js":33,"react":"react","react-dom":"react-dom"}],6:[function(require,module,exports){
+},{"../utils.js":32,"react":"react","react-dom":"react-dom"}],6:[function(require,module,exports){
"use strict";
Object.defineProperty(exports, "__esModule", {
@@ -1462,7 +1462,7 @@ var all_columns = [TLSColumn, IconColumn, PathColumn, MethodColumn, StatusColumn
exports.default = all_columns;
-},{"../flow/utils.js":30,"../utils.js":33,"react":"react"}],8:[function(require,module,exports){
+},{"../flow/utils.js":30,"../utils.js":32,"react":"react"}],8:[function(require,module,exports){
"use strict";
Object.defineProperty(exports, "__esModule", {
@@ -1505,6 +1505,10 @@ var _flowtableColumns = require("./flowtable-columns.js");
var _flowtableColumns2 = _interopRequireDefault(_flowtableColumns);
+var _filt = require("../filt/filt");
+
+var _filt2 = _interopRequireDefault(_filt);
+
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } }
@@ -1517,7 +1521,7 @@ FlowRow.propTypes = {
selectFlow: _react2.default.PropTypes.func.isRequired,
columns: _react2.default.PropTypes.array.isRequired,
flow: _react2.default.PropTypes.object.isRequired,
- highlighted: _react2.default.PropTypes.bool,
+ highlight: _react2.default.PropTypes.string,
selected: _react2.default.PropTypes.bool
};
@@ -1526,7 +1530,7 @@ function FlowRow(props) {
var className = (0, _classnames2.default)({
"selected": props.selected,
- "highlighted": props.highlighted,
+ "highlighted": props.highlight && parseFilter(props.highlight)(flow),
"intercepted": flow.intercepted,
"has-request": flow.request,
"has-response": flow.response
@@ -1545,9 +1549,11 @@ function FlowRow(props) {
var FlowRowContainer = (0, _reactRedux.connect)(function (state, ownProps) {
return {
- flow: state.flows.all.byId[ownProps.flowId]
+ flow: state.flows.all.byId[ownProps.flowId],
+ highlight: state.flows.highlight,
+ selected: state.flows.selected.indexOf(ownProps.flowId) >= 0
};
-}, function (dispatch) {
+}, function (dispatch, ownProps) {
return {};
})(FlowRow);
@@ -1629,9 +1635,8 @@ var FlowTable = function (_React$Component2) {
var _this3 = _possibleConstructorReturn(this, Object.getPrototypeOf(FlowTable).call(this, props, context));
- _this3.state = { flows: [], vScroll: (0, _VirtualScroll.calcVScroll)() };
+ _this3.state = { vScroll: (0, _VirtualScroll.calcVScroll)() };
- _this3.onChange = _this3.onChange.bind(_this3);
_this3.onViewportUpdate = _this3.onViewportUpdate.bind(_this3);
return _this3;
}
@@ -1640,19 +1645,22 @@ var FlowTable = function (_React$Component2) {
key: "componentWillMount",
value: function componentWillMount() {
window.addEventListener("resize", this.onViewportUpdate);
- this.context.view.addListener("add", this.onChange);
- this.context.view.addListener("update", this.onChange);
- this.context.view.addListener("remove", this.onChange);
- this.context.view.addListener("recalculate", this.onChange);
}
}, {
key: "componentWillUnmount",
value: function componentWillUnmount() {
window.removeEventListener("resize", this.onViewportUpdate);
- this.context.view.removeListener("add", this.onChange);
- this.context.view.removeListener("update", this.onChange);
- this.context.view.removeListener("remove", this.onChange);
- this.context.view.removeListener("recalculate", this.onChange);
+ }
+ }, {
+ key: "componentWillReceiveProps",
+ value: function componentWillReceiveProps(nextProps) {
+ var _this4 = this;
+
+ if (nextProps.selected && nextProps.selected !== this.props.selected) {
+ window.setTimeout(function () {
+ return _this4.scrollIntoView(nextProps.selected);
+ }, 1);
+ }
}
}, {
key: "componentDidUpdate",
@@ -1668,7 +1676,7 @@ var FlowTable = function (_React$Component2) {
var vScroll = (0, _VirtualScroll.calcVScroll)({
viewportTop: viewportTop,
viewportHeight: viewport.offsetHeight,
- itemCount: this.state.flows.length,
+ itemCount: this.props.flows.length,
rowHeight: this.props.rowHeight
});
@@ -1677,15 +1685,10 @@ var FlowTable = function (_React$Component2) {
}
}
}, {
- key: "onChange",
- value: function onChange() {
- this.setState({ flows: this.context.view.list });
- }
- }, {
key: "scrollIntoView",
value: function scrollIntoView(flow) {
var viewport = _reactDom2.default.findDOMNode(this);
- var index = this.context.view.indexOf(flow);
+ var index = this.props.flows.indexOf(flow);
var rowHeight = this.props.rowHeight;
var head = _reactDom2.default.findDOMNode(this.refs.head);
@@ -1707,11 +1710,10 @@ var FlowTable = function (_React$Component2) {
}, {
key: "render",
value: function render() {
- var _this4 = this;
+ var _this5 = this;
var vScroll = this.state.vScroll;
- var highlight = this.context.view._highlight;
- var flows = this.state.flows.slice(vScroll.start, vScroll.end);
+ var flows = this.props.flows.slice(vScroll.start, vScroll.end);
var transform = "translate(0," + this.state.viewportTop + "px)";
@@ -1735,12 +1737,10 @@ var FlowTable = function (_React$Component2) {
_react2.default.createElement("tr", { style: { height: vScroll.paddingTop } }),
flows.map(function (flow) {
return _react2.default.createElement(FlowRowContainer, {
- flowId: flow.id,
key: flow.id,
+ flowId: flow.id,
columns: _flowtableColumns2.default,
- selected: flow === _this4.props.selected,
- highlighted: highlight && highlight[flow.id],
- selectFlow: _this4.props.selectFlow
+ selectFlow: _this5.props.selectFlow
});
}),
_react2.default.createElement("tr", { style: { height: vScroll.paddingBottom } })
@@ -1753,18 +1753,29 @@ var FlowTable = function (_React$Component2) {
return FlowTable;
}(_react2.default.Component);
-FlowTable.contextTypes = {
- view: _react2.default.PropTypes.object.isRequired
-};
FlowTable.propTypes = {
rowHeight: _react2.default.PropTypes.number
};
FlowTable.defaultProps = {
rowHeight: 32
};
-exports.default = (0, _AutoScroll2.default)(FlowTable);
-},{"../utils.js":33,"./flowtable-columns.js":7,"./helpers/AutoScroll":16,"./helpers/VirtualScroll":17,"classnames":"classnames","lodash":"lodash","react":"react","react-dom":"react-dom","react-redux":"react-redux","shallowequal":"shallowequal"}],9:[function(require,module,exports){
+
+FlowTable = (0, _AutoScroll2.default)(FlowTable);
+
+var parseFilter = _lodash2.default.memoize(_filt2.default.parse);
+
+var FlowTableContainer = (0, _reactRedux.connect)(function (state) {
+ return {
+ flows: state.flows.view
+ };
+}, function (dispatch) {
+ return {};
+})(FlowTable);
+
+exports.default = FlowTableContainer;
+
+},{"../filt/filt":29,"../utils.js":32,"./flowtable-columns.js":7,"./helpers/AutoScroll":16,"./helpers/VirtualScroll":17,"classnames":"classnames","lodash":"lodash","react":"react","react-dom":"react-dom","react-redux":"react-redux","shallowequal":"shallowequal"}],9:[function(require,module,exports){
"use strict";
Object.defineProperty(exports, "__esModule", {
@@ -2118,7 +2129,7 @@ var ContentView = _react2.default.createClass({
exports.default = ContentView;
-},{"../../flow/utils.js":30,"../../utils.js":33,"lodash":"lodash","react":"react"}],10:[function(require,module,exports){
+},{"../../flow/utils.js":30,"../../utils.js":32,"lodash":"lodash","react":"react"}],10:[function(require,module,exports){
"use strict";
Object.defineProperty(exports, "__esModule", {
@@ -2386,7 +2397,7 @@ var Details = _react2.default.createClass({
exports.default = Details;
-},{"../../utils.js":33,"lodash":"lodash","react":"react"}],11:[function(require,module,exports){
+},{"../../utils.js":32,"lodash":"lodash","react":"react"}],11:[function(require,module,exports){
"use strict";
Object.defineProperty(exports, "__esModule", {
@@ -2880,7 +2891,7 @@ var Error = exports.Error = _react2.default.createClass({
}
});
-},{"../../actions.js":2,"../../flow/utils.js":30,"../../utils.js":33,"../editor.js":5,"./contentview.js":9,"lodash":"lodash","react":"react","react-dom":"react-dom"}],13:[function(require,module,exports){
+},{"../../actions.js":2,"../../flow/utils.js":30,"../../utils.js":32,"../editor.js":5,"./contentview.js":9,"lodash":"lodash","react":"react","react-dom":"react-dom"}],13:[function(require,module,exports){
"use strict";
Object.defineProperty(exports, "__esModule", {
@@ -3063,7 +3074,7 @@ function Footer(_ref) {
);
}
-},{"../utils.js":33,"./common.js":4,"react":"react"}],15:[function(require,module,exports){
+},{"../utils.js":32,"./common.js":4,"react":"react"}],15:[function(require,module,exports){
"use strict";
Object.defineProperty(exports, "__esModule", {
@@ -3638,7 +3649,7 @@ var Header = exports.Header = _react2.default.createClass({
}
});
-},{"../actions.js":2,"../filt/filt.js":29,"../utils.js":33,"./common.js":4,"./eventlog":6,"jquery":"jquery","react":"react","react-dom":"react-dom","react-redux":"react-redux"}],16:[function(require,module,exports){
+},{"../actions.js":2,"../filt/filt.js":29,"../utils.js":32,"./common.js":4,"./eventlog":6,"jquery":"jquery","react":"react","react-dom":"react-dom","react-redux":"react-redux"}],16:[function(require,module,exports){
"use strict";
Object.defineProperty(exports, "__esModule", {
@@ -3798,12 +3809,6 @@ var _actions = require("../actions.js");
var _utils = require("../utils.js");
-var _view = require("../store/view.js");
-
-var _filt = require("../filt/filt.js");
-
-var _filt2 = _interopRequireDefault(_filt);
-
var _common = require("./common.js");
var _flowtable = require("./flowtable.js");
@@ -3814,107 +3819,43 @@ var _index = require("./flowview/index.js");
var _index2 = _interopRequireDefault(_index);
+var _reactRedux = require("react-redux");
+
+var _flows = require("../ducks/flows");
+
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
var MainView = _react2.default.createClass({
displayName: "MainView",
- contextTypes: {
- flowStore: _react2.default.PropTypes.object.isRequired
- },
- childContextTypes: {
- view: _react2.default.PropTypes.object.isRequired
- },
- getChildContext: function getChildContext() {
- return {
- view: this.state.view
- };
- },
- getInitialState: function getInitialState() {
- var sortKeyFun = false;
- var view = new _view.StoreView(this.context.flowStore, this.getViewFilt(), sortKeyFun);
- view.addListener("recalculate", this.onRecalculate);
- view.addListener("add", this.onUpdate);
- view.addListener("update", this.onUpdate);
- view.addListener("remove", this.onUpdate);
- view.addListener("remove", this.onRemove);
-
- return {
- view: view,
- sortKeyFun: sortKeyFun
- };
- },
- componentWillUnmount: function componentWillUnmount() {
- this.state.view.close();
- },
- getViewFilt: function getViewFilt() {
- try {
- var filtStr = this.props.query[_actions.Query.SEARCH];
- var filt = filtStr ? _filt2.default.parse(filtStr) : function () {
- return true;
- };
- var highlightStr = this.props.query[_actions.Query.HIGHLIGHT];
- var highlight = highlightStr ? _filt2.default.parse(highlightStr) : function () {
- return false;
- };
- } catch (e) {
- console.error("Error when processing filter: " + e);
- }
-
- var fun = function filter_and_highlight(flow) {
- if (!this._highlight) {
- this._highlight = {};
- }
- this._highlight[flow.id] = highlight(flow);
- return filt(flow);
- };
- fun.highlightStr = highlightStr;
- fun.filtStr = filtStr;
- return fun;
- },
componentWillReceiveProps: function componentWillReceiveProps(nextProps) {
- var filterChanged = this.state.view.filt.filtStr !== nextProps.location.query[_actions.Query.SEARCH];
- var highlightChanged = this.state.view.filt.highlightStr !== nextProps.location.query[_actions.Query.HIGHLIGHT];
- if (filterChanged || highlightChanged) {
- this.state.view.recalculate(this.getViewFilt(), this.state.sortKeyFun);
+ // Update redux store with route changes
+ if (nextProps.routeParams.flowId !== (nextProps.selectedFlow || {}).id) {
+ this.props.selectFlow(nextProps.routeParams.flowId);
}
- },
- onRecalculate: function onRecalculate() {
- this.forceUpdate();
- var selected = this.getSelected();
- if (selected) {
- this.refs.flowTable.scrollIntoView(selected);
+ if (nextProps.location.query[_actions.Query.SEARCH] !== nextProps.filter) {
+ this.props.setFilter(nextProps.location.query[_actions.Query.SEARCH], false);
}
- },
- onUpdate: function onUpdate(flow) {
- if (flow.id === this.props.routeParams.flowId) {
- this.forceUpdate();
- }
- },
- onRemove: function onRemove(flow_id, index) {
- if (flow_id === this.props.routeParams.flowId) {
- var flow_to_select = this.state.view.list[Math.min(index, this.state.view.list.length - 1)];
- this.selectFlow(flow_to_select);
+ if (nextProps.location.query[_actions.Query.HIGHLIGHT] !== nextProps.highlight) {
+ this.props.setHighlight(nextProps.location.query[_actions.Query.HIGHLIGHT], false);
}
},
setSortKeyFun: function setSortKeyFun(sortKeyFun) {
- this.setState({
- sortKeyFun: sortKeyFun
- });
- this.state.view.recalculate(this.getViewFilt(), sortKeyFun);
+ // FIXME: Move to redux. This requires that sortKeyFun is not a function anymore.
},
selectFlow: function selectFlow(flow) {
+ // TODO: This belongs into redux
if (flow) {
var tab = this.props.routeParams.detailTab || "request";
this.props.updateLocation("/flows/" + flow.id + "/" + tab);
- this.refs.flowTable.scrollIntoView(flow);
} else {
this.props.updateLocation("/flows");
}
},
selectFlowRelative: function selectFlowRelative(shift) {
- var flows = this.state.view.list;
- var index;
+ // TODO: This belongs into redux
+ var flows = this.props.flows,
+ index = void 0;
if (!this.props.routeParams.flowId) {
if (shift < 0) {
index = flows.length - 1;
@@ -3922,20 +3863,13 @@ var MainView = _react2.default.createClass({
index = 0;
}
} else {
- var currFlowId = this.props.routeParams.flowId;
- var i = flows.length;
- while (i--) {
- if (flows[i].id === currFlowId) {
- index = i;
- break;
- }
- }
+ index = flows.indexOf(this.props.selectedFlow);
index = Math.min(Math.max(0, index + shift), flows.length - 1);
}
this.selectFlow(flows[index]);
},
onMainKeyDown: function onMainKeyDown(e) {
- var flow = this.getSelected();
+ var flow = this.props.selectedFlow;
if (e.ctrlKey) {
return;
}
@@ -4021,23 +3955,17 @@ var MainView = _react2.default.createClass({
}
e.preventDefault();
},
- getSelected: function getSelected() {
- return this.context.flowStore.get(this.props.routeParams.flowId);
- },
render: function render() {
- var selected = this.getSelected();
- var details;
- if (selected) {
+ var details = null;
+ if (this.props.selectedFlow) {
details = [_react2.default.createElement(_common.Splitter, { key: "splitter" }), _react2.default.createElement(_index2.default, {
key: "flowDetails",
ref: "flowDetails",
tab: this.props.routeParams.detailTab,
query: this.props.query,
updateLocation: this.props.updateLocation,
- flow: selected })];
- } else {
- details = null;
+ flow: this.props.selectedFlow })];
}
return _react2.default.createElement(
@@ -4046,15 +3974,36 @@ var MainView = _react2.default.createClass({
_react2.default.createElement(_flowtable2.default, { ref: "flowTable",
selectFlow: this.selectFlow,
setSortKeyFun: this.setSortKeyFun,
- selected: selected }),
+ selected: this.props.selectedFlow }),
details
);
}
});
-exports.default = MainView;
+var MainViewContainer = (0, _reactRedux.connect)(function (state) {
+ return {
+ flows: state.flows.view,
+ filter: state.flows.filter,
+ highlight: state.flows.highlight,
+ selectedFlow: state.flows.all.byId[state.flows.selected[0]]
+ };
+}, function (dispatch) {
+ return {
+ selectFlow: function selectFlow(flowId) {
+ return dispatch((0, _flows.selectFlow)(flowId));
+ },
+ setFilter: function setFilter(filter) {
+ return dispatch((0, _flows.setFilter)(filter));
+ },
+ setHighlight: function setHighlight(highlight) {
+ return dispatch((0, _flows.setHighlight)(highlight));
+ }
+ };
+}, undefined, { withRef: true })(MainView);
+
+exports.default = MainViewContainer;
-},{"../actions.js":2,"../filt/filt.js":29,"../store/view.js":32,"../utils.js":33,"./common.js":4,"./flowtable.js":8,"./flowview/index.js":11,"react":"react"}],19:[function(require,module,exports){
+},{"../actions.js":2,"../ducks/flows":24,"../utils.js":32,"./common.js":4,"./flowtable.js":8,"./flowview/index.js":11,"react":"react","react-redux":"react-redux"}],19:[function(require,module,exports){
"use strict";
Object.defineProperty(exports, "__esModule", {
@@ -4190,7 +4139,7 @@ var Prompt = _react2.default.createClass({
exports.default = Prompt;
-},{"../utils.js":33,"lodash":"lodash","react":"react","react-dom":"react-dom"}],20:[function(require,module,exports){
+},{"../utils.js":32,"lodash":"lodash","react":"react","react-dom":"react-dom"}],20:[function(require,module,exports){
"use strict";
Object.defineProperty(exports, "__esModule", {
@@ -4253,7 +4202,6 @@ var ProxyAppMain = _react2.default.createClass({
displayName: "ProxyAppMain",
childContextTypes: {
- flowStore: _react2.default.PropTypes.object.isRequired,
returnFocus: _react2.default.PropTypes.func.isRequired,
location: _react2.default.PropTypes.object.isRequired
},
@@ -4291,21 +4239,18 @@ var ProxyAppMain = _react2.default.createClass({
},
getChildContext: function getChildContext() {
return {
- flowStore: this.state.flowStore,
returnFocus: this.focus,
location: this.props.location
};
},
getInitialState: function getInitialState() {
- var flowStore = new _store.FlowStore();
var settingsStore = new _store.SettingsStore();
this.settingsStore = settingsStore;
// Default Settings before fetch
_lodash2.default.extend(settingsStore.dict, {});
return {
- settings: settingsStore.dict,
- flowStore: flowStore
+ settings: settingsStore.dict
};
},
focus: function focus() {
@@ -4314,7 +4259,7 @@ var ProxyAppMain = _react2.default.createClass({
_reactDom2.default.findDOMNode(this).focus();
},
getMainComponent: function getMainComponent() {
- return this.refs.view;
+ return this.refs.view.getWrappedInstance ? this.refs.view.getWrappedInstance() : this.refs.view;
},
onKeydown: function onKeydown(e) {
@@ -4382,7 +4327,7 @@ var App = exports.App = _react2.default.createElement(
)
);
-},{"../store/store.js":31,"../utils.js":33,"./common.js":4,"./eventlog.js":6,"./footer.js":14,"./header.js":15,"./mainview.js":18,"lodash":"lodash","react":"react","react-dom":"react-dom","react-redux":"react-redux","react-router":"react-router"}],21:[function(require,module,exports){
+},{"../store/store.js":31,"../utils.js":32,"./common.js":4,"./eventlog.js":6,"./footer.js":14,"./header.js":15,"./mainview.js":18,"lodash":"lodash","react":"react","react-dom":"react-dom","react-redux":"react-redux","react-router":"react-router"}],21:[function(require,module,exports){
"use strict";
Object.defineProperty(exports, "__esModule", {
@@ -4534,7 +4479,7 @@ function reducer() {
var filter = _extends({}, state.filter, _defineProperty({}, action.filter, !state.filter[action.filter]));
return _extends({}, state, {
filter: filter,
- filteredEvents: (0, _view.updateViewFilter)(state.events.list, function (x) {
+ filteredEvents: (0, _view.updateViewFilter)(state.events, function (x) {
return filter[x.level];
})
});
@@ -4580,19 +4525,31 @@ exports.fetchLogEntries = fetchList;
Object.defineProperty(exports, "__esModule", {
value: true
});
-exports.fetchFlows = exports.updateFlows = exports.UPDATE_FLOWS = undefined;
+exports.fetchFlows = exports.updateFlows = exports.SELECT_FLOW = exports.SET_HIGHLIGHT = exports.SET_FILTER = exports.UPDATE_FLOWS = undefined;
var _extends = Object.assign || function (target) { for (var i = 1; i < arguments.length; i++) { var source = arguments[i]; for (var key in source) { if (Object.prototype.hasOwnProperty.call(source, key)) { target[key] = source[key]; } } } return target; };
exports.default = reducer;
+exports.setFilter = setFilter;
+exports.setHighlight = setHighlight;
+exports.selectFlow = selectFlow;
var _list = require("./utils/list");
var _list2 = _interopRequireDefault(_list);
+var _filt = require("../filt/filt");
+
+var _filt2 = _interopRequireDefault(_filt);
+
+var _view = require("./utils/view");
+
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
var UPDATE_FLOWS = exports.UPDATE_FLOWS = "UPDATE_FLOWS";
+var SET_FILTER = exports.SET_FILTER = "SET_FLOW_FILTER";
+var SET_HIGHLIGHT = exports.SET_HIGHLIGHT = "SET_FLOW_HIGHLIGHT";
+var SELECT_FLOW = exports.SELECT_FLOW = "SELECT_FLOW";
var _makeList = (0, _list2.default)(UPDATE_FLOWS, "/flows");
@@ -4602,9 +4559,19 @@ var fetchList = _makeList.fetchList;
var defaultState = {
- all: reduceList()
+ all: reduceList(),
+ selected: [],
+ view: [],
+ filter: undefined,
+ highlight: undefined
};
+function makeFilterFn(filter) {
+ return filter ? _filt2.default.parse(filter) : function () {
+ return true;
+ };
+}
+
function reducer() {
var state = arguments.length <= 0 || arguments[0] === undefined ? defaultState : arguments[0];
var action = arguments[1];
@@ -4613,17 +4580,50 @@ function reducer() {
case UPDATE_FLOWS:
var all = reduceList(state.all, action);
return _extends({}, state, {
- all: all
+ all: all,
+ view: (0, _view.updateViewList)(state.view, state.all, all, action, makeFilterFn(action.filter))
+ });
+ case SET_FILTER:
+ return _extends({}, state, {
+ filter: action.filter,
+ view: (0, _view.updateViewFilter)(state.all, makeFilterFn(action.filter))
+ });
+ case SET_HIGHLIGHT:
+ return _extends({}, state, {
+ highlight: action.highlight
+ });
+ case SELECT_FLOW:
+ return _extends({}, state, {
+ selected: [action.flowId]
});
default:
return state;
}
}
+function setFilter(filter) {
+ return {
+ type: SET_FILTER,
+ filter: filter
+ };
+}
+function setHighlight(highlight) {
+ return {
+ type: SET_HIGHLIGHT,
+ highlight: highlight
+ };
+}
+function selectFlow(flowId) {
+ return {
+ type: SELECT_FLOW,
+ flowId: flowId
+ };
+}
+
exports.updateFlows = updateList;
exports.fetchFlows = fetchList;
-},{"./utils/list":26}],25:[function(require,module,exports){
+},{"../filt/filt":29,"./utils/list":26,"./utils/view":27}],25:[function(require,module,exports){
'use strict';
Object.defineProperty(exports, "__esModule", {
@@ -4855,12 +4855,16 @@ function makeList(actionType, fetchURL) {
return { reduceList: reduceList, updateList: updateList, fetchList: fetchList, addItem: addItem, updateItem: updateItem, removeItem: removeItem };
}
-},{"../../utils":33}],27:[function(require,module,exports){
+},{"../../utils":32}],27:[function(require,module,exports){
"use strict";
Object.defineProperty(exports, "__esModule", {
value: true
});
+
+var _typeof = typeof Symbol === "function" && typeof Symbol.iterator === "symbol" ? function (obj) { return typeof obj; } : function (obj) { return obj && typeof Symbol === "function" && obj.constructor === Symbol ? "symbol" : typeof obj; };
+
+exports.sortedIndexOf = sortedIndexOf;
exports.updateViewList = updateViewList;
exports.updateViewFilter = updateViewFilter;
@@ -4885,14 +4889,17 @@ var makeCompareFn = function makeCompareFn(sortFn) {
return 0;
}
};
- if (sortFn.reverse) return function (a, b) {
- return compareFn(b, a);
- };
+ // need to adjust sortedIndexOf as well
+ // if (sortFn.reverse)
+ // return (a, b) => compareFn(b, a)
return compareFn;
};
var sortedInsert = function sortedInsert(list, sortFn, item) {
var l = [].concat(_toConsumableArray(list), [item]);
+ l.indexOf = function (x) {
+ return sortedIndexOf(l, x, sortFn);
+ };
var compareFn = makeCompareFn(sortFn);
// only sort if sorting order is not correct yet
@@ -4906,26 +4913,61 @@ var sortedInsert = function sortedInsert(list, sortFn, item) {
var sortedRemove = function sortedRemove(list, sortFn, item) {
var itemId = item.id;
- return list.filter(function (x) {
+ var l = list.filter(function (x) {
return x.id !== itemId;
});
+ l.indexOf = function (x) {
+ return sortedIndexOf(l, x, sortFn);
+ };
+ return l;
};
+function sortedIndexOf(list, value, sortFn) {
+ if (sortFn === false) {
+ var i = 0;
+ while (i < list.length && list[i].id !== value.id) {
+ i++;
+ }
+ return i;
+ }
+
+ var low = 0,
+ high = list.length,
+ val = sortFn(value),
+ mid = void 0;
+ while (low < high) {
+ mid = low + high >>> 1;
+ if (sortFn(list[mid]) < val) {
+ low = mid + 1;
+ } else {
+ high = mid;
+ }
+ }
+
+ // Two flows may have the same sort value.
+ // we previously determined the leftmost flow with the same sort value,
+ // so no we need to scan linearly
+ while (list[low].id !== value.id && sortFn(list[low + 1]) === val) {
+ low++;
+ }
+ return low;
+}
+
// for when the list changes
-function updateViewList(state, currentList, nextList, action) {
+function updateViewList(currentView, currentList, nextList, action) {
var filterFn = arguments.length <= 4 || arguments[4] === undefined ? defaultFilterFn : arguments[4];
var sortFn = arguments.length <= 5 || arguments[5] === undefined ? defaultSortFn : arguments[5];
switch (action.cmd) {
case _list.REQUEST_LIST:
- return state;
+ return currentView;
case _list.RECEIVE_LIST:
- return updateViewFilter(nextList.list, filterFn, sortFn);
+ return updateViewFilter(nextList, filterFn, sortFn);
case _list.ADD:
if (filterFn(action.item)) {
- return sortedInsert(state, sortFn, action.item);
+ return sortedInsert(currentView, sortFn, action.item);
}
- return state;
+ return currentView;
case _list.UPDATE:
// let's determine if it's in the view currently and if it should be in the view.
var currentItemState = currentList.byId[action.item.id],
@@ -4933,23 +4975,32 @@ function updateViewList(state, currentList, nextList, action) {
isInView = filterFn(currentItemState),
shouldBeInView = filterFn(nextItemState);
- if (!isInView && shouldBeInView) return sortedInsert(state, sortFn, action.item);
- if (isInView && !shouldBeInView) return sortedRemove(state, sortFn, action.item);
- if (isInView && shouldBeInView && sortFn(currentItemState) !== sortFn(nextItemState)) {
- var s = [].concat(_toConsumableArray(state));
- s.sort(sortFn);
- return s;
+ if (!isInView && shouldBeInView) return sortedInsert(currentView, sortFn, action.item);
+ if (isInView && !shouldBeInView) return sortedRemove(currentView, sortFn, action.item);
+ if (isInView && shouldBeInView && sortFn && sortFn(currentItemState) !== sortFn(nextItemState)) {
+ var _ret = function () {
+ var s = [].concat(_toConsumableArray(currentView));
+ s.sort(makeCompareFn(sortFn));
+ s.indexOf = function (x) {
+ return sortedIndexOf(s, x, sortFn);
+ };
+ return {
+ v: s
+ };
+ }();
+
+ if ((typeof _ret === "undefined" ? "undefined" : _typeof(_ret)) === "object") return _ret.v;
}
- return state;
+ return currentView;
case _list.REMOVE:
var isInView_ = filterFn(currentList.byId[action.item.id]);
if (isInView_) {
- return sortedRemove(state, sortFn, action.item);
+ return sortedRemove(currentView, sortFn, action.item);
}
- return state;
+ return currentView;
default:
console.error("Unknown list action: ", action);
- return state;
+ return currentView;
}
}
@@ -4957,8 +5008,14 @@ function updateViewFilter(list) {
var filterFn = arguments.length <= 1 || arguments[1] === undefined ? defaultFilterFn : arguments[1];
var sortFn = arguments.length <= 2 || arguments[2] === undefined ? defaultSortFn : arguments[2];
- var filtered = list.filter(filterFn);
- if (sortFn) filtered.sort(makeCompareFn(sortFn));
+ var filtered = list.list.filter(filterFn);
+ if (sortFn) {
+ filtered.sort(makeCompareFn(sortFn));
+ }
+ filtered.indexOf = function (x) {
+ return sortedIndexOf(filtered, x, sortFn);
+ };
+
return filtered;
}
@@ -7047,7 +7104,6 @@ var parseHttpVersion = exports.parseHttpVersion = function parseHttpVersion(http
Object.defineProperty(exports, "__esModule", {
value: true
});
-exports.FlowStore = FlowStore;
exports.SettingsStore = SettingsStore;
var _lodash = require("lodash");
@@ -7066,54 +7122,6 @@ var _dispatcher = require("../dispatcher.js");
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
-function ListStore() {
- _events.EventEmitter.call(this);
- this.reset();
-}
-_lodash2.default.extend(ListStore.prototype, _events.EventEmitter.prototype, {
- add: function add(elem) {
- if (elem.id in this._pos_map) {
- return;
- }
- this._pos_map[elem.id] = this.list.length;
- this.list.push(elem);
- this.emit("add", elem);
- },
- update: function update(elem) {
- if (!(elem.id in this._pos_map)) {
- return;
- }
- this.list[this._pos_map[elem.id]] = elem;
- this.emit("update", elem);
- },
- remove: function remove(elem_id) {
- if (!(elem_id in this._pos_map)) {
- return;
- }
- this.list.splice(this._pos_map[elem_id], 1);
- this._build_map();
- this.emit("remove", elem_id);
- },
- reset: function reset(elems) {
- this.list = elems || [];
- this._build_map();
- this.emit("recalculate");
- },
- _build_map: function _build_map() {
- this._pos_map = {};
- for (var i = 0; i < this.list.length; i++) {
- var elem = this.list[i];
- this._pos_map[elem.id] = i;
- }
- },
- get: function get(elem_id) {
- return this.list[this._pos_map[elem_id]];
- },
- index: function index(elem_id) {
- return this._pos_map[elem_id];
- }
-});
-
function DictStore() {
_events.EventEmitter.call(this);
this.reset();
@@ -7190,22 +7198,12 @@ _lodash2.default.extend(LiveStoreMixin.prototype, {
}
});
-function LiveListStore(type) {
- ListStore.call(this);
- LiveStoreMixin.call(this, type);
-}
-_lodash2.default.extend(LiveListStore.prototype, ListStore.prototype, LiveStoreMixin.prototype);
-
function LiveDictStore(type) {
DictStore.call(this);
LiveStoreMixin.call(this, type);
}
_lodash2.default.extend(LiveDictStore.prototype, DictStore.prototype, LiveStoreMixin.prototype);
-function FlowStore() {
- return new LiveListStore(_actions.ActionTypes.FLOW_STORE);
-}
-
function SettingsStore() {
return new LiveDictStore(_actions.ActionTypes.SETTINGS_STORE);
}
@@ -7216,136 +7214,6 @@ function SettingsStore() {
Object.defineProperty(exports, "__esModule", {
value: true
});
-exports.StoreView = StoreView;
-
-var _events = require("events");
-
-var _lodash = require("lodash");
-
-var _lodash2 = _interopRequireDefault(_lodash);
-
-var _utils = require("../utils.js");
-
-var _utils2 = _interopRequireDefault(_utils);
-
-function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
-
-function SortByStoreOrder(elem) {
- return this.store.index(elem.id);
-}
-
-var default_sort = SortByStoreOrder;
-var default_filt = function default_filt(elem) {
- return true;
-};
-
-function StoreView(store, filt, sortfun) {
- _events.EventEmitter.call(this);
-
- this.store = store;
-
- this.add = this.add.bind(this);
- this.update = this.update.bind(this);
- this.remove = this.remove.bind(this);
- this.recalculate = this.recalculate.bind(this);
- this.store.addListener("add", this.add);
- this.store.addListener("update", this.update);
- this.store.addListener("remove", this.remove);
- this.store.addListener("recalculate", this.recalculate);
-
- this.recalculate(filt, sortfun);
-}
-
-_lodash2.default.extend(StoreView.prototype, _events.EventEmitter.prototype, {
- close: function close() {
- this.store.removeListener("add", this.add);
- this.store.removeListener("update", this.update);
- this.store.removeListener("remove", this.remove);
- this.store.removeListener("recalculate", this.recalculate);
- this.removeAllListeners();
- },
- recalculate: function recalculate(filt, sortfun) {
- filt = filt || this.filt || default_filt;
- sortfun = sortfun || this.sortfun || default_sort;
- filt = filt.bind(this);
- sortfun = sortfun.bind(this);
- this.filt = filt;
- this.sortfun = sortfun;
-
- this.list = this.store.list.filter(filt);
- this.list.sort(function (a, b) {
- var akey = sortfun(a);
- var bkey = sortfun(b);
- if (akey < bkey) {
- return -1;
- } else if (akey > bkey) {
- return 1;
- } else {
- return 0;
- }
- });
- this.emit("recalculate");
- },
- indexOf: function indexOf(elem) {
- return this.list.indexOf(elem, _lodash2.default.sortedIndexBy(this.list, elem, this.sortfun));
- },
- add: function add(elem) {
- if (this.filt(elem)) {
- var idx = _lodash2.default.sortedIndexBy(this.list, elem, this.sortfun);
- if (idx === this.list.length) {
- //happens often, .push is way faster.
- this.list.push(elem);
- } else {
- this.list.splice(idx, 0, elem);
- }
- this.emit("add", elem, idx);
- }
- },
- update: function update(elem) {
- var idx;
- var i = this.list.length;
- // Search from the back, we usually update the latest entries.
- while (i--) {
- if (this.list[i].id === elem.id) {
- idx = i;
- break;
- }
- }
-
- if (idx === -1) {
- //not contained in list
- this.add(elem);
- } else if (!this.filt(elem)) {
- this.remove(elem.id);
- } else {
- if (this.sortfun(this.list[idx]) !== this.sortfun(elem)) {
- //sortpos has changed
- this.remove(this.list[idx]);
- this.add(elem);
- } else {
- this.list[idx] = elem;
- this.emit("update", elem, idx);
- }
- }
- },
- remove: function remove(elem_id) {
- var idx = this.list.length;
- while (idx--) {
- if (this.list[idx].id === elem_id) {
- this.list.splice(idx, 1);
- this.emit("remove", elem_id, idx);
- break;
- }
- }
- }
-});
-
-},{"../utils.js":33,"events":1,"lodash":"lodash"}],33:[function(require,module,exports){
-"use strict";
-
-Object.defineProperty(exports, "__esModule", {
- value: true
-});
exports.formatTimeStamp = exports.formatTimeDelta = exports.formatSize = exports.Key = undefined;
var _extends = Object.assign || function (target) { for (var i = 1; i < arguments.length; i++) { var source = arguments[i]; for (var key in source) { if (Object.prototype.hasOwnProperty.call(source, key)) { target[key] = source[key]; } } } return target; };
diff --git a/web/src/js/components/flowtable.js b/web/src/js/components/flowtable.js
index 1a616eee..0241cd78 100644
--- a/web/src/js/components/flowtable.js
+++ b/web/src/js/components/flowtable.js
@@ -8,12 +8,14 @@ import shallowEqual from "shallowequal";
import AutoScroll from "./helpers/AutoScroll";
import {calcVScroll} from "./helpers/VirtualScroll";
import flowtable_columns from "./flowtable-columns.js";
+import Filt from "../filt/filt";
+
FlowRow.propTypes = {
selectFlow: React.PropTypes.func.isRequired,
columns: React.PropTypes.array.isRequired,
flow: React.PropTypes.object.isRequired,
- highlighted: React.PropTypes.bool,
+ highlight: React.PropTypes.string,
selected: React.PropTypes.bool,
};
@@ -22,7 +24,7 @@ function FlowRow(props) {
const className = classNames({
"selected": props.selected,
- "highlighted": props.highlighted,
+ "highlighted": props.highlight && parseFilter(props.highlight)(flow),
"intercepted": flow.intercepted,
"has-request": flow.request,
"has-response": flow.response,
@@ -39,9 +41,12 @@ function FlowRow(props) {
const FlowRowContainer = connect(
(state, ownProps) => ({
- flow: state.flows.all.byId[ownProps.flowId]
+ flow: state.flows.all.byId[ownProps.flowId],
+ highlight: state.flows.highlight,
+ selected: state.flows.selected.indexOf(ownProps.flowId) >= 0
}),
- dispatch => ({
+ (dispatch, ownProps) => ({
+
})
)(FlowRow);
@@ -102,10 +107,6 @@ class FlowTableHead extends React.Component {
class FlowTable extends React.Component {
- static contextTypes = {
- view: React.PropTypes.object.isRequired,
- };
-
static propTypes = {
rowHeight: React.PropTypes.number,
};
@@ -117,26 +118,23 @@ class FlowTable extends React.Component {
constructor(props, context) {
super(props, context);
- this.state = { flows: [], vScroll: calcVScroll() };
+ this.state = { vScroll: calcVScroll() };
- this.onChange = this.onChange.bind(this);
this.onViewportUpdate = this.onViewportUpdate.bind(this);
}
componentWillMount() {
window.addEventListener("resize", this.onViewportUpdate);
- this.context.view.addListener("add", this.onChange);
- this.context.view.addListener("update", this.onChange);
- this.context.view.addListener("remove", this.onChange);
- this.context.view.addListener("recalculate", this.onChange);
}
componentWillUnmount() {
window.removeEventListener("resize", this.onViewportUpdate);
- this.context.view.removeListener("add", this.onChange);
- this.context.view.removeListener("update", this.onChange);
- this.context.view.removeListener("remove", this.onChange);
- this.context.view.removeListener("recalculate", this.onChange);
+ }
+
+ componentWillReceiveProps(nextProps) {
+ if(nextProps.selected && nextProps.selected !== this.props.selected){
+ window.setTimeout(() => this.scrollIntoView(nextProps.selected), 1)
+ }
}
componentDidUpdate() {
@@ -150,7 +148,7 @@ class FlowTable extends React.Component {
const vScroll = calcVScroll({
viewportTop,
viewportHeight: viewport.offsetHeight,
- itemCount: this.state.flows.length,
+ itemCount: this.props.flows.length,
rowHeight: this.props.rowHeight,
});
@@ -160,13 +158,9 @@ class FlowTable extends React.Component {
}
}
- onChange() {
- this.setState({ flows: this.context.view.list });
- }
-
scrollIntoView(flow) {
const viewport = ReactDOM.findDOMNode(this);
- const index = this.context.view.indexOf(flow);
+ const index = this.props.flows.indexOf(flow);
const rowHeight = this.props.rowHeight;
const head = ReactDOM.findDOMNode(this.refs.head);
@@ -188,8 +182,7 @@ class FlowTable extends React.Component {
render() {
const vScroll = this.state.vScroll;
- const highlight = this.context.view._highlight;
- const flows = this.state.flows.slice(vScroll.start, vScroll.end);
+ const flows = this.props.flows.slice(vScroll.start, vScroll.end);
const transform = `translate(0,${this.state.viewportTop}px)`;
@@ -206,11 +199,9 @@ class FlowTable extends React.Component {
<tr style={{ height: vScroll.paddingTop }}></tr>
{flows.map(flow => (
<FlowRowContainer
- flowId={flow.id}
key={flow.id}
+ flowId={flow.id}
columns={flowtable_columns}
- selected={flow === this.props.selected}
- highlighted={highlight && highlight[flow.id]}
selectFlow={this.props.selectFlow}
/>
))}
@@ -222,4 +213,17 @@ class FlowTable extends React.Component {
}
}
-export default AutoScroll(FlowTable);
+FlowTable = AutoScroll(FlowTable)
+
+
+const parseFilter = _.memoize(Filt.parse)
+
+const FlowTableContainer = connect(
+ state => ({
+ flows: state.flows.view,
+ }),
+ dispatch => ({
+ })
+)(FlowTable)
+
+export default FlowTableContainer;
diff --git a/web/src/js/components/mainview.js b/web/src/js/components/mainview.js
index 964e82db..22895991 100644
--- a/web/src/js/components/mainview.js
+++ b/web/src/js/components/mainview.js
@@ -3,128 +3,59 @@ import React from "react";
import {FlowActions} from "../actions.js";
import {Query} from "../actions.js";
import {Key} from "../utils.js";
-import {StoreView} from "../store/view.js";
-import Filt from "../filt/filt.js";
import {Splitter} from "./common.js"
import FlowTable from "./flowtable.js";
import FlowView from "./flowview/index.js";
+import {connect} from 'react-redux'
+import {selectFlow, setFilter, setHighlight} from "../ducks/flows";
-var MainView = React.createClass({
- contextTypes: {
- flowStore: React.PropTypes.object.isRequired,
- },
- childContextTypes: {
- view: React.PropTypes.object.isRequired,
- },
- getChildContext: function () {
- return {
- view: this.state.view
- };
- },
- getInitialState: function () {
- var sortKeyFun = false;
- var view = new StoreView(this.context.flowStore, this.getViewFilt(), sortKeyFun);
- view.addListener("recalculate", this.onRecalculate);
- view.addListener("add", this.onUpdate);
- view.addListener("update", this.onUpdate);
- view.addListener("remove", this.onUpdate);
- view.addListener("remove", this.onRemove);
-
- return {
- view: view,
- sortKeyFun: sortKeyFun
- };
- },
- componentWillUnmount: function () {
- this.state.view.close();
- },
- getViewFilt: function () {
- try {
- var filtStr = this.props.query[Query.SEARCH];
- var filt = filtStr ? Filt.parse(filtStr) : () => true;
- var highlightStr = this.props.query[Query.HIGHLIGHT];
- var highlight = highlightStr ? Filt.parse(highlightStr) : () => false;
- } catch (e) {
- console.error("Error when processing filter: " + e);
- }
- var fun = function filter_and_highlight(flow) {
- if (!this._highlight) {
- this._highlight = {};
- }
- this._highlight[flow.id] = highlight(flow);
- return filt(flow);
- };
- fun.highlightStr = highlightStr;
- fun.filtStr = filtStr;
- return fun;
- },
+var MainView = React.createClass({
componentWillReceiveProps: function (nextProps) {
- var filterChanged = this.state.view.filt.filtStr !== nextProps.location.query[Query.SEARCH];
- var highlightChanged = this.state.view.filt.highlightStr !== nextProps.location.query[Query.HIGHLIGHT];
- if (filterChanged || highlightChanged) {
- this.state.view.recalculate(this.getViewFilt(), this.state.sortKeyFun);
+ // Update redux store with route changes
+ if(nextProps.routeParams.flowId !== (nextProps.selectedFlow || {}).id) {
+ this.props.selectFlow(nextProps.routeParams.flowId)
}
- },
- onRecalculate: function () {
- this.forceUpdate();
- var selected = this.getSelected();
- if (selected) {
- this.refs.flowTable.scrollIntoView(selected);
+ if(nextProps.location.query[Query.SEARCH] !== nextProps.filter) {
+ this.props.setFilter(nextProps.location.query[Query.SEARCH], false)
}
- },
- onUpdate: function (flow) {
- if (flow.id === this.props.routeParams.flowId) {
- this.forceUpdate();
- }
- },
- onRemove: function (flow_id, index) {
- if (flow_id === this.props.routeParams.flowId) {
- var flow_to_select = this.state.view.list[Math.min(index, this.state.view.list.length - 1)];
- this.selectFlow(flow_to_select);
+ if (nextProps.location.query[Query.HIGHLIGHT] !== nextProps.highlight) {
+ this.props.setHighlight(nextProps.location.query[Query.HIGHLIGHT], false)
}
},
setSortKeyFun: function (sortKeyFun) {
- this.setState({
- sortKeyFun: sortKeyFun
- });
- this.state.view.recalculate(this.getViewFilt(), sortKeyFun);
+ // FIXME: Move to redux. This requires that sortKeyFun is not a function anymore.
},
selectFlow: function (flow) {
+ // TODO: This belongs into redux
if (flow) {
- var tab = this.props.routeParams.detailTab || "request";
+ let tab = this.props.routeParams.detailTab || "request";
this.props.updateLocation(`/flows/${flow.id}/${tab}`);
- this.refs.flowTable.scrollIntoView(flow);
} else {
this.props.updateLocation("/flows");
}
},
selectFlowRelative: function (shift) {
- var flows = this.state.view.list;
- var index;
+ // TODO: This belongs into redux
+ let flows = this.props.flows,
+ index
if (!this.props.routeParams.flowId) {
if (shift < 0) {
- index = flows.length - 1;
+ index = flows.length - 1
} else {
- index = 0;
+ index = 0
}
} else {
- var currFlowId = this.props.routeParams.flowId;
- var i = flows.length;
- while (i--) {
- if (flows[i].id === currFlowId) {
- index = i;
- break;
- }
- }
+ index = flows.indexOf(this.props.selectedFlow)
index = Math.min(
Math.max(0, index + shift),
- flows.length - 1);
+ flows.length - 1
+ )
}
- this.selectFlow(flows[index]);
+ this.selectFlow(flows[index])
},
onMainKeyDown: function (e) {
- var flow = this.getSelected();
+ var flow = this.props.selectedFlow;
if (e.ctrlKey) {
return;
}
@@ -210,14 +141,10 @@ var MainView = React.createClass({
}
e.preventDefault();
},
- getSelected: function () {
- return this.context.flowStore.get(this.props.routeParams.flowId);
- },
render: function () {
- var selected = this.getSelected();
- var details;
- if (selected) {
+ var details = null;
+ if (this.props.selectedFlow) {
details = [
<Splitter key="splitter"/>,
<FlowView
@@ -226,10 +153,8 @@ var MainView = React.createClass({
tab={this.props.routeParams.detailTab}
query={this.props.query}
updateLocation={this.props.updateLocation}
- flow={selected}/>
- ];
- } else {
- details = null;
+ flow={this.props.selectedFlow}/>
+ ]
}
return (
@@ -237,11 +162,27 @@ var MainView = React.createClass({
<FlowTable ref="flowTable"
selectFlow={this.selectFlow}
setSortKeyFun={this.setSortKeyFun}
- selected={selected} />
+ selected={this.props.selectedFlow} />
{details}
</div>
);
}
});
-export default MainView;
+const MainViewContainer = connect(
+ state => ({
+ flows: state.flows.view,
+ filter: state.flows.filter,
+ highlight: state.flows.highlight,
+ selectedFlow: state.flows.all.byId[state.flows.selected[0]]
+ }),
+ dispatch => ({
+ selectFlow: flowId => dispatch(selectFlow(flowId)),
+ setFilter: filter => dispatch(setFilter(filter)),
+ setHighlight: highlight => dispatch(setHighlight(highlight))
+ }),
+ undefined,
+ {withRef: true}
+)(MainView);
+
+export default MainViewContainer;
diff --git a/web/src/js/components/proxyapp.js b/web/src/js/components/proxyapp.js
index 9e4bd0a4..e4489e18 100644
--- a/web/src/js/components/proxyapp.js
+++ b/web/src/js/components/proxyapp.js
@@ -9,7 +9,7 @@ import MainView from "./mainview.js";
import Footer from "./footer.js";
import {Header, MainMenu} from "./header.js";
import EventLog from "./eventlog.js"
-import {FlowStore, SettingsStore} from "../store/store.js";
+import {SettingsStore} from "../store/store.js";
import {Key} from "../utils.js";
@@ -23,7 +23,6 @@ var Reports = React.createClass({
var ProxyAppMain = React.createClass({
childContextTypes: {
- flowStore: React.PropTypes.object.isRequired,
returnFocus: React.PropTypes.func.isRequired,
location: React.PropTypes.object.isRequired,
},
@@ -61,13 +60,11 @@ var ProxyAppMain = React.createClass({
},
getChildContext: function () {
return {
- flowStore: this.state.flowStore,
returnFocus: this.focus,
location: this.props.location
};
},
getInitialState: function () {
- var flowStore = new FlowStore();
var settingsStore = new SettingsStore();
this.settingsStore = settingsStore;
@@ -75,7 +72,6 @@ var ProxyAppMain = React.createClass({
_.extend(settingsStore.dict, {});
return {
settings: settingsStore.dict,
- flowStore: flowStore,
};
},
focus: function () {
@@ -84,7 +80,7 @@ var ProxyAppMain = React.createClass({
ReactDOM.findDOMNode(this).focus();
},
getMainComponent: function () {
- return this.refs.view;
+ return this.refs.view.getWrappedInstance ? this.refs.view.getWrappedInstance() : this.refs.view;
},
onKeydown: function (e) {
diff --git a/web/src/js/ducks/eventLog.js b/web/src/js/ducks/eventLog.js
index e3661fe7..44b67b2c 100644
--- a/web/src/js/ducks/eventLog.js
+++ b/web/src/js/ducks/eventLog.js
@@ -35,7 +35,7 @@ export default function reducer(state = defaultState, action) {
...state,
filter,
filteredEvents: updateViewFilter(
- state.events.list,
+ state.events,
x => filter[x.level]
)
}
diff --git a/web/src/js/ducks/flows.js b/web/src/js/ducks/flows.js
index fb934489..fdbc42ee 100644
--- a/web/src/js/ducks/flows.js
+++ b/web/src/js/ducks/flows.js
@@ -1,6 +1,11 @@
import makeList from "./utils/list"
+import Filt from "../filt/filt"
+import {updateViewFilter, updateViewList} from "./utils/view"
export const UPDATE_FLOWS = "UPDATE_FLOWS"
+export const SET_FILTER = "SET_FLOW_FILTER"
+export const SET_HIGHLIGHT = "SET_FLOW_HIGHLIGHT"
+export const SELECT_FLOW = "SELECT_FLOW"
const {
reduceList,
@@ -11,6 +16,14 @@ const {
const defaultState = {
all: reduceList(),
+ selected: [],
+ view: [],
+ filter: undefined,
+ highlight: undefined,
+}
+
+function makeFilterFn(filter) {
+ return filter ? Filt.parse(filter) : () => true;
}
export default function reducer(state = defaultState, action) {
@@ -20,10 +33,48 @@ export default function reducer(state = defaultState, action) {
return {
...state,
all,
+ view: updateViewList(state.view, state.all, all, action, makeFilterFn(action.filter))
+ }
+ case SET_FILTER:
+ return {
+ ...state,
+ filter: action.filter,
+ view: updateViewFilter(state.all, makeFilterFn(action.filter))
+ }
+ case SET_HIGHLIGHT:
+ return {
+ ...state,
+ highlight: action.highlight
+ }
+ case SELECT_FLOW:
+ return {
+ ...state,
+ selected: [action.flowId]
}
default:
return state
}
}
+
+export function setFilter(filter) {
+ return {
+ type: SET_FILTER,
+ filter
+ }
+}
+export function setHighlight(highlight) {
+ return {
+ type: SET_HIGHLIGHT,
+ highlight
+ }
+}
+export function selectFlow(flowId) {
+ return {
+ type: SELECT_FLOW,
+ flowId
+ }
+}
+
+
export {updateList as updateFlows, fetchList as fetchFlows} \ No newline at end of file
diff --git a/web/src/js/ducks/utils/view.js b/web/src/js/ducks/utils/view.js
index 55fdf6c7..5535ed83 100644
--- a/web/src/js/ducks/utils/view.js
+++ b/web/src/js/ducks/utils/view.js
@@ -15,13 +15,15 @@ const makeCompareFn = sortFn => {
return 0
}
}
- if (sortFn.reverse)
- return (a, b) => compareFn(b, a)
+ // need to adjust sortedIndexOf as well
+ // if (sortFn.reverse)
+ // return (a, b) => compareFn(b, a)
return compareFn
}
const sortedInsert = (list, sortFn, item) => {
let l = [...list, item]
+ l.indexOf = x => sortedIndexOf(l, x, sortFn)
let compareFn = makeCompareFn(sortFn)
// only sort if sorting order is not correct yet
@@ -35,21 +37,54 @@ const sortedInsert = (list, sortFn, item) => {
const sortedRemove = (list, sortFn, item) => {
let itemId = item.id
- return list.filter(x => x.id !== itemId)
+ let l = list.filter(x => x.id !== itemId)
+ l.indexOf = x => sortedIndexOf(l, x, sortFn)
+ return l
+}
+
+export function sortedIndexOf(list, value, sortFn) {
+ if (sortFn === false){
+ let i = 0
+ while (i < list.length && list[i].id !== value.id){
+ i++
+ }
+ return i
+ }
+
+ let low = 0,
+ high = list.length,
+ val = sortFn(value),
+ mid;
+ while (low < high) {
+ mid = (low + high) >>> 1;
+ if ((sortFn(list[mid]) < val) ) {
+ low = mid + 1
+ } else {
+ high = mid
+ }
+ }
+
+ // Two flows may have the same sort value.
+ // we previously determined the leftmost flow with the same sort value,
+ // so no we need to scan linearly
+ while (list[low].id !== value.id && sortFn(list[low + 1]) === val) {
+ low++
+ }
+ return low;
}
// for when the list changes
-export function updateViewList(state, currentList, nextList, action, filterFn = defaultFilterFn, sortFn = defaultSortFn) {
+export function updateViewList(currentView, currentList, nextList, action, filterFn = defaultFilterFn, sortFn = defaultSortFn) {
switch (action.cmd) {
case REQUEST_LIST:
- return state
+ return currentView
case RECEIVE_LIST:
- return updateViewFilter(nextList.list, filterFn, sortFn)
+ return updateViewFilter(nextList, filterFn, sortFn)
case ADD:
if (filterFn(action.item)) {
- return sortedInsert(state, sortFn, action.item)
+ return sortedInsert(currentView, sortFn, action.item)
}
- return state
+ return currentView
case UPDATE:
// let's determine if it's in the view currently and if it should be in the view.
let currentItemState = currentList.byId[action.item.id],
@@ -58,30 +93,34 @@ export function updateViewList(state, currentList, nextList, action, filterFn =
shouldBeInView = filterFn(nextItemState)
if (!isInView && shouldBeInView)
- return sortedInsert(state, sortFn, action.item)
+ return sortedInsert(currentView, sortFn, action.item)
if (isInView && !shouldBeInView)
- return sortedRemove(state, sortFn, action.item)
- if (isInView && shouldBeInView && sortFn(currentItemState) !== sortFn(nextItemState)) {
- let s = [...state]
- s.sort(sortFn)
+ return sortedRemove(currentView, sortFn, action.item)
+ if (isInView && shouldBeInView && sortFn && sortFn(currentItemState) !== sortFn(nextItemState)) {
+ let s = [...currentView]
+ s.sort(makeCompareFn(sortFn))
+ s.indexOf = x => sortedIndexOf(s, x, sortFn)
return s
}
- return state
+ return currentView
case REMOVE:
let isInView_ = filterFn(currentList.byId[action.item.id])
if (isInView_) {
- return sortedRemove(state, sortFn, action.item)
+ return sortedRemove(currentView, sortFn, action.item)
}
- return state
+ return currentView
default:
console.error("Unknown list action: ", action)
- return state
+ return currentView
}
}
export function updateViewFilter(list, filterFn = defaultFilterFn, sortFn = defaultSortFn) {
- let filtered = list.filter(filterFn)
- if (sortFn)
+ let filtered = list.list.filter(filterFn)
+ if (sortFn){
filtered.sort(makeCompareFn(sortFn))
+ }
+ filtered.indexOf = x => sortedIndexOf(filtered, x, sortFn)
+
return filtered
} \ No newline at end of file
diff --git a/web/src/js/store/store.js b/web/src/js/store/store.js
index 65355684..f3e2074f 100644
--- a/web/src/js/store/store.js
+++ b/web/src/js/store/store.js
@@ -6,55 +6,6 @@ import {ActionTypes, StoreCmds} from "../actions.js";
import {AppDispatcher} from "../dispatcher.js";
-function ListStore() {
- EventEmitter.call(this);
- this.reset();
-}
-_.extend(ListStore.prototype, EventEmitter.prototype, {
- add: function (elem) {
- if (elem.id in this._pos_map) {
- return;
- }
- this._pos_map[elem.id] = this.list.length;
- this.list.push(elem);
- this.emit("add", elem);
- },
- update: function (elem) {
- if (!(elem.id in this._pos_map)) {
- return;
- }
- this.list[this._pos_map[elem.id]] = elem;
- this.emit("update", elem);
- },
- remove: function (elem_id) {
- if (!(elem_id in this._pos_map)) {
- return;
- }
- this.list.splice(this._pos_map[elem_id], 1);
- this._build_map();
- this.emit("remove", elem_id);
- },
- reset: function (elems) {
- this.list = elems || [];
- this._build_map();
- this.emit("recalculate");
- },
- _build_map: function () {
- this._pos_map = {};
- for (var i = 0; i < this.list.length; i++) {
- var elem = this.list[i];
- this._pos_map[elem.id] = i;
- }
- },
- get: function (elem_id) {
- return this.list[this._pos_map[elem_id]];
- },
- index: function (elem_id) {
- return this._pos_map[elem_id];
- }
-});
-
-
function DictStore() {
EventEmitter.call(this);
this.reset();
@@ -133,12 +84,6 @@ _.extend(LiveStoreMixin.prototype, {
},
});
-function LiveListStore(type) {
- ListStore.call(this);
- LiveStoreMixin.call(this, type);
-}
-_.extend(LiveListStore.prototype, ListStore.prototype, LiveStoreMixin.prototype);
-
function LiveDictStore(type) {
DictStore.call(this);
LiveStoreMixin.call(this, type);
@@ -146,10 +91,6 @@ function LiveDictStore(type) {
_.extend(LiveDictStore.prototype, DictStore.prototype, LiveStoreMixin.prototype);
-export function FlowStore() {
- return new LiveListStore(ActionTypes.FLOW_STORE);
-}
-
export function SettingsStore() {
return new LiveDictStore(ActionTypes.SETTINGS_STORE);
} \ No newline at end of file
diff --git a/web/src/js/store/view.js b/web/src/js/store/view.js
index d8aeba60..e69de29b 100644
--- a/web/src/js/store/view.js
+++ b/web/src/js/store/view.js
@@ -1,111 +0,0 @@
-import {EventEmitter} from 'events';
-import _ from "lodash";
-
-import utils from "../utils.js";
-
-function SortByStoreOrder(elem) {
- return this.store.index(elem.id);
-}
-
-var default_sort = SortByStoreOrder;
-var default_filt = function (elem) {
- return true;
-};
-
-export function StoreView(store, filt, sortfun) {
- EventEmitter.call(this);
-
- this.store = store;
-
- this.add = this.add.bind(this);
- this.update = this.update.bind(this);
- this.remove = this.remove.bind(this);
- this.recalculate = this.recalculate.bind(this);
- this.store.addListener("add", this.add);
- this.store.addListener("update", this.update);
- this.store.addListener("remove", this.remove);
- this.store.addListener("recalculate", this.recalculate);
-
- this.recalculate(filt, sortfun);
-}
-
-_.extend(StoreView.prototype, EventEmitter.prototype, {
- close: function () {
- this.store.removeListener("add", this.add);
- this.store.removeListener("update", this.update);
- this.store.removeListener("remove", this.remove);
- this.store.removeListener("recalculate", this.recalculate);
- this.removeAllListeners();
- },
- recalculate: function (filt, sortfun) {
- filt = filt || this.filt || default_filt;
- sortfun = sortfun || this.sortfun || default_sort;
- filt = filt.bind(this);
- sortfun = sortfun.bind(this);
- this.filt = filt;
- this.sortfun = sortfun;
-
- this.list = this.store.list.filter(filt);
- this.list.sort(function (a, b) {
- var akey = sortfun(a);
- var bkey = sortfun(b);
- if(akey < bkey){
- return -1;
- } else if(akey > bkey){
- return 1;
- } else {
- return 0;
- }
- });
- this.emit("recalculate");
- },
- indexOf: function (elem) {
- return this.list.indexOf(elem, _.sortedIndexBy(this.list, elem, this.sortfun));
- },
- add: function (elem) {
- if (this.filt(elem)) {
- var idx = _.sortedIndexBy(this.list, elem, this.sortfun);
- if (idx === this.list.length) { //happens often, .push is way faster.
- this.list.push(elem);
- } else {
- this.list.splice(idx, 0, elem);
- }
- this.emit("add", elem, idx);
- }
- },
- update: function (elem) {
- var idx;
- var i = this.list.length;
- // Search from the back, we usually update the latest entries.
- while (i--) {
- if (this.list[i].id === elem.id) {
- idx = i;
- break;
- }
- }
-
- if (idx === -1) { //not contained in list
- this.add(elem);
- } else if (!this.filt(elem)) {
- this.remove(elem.id);
- } else {
- if (this.sortfun(this.list[idx]) !== this.sortfun(elem)) { //sortpos has changed
- this.remove(this.list[idx]);
- this.add(elem);
- } else {
- this.list[idx] = elem;
- this.emit("update", elem, idx);
- }
- }
- },
- remove: function (elem_id) {
- var idx = this.list.length;
- while (idx--) {
- if (this.list[idx].id === elem_id) {
- this.list.splice(idx, 1);
- this.emit("remove", elem_id, idx);
- break;
- }
- }
- }
-});