mirror of
https://github.com/Grasscutters/mitmproxy.git
synced 2024-11-27 02:24:18 +00:00
web: various fixes, add clear button
This commit is contained in:
parent
7ca1ac0f3b
commit
c39b6e4277
@ -27,7 +27,7 @@ class WebFlowView(flow.FlowView):
|
|||||||
|
|
||||||
def _recalculate(self, flows):
|
def _recalculate(self, flows):
|
||||||
super(WebFlowView, self)._recalculate(flows)
|
super(WebFlowView, self)._recalculate(flows)
|
||||||
app.FlowUpdates.broadcast("recalculate", None)
|
app.FlowUpdates.broadcast("reset", None)
|
||||||
|
|
||||||
|
|
||||||
class WebState(flow.State):
|
class WebState(flow.State):
|
||||||
|
@ -1,4 +1,5 @@
|
|||||||
import os.path
|
import os.path
|
||||||
|
import sys
|
||||||
import tornado.web
|
import tornado.web
|
||||||
import tornado.websocket
|
import tornado.websocket
|
||||||
import logging
|
import logging
|
||||||
@ -8,6 +9,7 @@ from .. import flow
|
|||||||
|
|
||||||
class IndexHandler(tornado.web.RequestHandler):
|
class IndexHandler(tornado.web.RequestHandler):
|
||||||
def get(self):
|
def get(self):
|
||||||
|
_ = self.xsrf_token # https://github.com/tornadoweb/tornado/issues/645
|
||||||
self.render("index.html")
|
self.render("index.html")
|
||||||
|
|
||||||
|
|
||||||
@ -35,13 +37,18 @@ class WebSocketEventBroadcaster(tornado.websocket.WebSocketHandler):
|
|||||||
logging.error("Error sending message", exc_info=True)
|
logging.error("Error sending message", exc_info=True)
|
||||||
|
|
||||||
|
|
||||||
class FlowsHandler(tornado.web.RequestHandler):
|
class Flows(tornado.web.RequestHandler):
|
||||||
def get(self):
|
def get(self):
|
||||||
self.write(dict(
|
self.write(dict(
|
||||||
flows=[f.get_state(short=True) for f in self.application.state.flows]
|
flows=[f.get_state(short=True) for f in self.application.state.flows]
|
||||||
))
|
))
|
||||||
|
|
||||||
|
|
||||||
|
class FlowClear(tornado.web.RequestHandler):
|
||||||
|
def post(self):
|
||||||
|
self.application.state.clear()
|
||||||
|
|
||||||
|
|
||||||
class FlowUpdates(WebSocketEventBroadcaster):
|
class FlowUpdates(WebSocketEventBroadcaster):
|
||||||
connections = set()
|
connections = set()
|
||||||
|
|
||||||
@ -56,14 +63,15 @@ class Application(tornado.web.Application):
|
|||||||
handlers = [
|
handlers = [
|
||||||
(r"/", IndexHandler),
|
(r"/", IndexHandler),
|
||||||
(r"/updates", ClientConnection),
|
(r"/updates", ClientConnection),
|
||||||
(r"/flows", FlowsHandler),
|
(r"/flows", Flows),
|
||||||
|
(r"/flows/clear", FlowClear),
|
||||||
(r"/flows/updates", FlowUpdates),
|
(r"/flows/updates", FlowUpdates),
|
||||||
]
|
]
|
||||||
settings = dict(
|
settings = dict(
|
||||||
template_path=os.path.join(os.path.dirname(__file__), "templates"),
|
template_path=os.path.join(os.path.dirname(__file__), "templates"),
|
||||||
static_path=os.path.join(os.path.dirname(__file__), "static"),
|
static_path=os.path.join(os.path.dirname(__file__), "static"),
|
||||||
xsrf_cookies=True,
|
xsrf_cookies=True,
|
||||||
cookie_secret="__TODO:_GENERATE_YOUR_OWN_RANDOM_VALUE_HERE__",
|
cookie_secret=os.urandom(256),
|
||||||
debug=debug,
|
debug=debug,
|
||||||
)
|
)
|
||||||
tornado.web.Application.__init__(self, handlers, **settings)
|
tornado.web.Application.__init__(self, handlers, **settings)
|
||||||
|
@ -386,7 +386,8 @@ _.extend(FlowStore.prototype, {
|
|||||||
|
|
||||||
function LiveFlowStore(endpoint) {
|
function LiveFlowStore(endpoint) {
|
||||||
FlowStore.call(this);
|
FlowStore.call(this);
|
||||||
this.updates_before_init = []; // (empty array is true in js)
|
this.updates_before_fetch = undefined;
|
||||||
|
this._fetchxhr = false;
|
||||||
this.endpoint = endpoint || "/flows";
|
this.endpoint = endpoint || "/flows";
|
||||||
this.conn = new Connection(this.endpoint + "/updates");
|
this.conn = new Connection(this.endpoint + "/updates");
|
||||||
this.conn.onopen = this._onopen.bind(this);
|
this.conn.onopen = this._onopen.bind(this);
|
||||||
@ -401,32 +402,45 @@ _.extend(LiveFlowStore.prototype, FlowStore.prototype, {
|
|||||||
},
|
},
|
||||||
add: function (flow) {
|
add: function (flow) {
|
||||||
// Make sure that deferred adds don't add an element twice.
|
// Make sure that deferred adds don't add an element twice.
|
||||||
if (!this._pos_map[flow.id]) {
|
if (!(flow.id in this._pos_map)) {
|
||||||
FlowStore.prototype.add.call(this, flow);
|
FlowStore.prototype.add.call(this, flow);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
handle_update: function (type, data) {
|
|
||||||
console.log("LiveFlowStore.handle_update", type, data);
|
|
||||||
if (this.updates_before_init) {
|
|
||||||
console.log("defer update", type, data);
|
|
||||||
this.updates_before_init.push(arguments);
|
|
||||||
} else {
|
|
||||||
this[type](data);
|
|
||||||
}
|
|
||||||
},
|
|
||||||
handle_fetch: function (data) {
|
|
||||||
console.log("Flows fetched.");
|
|
||||||
this.reset(data.flows);
|
|
||||||
var updates = this.updates_before_init;
|
|
||||||
this.updates_before_init = false;
|
|
||||||
for (var i = 0; i < updates.length; i++) {
|
|
||||||
this.handle_update.apply(this, updates[i]);
|
|
||||||
}
|
|
||||||
},
|
|
||||||
_onopen: function () {
|
_onopen: function () {
|
||||||
//Update stream openend, fetch list of flows.
|
//Update stream openend, fetch list of flows.
|
||||||
console.log("Update Connection opened, fetching flows...");
|
console.log("Update Connection opened, fetching flows...");
|
||||||
$.getJSON(this.endpoint, this.handle_fetch.bind(this));
|
this.fetch();
|
||||||
|
},
|
||||||
|
fetch: function () {
|
||||||
|
if (this._fetchxhr) {
|
||||||
|
this._fetchxhr.abort();
|
||||||
|
}
|
||||||
|
this._fetchxhr = $.getJSON(this.endpoint, this.handle_fetch.bind(this));
|
||||||
|
this.updates_before_fetch = []; // (JS: empty array is true)
|
||||||
|
},
|
||||||
|
handle_update: function (type, data) {
|
||||||
|
console.log("LiveFlowStore.handle_update", type, data);
|
||||||
|
|
||||||
|
if (type === "reset") {
|
||||||
|
return this.fetch();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (this.updates_before_fetch) {
|
||||||
|
console.log("defer update", type, data);
|
||||||
|
this.updates_before_fetch.push(arguments);
|
||||||
|
} else {
|
||||||
|
this[type](data);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
handle_fetch: function (data) {
|
||||||
|
this._fetchxhr = false;
|
||||||
|
console.log("Flows fetched.");
|
||||||
|
this.reset(data.flows);
|
||||||
|
var updates = this.updates_before_fetch;
|
||||||
|
this.updates_before_fetch = false;
|
||||||
|
for (var i = 0; i < updates.length; i++) {
|
||||||
|
this.handle_update.apply(this, updates[i]);
|
||||||
|
}
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -471,20 +485,22 @@ _.extend(FlowView.prototype, EventEmitter.prototype, {
|
|||||||
|
|
||||||
//Ugly workaround: Call .sortfun() for each flow once in order,
|
//Ugly workaround: Call .sortfun() for each flow once in order,
|
||||||
//so that SortByInsertionOrder make sense.
|
//so that SortByInsertionOrder make sense.
|
||||||
var i = flows.length;
|
for(var i = 0; i < flows.length; i++) {
|
||||||
while(i--){
|
|
||||||
this.sortfun(flows[i]);
|
this.sortfun(flows[i]);
|
||||||
}
|
}
|
||||||
|
|
||||||
this.flows = flows.filter(this.filt);
|
this.flows = flows.filter(this.filt);
|
||||||
this.flows.sort(function (a, b) {
|
this.flows.sort(function (a, b) {
|
||||||
return this.sortfun(b) - this.sortfun(a);
|
return this.sortfun(a) - this.sortfun(b);
|
||||||
}.bind(this));
|
}.bind(this));
|
||||||
this.emit("recalculate");
|
this.emit("recalculate");
|
||||||
},
|
},
|
||||||
|
index: function (flow) {
|
||||||
|
return _.sortedIndex(this.flows, flow, this.sortfun);
|
||||||
|
},
|
||||||
add: function (flow) {
|
add: function (flow) {
|
||||||
if (this.filt(flow)) {
|
if (this.filt(flow)) {
|
||||||
var idx = _.sortedIndex(this.flows, flow, this.sortfun);
|
var idx = this.index(flow);
|
||||||
if (idx === this.flows.length) { //happens often, .push is way faster.
|
if (idx === this.flows.length) { //happens often, .push is way faster.
|
||||||
this.flows.push(flow);
|
this.flows.push(flow);
|
||||||
} else {
|
} else {
|
||||||
@ -665,6 +681,23 @@ var Splitter = React.createClass({displayName: 'Splitter',
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
function getCookie(name) {
|
||||||
|
var r = document.cookie.match("\\b" + name + "=([^;]*)\\b");
|
||||||
|
return r ? r[1] : undefined;
|
||||||
|
}
|
||||||
|
var xsrf = $.param({_xsrf: getCookie("_xsrf")});
|
||||||
|
|
||||||
|
//Tornado XSRF Protection.
|
||||||
|
$.ajaxPrefilter(function(options){
|
||||||
|
if(options.type === "post" && options.url[0] === "/"){
|
||||||
|
if(options.data){
|
||||||
|
options.data += ("&" + xsrf);
|
||||||
|
} else {
|
||||||
|
options.data = xsrf;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
var MainMenu = React.createClass({displayName: 'MainMenu',
|
var MainMenu = React.createClass({displayName: 'MainMenu',
|
||||||
statics: {
|
statics: {
|
||||||
title: "Traffic",
|
title: "Traffic",
|
||||||
@ -675,12 +708,17 @@ var MainMenu = React.createClass({displayName: 'MainMenu',
|
|||||||
showEventLog: !this.props.settings.showEventLog
|
showEventLog: !this.props.settings.showEventLog
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
|
clearFlows: function(){
|
||||||
|
$.post("/flows/clear");
|
||||||
|
},
|
||||||
render: function () {
|
render: function () {
|
||||||
return (
|
return (
|
||||||
React.createElement("div", null,
|
React.createElement("div", null,
|
||||||
React.createElement("button", {className: "btn " + (this.props.settings.showEventLog ? "btn-primary" : "btn-default"), onClick: this.toggleEventLog},
|
React.createElement("button", {className: "btn " + (this.props.settings.showEventLog ? "btn-primary" : "btn-default"), onClick: this.toggleEventLog},
|
||||||
React.createElement("i", {className: "fa fa-database"}),
|
React.createElement("i", {className: "fa fa-database"}), " Display Event Log"
|
||||||
"Display Event Log"
|
), " ",
|
||||||
|
React.createElement("button", {className: "btn btn-default", onClick: this.clearFlows},
|
||||||
|
React.createElement("i", {className: "fa fa-eraser"}), " Clear Flows"
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
);
|
);
|
||||||
@ -999,20 +1037,23 @@ var FlowTable = React.createClass({displayName: 'FlowTable',
|
|||||||
scrollIntoView: function (flow) {
|
scrollIntoView: function (flow) {
|
||||||
// Now comes the fun part: Scroll the flow into the view.
|
// Now comes the fun part: Scroll the flow into the view.
|
||||||
var viewport = this.getDOMNode();
|
var viewport = this.getDOMNode();
|
||||||
var flowNode = this.refs.body.refs[flow.id].getDOMNode();
|
var thead_height = this.refs.body.getDOMNode().offsetTop;
|
||||||
|
|
||||||
|
var flow_top = (this.props.view.index(flow) * ROW_HEIGHT) + thead_height;
|
||||||
|
|
||||||
var viewport_top = viewport.scrollTop;
|
var viewport_top = viewport.scrollTop;
|
||||||
var viewport_bottom = viewport_top + viewport.offsetHeight;
|
var viewport_bottom = viewport_top + viewport.offsetHeight;
|
||||||
var flowNode_top = flowNode.offsetTop;
|
var flow_bottom = flow_top + ROW_HEIGHT;
|
||||||
var flowNode_bottom = flowNode_top + flowNode.offsetHeight;
|
|
||||||
|
|
||||||
// Account for pinned thead by pretending that the flowNode starts
|
// Account for pinned thead
|
||||||
// -thead_height pixel earlier.
|
|
||||||
flowNode_top -= this.refs.body.getDOMNode().offsetTop;
|
|
||||||
|
|
||||||
if (flowNode_top < viewport_top) {
|
|
||||||
viewport.scrollTop = flowNode_top;
|
console.log("scrollInto", flow_top, flow_bottom, viewport_top, viewport_bottom, thead_height);
|
||||||
} else if (flowNode_bottom > viewport_bottom) {
|
|
||||||
viewport.scrollTop = flowNode_bottom - viewport.offsetHeight;
|
if (flow_top - thead_height < viewport_top) {
|
||||||
|
viewport.scrollTop = flow_top - thead_height;
|
||||||
|
} else if (flow_bottom > viewport_bottom) {
|
||||||
|
viewport.scrollTop = flow_bottom - viewport.offsetHeight;
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
render: function () {
|
render: function () {
|
||||||
@ -1050,7 +1091,7 @@ var FlowTable = React.createClass({displayName: 'FlowTable',
|
|||||||
React.createElement("table", null,
|
React.createElement("table", null,
|
||||||
React.createElement(FlowTableHead, {ref: "head",
|
React.createElement(FlowTableHead, {ref: "head",
|
||||||
columns: this.state.columns}),
|
columns: this.state.columns}),
|
||||||
React.createElement("tbody", null,
|
React.createElement("tbody", {ref: "body"},
|
||||||
React.createElement("tr", {style: {height: space_top}}),
|
React.createElement("tr", {style: {height: space_top}}),
|
||||||
fix_nth_row,
|
fix_nth_row,
|
||||||
rows,
|
rows,
|
||||||
@ -1068,9 +1109,9 @@ var FlowDetailNav = React.createClass({displayName: 'FlowDetailNav',
|
|||||||
var items = this.props.tabs.map(function (e) {
|
var items = this.props.tabs.map(function (e) {
|
||||||
var str = e.charAt(0).toUpperCase() + e.slice(1);
|
var str = e.charAt(0).toUpperCase() + e.slice(1);
|
||||||
var className = this.props.active === e ? "active" : "";
|
var className = this.props.active === e ? "active" : "";
|
||||||
var onClick = function (e) {
|
var onClick = function (event) {
|
||||||
this.props.selectTab(e);
|
this.props.selectTab(e);
|
||||||
e.preventDefault();
|
event.preventDefault();
|
||||||
}.bind(this);
|
}.bind(this);
|
||||||
return React.createElement("a", {key: e,
|
return React.createElement("a", {key: e,
|
||||||
href: "#",
|
href: "#",
|
||||||
@ -1366,7 +1407,6 @@ var FlowDetail = React.createClass({displayName: 'FlowDetail',
|
|||||||
);
|
);
|
||||||
},
|
},
|
||||||
render: function () {
|
render: function () {
|
||||||
var flow = JSON.stringify(this.props.flow, null, 2);
|
|
||||||
var Tab = tabs[this.props.active];
|
var Tab = tabs[this.props.active];
|
||||||
return (
|
return (
|
||||||
React.createElement("div", {className: "flow-detail", onScroll: this.adjustHead},
|
React.createElement("div", {className: "flow-detail", onScroll: this.adjustHead},
|
||||||
@ -1416,8 +1456,7 @@ var MainView = React.createClass({displayName: 'MainView',
|
|||||||
detailTab: this.getParams().detailTab || "request"
|
detailTab: this.getParams().detailTab || "request"
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
console.log("TODO: Scroll into view");
|
this.refs.flowTable.scrollIntoView(flow);
|
||||||
//this.refs.flowTable.scrollIntoView(flow);
|
|
||||||
} else {
|
} else {
|
||||||
this.replaceWith("flows");
|
this.replaceWith("flows");
|
||||||
}
|
}
|
||||||
|
@ -4,9 +4,9 @@ var FlowDetailNav = React.createClass({
|
|||||||
var items = this.props.tabs.map(function (e) {
|
var items = this.props.tabs.map(function (e) {
|
||||||
var str = e.charAt(0).toUpperCase() + e.slice(1);
|
var str = e.charAt(0).toUpperCase() + e.slice(1);
|
||||||
var className = this.props.active === e ? "active" : "";
|
var className = this.props.active === e ? "active" : "";
|
||||||
var onClick = function (e) {
|
var onClick = function (event) {
|
||||||
this.props.selectTab(e);
|
this.props.selectTab(e);
|
||||||
e.preventDefault();
|
event.preventDefault();
|
||||||
}.bind(this);
|
}.bind(this);
|
||||||
return <a key={e}
|
return <a key={e}
|
||||||
href="#"
|
href="#"
|
||||||
@ -302,7 +302,6 @@ var FlowDetail = React.createClass({
|
|||||||
);
|
);
|
||||||
},
|
},
|
||||||
render: function () {
|
render: function () {
|
||||||
var flow = JSON.stringify(this.props.flow, null, 2);
|
|
||||||
var Tab = tabs[this.props.active];
|
var Tab = tabs[this.props.active];
|
||||||
return (
|
return (
|
||||||
<div className="flow-detail" onScroll={this.adjustHead}>
|
<div className="flow-detail" onScroll={this.adjustHead}>
|
||||||
|
@ -83,20 +83,23 @@ var FlowTable = React.createClass({
|
|||||||
scrollIntoView: function (flow) {
|
scrollIntoView: function (flow) {
|
||||||
// Now comes the fun part: Scroll the flow into the view.
|
// Now comes the fun part: Scroll the flow into the view.
|
||||||
var viewport = this.getDOMNode();
|
var viewport = this.getDOMNode();
|
||||||
var flowNode = this.refs.body.refs[flow.id].getDOMNode();
|
var thead_height = this.refs.body.getDOMNode().offsetTop;
|
||||||
|
|
||||||
|
var flow_top = (this.props.view.index(flow) * ROW_HEIGHT) + thead_height;
|
||||||
|
|
||||||
var viewport_top = viewport.scrollTop;
|
var viewport_top = viewport.scrollTop;
|
||||||
var viewport_bottom = viewport_top + viewport.offsetHeight;
|
var viewport_bottom = viewport_top + viewport.offsetHeight;
|
||||||
var flowNode_top = flowNode.offsetTop;
|
var flow_bottom = flow_top + ROW_HEIGHT;
|
||||||
var flowNode_bottom = flowNode_top + flowNode.offsetHeight;
|
|
||||||
|
|
||||||
// Account for pinned thead by pretending that the flowNode starts
|
// Account for pinned thead
|
||||||
// -thead_height pixel earlier.
|
|
||||||
flowNode_top -= this.refs.body.getDOMNode().offsetTop;
|
|
||||||
|
|
||||||
if (flowNode_top < viewport_top) {
|
|
||||||
viewport.scrollTop = flowNode_top;
|
console.log("scrollInto", flow_top, flow_bottom, viewport_top, viewport_bottom, thead_height);
|
||||||
} else if (flowNode_bottom > viewport_bottom) {
|
|
||||||
viewport.scrollTop = flowNode_bottom - viewport.offsetHeight;
|
if (flow_top - thead_height < viewport_top) {
|
||||||
|
viewport.scrollTop = flow_top - thead_height;
|
||||||
|
} else if (flow_bottom > viewport_bottom) {
|
||||||
|
viewport.scrollTop = flow_bottom - viewport.offsetHeight;
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
render: function () {
|
render: function () {
|
||||||
@ -134,7 +137,7 @@ var FlowTable = React.createClass({
|
|||||||
<table>
|
<table>
|
||||||
<FlowTableHead ref="head"
|
<FlowTableHead ref="head"
|
||||||
columns={this.state.columns}/>
|
columns={this.state.columns}/>
|
||||||
<tbody>
|
<tbody ref="body">
|
||||||
<tr style={{height: space_top}}></tr>
|
<tr style={{height: space_top}}></tr>
|
||||||
{ fix_nth_row }
|
{ fix_nth_row }
|
||||||
{rows}
|
{rows}
|
||||||
|
@ -8,12 +8,17 @@ var MainMenu = React.createClass({
|
|||||||
showEventLog: !this.props.settings.showEventLog
|
showEventLog: !this.props.settings.showEventLog
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
|
clearFlows: function(){
|
||||||
|
$.post("/flows/clear");
|
||||||
|
},
|
||||||
render: function () {
|
render: function () {
|
||||||
return (
|
return (
|
||||||
<div>
|
<div>
|
||||||
<button className={"btn " + (this.props.settings.showEventLog ? "btn-primary" : "btn-default")} onClick={this.toggleEventLog}>
|
<button className={"btn " + (this.props.settings.showEventLog ? "btn-primary" : "btn-default")} onClick={this.toggleEventLog}>
|
||||||
<i className="fa fa-database"></i>
|
<i className="fa fa-database"></i> Display Event Log
|
||||||
Display Event Log
|
</button>
|
||||||
|
<button className="btn btn-default" onClick={this.clearFlows}>
|
||||||
|
<i className="fa fa-eraser"></i> Clear Flows
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
|
@ -35,8 +35,7 @@ var MainView = React.createClass({
|
|||||||
detailTab: this.getParams().detailTab || "request"
|
detailTab: this.getParams().detailTab || "request"
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
console.log("TODO: Scroll into view");
|
this.refs.flowTable.scrollIntoView(flow);
|
||||||
//this.refs.flowTable.scrollIntoView(flow);
|
|
||||||
} else {
|
} else {
|
||||||
this.replaceWith("flows");
|
this.replaceWith("flows");
|
||||||
}
|
}
|
||||||
|
@ -96,3 +96,20 @@ var Splitter = React.createClass({
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
function getCookie(name) {
|
||||||
|
var r = document.cookie.match("\\b" + name + "=([^;]*)\\b");
|
||||||
|
return r ? r[1] : undefined;
|
||||||
|
}
|
||||||
|
var xsrf = $.param({_xsrf: getCookie("_xsrf")});
|
||||||
|
|
||||||
|
//Tornado XSRF Protection.
|
||||||
|
$.ajaxPrefilter(function(options){
|
||||||
|
if(options.type === "post" && options.url[0] === "/"){
|
||||||
|
if(options.data){
|
||||||
|
options.data += ("&" + xsrf);
|
||||||
|
} else {
|
||||||
|
options.data = xsrf;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
@ -45,7 +45,8 @@ _.extend(FlowStore.prototype, {
|
|||||||
|
|
||||||
function LiveFlowStore(endpoint) {
|
function LiveFlowStore(endpoint) {
|
||||||
FlowStore.call(this);
|
FlowStore.call(this);
|
||||||
this.updates_before_init = []; // (empty array is true in js)
|
this.updates_before_fetch = undefined;
|
||||||
|
this._fetchxhr = false;
|
||||||
this.endpoint = endpoint || "/flows";
|
this.endpoint = endpoint || "/flows";
|
||||||
this.conn = new Connection(this.endpoint + "/updates");
|
this.conn = new Connection(this.endpoint + "/updates");
|
||||||
this.conn.onopen = this._onopen.bind(this);
|
this.conn.onopen = this._onopen.bind(this);
|
||||||
@ -60,32 +61,45 @@ _.extend(LiveFlowStore.prototype, FlowStore.prototype, {
|
|||||||
},
|
},
|
||||||
add: function (flow) {
|
add: function (flow) {
|
||||||
// Make sure that deferred adds don't add an element twice.
|
// Make sure that deferred adds don't add an element twice.
|
||||||
if (!this._pos_map[flow.id]) {
|
if (!(flow.id in this._pos_map)) {
|
||||||
FlowStore.prototype.add.call(this, flow);
|
FlowStore.prototype.add.call(this, flow);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
handle_update: function (type, data) {
|
|
||||||
console.log("LiveFlowStore.handle_update", type, data);
|
|
||||||
if (this.updates_before_init) {
|
|
||||||
console.log("defer update", type, data);
|
|
||||||
this.updates_before_init.push(arguments);
|
|
||||||
} else {
|
|
||||||
this[type](data);
|
|
||||||
}
|
|
||||||
},
|
|
||||||
handle_fetch: function (data) {
|
|
||||||
console.log("Flows fetched.");
|
|
||||||
this.reset(data.flows);
|
|
||||||
var updates = this.updates_before_init;
|
|
||||||
this.updates_before_init = false;
|
|
||||||
for (var i = 0; i < updates.length; i++) {
|
|
||||||
this.handle_update.apply(this, updates[i]);
|
|
||||||
}
|
|
||||||
},
|
|
||||||
_onopen: function () {
|
_onopen: function () {
|
||||||
//Update stream openend, fetch list of flows.
|
//Update stream openend, fetch list of flows.
|
||||||
console.log("Update Connection opened, fetching flows...");
|
console.log("Update Connection opened, fetching flows...");
|
||||||
$.getJSON(this.endpoint, this.handle_fetch.bind(this));
|
this.fetch();
|
||||||
|
},
|
||||||
|
fetch: function () {
|
||||||
|
if (this._fetchxhr) {
|
||||||
|
this._fetchxhr.abort();
|
||||||
|
}
|
||||||
|
this._fetchxhr = $.getJSON(this.endpoint, this.handle_fetch.bind(this));
|
||||||
|
this.updates_before_fetch = []; // (JS: empty array is true)
|
||||||
|
},
|
||||||
|
handle_update: function (type, data) {
|
||||||
|
console.log("LiveFlowStore.handle_update", type, data);
|
||||||
|
|
||||||
|
if (type === "reset") {
|
||||||
|
return this.fetch();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (this.updates_before_fetch) {
|
||||||
|
console.log("defer update", type, data);
|
||||||
|
this.updates_before_fetch.push(arguments);
|
||||||
|
} else {
|
||||||
|
this[type](data);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
handle_fetch: function (data) {
|
||||||
|
this._fetchxhr = false;
|
||||||
|
console.log("Flows fetched.");
|
||||||
|
this.reset(data.flows);
|
||||||
|
var updates = this.updates_before_fetch;
|
||||||
|
this.updates_before_fetch = false;
|
||||||
|
for (var i = 0; i < updates.length; i++) {
|
||||||
|
this.handle_update.apply(this, updates[i]);
|
||||||
|
}
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -130,20 +144,22 @@ _.extend(FlowView.prototype, EventEmitter.prototype, {
|
|||||||
|
|
||||||
//Ugly workaround: Call .sortfun() for each flow once in order,
|
//Ugly workaround: Call .sortfun() for each flow once in order,
|
||||||
//so that SortByInsertionOrder make sense.
|
//so that SortByInsertionOrder make sense.
|
||||||
var i = flows.length;
|
for(var i = 0; i < flows.length; i++) {
|
||||||
while(i--){
|
|
||||||
this.sortfun(flows[i]);
|
this.sortfun(flows[i]);
|
||||||
}
|
}
|
||||||
|
|
||||||
this.flows = flows.filter(this.filt);
|
this.flows = flows.filter(this.filt);
|
||||||
this.flows.sort(function (a, b) {
|
this.flows.sort(function (a, b) {
|
||||||
return this.sortfun(b) - this.sortfun(a);
|
return this.sortfun(a) - this.sortfun(b);
|
||||||
}.bind(this));
|
}.bind(this));
|
||||||
this.emit("recalculate");
|
this.emit("recalculate");
|
||||||
},
|
},
|
||||||
|
index: function (flow) {
|
||||||
|
return _.sortedIndex(this.flows, flow, this.sortfun);
|
||||||
|
},
|
||||||
add: function (flow) {
|
add: function (flow) {
|
||||||
if (this.filt(flow)) {
|
if (this.filt(flow)) {
|
||||||
var idx = _.sortedIndex(this.flows, flow, this.sortfun);
|
var idx = this.index(flow);
|
||||||
if (idx === this.flows.length) { //happens often, .push is way faster.
|
if (idx === this.flows.length) { //happens often, .push is way faster.
|
||||||
this.flows.push(flow);
|
this.flows.push(flow);
|
||||||
} else {
|
} else {
|
||||||
|
Loading…
Reference in New Issue
Block a user