aboutsummaryrefslogtreecommitdiffstats
path: root/web
diff options
context:
space:
mode:
Diffstat (limited to 'web')
-rw-r--r--web/gulpfile.js19
-rw-r--r--web/src/css/app.less1
-rw-r--r--web/src/css/eventlog.less11
-rw-r--r--web/src/css/footer.less4
-rw-r--r--web/src/css/header.less9
-rw-r--r--web/src/css/layout.less33
-rw-r--r--web/src/index.html3
-rw-r--r--web/src/js/Connection.es6.js33
-rw-r--r--web/src/js/Dispatcher.es6.js36
-rw-r--r--web/src/js/actions.es6.js14
-rw-r--r--web/src/js/app.js5
-rw-r--r--web/src/js/components/EventLog.react.js13
-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.js88
-rw-r--r--web/src/js/components/ProxyApp.react.js35
-rw-r--r--web/src/js/components/TrafficTable.react.js38
-rw-r--r--web/src/js/datastructures.es6.js105
-rw-r--r--web/src/js/header.react.js72
-rw-r--r--web/src/js/mitmproxy.react.js82
-rw-r--r--web/src/js/stores/EventLogStore.es6.js42
-rw-r--r--web/src/js/stores/SettingsStore.es6.js42
-rw-r--r--web/src/js/stores/base.es6.js26
22 files changed, 409 insertions, 304 deletions
diff --git a/web/gulpfile.js b/web/gulpfile.js
index bae4955b..68a5e479 100644
--- a/web/gulpfile.js
+++ b/web/gulpfile.js
@@ -32,10 +32,19 @@ var path = {
'vendor/react-bootstrap/react-bootstrap.js'
],
app: [
- 'js/datastructures.es6.js',
- 'js/footer.react.js',
- 'js/header.react.js',
- 'js/mitmproxy.react.js',
+ 'js/Dispatcher.es6.js',
+ 'js/actions.es6.js',
+ 'js/stores/base.es6.js',
+ 'js/stores/SettingsStore.es6.js',
+ 'js/stores/EventLogStore.es6.js',
+ 'js/Connection.es6.js',
+ 'js/connection.es6.js',
+ 'js/components/Header.react.js',
+ 'js/components/TrafficTable.react.js',
+ 'js/components/EventLog.react.js',
+ 'js/components/Footer.react.js',
+ 'js/components/ProxyApp.react.js',
+ 'js/app.js',
],
},
css: {
@@ -116,4 +125,4 @@ gulp.task("default", ["dev"], function () {
gulp.watch(["src/js/**"], ["scripts-app-dev", "jshint"]);
gulp.watch(["src/css/**"], ["styles-app-dev"]);
gulp.watch(["src/*.html"], ["html"]);
-}); \ No newline at end of file
+});
diff --git a/web/src/css/app.less b/web/src/css/app.less
index ff3c614c..ce9d9149 100644
--- a/web/src/css/app.less
+++ b/web/src/css/app.less
@@ -9,5 +9,6 @@ html {
@import (less) "layout.less";
@import (less) "header.less";
+@import (less) "eventlog.less";
@import (less) "footer.less";
diff --git a/web/src/css/eventlog.less b/web/src/css/eventlog.less
new file mode 100644
index 00000000..0e97832b
--- /dev/null
+++ b/web/src/css/eventlog.less
@@ -0,0 +1,11 @@
+.eventlog {
+
+ flex: 0 0 auto;
+
+ pre {
+ margin: 0;
+ border-radius: 0;
+ height: 200px;
+ overflow: auto;
+ }
+} \ No newline at end of file
diff --git a/web/src/css/footer.less b/web/src/css/footer.less
index 69ab62ce..be7a1d76 100644
--- a/web/src/css/footer.less
+++ b/web/src/css/footer.less
@@ -1,4 +1,4 @@
footer {
- padding: 0 10px;
- //text-align: center;
+ box-shadow: 0 -1px 3px lightgray;
+ padding: 0px 10px 3px;
} \ No newline at end of file
diff --git a/web/src/css/header.less b/web/src/css/header.less
index 4f4af121..69a947c5 100644
--- a/web/src/css/header.less
+++ b/web/src/css/header.less
@@ -42,15 +42,6 @@ header {
}
}
}
-
- &:before {
- content: " ";
- }
-
- &:after {
- clear: both;
- }
-
}
.menu {
diff --git a/web/src/css/layout.less b/web/src/css/layout.less
index 7c5f79b9..c8fad204 100644
--- a/web/src/css/layout.less
+++ b/web/src/css/layout.less
@@ -4,33 +4,16 @@ html, body, #container {
overflow: hidden;
}
-header, footer {
- display: block;
-}
-
-@headerheight: 153px;
-@footerheight: 25px;
-
#container {
- //Set padding on container so that #main can take 100% height
- //If we don't do it, the scrollbars will be too large.
- padding: @headerheight 0 @footerheight;
-}
+ display: flex;
+ flex-direction: column;
-header {
- height: @headerheight;
- //Substract #container padding
- margin-top: -@headerheight;
+ > header, > footer, > .eventlog {
+ flex: 0 0 auto;
+ }
}
#main {
- height: 100%;
- display: block;
- overflow-y: auto;
-}
-
-footer {
- //This starts at the beginning of the #container padding, all fine.
- height: @footerheight;
- line-height: @footerheight;
-}
+ flex: 1 1 auto;
+ overflow: auto;
+} \ No newline at end of file
diff --git a/web/src/index.html b/web/src/index.html
index 509ef1eb..6cef0d25 100644
--- a/web/src/index.html
+++ b/web/src/index.html
@@ -12,7 +12,4 @@
<body>
<div id="mitmproxy"></div>
</body>
-<script>
- app = React.renderComponent(routes, document.body);
-</script>
</html> \ No newline at end of file
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