diff options
| -rw-r--r-- | libmproxy/web/static/css/app.css | 43 | ||||
| -rw-r--r-- | libmproxy/web/static/js/app.js | 121 | ||||
| -rw-r--r-- | web/src/css/eventlog.less | 45 | ||||
| -rw-r--r-- | web/src/css/flowdetail.less | 14 | ||||
| -rw-r--r-- | web/src/css/layout.less | 2 | ||||
| -rw-r--r-- | web/src/js/actions.js | 7 | ||||
| -rw-r--r-- | web/src/js/components/eventlog.jsx.js | 102 | ||||
| -rw-r--r-- | web/src/js/components/flowdetail.jsx.js | 2 | ||||
| -rw-r--r-- | web/src/js/components/mainview.jsx.js | 1 | ||||
| -rw-r--r-- | web/src/js/connection.js | 6 | ||||
| -rw-r--r-- | web/src/js/stores/eventlogstore.js | 4 | 
11 files changed, 291 insertions, 56 deletions
diff --git a/libmproxy/web/static/css/app.css b/libmproxy/web/static/css/app.css index f0ed95dd..8cdfbac6 100644 --- a/libmproxy/web/static/css/app.css +++ b/libmproxy/web/static/css/app.css @@ -60,6 +60,7 @@ body,    flex: 1 1 auto;    display: flex;    flex-direction: row; +  outline: 0;  }  .main-view.vertical {    flex-direction: column; @@ -212,27 +213,61 @@ header .menu {    table-layout: fixed;    word-break: break-all;  } -.flow-detail table tr { +.flow-detail table tr:not(:first-child) {    border-top: 1px solid #f7f7f7;  }  .flow-detail table td {    vertical-align: top;  }  .connection-table td:first-child { -  width: 33%; +  width: 50%;    padding-right: 1em;  }  .header-table .header-name {    width: 33%;    padding-right: 1em;  } +.connection-table td, +.timing-table td { +  overflow: hidden; +  text-overflow: ellipsis; +  white-space: nowrap; +}  .eventlog { +  height: 200px; +  flex: 0 0 auto; +  display: flex; +  flex-direction: column; +} +.eventlog > div { +  background-color: #F2F2F2; +  padding: 0 5px;    flex: 0 0 auto; +} +.eventlog > pre { +  flex: 1 1 auto;    margin: 0;    border-radius: 0; -  height: 200px; -  overflow: auto; +  overflow-x: auto;    overflow-y: scroll; +  background-color: #fcfcfc; +} +.eventlog .fa-close { +  cursor: pointer; +  float: right; +  color: grey; +  padding: 3px 0; +  padding-left: 10px; +} +.eventlog .fa-close:hover { +  color: black; +} +.eventlog .label { +  cursor: pointer; +  vertical-align: middle; +  display: inline-block; +  margin-top: -2px; +  margin-left: 3px;  }  footer {    box-shadow: 0 -1px 3px #d3d3d3; diff --git a/libmproxy/web/static/js/app.js b/libmproxy/web/static/js/app.js index 4fb9f92d..fe317d7f 100644 --- a/libmproxy/web/static/js/app.js +++ b/libmproxy/web/static/js/app.js @@ -121,14 +121,15 @@ var SettingsActions = {      }  }; +var event_id = 0;  var EventLogActions = { -    add_event: function(message, level){ +    add_event: function(message){          AppDispatcher.dispatchViewAction({              type: ActionTypes.ADD_EVENT,              data: {                  message: message, -                level: level || "info", -                source: "ui" +                level: "web", +                id: "viewAction-"+event_id++              }          });      } @@ -263,7 +264,7 @@ _.extend(EventLogView.prototype, EventEmitter.prototype, {      },      add: function (entry) {          this.log.push(entry); -        if(this.log.length > 50){ +        if(this.log.length > 200){              this.log.shift();          }          this.emit("change"); @@ -334,7 +335,6 @@ _.extend(_EventLogStore.prototype, EventEmitter.prototype, {  var EventLogStore = new _EventLogStore();  AppDispatcher.register(EventLogStore.handle.bind(EventLogStore)); -  function FlowView(store, live) {      EventEmitter.call(this);      this._store = store; @@ -443,7 +443,7 @@ _Connection.prototype.openWebSocketConnection = function () {      ws.onclose = this.onclose.bind(this);  };  _Connection.prototype.onopen = function (open) { -    console.log("onopen", this, arguments); +    console.debug("onopen", this, arguments);  };  _Connection.prototype.onmessage = function (message) {      //AppDispatcher.dispatchServerAction(...); @@ -452,11 +452,11 @@ _Connection.prototype.onmessage = function (message) {  };  _Connection.prototype.onerror = function (error) {      EventLogActions.add_event("WebSocket Connection Error."); -    console.log("onerror", this, arguments); +    console.debug("onerror", this, arguments);  };  _Connection.prototype.onclose = function (close) {      EventLogActions.add_event("WebSocket Connection closed."); -    console.log("onclose", this, arguments); +    console.debug("onclose", this, arguments);  };  var Connection = new _Connection(location.origin + "/updates"); @@ -1139,7 +1139,7 @@ var Timing = React.createClass({displayName: 'Timing',          return (              React.DOM.div(null,               React.DOM.h4(null, "Timing"),  -            React.DOM.table(null,  +            React.DOM.table({className: "timing-table"},                   React.DOM.tbody(null,                       rows                  ) @@ -1214,7 +1214,6 @@ var MainView = React.createClass({displayName: 'MainView',          };      },      componentDidMount: function () { -        console.log("get view");          this.flowStore = FlowStore.getView();          this.flowStore.addListener("change",this.onFlowChange);      }, @@ -1334,7 +1333,32 @@ var MainView = React.createClass({displayName: 'MainView',  });  /** @jsx React.DOM */ -var EventLog = React.createClass({displayName: 'EventLog', +var LogMessage = React.createClass({displayName: 'LogMessage', +    render: function(){ +        var entry = this.props.entry; +        var indicator; +        switch(entry.level){ +            case "web": +                indicator = React.DOM.i({className: "fa fa-fw fa-html5"}); +                break; +            case "debug": +                indicator = React.DOM.i({className: "fa fa-fw fa-bug"}); +                break; +            default: +                indicator = React.DOM.i({className: "fa fa-fw fa-info"}); +        } +        return ( +            React.DOM.div(null,  +                indicator, " ", entry.message +            ) +        ); +    }, +    shouldComponentUpdate: function(){ +        return false; // log entries are immutable. +    } +}); + +var EventLogContents = React.createClass({displayName: 'EventLogContents',      mixins:[AutoScrollMixin],      getInitialState: function () {          return { @@ -1354,23 +1378,76 @@ var EventLog = React.createClass({displayName: 'EventLog',              log: this.log.getAll()          });      }, +    render: function () { +        var messages = this.state.log.map(function(row) { +            if(!this.props.filter[row.level]){ +                return null; +            } +            return LogMessage({key: row.id, entry: row}); +        }.bind(this)); +        return React.DOM.pre(null, messages); +    } +}); + +var ToggleFilter = React.createClass({displayName: 'ToggleFilter', +    toggle: function(){ +        return this.props.toggleLevel(this.props.name); +    }, +    render: function(){ +        var className = "label "; +        if (this.props.active) { +            className += "label-primary"; +        } else { +            className += "label-default"; +        } +        return ( +            React.DOM.a({ +                href: "#",  +                className: className,  +                onClick: this.toggle},  +                this.props.name +            ) +        ); +   }  +}); + +var EventLog = React.createClass({displayName: 'EventLog', +    getInitialState: function(){ +        return { +            filter: { +                "debug": false, +                "info": true, +                "web": true +            } +        }; +    },      close: function () {          SettingsActions.update({              showEventLog: false          });      }, +    toggleLevel: function(level){ +        var filter = this.state.filter; +        filter[level] = !filter[level]; +        this.setState({filter: filter}); +        return false; +    },      render: function () { -        var messages = this.state.log.map(function(row) { -            var indicator = null; -            if(row.source === "ui"){ -                indicator = React.DOM.i({className: "fa fa-html5"}); -            } -            return ( -                React.DOM.div({key: row.id},  -                    indicator, " ", row.message -                )); -        }); -        return React.DOM.pre({className: "eventlog"}, messages); +        return ( +            React.DOM.div({className: "eventlog"},  +                React.DOM.div(null,  +                    "Eventlog",  +                    React.DOM.div({className: "pull-right"},  +                        ToggleFilter({name: "debug", active: this.state.filter.debug, toggleLevel: this.toggleLevel}),  +                        ToggleFilter({name: "info", active: this.state.filter.info, toggleLevel: this.toggleLevel}),  +                        ToggleFilter({name: "web", active: this.state.filter.web, toggleLevel: this.toggleLevel}),  +                        React.DOM.i({onClick: this.close, className: "fa fa-close"}) +                    ) + +                ),  +                EventLogContents({filter: this.state.filter}) +            ) +        );      }  });  /** @jsx React.DOM */ diff --git a/web/src/css/eventlog.less b/web/src/css/eventlog.less index 993304cf..8b0a7647 100644 --- a/web/src/css/eventlog.less +++ b/web/src/css/eventlog.less @@ -1,10 +1,45 @@  .eventlog { +    height: 200px;      flex: 0 0 auto; -    margin: 0; -    border-radius: 0; -    height: 200px; -    overflow: auto; -    overflow-y: scroll; +    display: flex; +    flex-direction: column; + + +    > div { +        background-color: #F2F2F2; +        padding: 0 5px; +        flex: 0 0 auto; +    } + +    > pre { +        flex: 1 1 auto; + +        margin: 0; +        border-radius: 0; +        overflow-x: auto; +        overflow-y: scroll; +        background-color: #fcfcfc; +    } + + +    .fa-close { +        cursor: pointer; +        float: right; +        color: grey; +        padding: 3px 0; +        padding-left: 10px; +        &:hover { +            color: black; +        } +    } + +    .label { +        cursor: pointer; +        vertical-align: middle; +        display: inline-block; +        margin-top: -2px; +        margin-left: 3px; +    }  }
\ No newline at end of file diff --git a/web/src/css/flowdetail.less b/web/src/css/flowdetail.less index 94920caa..3a6191d4 100644 --- a/web/src/css/flowdetail.less +++ b/web/src/css/flowdetail.less @@ -35,9 +35,9 @@      word-break: break-all;      tr { -        //&:not(:first-child){ +        &:not(:first-child){          border-top: 1px solid #f7f7f7; -        //} +        }      }      td { @@ -51,7 +51,7 @@  .connection-table {      td:first-child { -        width: 33%; +        width: 50%;          padding-right: 1em;      }  } @@ -64,4 +64,12 @@      .header-value {      } +} + +.connection-table, .timing-table { +    td { +        overflow: hidden; +        text-overflow: ellipsis; +        white-space: nowrap; +    }  }
\ No newline at end of file diff --git a/web/src/css/layout.less b/web/src/css/layout.less index 510fde6a..f6807f24 100644 --- a/web/src/css/layout.less +++ b/web/src/css/layout.less @@ -19,6 +19,8 @@ html, body, #container {      display: flex;      flex-direction: row; +    outline: 0; +      &.vertical {          flex-direction: column;      } diff --git a/web/src/js/actions.js b/web/src/js/actions.js index 43fbfb14..9211403f 100644 --- a/web/src/js/actions.js +++ b/web/src/js/actions.js @@ -23,14 +23,15 @@ var SettingsActions = {      }  }; +var event_id = 0;  var EventLogActions = { -    add_event: function(message, level){ +    add_event: function(message){          AppDispatcher.dispatchViewAction({              type: ActionTypes.ADD_EVENT,              data: {                  message: message, -                level: level || "info", -                source: "ui" +                level: "web", +                id: "viewAction-"+event_id++              }          });      } diff --git a/web/src/js/components/eventlog.jsx.js b/web/src/js/components/eventlog.jsx.js index df212177..08a6dfb4 100644 --- a/web/src/js/components/eventlog.jsx.js +++ b/web/src/js/components/eventlog.jsx.js @@ -1,6 +1,31 @@  /** @jsx React.DOM */ -var EventLog = React.createClass({ +var LogMessage = React.createClass({ +    render: function(){ +        var entry = this.props.entry; +        var indicator; +        switch(entry.level){ +            case "web": +                indicator = <i className="fa fa-fw fa-html5"></i>; +                break; +            case "debug": +                indicator = <i className="fa fa-fw fa-bug"></i>; +                break; +            default: +                indicator = <i className="fa fa-fw fa-info"></i>; +        } +        return ( +            <div> +                { indicator } {entry.message} +            </div> +        ); +    }, +    shouldComponentUpdate: function(){ +        return false; // log entries are immutable. +    } +}); + +var EventLogContents = React.createClass({      mixins:[AutoScrollMixin],      getInitialState: function () {          return { @@ -20,22 +45,75 @@ var EventLog = React.createClass({              log: this.log.getAll()          });      }, +    render: function () { +        var messages = this.state.log.map(function(row) { +            if(!this.props.filter[row.level]){ +                return null; +            } +            return <LogMessage key={row.id} entry={row}/>; +        }.bind(this)); +        return <pre>{messages}</pre>; +    } +}); + +var ToggleFilter = React.createClass({ +    toggle: function(){ +        return this.props.toggleLevel(this.props.name); +    }, +    render: function(){ +        var className = "label "; +        if (this.props.active) { +            className += "label-primary"; +        } else { +            className += "label-default"; +        } +        return ( +            <a +                href="#" +                className={className} +                onClick={this.toggle}> +                {this.props.name} +            </a> +        ); +   }  +}); + +var EventLog = React.createClass({ +    getInitialState: function(){ +        return { +            filter: { +                "debug": false, +                "info": true, +                "web": true +            } +        }; +    },      close: function () {          SettingsActions.update({              showEventLog: false          });      }, +    toggleLevel: function(level){ +        var filter = this.state.filter; +        filter[level] = !filter[level]; +        this.setState({filter: filter}); +        return false; +    },      render: function () { -        var messages = this.state.log.map(function(row) { -            var indicator = null; -            if(row.source === "ui"){ -                indicator = <i className="fa fa-html5"></i>; -            } -            return ( -                <div key={row.id}> -                    { indicator } {row.message} -                </div>); -        }); -        return <pre className="eventlog">{messages}</pre>; +        return ( +            <div className="eventlog"> +                <div> +                    Eventlog +                    <div className="pull-right"> +                        <ToggleFilter name="debug" active={this.state.filter.debug} toggleLevel={this.toggleLevel}/> +                        <ToggleFilter name="info" active={this.state.filter.info} toggleLevel={this.toggleLevel}/> +                        <ToggleFilter name="web" active={this.state.filter.web} toggleLevel={this.toggleLevel}/> +                        <i onClick={this.close} className="fa fa-close"></i> +                    </div> + +                </div> +                <EventLogContents filter={this.state.filter}/> +            </div> +        );      }  });
\ No newline at end of file diff --git a/web/src/js/components/flowdetail.jsx.js b/web/src/js/components/flowdetail.jsx.js index ad1cfe67..3ba025a9 100644 --- a/web/src/js/components/flowdetail.jsx.js +++ b/web/src/js/components/flowdetail.jsx.js @@ -231,7 +231,7 @@ var Timing = React.createClass({          return (              <div>              <h4>Timing</h4> -            <table> +            <table className="timing-table">                  <tbody>                      {rows}                  </tbody> diff --git a/web/src/js/components/mainview.jsx.js b/web/src/js/components/mainview.jsx.js index d521635a..795b8136 100644 --- a/web/src/js/components/mainview.jsx.js +++ b/web/src/js/components/mainview.jsx.js @@ -7,7 +7,6 @@ var MainView = React.createClass({          };      },      componentDidMount: function () { -        console.log("get view");          this.flowStore = FlowStore.getView();          this.flowStore.addListener("change",this.onFlowChange);      }, diff --git a/web/src/js/connection.js b/web/src/js/connection.js index 640e9742..3edbfc20 100644 --- a/web/src/js/connection.js +++ b/web/src/js/connection.js @@ -14,7 +14,7 @@ _Connection.prototype.openWebSocketConnection = function () {      ws.onclose = this.onclose.bind(this);  };  _Connection.prototype.onopen = function (open) { -    console.log("onopen", this, arguments); +    console.debug("onopen", this, arguments);  };  _Connection.prototype.onmessage = function (message) {      //AppDispatcher.dispatchServerAction(...); @@ -23,11 +23,11 @@ _Connection.prototype.onmessage = function (message) {  };  _Connection.prototype.onerror = function (error) {      EventLogActions.add_event("WebSocket Connection Error."); -    console.log("onerror", this, arguments); +    console.debug("onerror", this, arguments);  };  _Connection.prototype.onclose = function (close) {      EventLogActions.add_event("WebSocket Connection closed."); -    console.log("onclose", this, arguments); +    console.debug("onclose", this, arguments);  };  var Connection = new _Connection(location.origin + "/updates"); diff --git a/web/src/js/stores/eventlogstore.js b/web/src/js/stores/eventlogstore.js index dc6dbbbf..e356959a 100644 --- a/web/src/js/stores/eventlogstore.js +++ b/web/src/js/stores/eventlogstore.js @@ -26,7 +26,7 @@ _.extend(EventLogView.prototype, EventEmitter.prototype, {      },      add: function (entry) {          this.log.push(entry); -        if(this.log.length > 50){ +        if(this.log.length > 200){              this.log.shift();          }          this.emit("change"); @@ -96,4 +96,4 @@ _.extend(_EventLogStore.prototype, EventEmitter.prototype, {  var EventLogStore = new _EventLogStore(); -AppDispatcher.register(EventLogStore.handle.bind(EventLogStore)); +AppDispatcher.register(EventLogStore.handle.bind(EventLogStore));
\ No newline at end of file  | 
