diff options
Diffstat (limited to 'web/src/js')
-rw-r--r-- | web/src/js/Connection.es6.js | 33 | ||||
-rw-r--r-- | web/src/js/Dispatcher.es6.js | 36 | ||||
-rw-r--r-- | web/src/js/actions.es6.js | 14 | ||||
-rw-r--r-- | web/src/js/app.js | 5 | ||||
-rw-r--r-- | web/src/js/components/EventLog.react.js | 13 | ||||
-rw-r--r-- | web/src/js/components/Footer.react.js (renamed from web/src/js/footer.react.js) | 2 | ||||
-rw-r--r-- | web/src/js/components/Header.react.js | 88 | ||||
-rw-r--r-- | web/src/js/components/ProxyApp.react.js | 35 | ||||
-rw-r--r-- | web/src/js/components/TrafficTable.react.js | 38 | ||||
-rw-r--r-- | web/src/js/datastructures.es6.js | 105 | ||||
-rw-r--r-- | web/src/js/header.react.js | 72 | ||||
-rw-r--r-- | web/src/js/mitmproxy.react.js | 82 | ||||
-rw-r--r-- | web/src/js/stores/EventLogStore.es6.js | 42 | ||||
-rw-r--r-- | web/src/js/stores/SettingsStore.es6.js | 42 | ||||
-rw-r--r-- | web/src/js/stores/base.es6.js | 26 |
15 files changed, 373 insertions, 260 deletions
diff --git a/web/src/js/Connection.es6.js b/web/src/js/Connection.es6.js new file mode 100644 index 00000000..9daa82e2 --- /dev/null +++ b/web/src/js/Connection.es6.js @@ -0,0 +1,33 @@ +class Connection { + constructor(root){ + if(!root){ + root = location.origin + "/api/v1"; + } + this.root = root; + this.openWebSocketConnection(); + } + + openWebSocketConnection(){ + 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); + } + + onopen(open){ + console.log("onopen", this, arguments); + } + onmessage(message){ + console.log("onmessage", this, arguments); + } + onerror(error){ + console.log("onerror", this, arguments); + } + onclose(close){ + console.log("onclose", this, arguments); + } + +} diff --git a/web/src/js/Dispatcher.es6.js b/web/src/js/Dispatcher.es6.js new file mode 100644 index 00000000..9bf70878 --- /dev/null +++ b/web/src/js/Dispatcher.es6.js @@ -0,0 +1,36 @@ +const PayloadSources = { + VIEW_ACTION: "VIEW_ACTION", + SERVER_ACTION: "SERVER_ACTION" +}; + +class Dispatcher { + + constructor() { + this.callbacks = []; + } + + register(callback){ + this.callbacks.push(callback); + } + + unregister(callback){ + var index = this.callbacks.indexOf(f); + if (index >= 0) { + this.callbacks.splice(this.callbacks.indexOf(f), 1); + } + } + + dispatch(payload){ + console.debug("dispatch", payload); + this.callbacks.forEach((callback) => { + callback(payload); + }); + } + +} + +AppDispatcher = new Dispatcher(); +AppDispatcher.dispatchViewAction = function(action){ + action.actionSource = PayloadSources.VIEW_ACTION; + this.dispatch(action); +};
\ No newline at end of file diff --git a/web/src/js/actions.es6.js b/web/src/js/actions.es6.js new file mode 100644 index 00000000..b6770074 --- /dev/null +++ b/web/src/js/actions.es6.js @@ -0,0 +1,14 @@ +var ActionTypes = { + SETTINGS_UPDATE: "SETTINGS_UPDATE", + LOG_ADD: "LOG_ADD" +}; + +var SettingsActions = { + update(settings) { + settings = _.merge({}, SettingsStore.getSettings(), settings); + AppDispatcher.dispatchViewAction({ + actionType: ActionTypes.SETTINGS_UPDATE, + settings: settings + }); + } +};
\ No newline at end of file diff --git a/web/src/js/app.js b/web/src/js/app.js new file mode 100644 index 00000000..2e4557af --- /dev/null +++ b/web/src/js/app.js @@ -0,0 +1,5 @@ +$(function(){ + + app = React.renderComponent(ProxyApp, document.body); + +});
\ No newline at end of file diff --git a/web/src/js/components/EventLog.react.js b/web/src/js/components/EventLog.react.js new file mode 100644 index 00000000..e710d30c --- /dev/null +++ b/web/src/js/components/EventLog.react.js @@ -0,0 +1,13 @@ +/** @jsx React.DOM */ + +var EventLog = React.createClass({ + render(){ + return ( + <div className="eventlog"> + <pre> + much log. + </pre> + </div> + ); + } +});
\ No newline at end of file diff --git a/web/src/js/footer.react.js b/web/src/js/components/Footer.react.js index 1b65e19d..ae0ccbe5 100644 --- a/web/src/js/footer.react.js +++ b/web/src/js/components/Footer.react.js @@ -1,7 +1,7 @@ /** @jsx React.DOM */ var Footer = React.createClass({ - render : function(){ + render(){ return ( <footer> <span className="label label-success">transparent mode</span> diff --git a/web/src/js/components/Header.react.js b/web/src/js/components/Header.react.js new file mode 100644 index 00000000..dc304d81 --- /dev/null +++ b/web/src/js/components/Header.react.js @@ -0,0 +1,88 @@ +/** @jsx React.DOM */ + +var MainMenu = React.createClass({ + mixins: [SettingsMixin], + handleSettingsChange() { + SettingsActions.update({ + showEventLog: this.refs.showEventLogInput.getDOMNode().checked + }); + }, + render(){ + return <div> + <label> + <input type="checkbox" ref="showEventLogInput" checked={this.state.settings.showEventLog} onChange={this.handleSettingsChange}/> + Show Event Log + </label> + </div>; + } +}); +var ToolsMenu = React.createClass({ + render(){ + return (<div>Tools Menu</div>); + } +}); +var ReportsMenu = React.createClass({ + render(){ + return (<div>Reports Menu</div>); + } +}); + + +var _Header_Entries = { + main: { + title: "Traffic", + route: "main", + menu: MainMenu + }, + tools: { + title: "Tools", + route: "main", + menu: ToolsMenu + }, + reports: { + title: "Visualization", + route: "reports", + menu: ReportsMenu + } +}; + +var Header = React.createClass({ + mixins: [SettingsMixin], + getInitialState(){ + return { + active: "main" + }; + }, + handleClick(active){ + this.setState({active: active}); + ReactRouter.transitionTo(_Header_Entries[active].route); + return false; + }, + handleFileClick(){ + console.log("File click"); + }, + + render(){ + var header = []; + for(var item in _Header_Entries){ + var classes = this.state.active == item ? "active" : ""; + header.push(<a key={item} href="#" className={classes} + onClick={this.handleClick.bind(this, item)}>{ _Header_Entries[item].title }</a>); + } + + var menu = _Header_Entries[this.state.active].menu(); + return ( + <header> + <div className="title-bar"> + mitmproxy { this.state.settings.version } + </div> + <nav> + <a href="#" className="special" onClick={this.handleFileClick}> File </a> + {header} + </nav> + <div className="menu"> + { menu } + </div> + </header>); + } +});
\ No newline at end of file diff --git a/web/src/js/components/ProxyApp.react.js b/web/src/js/components/ProxyApp.react.js new file mode 100644 index 00000000..7953d938 --- /dev/null +++ b/web/src/js/components/ProxyApp.react.js @@ -0,0 +1,35 @@ +/** @jsx React.DOM */ + +//TODO: Move out of here, just a stub. +var Reports = React.createClass({ + render(){ + return (<div>Report Editor</div>); + } +}); + + + +var ProxyAppMain = React.createClass({ + mixins: [SettingsMixin], + render() { + return ( + <div id="container"> + <Header/> + <div id="main"><this.props.activeRouteHandler/></div> + {this.state.settings.showEventLog ? <EventLog/> : null} + <Footer/> + </div> + ); + } +}); + + +var ProxyApp = ( + <ReactRouter.Routes location="hash"> + <ReactRouter.Route name="app" path="/" handler={ProxyAppMain}> + <ReactRouter.Route name="main" handler={TrafficTable}/> + <ReactRouter.Route name="reports" handler={Reports}/> + <ReactRouter.Redirect to="main"/> + </ReactRouter.Route> + </ReactRouter.Routes> +); diff --git a/web/src/js/components/TrafficTable.react.js b/web/src/js/components/TrafficTable.react.js new file mode 100644 index 00000000..442f8da2 --- /dev/null +++ b/web/src/js/components/TrafficTable.react.js @@ -0,0 +1,38 @@ +/** @jsx React.DOM */ + +var TrafficTable = React.createClass({ + /*getInitialState: function(){ + return { + flows: [] + }; + },*/ + componentDidMount: function () { + /*var flowStore = new DummyFlowStore([]); + this.setState({flowStore: flowStore}); + + flowStore.addChangeListener(this.onFlowsChange); + + $.getJSON("/flows.json").success(function (flows) { + flows.forEach(function (flow, i) { + window.setTimeout(function () { + flowStore.addFlow(flow); + }, _.random(i*400,i*400+1000)); + }); + }.bind(this));*/ + }, + componentWillUnmount: function(){ + //this.state.flowStore.close(); + }, + onFlowsChange: function(event, flows){ + //this.setState({flows: flows.getAll()}); + }, + render: function () { + /*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 <div><pre>{x}</pre></div>; + } +});
\ No newline at end of file diff --git a/web/src/js/datastructures.es6.js b/web/src/js/datastructures.es6.js deleted file mode 100644 index e9e2ee77..00000000 --- a/web/src/js/datastructures.es6.js +++ /dev/null @@ -1,105 +0,0 @@ -class EventEmitter { - constructor(){ - this.listeners = {}; - } - emit(event){ - if(!(event in this.listeners)){ - return; - } - this.listeners[event].forEach(function (listener) { - listener(event, this); - }.bind(this)); - } - addListener(event, f){ - this.listeners[event] = this.listeners[event] || []; - this.listeners[event].push(f); - } - removeListener(event, f){ - if(!(event in this.listeners)){ - return false; - } - var index = this.listeners.indexOf(f); - if (index >= 0) { - this.listeners.splice(this.listeners.indexOf(f), 1); - } - } -} - -var FLOW_CHANGED = "flow.changed"; - -class FlowStore extends EventEmitter{ - constructor() { - super(); - this.flows = []; - } - - getAll() { - return this.flows; - } - - close(){ - console.log("FlowStore.close()"); - this.listeners = []; - } - - emitChange() { - return this.emit(FLOW_CHANGED); - } - - addChangeListener(f) { - this.addListener(FLOW_CHANGED, f); - } - - removeChangeListener(f) { - this.removeListener(FLOW_CHANGED, f); - } -} - -class DummyFlowStore extends FlowStore { - constructor(flows) { - super(); - this.flows = flows; - } - - addFlow(flow) { - this.flows.push(flow); - this.emitChange(); - } -} - - -var SETTINGS_CHANGED = "settings.changed"; - -class Settings extends EventEmitter { - constructor(){ - super(); - this.settings = false; - } - - getAll(){ - return this.settings; - } - - emitChange() { - return this.emit(SETTINGS_CHANGED); - } - - addChangeListener(f) { - this.addListener(SETTINGS_CHANGED, f); - } - - removeChangeListener(f) { - this.removeListener(SETTINGS_CHANGED, f); - } -} - -class DummySettings extends Settings { - constructor(settings){ - super(); - this.settings = settings; - } - update(obj){ - _.merge(this.settings, obj); - this.emitChange(); - } -}
\ No newline at end of file diff --git a/web/src/js/header.react.js b/web/src/js/header.react.js deleted file mode 100644 index 85dc3106..00000000 --- a/web/src/js/header.react.js +++ /dev/null @@ -1,72 +0,0 @@ -/** @jsx React.DOM */ - -var MainMenu = React.createClass({ - render: function(){ - return (<div>Main Menu</div>); - } -}); -var ToolsMenu = React.createClass({ - render: function(){ - return (<div>Tools Menu</div>); - } -}); -var ReportsMenu = React.createClass({ - render: function(){ - return (<div>Reports Menu</div>); - } -}); - -var _Header_Entries = { - main: { - title: "Traffic", - route: "main", - menu: MainMenu - }, - tools: { - title: "Tools", - route: "main", - menu: ToolsMenu - }, - reports: { - title: "Visualization", - route: "reports", - menu: ReportsMenu - } -}; - -var Header = React.createClass({ - getInitialState: function(){ - return {active: "main"}; - }, - handleClick: function(active){ - this.setState({active: active}); - ReactRouter.transitionTo(_Header_Entries[active].route); - return false; - }, - handleFileClick: function(){ - console.log("File click"); - }, - render: function(){ - var header = []; - for(var item in _Header_Entries){ - var classes = this.state.active == item ? "active" : ""; - header.push(<a key={item} href="#" className={classes} - onClick={this.handleClick.bind(this, item)}>{ _Header_Entries[item].title }</a>); - } - - var menu = _Header_Entries[this.state.active].menu(); - return ( - <header> - <div className="title-bar"> - mitmproxy { this.props.settings.version } - </div> - <nav> - <a href="#" className="special" onClick={this.handleFileClick}> File </a> - {header} - </nav> - <div className="menu"> - { menu } - </div> - </header>); - } -});
\ No newline at end of file diff --git a/web/src/js/mitmproxy.react.js b/web/src/js/mitmproxy.react.js deleted file mode 100644 index 609d2014..00000000 --- a/web/src/js/mitmproxy.react.js +++ /dev/null @@ -1,82 +0,0 @@ -/** @jsx React.DOM */ - -var App = React.createClass({ - 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 ( - <div id="container"> - <Header settings={this.state.settings}/> - <div id="main"> - <this.props.activeRouteHandler settings={this.state.settings}/> - </div> - <Footer/> - </div> - ); - } -}); - -var TrafficTable = React.createClass({ - getInitialState: function(){ - return { - flows: [] - }; - }, - componentDidMount: function () { - var flowStore = new DummyFlowStore([]); - this.setState({flowStore: flowStore}); - - flowStore.addChangeListener(this.onFlowsChange); - - $.getJSON("/flows.json").success(function (flows) { - flows.forEach(function (flow, i) { - window.setTimeout(function () { - flowStore.addFlow(flow); - }, _.random(i*400,i*400+1000)); - }); - }.bind(this)); - }, - componentWillUnmount: function(){ - this.state.flowStore.close(); - }, - onFlowsChange: function(event, flows){ - this.setState({flows: flows.getAll()}); - }, - render: function () { - var flows = this.state.flows.map(function(flow){ - return <div>{flow.request.method} {flow.request.scheme}://{flow.request.host}{flow.request.path}</div>; - }); - return <pre>{flows}</pre>; - } -}); - -var Reports = React.createClass({ - render: function(){ - return (<div>Report Editor</div>); - } -}); - -var routes = ( - <ReactRouter.Routes location="hash"> - <ReactRouter.Route name="app" path="/" handler={App}> - <ReactRouter.Route name="main" handler={TrafficTable}/> - <ReactRouter.Route name="reports" handler={Reports}/> - <ReactRouter.Redirect to="main"/> - </ReactRouter.Route> - </ReactRouter.Routes> -);
\ No newline at end of file diff --git a/web/src/js/stores/EventLogStore.es6.js b/web/src/js/stores/EventLogStore.es6.js new file mode 100644 index 00000000..caa9d77d --- /dev/null +++ b/web/src/js/stores/EventLogStore.es6.js @@ -0,0 +1,42 @@ +class _EventLogStore extends EventEmitter { + constructor() { + /*jshint validthis: true */ + super(); + this.log = []; + } + getAll() { + return this.log; + } + handle(action) { + 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(){ + return { + log: EventLog.getAll() + }; + }, + componentDidMount(){ + SettingsStore.addListener("change", this._onEventLogChange); + }, + componentWillUnmount(){ + SettingsStore.removeListener("change", this._onEventLogChange); + }, + _onEventLogChange(){ + this.setState({ + log: EventLog.getAll() + }); + } +};
\ No newline at end of file diff --git a/web/src/js/stores/SettingsStore.es6.js b/web/src/js/stores/SettingsStore.es6.js new file mode 100644 index 00000000..7f3a6837 --- /dev/null +++ b/web/src/js/stores/SettingsStore.es6.js @@ -0,0 +1,42 @@ +class _SettingsStore extends EventEmitter { + constructor() { + /*jshint validthis: true */ + super(); + this.settings = { version: "0.12", showEventLog: true }; //FIXME: Need to get that from somewhere. + } + getSettings() { + return this.settings; + } + handle(action) { + 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(){ + return { + settings: SettingsStore.getSettings() + }; + }, + componentDidMount(){ + SettingsStore.addListener("change", this._onSettingsChange); + }, + componentWillUnmount(){ + SettingsStore.removeListener("change", this._onSettingsChange); + }, + _onSettingsChange(){ + this.setState({ + settings: SettingsStore.getSettings() + }); + } +};
\ No newline at end of file diff --git a/web/src/js/stores/base.es6.js b/web/src/js/stores/base.es6.js new file mode 100644 index 00000000..9e9c69aa --- /dev/null +++ b/web/src/js/stores/base.es6.js @@ -0,0 +1,26 @@ +class EventEmitter { + constructor() { + this.listeners = {}; + } + emit(event) { + if (!(event in this.listeners)) { + return; + } + this.listeners[event].forEach(function(listener) { + listener(event, this); + }.bind(this)); + } + addListener(event, f) { + this.listeners[event] = this.listeners[event] || []; + this.listeners[event].push(f); + } + removeListener(event, f) { + if (!(event in this.listeners)) { + return false; + } + var index = this.listeners[event].indexOf(f); + if (index >= 0) { + this.listeners[event].splice(index, 1); + } + } +}
\ No newline at end of file |