aboutsummaryrefslogtreecommitdiffstats
path: root/libmproxy/web/static
diff options
context:
space:
mode:
authorMaximilian Hils <git@maximilianhils.com>2014-09-15 18:08:26 +0200
committerMaximilian Hils <git@maximilianhils.com>2014-09-15 18:08:26 +0200
commitcbf18320cdbd05197f232da69b3c9a5391735156 (patch)
tree63b9cf8fd6a8e8fb5be6b2d5a655acf3d33b6229 /libmproxy/web/static
parent9f8d2eea64d4611c1e2f7e7043fe6d3ef9a6aa40 (diff)
downloadmitmproxy-cbf18320cdbd05197f232da69b3c9a5391735156.tar.gz
mitmproxy-cbf18320cdbd05197f232da69b3c9a5391735156.tar.bz2
mitmproxy-cbf18320cdbd05197f232da69b3c9a5391735156.zip
client-side structure
Diffstat (limited to 'libmproxy/web/static')
-rw-r--r--libmproxy/web/static/css/app.css51
-rw-r--r--libmproxy/web/static/fonts/FontAwesome.otfbin85908 -> 0 bytes
-rw-r--r--libmproxy/web/static/index.html18
-rw-r--r--libmproxy/web/static/js/app.js452
4 files changed, 325 insertions, 196 deletions
diff --git a/libmproxy/web/static/css/app.css b/libmproxy/web/static/css/app.css
index 8ef7842a..fe8fd79a 100644
--- a/libmproxy/web/static/css/app.css
+++ b/libmproxy/web/static/css/app.css
@@ -13,25 +13,18 @@ body,
margin: 0;
overflow: hidden;
}
-header,
-footer {
- display: block;
-}
#container {
- padding: 153px 0 25px;
+ display: flex;
+ flex-direction: column;
}
-header {
- height: 153px;
- margin-top: -153px;
+#container > header,
+#container > footer,
+#container > .eventlog {
+ flex: 0 0 auto;
}
#main {
- height: 100%;
- display: block;
- overflow-y: auto;
-}
-footer {
- height: 25px;
- line-height: 25px;
+ flex: 1 1 auto;
+ overflow: auto;
}
header {
background-color: white;
@@ -55,11 +48,11 @@ header nav a.active {
}
header nav a:hover {
/*
- @preview: lightgrey;
- border-top-color: @preview;
- border-left-color: @preview;
- border-right-color: @preview;
- */
+ @preview: lightgrey;
+ border-top-color: @preview;
+ border-left-color: @preview;
+ border-right-color: @preview;
+ */
text-decoration: none;
}
header nav a.special {
@@ -70,18 +63,22 @@ header nav a.special {
header nav a.special:hover {
background-color: #5386c6;
}
-header nav:before {
- content: " ";
-}
-header nav:after {
- clear: both;
-}
header .menu {
height: 100px;
border-bottom: solid #a6a6a6 1px;
}
+.eventlog {
+ flex: 0 0 auto;
+}
+.eventlog pre {
+ margin: 0;
+ border-radius: 0;
+ height: 200px;
+ overflow: auto;
+}
footer {
- padding: 0 10px;
+ box-shadow: 0 -1px 3px #d3d3d3;
+ padding: 0px 10px 3px;
}
/*# sourceMappingURL=../css/app.css.map */ \ No newline at end of file
diff --git a/libmproxy/web/static/fonts/FontAwesome.otf b/libmproxy/web/static/fonts/FontAwesome.otf
deleted file mode 100644
index 81c9ad94..00000000
--- a/libmproxy/web/static/fonts/FontAwesome.otf
+++ /dev/null
Binary files differ
diff --git a/libmproxy/web/static/index.html b/libmproxy/web/static/index.html
deleted file mode 100644
index 509ef1eb..00000000
--- a/libmproxy/web/static/index.html
+++ /dev/null
@@ -1,18 +0,0 @@
-<!DOCTYPE html>
-<html>
-<head lang="en">
- <meta charset="UTF-8">
- <title>mitmproxy</title>
- <meta name="viewport" content="width=device-width, initial-scale=1">
- <link rel="stylesheet" href="css/vendor.css"/>
- <link rel="stylesheet" href="css/app.css"/>
- <script src="js/vendor.js"></script>
- <script src="js/app.js"></script>
-</head>
-<body>
-<div id="mitmproxy"></div>
-</body>
-<script>
- app = React.renderComponent(routes, document.body);
-</script>
-</html> \ No newline at end of file
diff --git a/libmproxy/web/static/js/app.js b/libmproxy/web/static/js/app.js
index eeb6e5dd..7873046a 100644
--- a/libmproxy/web/static/js/app.js
+++ b/libmproxy/web/static/js/app.js
@@ -1,135 +1,261 @@
+const PayloadSources = {
+ VIEW_ACTION: "VIEW_ACTION",
+ SERVER_ACTION: "SERVER_ACTION"
+};
+
- function EventEmitter(){"use strict";
- this.listeners = {};
- }
- EventEmitter.prototype.emit=function(event){"use strict";
- if(!(event in this.listeners)){
- return;
- }
- this.listeners[event].forEach(function (listener) {
- listener(event, this);
- }.bind(this));
- };
- EventEmitter.prototype.addListener=function(event, f){"use strict";
- this.listeners[event] = this.listeners[event] || [];
- this.listeners[event].push(f);
- };
- EventEmitter.prototype.removeListener=function(event, f){"use strict";
- if(!(event in this.listeners)){
- return false;
- }
- var index = this.listeners.indexOf(f);
- if (index >= 0) {
- this.listeners.splice(this.listeners.indexOf(f), 1);
- }
- };
+ function Dispatcher() {"use strict";
+ this.callbacks = [];
+ }
-var FLOW_CHANGED = "flow.changed";
+ Dispatcher.prototype.register=function(callback){"use strict";
+ this.callbacks.push(callback);
+ };
-for(var EventEmitter____Key in EventEmitter){if(EventEmitter.hasOwnProperty(EventEmitter____Key)){FlowStore[EventEmitter____Key]=EventEmitter[EventEmitter____Key];}}var ____SuperProtoOfEventEmitter=EventEmitter===null?null:EventEmitter.prototype;FlowStore.prototype=Object.create(____SuperProtoOfEventEmitter);FlowStore.prototype.constructor=FlowStore;FlowStore.__superConstructor__=EventEmitter;
- function FlowStore() {"use strict";
- EventEmitter.call(this);
- this.flows = [];
+ Dispatcher.prototype.unregister=function(callback){"use strict";
+ var index = this.callbacks.indexOf(f);
+ if (index >= 0) {
+ this.callbacks.splice(this.callbacks.indexOf(f), 1);
}
+ };
- FlowStore.prototype.getAll=function() {"use strict";
- return this.flows;
- };
+ Dispatcher.prototype.dispatch=function(payload){"use strict";
+ console.debug("dispatch", payload);
+ this.callbacks.forEach(function(callback) {
+ callback(payload);
+ });
+ };
- FlowStore.prototype.close=function(){"use strict";
- console.log("FlowStore.close()");
- this.listeners = [];
- };
- FlowStore.prototype.emitChange=function() {"use strict";
- return this.emit(FLOW_CHANGED);
- };
- FlowStore.prototype.addChangeListener=function(f) {"use strict";
- this.addListener(FLOW_CHANGED, f);
- };
+AppDispatcher = new Dispatcher();
+AppDispatcher.dispatchViewAction = function(action){
+ action.actionSource = PayloadSources.VIEW_ACTION;
+ this.dispatch(action);
+};
+var ActionTypes = {
+ SETTINGS_UPDATE: "SETTINGS_UPDATE",
+ LOG_ADD: "LOG_ADD"
+};
- FlowStore.prototype.removeChangeListener=function(f) {"use strict";
- this.removeListener(FLOW_CHANGED, f);
- };
+var SettingsActions = {
+ update:function(settings) {
+ settings = _.merge({}, SettingsStore.getSettings(), settings);
+ AppDispatcher.dispatchViewAction({
+ actionType: ActionTypes.SETTINGS_UPDATE,
+ settings: settings
+ });
+ }
+};
+ function EventEmitter() {"use strict";
+ this.listeners = {};
+ }
+ EventEmitter.prototype.emit=function(event) {"use strict";
+ if (!(event in this.listeners)) {
+ return;
+ }
+ this.listeners[event].forEach(function(listener) {
+ listener(event, this);
+ }.bind(this));
+ };
+ EventEmitter.prototype.addListener=function(event, f) {"use strict";
+ this.listeners[event] = this.listeners[event] || [];
+ this.listeners[event].push(f);
+ };
+ EventEmitter.prototype.removeListener=function(event, f) {"use strict";
+ if (!(event in this.listeners)) {
+ return false;
+ }
+ var index = this.listeners[event].indexOf(f);
+ if (index >= 0) {
+ this.listeners[event].splice(index, 1);
+ }
+ };
+
+for(var EventEmitter____Key in EventEmitter){if(EventEmitter.hasOwnProperty(EventEmitter____Key)){_SettingsStore[EventEmitter____Key]=EventEmitter[EventEmitter____Key];}}var ____SuperProtoOfEventEmitter=EventEmitter===null?null:EventEmitter.prototype;_SettingsStore.prototype=Object.create(____SuperProtoOfEventEmitter);_SettingsStore.prototype.constructor=_SettingsStore;_SettingsStore.__superConstructor__=EventEmitter;
+ function _SettingsStore() {"use strict";
+ /*jshint validthis: true */
+ EventEmitter.call(this);
+ this.settings = { version: "0.12", showEventLog: true }; //FIXME: Need to get that from somewhere.
+ }
+ _SettingsStore.prototype.getSettings=function() {"use strict";
+ return this.settings;
+ };
+ _SettingsStore.prototype.handle=function(action) {"use strict";
+ switch (action.actionType) {
+ case ActionTypes.SETTINGS_UPDATE:
+ this.settings = action.settings;
+ this.emit("change");
+ break;
+ default:
+ return;
+ }
+ };
+
+var SettingsStore = new _SettingsStore();
+AppDispatcher.register(SettingsStore.handle.bind(SettingsStore));
+
+
+var SettingsMixin = {
+ getInitialState:function(){
+ return {
+ settings: SettingsStore.getSettings()
+ };
+ },
+ componentDidMount:function(){
+ SettingsStore.addListener("change", this._onSettingsChange);
+ },
+ componentWillUnmount:function(){
+ SettingsStore.removeListener("change", this._onSettingsChange);
+ },
+ _onSettingsChange:function(){
+ this.setState({
+ settings: SettingsStore.getSettings()
+ });
+ }
+};
+for(var EventEmitter____Key in EventEmitter){if(EventEmitter.hasOwnProperty(EventEmitter____Key)){_EventLogStore[EventEmitter____Key]=EventEmitter[EventEmitter____Key];}}var ____SuperProtoOfEventEmitter=EventEmitter===null?null:EventEmitter.prototype;_EventLogStore.prototype=Object.create(____SuperProtoOfEventEmitter);_EventLogStore.prototype.constructor=_EventLogStore;_EventLogStore.__superConstructor__=EventEmitter;
+ function _EventLogStore() {"use strict";
+ /*jshint validthis: true */
+ EventEmitter.call(this);
+ this.log = [];
+ }
+ _EventLogStore.prototype.getAll=function() {"use strict";
+ return this.log;
+ };
+ _EventLogStore.prototype.handle=function(action) {"use strict";
+ switch (action.actionType) {
+ case ActionTypes.LOG_ADD:
+ this.log.push(action.message);
+ this.emit("change");
+ break;
+ default:
+ return;
+ }
+ };
+
+var EventLogStore = new _EventLogStore();
+AppDispatcher.register(EventLogStore.handle.bind(EventLogStore));
+
+
+var EventLogMixin = {
+ getInitialState:function(){
+ return {
+ log: EventLog.getAll()
+ };
+ },
+ componentDidMount:function(){
+ SettingsStore.addListener("change", this._onEventLogChange);
+ },
+ componentWillUnmount:function(){
+ SettingsStore.removeListener("change", this._onEventLogChange);
+ },
+ _onEventLogChange:function(){
+ this.setState({
+ log: EventLog.getAll()
+ });
+ }
+};
-for(var FlowStore____Key in FlowStore){if(FlowStore.hasOwnProperty(FlowStore____Key)){DummyFlowStore[FlowStore____Key]=FlowStore[FlowStore____Key];}}var ____SuperProtoOfFlowStore=FlowStore===null?null:FlowStore.prototype;DummyFlowStore.prototype=Object.create(____SuperProtoOfFlowStore);DummyFlowStore.prototype.constructor=DummyFlowStore;DummyFlowStore.__superConstructor__=FlowStore;
- function DummyFlowStore(flows) {"use strict";
- FlowStore.call(this);
- this.flows = flows;
+ function Connection(root){"use strict";
+ if(!root){
+ root = location.origin + "/api/v1";
+ }
+ this.root = root;
+ this.openWebSocketConnection();
}
- DummyFlowStore.prototype.addFlow=function(flow) {"use strict";
- this.flows.push(flow);
- this.emitChange();
+ Connection.prototype.openWebSocketConnection=function(){"use strict";
+ this.ws = new WebSocket(this.root.replace("http","ws") + "/ws");
+ var ws = this.ws;
+
+ ws.onopen = this.onopen.bind(this);
+ ws.onmessage = this.onmessage.bind(this);
+ ws.onerror = this.onerror.bind(this);
+ ws.onclose = this.onclose.bind(this);
+ };
+
+ Connection.prototype.onopen=function(open){"use strict";
+ console.log("onopen", this, arguments);
+ };
+ Connection.prototype.onmessage=function(message){"use strict";
+ console.log("onmessage", this, arguments);
+ };
+ Connection.prototype.onerror=function(error){"use strict";
+ console.log("onerror", this, arguments);
+ };
+ Connection.prototype.onclose=function(close){"use strict";
+ console.log("onclose", this, arguments);
};
-var SETTINGS_CHANGED = "settings.changed";
-for(EventEmitter____Key in EventEmitter){if(EventEmitter.hasOwnProperty(EventEmitter____Key)){Settings[EventEmitter____Key]=EventEmitter[EventEmitter____Key];}}Settings.prototype=Object.create(____SuperProtoOfEventEmitter);Settings.prototype.constructor=Settings;Settings.__superConstructor__=EventEmitter;
- function Settings(){"use strict";
- EventEmitter.call(this);
- this.settings = false;
+ function Connection(root){"use strict";
+ if(!root){
+ root = location.origin + "/api/v1";
+ }
+ this.root = root;
+ this.openWebSocketConnection();
}
- Settings.prototype.getAll=function(){"use strict";
- return this.settings;
- };
+ Connection.prototype.openWebSocketConnection=function(){"use strict";
+ this.ws = new WebSocket(this.root.replace("http","ws") + "/ws");
+ var ws = this.ws;
- Settings.prototype.emitChange=function() {"use strict";
- return this.emit(SETTINGS_CHANGED);
+ ws.onopen = this.onopen.bind(this);
+ ws.onmessage = this.onmessage.bind(this);
+ ws.onerror = this.onerror.bind(this);
+ ws.onclose = this.onclose.bind(this);
};
- Settings.prototype.addChangeListener=function(f) {"use strict";
- this.addListener(SETTINGS_CHANGED, f);
+ Connection.prototype.onopen=function(open){"use strict";
+ console.log("onopen", this, arguments);
};
-
- Settings.prototype.removeChangeListener=function(f) {"use strict";
- this.removeListener(SETTINGS_CHANGED, f);
+ Connection.prototype.onmessage=function(message){"use strict";
+ console.log("onmessage", this, arguments);
};
-
-
-for(var Settings____Key in Settings){if(Settings.hasOwnProperty(Settings____Key)){DummySettings[Settings____Key]=Settings[Settings____Key];}}var ____SuperProtoOfSettings=Settings===null?null:Settings.prototype;DummySettings.prototype=Object.create(____SuperProtoOfSettings);DummySettings.prototype.constructor=DummySettings;DummySettings.__superConstructor__=Settings;
- function DummySettings(settings){"use strict";
- Settings.call(this);
- this.settings = settings;
- }
- DummySettings.prototype.update=function(obj){"use strict";
- _.merge(this.settings, obj);
- this.emitChange();
+ Connection.prototype.onerror=function(error){"use strict";
+ console.log("onerror", this, arguments);
+ };
+ Connection.prototype.onclose=function(close){"use strict";
+ console.log("onclose", this, arguments);
};
-/** @jsx React.DOM */
-var Footer = React.createClass({displayName: 'Footer',
- render : function(){
- return (React.DOM.footer(null,
- React.DOM.span({className: "label label-success"}, "transparent mode")
- ));
- }
-});
+
/** @jsx React.DOM */
var MainMenu = React.createClass({displayName: 'MainMenu',
- render : function(){
- return (React.DOM.div(null, "Main Menu"));
+ mixins: [SettingsMixin],
+ handleSettingsChange:function() {
+ SettingsActions.update({
+ showEventLog: this.refs.showEventLogInput.getDOMNode().checked
+ });
+ },
+ render:function(){
+ return React.DOM.div(null,
+ React.DOM.label(null,
+ React.DOM.input({type: "checkbox", ref: "showEventLogInput", checked: this.state.settings.showEventLog, onChange: this.handleSettingsChange}),
+ "Show Event Log"
+ )
+ );
}
});
var ToolsMenu = React.createClass({displayName: 'ToolsMenu',
- render : function(){
+ render:function(){
return (React.DOM.div(null, "Tools Menu"));
}
});
var ReportsMenu = React.createClass({displayName: 'ReportsMenu',
- render : function(){
+ render:function(){
return (React.DOM.div(null, "Reports Menu"));
}
});
+
var _Header_Entries = {
main: {
title: "Traffic",
@@ -149,83 +275,55 @@ var _Header_Entries = {
};
var Header = React.createClass({displayName: 'Header',
- getInitialState: function(){
- return {active: "main"};
+ mixins: [SettingsMixin],
+ getInitialState:function(){
+ return {
+ active: "main"
+ };
},
- handleClick: function(active){
+ handleClick:function(active){
this.setState({active: active});
ReactRouter.transitionTo(_Header_Entries[active].route);
return false;
},
- handleFileClick: function(){
+ handleFileClick:function(){
console.log("File click");
},
- render: function(){
+ render:function(){
var header = [];
for(var item in _Header_Entries){
var classes = this.state.active == item ? "active" : "";
header.push(React.DOM.a({key: item, href: "#", className: classes,
- onClick: this.handleClick.bind(this, item)}, _Header_Entries[item].title));
+ onClick: this.handleClick.bind(this, item)}, _Header_Entries[item].title));
}
var menu = _Header_Entries[this.state.active].menu();
return (
- React.DOM.header(null,
- React.DOM.div({className: "title-bar"},
- "mitmproxy ", this.props.settings.version
- ),
- React.DOM.nav(null,
- React.DOM.a({href: "#", className: "special", onClick: this.handleFileClick}, " File "),
- header
- ),
- React.DOM.div({className: "menu"},
- menu
- )
- ));
+ React.DOM.header(null,
+ React.DOM.div({className: "title-bar"},
+ "mitmproxy ", this.state.settings.version
+ ),
+ React.DOM.nav(null,
+ React.DOM.a({href: "#", className: "special", onClick: this.handleFileClick}, " File "),
+ header
+ ),
+ React.DOM.div({className: "menu"},
+ menu
+ )
+ ));
}
});
/** @jsx React.DOM */
-var App = React.createClass({displayName: 'App',
- getInitialState: function () {
- return {
- settings: {} //TODO: How explicit should we get here?
- //List all subattributes?
- };
- },
- componentDidMount: function () {
- //TODO: Replace DummyStore with real settings over WS (https://facebook.github.io/react/tips/initial-ajax.html)
- var settingsStore = new DummySettings({
- version: "0.12"
- });
- this.setState({settingsStore: settingsStore});
- settingsStore.addChangeListener(this.onSettingsChange);
- },
- onSettingsChange: function(event, settings){
- this.setState({settings: settings.getAll()});
- },
- render: function () {
- return (
- React.DOM.div({id: "container"},
- Header({settings: this.state.settings}),
- React.DOM.div({id: "main"},
- this.props.activeRouteHandler({settings: this.state.settings})
- ),
- Footer(null)
- )
- );
- }
-});
-
var TrafficTable = React.createClass({displayName: 'TrafficTable',
- getInitialState: function(){
+ /*getInitialState: function(){
return {
flows: []
};
- },
+ },*/
componentDidMount: function () {
- var flowStore = new DummyFlowStore([]);
+ /*var flowStore = new DummyFlowStore([]);
this.setState({flowStore: flowStore});
flowStore.addChangeListener(this.onFlowsChange);
@@ -236,35 +334,87 @@ var TrafficTable = React.createClass({displayName: 'TrafficTable',
flowStore.addFlow(flow);
}, _.random(i*400,i*400+1000));
});
- }.bind(this));
+ }.bind(this));*/
},
componentWillUnmount: function(){
- this.state.flowStore.close();
+ //this.state.flowStore.close();
},
onFlowsChange: function(event, flows){
- this.setState({flows: flows.getAll()});
+ //this.setState({flows: flows.getAll()});
},
render: function () {
- var flows = this.state.flows.map(function(flow){
- return React.DOM.div(null, flow.request.method, " ", flow.request.scheme, "://", flow.request.host, flow.request.path);
- });
- return React.DOM.pre(null, flows);
+ /*var flows = this.state.flows.map(function(flow){
+ return <div>{flow.request.method} {flow.request.scheme}://{flow.request.host}{flow.request.path}</div>;
+ }); *//**/
+ x = "WTF";
+ i = 12;
+ while(i--) x += x;
+ return React.DOM.div(null, React.DOM.pre(null, x));
}
});
+/** @jsx React.DOM */
+var EventLog = React.createClass({displayName: 'EventLog',
+ render:function(){
+ return (
+ React.DOM.div({className: "eventlog"},
+ React.DOM.pre(null,
+ "much log."
+ )
+ )
+ );
+ }
+});
+/** @jsx React.DOM */
+
+var Footer = React.createClass({displayName: 'Footer',
+ render:function(){
+ return (
+ React.DOM.footer(null,
+ React.DOM.span({className: "label label-success"}, "transparent mode")
+ )
+ );
+ }
+});
+/** @jsx React.DOM */
+
+//TODO: Move out of here, just a stub.
var Reports = React.createClass({displayName: 'Reports',
- render: function(){
+ render:function(){
return (React.DOM.div(null, "Report Editor"));
}
});
-var routes = (
+
+
+var ProxyAppMain = React.createClass({displayName: 'ProxyAppMain',
+ mixins: [SettingsMixin],
+ render:function() {
+ return (
+ React.DOM.div({id: "container"},
+ Header(null),
+ React.DOM.div({id: "main"}, this.props.activeRouteHandler(null)),
+ this.state.settings.showEventLog ? EventLog(null) : null,
+ Footer(null)
+ )
+ );
+ }
+});
+
+
+var ProxyApp = (
ReactRouter.Routes({location: "hash"},
- ReactRouter.Route({name: "app", path: "/", handler: App},
+ ReactRouter.Route({name: "app", path: "/", handler: ProxyAppMain},
ReactRouter.Route({name: "main", handler: TrafficTable}),
ReactRouter.Route({name: "reports", handler: Reports}),
ReactRouter.Redirect({to: "main"})
)
)
);
+
+$(function(){
+
+ app = React.renderComponent(ProxyApp, document.body);
+
+});
//# sourceMappingURL=app.js.map \ No newline at end of file