add features to the traffic table, introduce image spriting
@ -77,7 +77,7 @@ class Slave(threading.Thread):
|
||||
self.server.serve_forever()
|
||||
|
||||
|
||||
class Master:
|
||||
class Master(object):
|
||||
"""
|
||||
Masters get and respond to messages from slaves.
|
||||
"""
|
||||
|
@ -1,8 +1,8 @@
|
||||
from __future__ import absolute_import, print_function
|
||||
import tornado.ioloop
|
||||
import tornado.httpserver
|
||||
from .. import controller, utils, flow, script, proxy
|
||||
import app
|
||||
import pprint
|
||||
from .. import controller, flow
|
||||
from . import app
|
||||
|
||||
|
||||
class Stop(Exception):
|
||||
@ -59,7 +59,7 @@ class WebMaster(flow.FlowMaster):
|
||||
def __init__(self, server, options):
|
||||
self.options = options
|
||||
self.app = app.Application(self.options.wdebug)
|
||||
flow.FlowMaster.__init__(self, server, WebState())
|
||||
super(WebMaster, self).__init__(server, WebState())
|
||||
|
||||
self.last_log_id = 0
|
||||
|
||||
@ -90,7 +90,6 @@ class WebMaster(flow.FlowMaster):
|
||||
return f
|
||||
|
||||
def handle_response(self, f):
|
||||
s = f.get_state(True)
|
||||
app.ClientConnection.broadcast("update_flow", f.get_state(True))
|
||||
flow.FlowMaster.handle_response(self, f)
|
||||
if f:
|
||||
|
@ -6,6 +6,60 @@ html {
|
||||
*:after {
|
||||
box-sizing: inherit;
|
||||
}
|
||||
.resource-icon {
|
||||
width: 32px;
|
||||
height: 32px;
|
||||
}
|
||||
.resource-icon-css {
|
||||
background-image: url("../images/sprite.png");
|
||||
background-position: 0px 0px;
|
||||
background-size: 32px 320px!important;
|
||||
}
|
||||
.resource-icon-document {
|
||||
background-image: url("../images/sprite.png");
|
||||
background-position: 0px -32px;
|
||||
background-size: 32px 320px!important;
|
||||
}
|
||||
.resource-icon-js {
|
||||
background-image: url("../images/sprite.png");
|
||||
background-position: 0px -64px;
|
||||
background-size: 32px 320px!important;
|
||||
}
|
||||
.resource-icon-plain {
|
||||
background-image: url("../images/sprite.png");
|
||||
background-position: 0px -96px;
|
||||
background-size: 32px 320px!important;
|
||||
}
|
||||
.resource-icon-executable {
|
||||
background-image: url("../images/sprite.png");
|
||||
background-position: 0px -128px;
|
||||
background-size: 32px 320px!important;
|
||||
}
|
||||
.resource-icon-flash {
|
||||
background-image: url("../images/sprite.png");
|
||||
background-position: 0px -160px;
|
||||
background-size: 32px 320px!important;
|
||||
}
|
||||
.resource-icon-image {
|
||||
background-image: url("../images/sprite.png");
|
||||
background-position: 0px -192px;
|
||||
background-size: 32px 320px!important;
|
||||
}
|
||||
.resource-icon-java {
|
||||
background-image: url("../images/sprite.png");
|
||||
background-position: 0px -224px;
|
||||
background-size: 32px 320px!important;
|
||||
}
|
||||
.resource-icon-not-modified {
|
||||
background-image: url("../images/sprite.png");
|
||||
background-position: 0px -256px;
|
||||
background-size: 32px 320px!important;
|
||||
}
|
||||
.resource-icon-redirect {
|
||||
background-image: url("../images/sprite.png");
|
||||
background-position: 0px -288px;
|
||||
background-size: 32px 320px!important;
|
||||
}
|
||||
html,
|
||||
body,
|
||||
#container {
|
||||
@ -69,6 +123,33 @@ header .menu {
|
||||
}
|
||||
.flow-table {
|
||||
width: 100%;
|
||||
table-layout: fixed;
|
||||
}
|
||||
.flow-table thead {
|
||||
background-color: #dadada;
|
||||
}
|
||||
.flow-table td {
|
||||
overflow: hidden;
|
||||
white-space: nowrap;
|
||||
text-overflow: ellipsis;
|
||||
}
|
||||
.flow-table .col-tls {
|
||||
width: 10px;
|
||||
}
|
||||
.flow-table .col-tls-https {
|
||||
background-color: rgba(0, 185, 0, 0.5);
|
||||
}
|
||||
.flow-table .col-icon {
|
||||
width: 32px;
|
||||
}
|
||||
.flow-table .col-method {
|
||||
width: 60px;
|
||||
}
|
||||
.flow-table .col-status {
|
||||
width: 50px;
|
||||
}
|
||||
.flow-table .col-time {
|
||||
width: 120px;
|
||||
}
|
||||
.eventlog {
|
||||
flex: 0 0 auto;
|
||||
|
BIN
libmproxy/web/static/images/sprite.png
Normal file
After Width: | Height: | Size: 11 KiB |
@ -269,13 +269,14 @@ _.extend(FlowView.prototype, EventEmitter.prototype, {
|
||||
var updates = this.flows;
|
||||
this.flows = flows;
|
||||
updates.forEach(function(flow){
|
||||
this.update(flow);
|
||||
this._update(flow);
|
||||
}.bind(this));
|
||||
this.emit("change");
|
||||
},
|
||||
update: function(flow){
|
||||
_update: function(flow){
|
||||
console.debug("FIXME: Use UUID");
|
||||
var idx = _.findIndex(this.flows, function(f){
|
||||
return flow.request.timestamp_start == f.request.timestamp_start
|
||||
return flow.request.timestamp_start == f.request.timestamp_start;
|
||||
});
|
||||
|
||||
if(idx < 0){
|
||||
@ -283,6 +284,9 @@ _.extend(FlowView.prototype, EventEmitter.prototype, {
|
||||
} else {
|
||||
this.flows[idx] = flow;
|
||||
}
|
||||
},
|
||||
update: function(flow){
|
||||
this._update(flow);
|
||||
this.emit("change");
|
||||
},
|
||||
});
|
||||
@ -294,6 +298,11 @@ function _FlowStore() {
|
||||
_.extend(_FlowStore.prototype, EventEmitter.prototype, {
|
||||
getView: function (since) {
|
||||
var view = new FlowView(this, !since);
|
||||
|
||||
$.getJSON("/static/flows.json", function(flows){
|
||||
view.add_bulk(flows);
|
||||
});
|
||||
|
||||
return view;
|
||||
},
|
||||
handle: function (action) {
|
||||
@ -442,7 +451,10 @@ var FlowRow = React.createClass({displayName: 'FlowRow',
|
||||
render: function(){
|
||||
var flow = this.props.flow;
|
||||
var columns = this.props.columns.map(function(column){
|
||||
return column({flow: flow});
|
||||
return column({
|
||||
key: column.displayName,
|
||||
flow: flow
|
||||
});
|
||||
}.bind(this));
|
||||
return React.DOM.tr(null, columns);
|
||||
}
|
||||
@ -460,55 +472,89 @@ var FlowTableHead = React.createClass({displayName: 'FlowTableHead',
|
||||
var FlowTableBody = React.createClass({displayName: 'FlowTableBody',
|
||||
render: function(){
|
||||
var rows = this.props.flows.map(function(flow){
|
||||
return FlowRow({flow: flow, columns: this.props.columns})
|
||||
//TODO: Add UUID
|
||||
return FlowRow({flow: flow, columns: this.props.columns});
|
||||
}.bind(this));
|
||||
return React.DOM.tbody(null, rows);
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
var TLSColumn = React.createClass({displayName: 'TLSColumn',
|
||||
statics: {
|
||||
renderTitle: function(){
|
||||
return React.DOM.th({key: "tls", className: "col-tls"});
|
||||
}
|
||||
},
|
||||
render: function(){
|
||||
var flow = this.props.flow;
|
||||
var ssl = (flow.request.scheme == "https");
|
||||
return React.DOM.td({className: ssl ? "col-tls-https" : "col-tls-http"});
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
var IconColumn = React.createClass({displayName: 'IconColumn',
|
||||
statics: {
|
||||
renderTitle: function(){
|
||||
return React.DOM.th({key: "icon", className: "col-icon"});
|
||||
}
|
||||
},
|
||||
render: function(){
|
||||
var flow = this.props.flow;
|
||||
return React.DOM.td({className: "resource-icon resource-icon-plain"});
|
||||
}
|
||||
});
|
||||
|
||||
var PathColumn = React.createClass({displayName: 'PathColumn',
|
||||
statics: {
|
||||
renderTitle: function(){
|
||||
return React.DOM.th({key: "PathColumn"}, "Path");
|
||||
return React.DOM.th({key: "path", className: "col-path"}, "Path");
|
||||
}
|
||||
},
|
||||
render: function(){
|
||||
var flow = this.props.flow;
|
||||
return React.DOM.td({key: "PathColumn"}, flow.request.scheme + "://" + flow.request.host + flow.request.path);
|
||||
return React.DOM.td(null, flow.request.scheme + "://" + flow.request.host + flow.request.path);
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
var MethodColumn = React.createClass({displayName: 'MethodColumn',
|
||||
statics: {
|
||||
renderTitle: function(){
|
||||
return React.DOM.th({key: "MethodColumn"}, "Method");
|
||||
return React.DOM.th({key: "method", className: "col-method"}, "Method");
|
||||
}
|
||||
},
|
||||
render: function(){
|
||||
var flow = this.props.flow;
|
||||
return React.DOM.td({key: "MethodColumn"}, flow.request.method);
|
||||
return React.DOM.td(null, flow.request.method);
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
var StatusColumn = React.createClass({displayName: 'StatusColumn',
|
||||
statics: {
|
||||
renderTitle: function(){
|
||||
return React.DOM.th({key: "StatusColumn"}, "Status");
|
||||
return React.DOM.th({key: "status", className: "col-status"}, "Status");
|
||||
}
|
||||
},
|
||||
render: function(){
|
||||
var flow = this.props.flow;
|
||||
var status;
|
||||
if(flow.response){
|
||||
status = flow.response.code + " " + flow.response.msg;
|
||||
status = flow.response.code;
|
||||
} else {
|
||||
status = null;
|
||||
}
|
||||
return React.DOM.td({key: "StatusColumn"}, status);
|
||||
return React.DOM.td(null, status);
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
var TimeColumn = React.createClass({displayName: 'TimeColumn',
|
||||
statics: {
|
||||
renderTitle: function(){
|
||||
return React.DOM.th({key: "TimeColumn"}, "Time");
|
||||
return React.DOM.th({key: "time", className: "col-time"}, "Time");
|
||||
}
|
||||
},
|
||||
render: function(){
|
||||
@ -519,11 +565,13 @@ var TimeColumn = React.createClass({displayName: 'TimeColumn',
|
||||
} else {
|
||||
time = "...";
|
||||
}
|
||||
return React.DOM.td({key: "TimeColumn"}, time);
|
||||
return React.DOM.td(null, time);
|
||||
}
|
||||
});
|
||||
|
||||
var all_columns = [PathColumn, MethodColumn, StatusColumn, TimeColumn];
|
||||
|
||||
var all_columns = [TLSColumn, IconColumn, PathColumn, MethodColumn, StatusColumn, TimeColumn];
|
||||
|
||||
|
||||
var FlowTable = React.createClass({displayName: 'FlowTable',
|
||||
getInitialState: function () {
|
||||
|
@ -1,7 +1,7 @@
|
||||
var gulp = require("gulp");
|
||||
var merge = require('merge-stream');
|
||||
|
||||
var concat = require('gulp-concat');
|
||||
var gutil = require('gulp-util');
|
||||
var jshint = require("gulp-jshint");
|
||||
var less = require("gulp-less");
|
||||
var livereload = require("gulp-livereload");
|
||||
@ -10,7 +10,9 @@ var notify = require("gulp-notify");
|
||||
var plumber = require("gulp-plumber");
|
||||
var qunit = require("gulp-qunit");
|
||||
var react = require("gulp-react");
|
||||
var rename = require("gulp-rename");
|
||||
var sourcemaps = require('gulp-sourcemaps');
|
||||
var sprite = require('gulp-sprite-generator');
|
||||
var uglify = require('gulp-uglify');
|
||||
|
||||
|
||||
@ -50,10 +52,12 @@ var path = {
|
||||
},
|
||||
css: {
|
||||
vendor: ["css/vendor.less"],
|
||||
app: ["css/app.less"]
|
||||
app: ["css/app.less"],
|
||||
spritefile: "css/sprites.less"
|
||||
},
|
||||
fonts: ["src/vendor/fontawesome/fontawesome-webfont.*"],
|
||||
html: ["src/*.html", "!src/benchmark.html", "!src/test.html"]
|
||||
html: ["src/*.html", "!src/benchmark.html", "!src/test.html"],
|
||||
images: "src/images",
|
||||
};
|
||||
|
||||
|
||||
@ -124,9 +128,24 @@ gulp.task("jshint", function () {
|
||||
.pipe(dont_break_on_errors())
|
||||
.pipe(react())
|
||||
.pipe(jshint())
|
||||
.pipe(jshint.reporter("jshint-stylish"))
|
||||
.pipe(jshint.reporter("jshint-stylish"));
|
||||
});
|
||||
|
||||
gulp.task("sprites", function () {
|
||||
// Sprite generator is a gulp task, which accepts options object and
|
||||
// returns two streams for style and image piping.
|
||||
var spriteOutput = gulp.src([path.css.spritefile], {base: "src", cwd: "src"})
|
||||
.pipe(sprite({
|
||||
spriteSheetName: "sprite.png",
|
||||
spriteSheetPath: "../images",
|
||||
}));
|
||||
var css = spriteOutput.css
|
||||
.pipe(rename({extname:".compiled.less"}))
|
||||
.pipe(gulp.dest("src/css"));
|
||||
var img = spriteOutput.img.pipe(gulp.dest(path.dist + "static/images"));
|
||||
// https://github.com/gulpjs/gulp/blob/master/docs/recipes/using-multiple-sources-in-one-task.md
|
||||
return merge(css, img);
|
||||
});
|
||||
|
||||
gulp.task("html", function () {
|
||||
return gulp.src(path.html)
|
||||
@ -141,7 +160,7 @@ gulp.task('test', function() {
|
||||
});
|
||||
|
||||
|
||||
common = ["fonts", "html", "jshint"];
|
||||
common = ["fonts", "html", "jshint", "sprites"];
|
||||
gulp.task("dev", common.concat(["styles-dev", "scripts-dev"]));
|
||||
gulp.task("prod", common.concat(["styles-prod", "scripts-prod"]));
|
||||
|
||||
@ -150,5 +169,6 @@ gulp.task("default", ["dev"], function () {
|
||||
gulp.watch(["src/vendor/**"], ["scripts-vendor-dev", "styles-vendor-dev"]);
|
||||
gulp.watch(["src/js/**"], ["scripts-app-dev", "jshint"]);
|
||||
gulp.watch(["src/css/**"], ["styles-app-dev"]);
|
||||
gulp.watch(["src/images/**", "src/css/sprites.less"], ["sprites"]);
|
||||
gulp.watch(["src/*.html"], ["html"]);
|
||||
});
|
||||
|
@ -15,10 +15,13 @@
|
||||
"gulp-plumber": "^0.6.5",
|
||||
"gulp-qunit": "^0.3.3",
|
||||
"gulp-react": "^1.0.1",
|
||||
"gulp-rename": "^1.2.0",
|
||||
"gulp-sourcemaps": "^1.1.5",
|
||||
"gulp-sprite-generator": "^0.2.0",
|
||||
"gulp-uglify": "^1.0.1",
|
||||
"gulp-util": "^3.0.1",
|
||||
"jshint-stylish": "^0.4.0",
|
||||
"merge-stream": "^0.1.5",
|
||||
"react": "",
|
||||
"react-tools": ""
|
||||
}
|
||||
|
@ -7,6 +7,7 @@ html {
|
||||
box-sizing: inherit;
|
||||
}
|
||||
|
||||
@import (less) "sprites.compiled.less";
|
||||
@import (less) "layout.less";
|
||||
@import (less) "header.less";
|
||||
@import (less) "flowtable.less";
|
||||
|
@ -1,5 +1,34 @@
|
||||
.flow-table {
|
||||
width: 100%;
|
||||
table-layout: fixed;
|
||||
|
||||
thead {
|
||||
background-color: #dadada;
|
||||
}
|
||||
|
||||
td {
|
||||
overflow: hidden;
|
||||
white-space: nowrap;
|
||||
text-overflow: ellipsis;
|
||||
}
|
||||
|
||||
|
||||
.col-tls {
|
||||
width: 10px;
|
||||
}
|
||||
.col-tls-https {
|
||||
background-color: rgba(0, 185, 0, 0.5);
|
||||
}
|
||||
.col-icon {
|
||||
width: 32px;
|
||||
}
|
||||
.col-method {
|
||||
width: 60px;
|
||||
}
|
||||
.col-status {
|
||||
width: 50px;
|
||||
}
|
||||
.col-time {
|
||||
width: 120px;
|
||||
}
|
||||
}
|
58
web/src/css/sprites.compiled.less
Normal file
@ -0,0 +1,58 @@
|
||||
.resource-icon {
|
||||
width: 32px;
|
||||
height: 32px;
|
||||
}
|
||||
|
||||
// From Chrome Dev Tools
|
||||
.resource-icon-css {
|
||||
background-image: url("../images/sprite.png");
|
||||
background-position: -0px -0px;
|
||||
background-size: 32px 320px!important;
|
||||
}
|
||||
.resource-icon-document {
|
||||
background-image: url("../images/sprite.png");
|
||||
background-position: -0px -32px;
|
||||
background-size: 32px 320px!important;
|
||||
}
|
||||
.resource-icon-js {
|
||||
background-image: url("../images/sprite.png");
|
||||
background-position: -0px -64px;
|
||||
background-size: 32px 320px!important;
|
||||
}
|
||||
.resource-icon-plain {
|
||||
background-image: url("../images/sprite.png");
|
||||
background-position: -0px -96px;
|
||||
background-size: 32px 320px!important;
|
||||
}
|
||||
|
||||
// Own
|
||||
.resource-icon-executable {
|
||||
background-image: url("../images/sprite.png");
|
||||
background-position: -0px -128px;
|
||||
background-size: 32px 320px!important;
|
||||
}
|
||||
.resource-icon-flash {
|
||||
background-image: url("../images/sprite.png");
|
||||
background-position: -0px -160px;
|
||||
background-size: 32px 320px!important;
|
||||
}
|
||||
.resource-icon-image {
|
||||
background-image: url("../images/sprite.png");
|
||||
background-position: -0px -192px;
|
||||
background-size: 32px 320px!important;
|
||||
}
|
||||
.resource-icon-java {
|
||||
background-image: url("../images/sprite.png");
|
||||
background-position: -0px -224px;
|
||||
background-size: 32px 320px!important;
|
||||
}
|
||||
.resource-icon-not-modified {
|
||||
background-image: url("../images/sprite.png");
|
||||
background-position: -0px -256px;
|
||||
background-size: 32px 320px!important;
|
||||
}
|
||||
.resource-icon-redirect {
|
||||
background-image: url("../images/sprite.png");
|
||||
background-position: -0px -288px;
|
||||
background-size: 32px 320px!important;
|
||||
}
|
38
web/src/css/sprites.less
Normal file
@ -0,0 +1,38 @@
|
||||
.resource-icon {
|
||||
width: 32px;
|
||||
height: 32px;
|
||||
}
|
||||
|
||||
// From Chrome Dev Tools
|
||||
.resource-icon-css {
|
||||
background-image: url(../images/chrome-devtools/resourceCSSIcon.png);
|
||||
}
|
||||
.resource-icon-document {
|
||||
background-image: url(../images/chrome-devtools/resourceDocumentIcon.png);
|
||||
}
|
||||
.resource-icon-js {
|
||||
background-image: url(../images/chrome-devtools/resourceJSIcon.png);
|
||||
}
|
||||
.resource-icon-plain {
|
||||
background-image: url(../images/chrome-devtools/resourcePlainIcon.png);
|
||||
}
|
||||
|
||||
// Own
|
||||
.resource-icon-executable {
|
||||
background-image: url(../images/resourceExecutableIcon.png);
|
||||
}
|
||||
.resource-icon-flash {
|
||||
background-image: url(../images/resourceFlashIcon.png);
|
||||
}
|
||||
.resource-icon-image {
|
||||
background-image: url(../images/resourceImageIcon.png);
|
||||
}
|
||||
.resource-icon-java {
|
||||
background-image: url(../images/resourceJavaIcon.png);
|
||||
}
|
||||
.resource-icon-not-modified {
|
||||
background-image: url(../images/resourceNotModifiedIcon.png);
|
||||
}
|
||||
.resource-icon-redirect {
|
||||
background-image: url(../images/resourceRedirectIcon.png);
|
||||
}
|
30
web/src/images/chrome-devtools/LICENSE
Normal file
@ -0,0 +1,30 @@
|
||||
// Copyright 2014 The Chromium Authors. All rights reserved.
|
||||
//
|
||||
// The Chromium Authors can be found at
|
||||
// http://src.chromium.org/svn/trunk/src/AUTHORS
|
||||
//
|
||||
// Redistribution and use in source and binary forms, with or without
|
||||
// modification, are permitted provided that the following conditions are
|
||||
// met:
|
||||
//
|
||||
// * Redistributions of source code must retain the above copyright
|
||||
// notice, this list of conditions and the following disclaimer.
|
||||
// * Redistributions in binary form must reproduce the above
|
||||
// copyright notice, this list of conditions and the following disclaimer
|
||||
// in the documentation and/or other materials provided with the
|
||||
// distribution.
|
||||
// * Neither the name of Google Inc. nor the names of its
|
||||
// contributors may be used to endorse or promote products derived from
|
||||
// this software without specific prior written permission.
|
||||
//
|
||||
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
BIN
web/src/images/chrome-devtools/resourceCSSIcon.png
Normal file
After Width: | Height: | Size: 1005 B |
BIN
web/src/images/chrome-devtools/resourceDocumentIcon.png
Normal file
After Width: | Height: | Size: 951 B |
BIN
web/src/images/chrome-devtools/resourceJSIcon.png
Normal file
After Width: | Height: | Size: 787 B |
BIN
web/src/images/chrome-devtools/resourcePlainIcon.png
Normal file
After Width: | Height: | Size: 295 B |
BIN
web/src/images/resourceExecutableIcon.png
Normal file
After Width: | Height: | Size: 853 B |
BIN
web/src/images/resourceFlashIcon.png
Normal file
After Width: | Height: | Size: 921 B |
BIN
web/src/images/resourceImageIcon.png
Normal file
After Width: | Height: | Size: 976 B |
BIN
web/src/images/resourceJavaIcon.png
Normal file
After Width: | Height: | Size: 861 B |
BIN
web/src/images/resourceNotModifiedIcon.png
Normal file
After Width: | Height: | Size: 1.0 KiB |
BIN
web/src/images/resourceRedirectIcon.png
Normal file
After Width: | Height: | Size: 1.1 KiB |
@ -4,7 +4,10 @@ var FlowRow = React.createClass({
|
||||
render: function(){
|
||||
var flow = this.props.flow;
|
||||
var columns = this.props.columns.map(function(column){
|
||||
return column({flow: flow});
|
||||
return column({
|
||||
key: column.displayName,
|
||||
flow: flow
|
||||
});
|
||||
}.bind(this));
|
||||
return <tr>{columns}</tr>;
|
||||
}
|
||||
@ -22,55 +25,89 @@ var FlowTableHead = React.createClass({
|
||||
var FlowTableBody = React.createClass({
|
||||
render: function(){
|
||||
var rows = this.props.flows.map(function(flow){
|
||||
return <FlowRow flow={flow} columns={this.props.columns}/>
|
||||
//TODO: Add UUID
|
||||
return <FlowRow flow={flow} columns={this.props.columns}/>;
|
||||
}.bind(this));
|
||||
return <tbody>{rows}</tbody>;
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
var TLSColumn = React.createClass({
|
||||
statics: {
|
||||
renderTitle: function(){
|
||||
return <th key="tls" className="col-tls"></th>;
|
||||
}
|
||||
},
|
||||
render: function(){
|
||||
var flow = this.props.flow;
|
||||
var ssl = (flow.request.scheme == "https");
|
||||
return <td className={ssl ? "col-tls-https" : "col-tls-http"}></td>;
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
var IconColumn = React.createClass({
|
||||
statics: {
|
||||
renderTitle: function(){
|
||||
return <th key="icon" className="col-icon"></th>;
|
||||
}
|
||||
},
|
||||
render: function(){
|
||||
var flow = this.props.flow;
|
||||
return <td className="resource-icon resource-icon-plain"></td>;
|
||||
}
|
||||
});
|
||||
|
||||
var PathColumn = React.createClass({
|
||||
statics: {
|
||||
renderTitle: function(){
|
||||
return <th key="PathColumn">Path</th>;
|
||||
return <th key="path" className="col-path">Path</th>;
|
||||
}
|
||||
},
|
||||
render: function(){
|
||||
var flow = this.props.flow;
|
||||
return <td key="PathColumn">{flow.request.scheme + "://" + flow.request.host + flow.request.path}</td>;
|
||||
return <td>{flow.request.scheme + "://" + flow.request.host + flow.request.path}</td>;
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
var MethodColumn = React.createClass({
|
||||
statics: {
|
||||
renderTitle: function(){
|
||||
return <th key="MethodColumn">Method</th>;
|
||||
return <th key="method" className="col-method">Method</th>;
|
||||
}
|
||||
},
|
||||
render: function(){
|
||||
var flow = this.props.flow;
|
||||
return <td key="MethodColumn">{flow.request.method}</td>;
|
||||
return <td>{flow.request.method}</td>;
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
var StatusColumn = React.createClass({
|
||||
statics: {
|
||||
renderTitle: function(){
|
||||
return <th key="StatusColumn">Status</th>;
|
||||
return <th key="status" className="col-status">Status</th>;
|
||||
}
|
||||
},
|
||||
render: function(){
|
||||
var flow = this.props.flow;
|
||||
var status;
|
||||
if(flow.response){
|
||||
status = flow.response.code + " " + flow.response.msg;
|
||||
status = flow.response.code;
|
||||
} else {
|
||||
status = null;
|
||||
}
|
||||
return <td key="StatusColumn">{status}</td>;
|
||||
return <td>{status}</td>;
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
var TimeColumn = React.createClass({
|
||||
statics: {
|
||||
renderTitle: function(){
|
||||
return <th key="TimeColumn">Time</th>;
|
||||
return <th key="time" className="col-time">Time</th>;
|
||||
}
|
||||
},
|
||||
render: function(){
|
||||
@ -81,11 +118,13 @@ var TimeColumn = React.createClass({
|
||||
} else {
|
||||
time = "...";
|
||||
}
|
||||
return <td key="TimeColumn">{time}</td>;
|
||||
return <td>{time}</td>;
|
||||
}
|
||||
});
|
||||
|
||||
var all_columns = [PathColumn, MethodColumn, StatusColumn, TimeColumn];
|
||||
|
||||
var all_columns = [TLSColumn, IconColumn, PathColumn, MethodColumn, StatusColumn, TimeColumn];
|
||||
|
||||
|
||||
var FlowTable = React.createClass({
|
||||
getInitialState: function () {
|
||||
|
@ -30,13 +30,14 @@ _.extend(FlowView.prototype, EventEmitter.prototype, {
|
||||
var updates = this.flows;
|
||||
this.flows = flows;
|
||||
updates.forEach(function(flow){
|
||||
this.update(flow);
|
||||
this._update(flow);
|
||||
}.bind(this));
|
||||
this.emit("change");
|
||||
},
|
||||
update: function(flow){
|
||||
_update: function(flow){
|
||||
console.debug("FIXME: Use UUID");
|
||||
var idx = _.findIndex(this.flows, function(f){
|
||||
return flow.request.timestamp_start == f.request.timestamp_start
|
||||
return flow.request.timestamp_start == f.request.timestamp_start;
|
||||
});
|
||||
|
||||
if(idx < 0){
|
||||
@ -44,6 +45,9 @@ _.extend(FlowView.prototype, EventEmitter.prototype, {
|
||||
} else {
|
||||
this.flows[idx] = flow;
|
||||
}
|
||||
},
|
||||
update: function(flow){
|
||||
this._update(flow);
|
||||
this.emit("change");
|
||||
},
|
||||
});
|
||||
@ -55,6 +59,11 @@ function _FlowStore() {
|
||||
_.extend(_FlowStore.prototype, EventEmitter.prototype, {
|
||||
getView: function (since) {
|
||||
var view = new FlowView(this, !since);
|
||||
|
||||
$.getJSON("/static/flows.json", function(flows){
|
||||
view.add_bulk(flows);
|
||||
});
|
||||
|
||||
return view;
|
||||
},
|
||||
handle: function (action) {
|
||||
|