diff options
author | Maximilian Hils <git@maximilianhils.com> | 2014-12-25 02:03:55 +0100 |
---|---|---|
committer | Maximilian Hils <git@maximilianhils.com> | 2014-12-25 02:03:55 +0100 |
commit | 7ed6f10e357ef7b08fc94b46a0901218e62f418e (patch) | |
tree | 02f1d8b9401643f82c715c6cc55c9ebb1b36eaf2 | |
parent | bd1c04ac56c57e13edb2e20aeea7226ab554f516 (diff) | |
download | mitmproxy-7ed6f10e357ef7b08fc94b46a0901218e62f418e.tar.gz mitmproxy-7ed6f10e357ef7b08fc94b46a0901218e62f418e.tar.bz2 mitmproxy-7ed6f10e357ef7b08fc94b46a0901218e62f418e.zip |
web: much ui work, such christmas
-rw-r--r-- | libmproxy/protocol/http.py | 3 | ||||
-rw-r--r-- | libmproxy/web/app.py | 1 | ||||
-rw-r--r-- | libmproxy/web/static/css/app.css | 17 | ||||
-rw-r--r-- | libmproxy/web/static/js/app.js | 201 | ||||
-rw-r--r-- | web/src/css/flowtable.less | 153 | ||||
-rw-r--r-- | web/src/css/tabs.less | 6 | ||||
-rw-r--r-- | web/src/js/components/flowdetail.jsx.js | 79 | ||||
-rw-r--r-- | web/src/js/components/flowtable-columns.jsx.js | 6 | ||||
-rw-r--r-- | web/src/js/components/header.jsx.js | 103 | ||||
-rw-r--r-- | web/src/js/components/mainview.jsx.js | 13 |
10 files changed, 400 insertions, 182 deletions
diff --git a/libmproxy/protocol/http.py b/libmproxy/protocol/http.py index c6e67498..da7c4240 100644 --- a/libmproxy/protocol/http.py +++ b/libmproxy/protocol/http.py @@ -309,7 +309,8 @@ class HTTPRequest(HTTPMessage): host=str, port=int, path=str, - form_out=str + form_out=str, + is_replay=bool ) @classmethod diff --git a/libmproxy/web/app.py b/libmproxy/web/app.py index c90922cb..d3c44bf7 100644 --- a/libmproxy/web/app.py +++ b/libmproxy/web/app.py @@ -112,6 +112,7 @@ class Settings(RequestHandler): def get(self): self.write(dict( data=dict( + mode=str(self.master.server.config.mode), intercept=self.state.intercept_txt ) )) diff --git a/libmproxy/web/static/css/app.css b/libmproxy/web/static/css/app.css index 93ace709..2ec275a3 100644 --- a/libmproxy/web/static/css/app.css +++ b/libmproxy/web/static/css/app.css @@ -122,6 +122,11 @@ body, padding: 0px 7px; margin: 2px 2px -1px; } +.nav-tabs-sm a.nav-action { + float: right; + padding: 0; + margin: 1px 0 0px; +} header { background-color: white; } @@ -183,6 +188,12 @@ header .menu { .flow-table tr.intercepted.has-response .col-time { color: #ff8000; } +.flow-table .fa { + line-height: inherit; +} +.flow-table .fa.pull-right { + margin-left: 0; +} .flow-table .col-tls { width: 10px; } @@ -192,6 +203,12 @@ header .menu { .flow-table .col-icon { width: 32px; } +.flow-table .col-path .fa-repeat { + color: green; +} +.flow-table .col-path .fa-pause { + color: #ff8000; +} .flow-table .col-method { width: 60px; } diff --git a/libmproxy/web/static/js/app.js b/libmproxy/web/static/js/app.js index ff961294..92f48d14 100644 --- a/libmproxy/web/static/js/app.js +++ b/libmproxy/web/static/js/app.js @@ -2742,23 +2742,9 @@ var FilterInput = React.createClass({displayName: 'FilterInput', var MainMenu = React.createClass({displayName: 'MainMenu', mixins: [Navigation, State], statics: { - title: "Traffic", + title: "Start", route: "flows" }, - toggleEventLog: function () { - var d = {}; - - if(this.getQuery()[Query.SHOW_EVENTLOG]){ - d[Query.SHOW_EVENTLOG] = undefined; - } else { - d[Query.SHOW_EVENTLOG] = "t"; // any non-false value will do it, keep it short - } - - this.setQuery(d); - }, - clearFlows: function () { - FlowActions.clear(); - }, onFilterChange: function (val) { var d = {}; d[Query.FILTER] = val; @@ -2776,22 +2762,9 @@ var MainMenu = React.createClass({displayName: 'MainMenu', var filter = this.getQuery()[Query.FILTER] || ""; var highlight = this.getQuery()[Query.HIGHLIGHT] || ""; var intercept = this.props.settings.intercept || ""; - var showEventLog = this.getQuery()[Query.SHOW_EVENTLOG]; return ( React.createElement("div", null, - React.createElement("button", { - className: "btn " + (showEventLog ? "btn-primary" : "btn-default"), - onClick: this.toggleEventLog}, - React.createElement("i", {className: "fa fa-database"}), - " Display Event Log" - ), - React.createElement("span", null, " "), - React.createElement("button", {className: "btn btn-default", onClick: this.clearFlows}, - React.createElement("i", {className: "fa fa-eraser"}), - " Clear Flows" - ), - React.createElement("span", null, " "), React.createElement("form", {className: "form-inline", style: {display: "inline"}}, React.createElement(FilterInput, { placeholder: "Filter", @@ -2820,13 +2793,36 @@ var MainMenu = React.createClass({displayName: 'MainMenu', }); -var ToolsMenu = React.createClass({displayName: 'ToolsMenu', +var ViewMenu = React.createClass({displayName: 'ViewMenu', statics: { - title: "Tools", + title: "View", route: "flows" }, + mixins: [Navigation, State], + toggleEventLog: function () { + var d = {}; + + if (this.getQuery()[Query.SHOW_EVENTLOG]) { + d[Query.SHOW_EVENTLOG] = undefined; + } else { + d[Query.SHOW_EVENTLOG] = "t"; // any non-false value will do it, keep it short + } + + this.setQuery(d); + }, render: function () { - return React.createElement("div", null, "Tools Menu"); + var showEventLog = this.getQuery()[Query.SHOW_EVENTLOG]; + return ( + React.createElement("div", null, + React.createElement("button", { + className: "btn " + (showEventLog ? "btn-primary" : "btn-default"), + onClick: this.toggleEventLog}, + React.createElement("i", {className: "fa fa-database"}), + " Show Eventlog" + ), + React.createElement("span", null, " ") + ) + ); } }); @@ -2863,7 +2859,9 @@ var FileMenu = React.createClass({displayName: 'FileMenu', }, handleNewClick: function (e) { e.preventDefault(); - console.error("unimplemented: handleNewClick"); + if (confirm("Delete all flows?")) { + FlowActions.clear(); + } }, handleOpenClick: function (e) { e.preventDefault(); @@ -2890,25 +2888,34 @@ var FileMenu = React.createClass({displayName: 'FileMenu', "New" ) ), - React.createElement("li", null, - React.createElement("a", {href: "#", onClick: this.handleOpenClick}, - React.createElement("i", {className: "fa fa-fw fa-folder-open"}), - "Open" - ) - ), - React.createElement("li", null, - React.createElement("a", {href: "#", onClick: this.handleSaveClick}, - React.createElement("i", {className: "fa fa-fw fa-save"}), - "Save" - ) - ), React.createElement("li", {role: "presentation", className: "divider"}), React.createElement("li", null, - React.createElement("a", {href: "#", onClick: this.handleShutdownClick}, - React.createElement("i", {className: "fa fa-fw fa-plug"}), - "Shutdown" + React.createElement("a", {href: "http://mitm.it/", target: "_blank"}, + React.createElement("i", {className: "fa fa-fw fa-lock"}), + "Install Certificates..." ) ) + /* + <li> + <a href="#" onClick={this.handleOpenClick}> + <i className="fa fa-fw fa-folder-open"></i> + Open + </a> + </li> + <li> + <a href="#" onClick={this.handleSaveClick}> + <i className="fa fa-fw fa-save"></i> + Save + </a> + </li> + <li role="presentation" className="divider"></li> + <li> + <a href="#" onClick={this.handleShutdownClick}> + <i className="fa fa-fw fa-plug"></i> + Shutdown + </a> + </li> + */ ) ) ); @@ -2916,7 +2923,7 @@ var FileMenu = React.createClass({displayName: 'FileMenu', }); -var header_entries = [MainMenu, ToolsMenu, ReportsMenu]; +var header_entries = [MainMenu, ViewMenu /*, ReportsMenu */]; var Header = React.createClass({displayName: 'Header', @@ -3032,7 +3039,11 @@ var PathColumn = React.createClass({displayName: 'PathColumn', }, render: function () { var flow = this.props.flow; - return React.createElement("td", {className: "col-path"}, flow.request.scheme + "://" + flow.request.host + flow.request.path); + return React.createElement("td", {className: "col-path"}, + flow.request.is_replay ? React.createElement("i", {className: "fa fa-fw fa-repeat pull-right"}) : null, + flow.intercepted ? React.createElement("i", {className: "fa fa-fw fa-pause pull-right"}) : null, + flow.request.scheme + "://" + flow.request.host + flow.request.path + ); } }); @@ -3247,10 +3258,75 @@ var FlowTable = React.createClass({displayName: 'FlowTable', } }); +var DeleteButton = React.createClass({displayName: 'DeleteButton', + onClick: function (e) { + e.preventDefault(); + FlowActions.delete(this.props.flow); + }, + render: function () { + return ( + React.createElement("a", {title: "[d]elete Flow", + href: "#", + className: "nav-action", + onClick: this.onClick}, + React.createElement("i", {className: "fa fa-fw fa-trash"}) + ) + ); + } +}); +var DuplicateButton = React.createClass({displayName: 'DuplicateButton', + onClick: function (e) { + e.preventDefault(); + FlowActions.duplicate(this.props.flow); + }, + render: function () { + return ( + React.createElement("a", {title: "[D]uplicate Flow", + href: "#", + className: "nav-action", + onClick: this.onClick}, + React.createElement("i", {className: "fa fa-fw fa-edit"}) + ) + ); + } +}); +var ReplayButton = React.createClass({displayName: 'ReplayButton', + onClick: function (e) { + e.preventDefault(); + FlowActions.replay(this.props.flow); + }, + render: function () { + return ( + React.createElement("a", {title: "[r]eplay Flow", + href: "#", + className: "nav-action", + onClick: this.onClick}, + React.createElement("i", {className: "fa fa-fw fa-close"}) + ) + ); + } +}); +var AcceptButton = React.createClass({displayName: 'AcceptButton', + onClick: function (e) { + e.preventDefault(); + FlowActions.accept(this.props.flow); + }, + render: function () { + return ( + React.createElement("a", {title: "[a]ccept (resume) Flow", + href: "#", + className: "nav-action", + onClick: this.onClick}, + React.createElement("i", {className: "fa fa-fw fa-play"}) + ) + ); + } +}); var FlowDetailNav = React.createClass({displayName: 'FlowDetailNav', render: function () { + var flow = this.props.flow; - var items = this.props.tabs.map(function (e) { + var tabs = this.props.tabs.map(function (e) { var str = e.charAt(0).toUpperCase() + e.slice(1); var className = this.props.active === e ? "active" : ""; var onClick = function (event) { @@ -3262,9 +3338,14 @@ var FlowDetailNav = React.createClass({displayName: 'FlowDetailNav', className: className, onClick: onClick}, str); }.bind(this)); + return ( React.createElement("nav", {ref: "head", className: "nav-tabs nav-tabs-sm"}, - items + tabs, + React.createElement(DeleteButton, {flow: flow}), + React.createElement(DuplicateButton, {flow: flow}), + React.createElement(ReplayButton, {flow: flow}), + flow.intercepted ? React.createElement(AcceptButton, {flow: this.props.flow}) : null ) ); } @@ -3353,7 +3434,9 @@ var FlowDetailError = React.createClass({displayName: 'FlowDetailError', React.createElement("section", null, React.createElement("div", {className: "alert alert-warning"}, flow.error.msg, - React.createElement("div", null, React.createElement("small", null, formatTimeStamp(flow.error.timestamp) )) + React.createElement("div", null, + React.createElement("small", null, formatTimeStamp(flow.error.timestamp) ) + ) ) ) ); @@ -3590,6 +3673,7 @@ var FlowDetail = React.createClass({displayName: 'FlowDetail', return ( React.createElement("div", {className: "flow-detail", onScroll: this.adjustHead}, React.createElement(FlowDetailNav, {ref: "head", + flow: flow, tabs: tabs, active: active, selectTab: this.selectTab}), @@ -3644,6 +3728,7 @@ var MainView = React.createClass({displayName: 'MainView', view.addListener("recalculate", this.onRecalculate); view.addListener("add update remove", this.onUpdate); + view.addListener("remove", this.onRemove); }, onRecalculate: function () { this.forceUpdate(); @@ -3657,6 +3742,12 @@ var MainView = React.createClass({displayName: 'MainView', this.forceUpdate(); } }, + onRemove: function (flow_id, index) { + if (flow_id === this.getParams().flowId) { + var flow_to_select = this.state.view.list[Math.min(index, this.state.view.list.length -1)]; + this.selectFlow(flow_to_select); + } + }, closeView: function () { this.state.view.close(); }, @@ -3706,7 +3797,7 @@ var MainView = React.createClass({displayName: 'MainView', }, onKeyDown: function (e) { var flow = this.getSelected(); - if(e.ctrlKey){ + if (e.ctrlKey) { return; } switch (e.keyCode) { @@ -3757,8 +3848,6 @@ var MainView = React.createClass({displayName: 'MainView', if (e.shiftKey) { FlowActions.duplicate(flow); } else { - var last_flow = this.state.view.index(flow) === this.state.view.list.length - 1; - this.selectFlowRelative(last_flow ? -1 : +1); FlowActions.delete(flow); } } @@ -3771,7 +3860,7 @@ var MainView = React.createClass({displayName: 'MainView', } break; case Key.R: - if(!e.shiftKey && flow){ + if (!e.shiftKey && flow) { FlowActions.replay(flow); } break; diff --git a/web/src/css/flowtable.less b/web/src/css/flowtable.less index 38f5e0e8..9988f1a8 100644 --- a/web/src/css/flowtable.less +++ b/web/src/css/flowtable.less @@ -1,78 +1,95 @@ .flow-table { - width: 100%; - overflow: auto; + width: 100%; + overflow: auto; - table { - width: 100%; - table-layout: fixed; - } + table { + width: 100%; + table-layout: fixed; + } - thead { - background-color: #F2F2F2; - line-height: 23px; - } + thead { + background-color: #F2F2F2; + line-height: 23px; + } - th { - font-weight: normal; - box-shadow: 0 1px 0 #a6a6a6; - } + th { + font-weight: normal; + box-shadow: 0 1px 0 #a6a6a6; + } - tr { - cursor: pointer; + tr { + cursor: pointer; - &:nth-child(even) { - background-color : rgba(0,0,0,0.05); - } - &.selected { - background-color: hsla(209, 52%, 84%, 0.5) !important; - } - &.highlighted { - background-color: hsla(48, 100%, 50%, 0.4); - } - &.highlighted:nth-child(even) { - background-color: hsla(48, 100%, 50%, 0.5); - } - } + &:nth-child(even) { + background-color: rgba(0, 0, 0, 0.05); + } + &.selected { + background-color: hsla(209, 52%, 84%, 0.5) !important; + } + &.highlighted { + background-color: hsla(48, 100%, 50%, 0.4); + } + &.highlighted:nth-child(even) { + background-color: hsla(48, 100%, 50%, 0.5); + } + } - td { - overflow: hidden; - white-space: nowrap; - text-overflow: ellipsis; - } + td { + overflow: hidden; + white-space: nowrap; + text-overflow: ellipsis; + } - tr.intercepted:not(.has-response) { - .col-path, .col-method { - color: hsl(30, 100%, 50%); - } - } - tr.intercepted.has-response { - .col-status, .col-size, .col-time { - color: hsl(30, 100%, 50%); - } - } + @interceptorange: hsl(30, 100%, 50%); - .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-size { - width: 70px; - } - .col-time { - width: 50px; - } - td.col-time, td.col-size { - text-align: right; - } + tr.intercepted:not(.has-response) { + .col-path, .col-method { + color: @interceptorange; + } + } + tr.intercepted.has-response { + .col-status, .col-size, .col-time { + color: @interceptorange; + } + } + + .fa { + line-height: inherit; + &.pull-right { + margin-left: 0; + } + } + + .col-tls { + width: 10px; + } + .col-tls-https { + background-color: rgba(0, 185, 0, 0.5); + } + .col-icon { + width: 32px; + } + .col-path { + .fa-repeat { + color: green; + } + .fa-pause { + color: @interceptorange; + } + } + .col-method { + width: 60px; + } + .col-status { + width: 50px; + } + .col-size { + width: 70px; + } + .col-time { + width: 50px; + } + td.col-time, td.col-size { + text-align: right; + } }
\ No newline at end of file diff --git a/web/src/css/tabs.less b/web/src/css/tabs.less index 36bc5b68..43f7264e 100644 --- a/web/src/css/tabs.less +++ b/web/src/css/tabs.less @@ -40,6 +40,10 @@ a { padding: 0px 7px; margin: 2px 2px -1px; - + } + a.nav-action { + float: right; + padding: 0; + margin: 1px 0 0px; } }
\ 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 5727ec75..dfc0099e 100644 --- a/web/src/js/components/flowdetail.jsx.js +++ b/web/src/js/components/flowdetail.jsx.js @@ -1,7 +1,72 @@ +var DeleteButton = React.createClass({ + onClick: function (e) { + e.preventDefault(); + FlowActions.delete(this.props.flow); + }, + render: function () { + return ( + <a title="[d]elete Flow" + href="#" + className="nav-action" + onClick={this.onClick}> + <i className="fa fa-fw fa-trash"></i> + </a> + ); + } +}); +var DuplicateButton = React.createClass({ + onClick: function (e) { + e.preventDefault(); + FlowActions.duplicate(this.props.flow); + }, + render: function () { + return ( + <a title="[D]uplicate Flow" + href="#" + className="nav-action" + onClick={this.onClick}> + <i className="fa fa-fw fa-edit"></i> + </a> + ); + } +}); +var ReplayButton = React.createClass({ + onClick: function (e) { + e.preventDefault(); + FlowActions.replay(this.props.flow); + }, + render: function () { + return ( + <a title="[r]eplay Flow" + href="#" + className="nav-action" + onClick={this.onClick}> + <i className="fa fa-fw fa-close"></i> + </a> + ); + } +}); +var AcceptButton = React.createClass({ + onClick: function (e) { + e.preventDefault(); + FlowActions.accept(this.props.flow); + }, + render: function () { + return ( + <a title="[a]ccept (resume) Flow" + href="#" + className="nav-action" + onClick={this.onClick}> + <i className="fa fa-fw fa-play"></i> + </a> + ); + } +}); var FlowDetailNav = React.createClass({ render: function () { + var flow = this.props.flow; - var items = this.props.tabs.map(function (e) { + var tabs = this.props.tabs.map(function (e) { var str = e.charAt(0).toUpperCase() + e.slice(1); var className = this.props.active === e ? "active" : ""; var onClick = function (event) { @@ -13,9 +78,14 @@ var FlowDetailNav = React.createClass({ className={className} onClick={onClick}>{str}</a>; }.bind(this)); + return ( <nav ref="head" className="nav-tabs nav-tabs-sm"> - {items} + {tabs} + <DeleteButton flow={flow}/> + <DuplicateButton flow={flow}/> + <ReplayButton flow={flow}/> + { flow.intercepted ? <AcceptButton flow={this.props.flow}/> : null } </nav> ); } @@ -104,7 +174,9 @@ var FlowDetailError = React.createClass({ <section> <div className="alert alert-warning"> {flow.error.msg} - <div><small>{ formatTimeStamp(flow.error.timestamp) }</small></div> + <div> + <small>{ formatTimeStamp(flow.error.timestamp) }</small> + </div> </div> </section> ); @@ -341,6 +413,7 @@ var FlowDetail = React.createClass({ return ( <div className="flow-detail" onScroll={this.adjustHead}> <FlowDetailNav ref="head" + flow={flow} tabs={tabs} active={active} selectTab={this.selectTab}/> diff --git a/web/src/js/components/flowtable-columns.jsx.js b/web/src/js/components/flowtable-columns.jsx.js index 1aa256c4..9162e077 100644 --- a/web/src/js/components/flowtable-columns.jsx.js +++ b/web/src/js/components/flowtable-columns.jsx.js @@ -66,7 +66,11 @@ var PathColumn = React.createClass({ }, render: function () { var flow = this.props.flow; - return <td className="col-path">{flow.request.scheme + "://" + flow.request.host + flow.request.path}</td>; + return <td className="col-path"> + {flow.request.is_replay ? <i className="fa fa-fw fa-repeat pull-right"></i> : null} + {flow.intercepted ? <i className="fa fa-fw fa-pause pull-right"></i> : null} + {flow.request.scheme + "://" + flow.request.host + flow.request.path} + </td>; } }); diff --git a/web/src/js/components/header.jsx.js b/web/src/js/components/header.jsx.js index 9e6b8f2f..e1016950 100644 --- a/web/src/js/components/header.jsx.js +++ b/web/src/js/components/header.jsx.js @@ -111,23 +111,9 @@ var FilterInput = React.createClass({ var MainMenu = React.createClass({ mixins: [Navigation, State], statics: { - title: "Traffic", + title: "Start", route: "flows" }, - toggleEventLog: function () { - var d = {}; - - if(this.getQuery()[Query.SHOW_EVENTLOG]){ - d[Query.SHOW_EVENTLOG] = undefined; - } else { - d[Query.SHOW_EVENTLOG] = "t"; // any non-false value will do it, keep it short - } - - this.setQuery(d); - }, - clearFlows: function () { - FlowActions.clear(); - }, onFilterChange: function (val) { var d = {}; d[Query.FILTER] = val; @@ -145,22 +131,9 @@ var MainMenu = React.createClass({ var filter = this.getQuery()[Query.FILTER] || ""; var highlight = this.getQuery()[Query.HIGHLIGHT] || ""; var intercept = this.props.settings.intercept || ""; - var showEventLog = this.getQuery()[Query.SHOW_EVENTLOG]; return ( <div> - <button - className={"btn " + (showEventLog ? "btn-primary" : "btn-default")} - onClick={this.toggleEventLog}> - <i className="fa fa-database"></i> - Display Event Log - </button> - <span> </span> - <button className="btn btn-default" onClick={this.clearFlows}> - <i className="fa fa-eraser"></i> - Clear Flows - </button> - <span> </span> <form className="form-inline" style={{display: "inline"}}> <FilterInput placeholder="Filter" @@ -189,13 +162,36 @@ var MainMenu = React.createClass({ }); -var ToolsMenu = React.createClass({ +var ViewMenu = React.createClass({ statics: { - title: "Tools", + title: "View", route: "flows" }, + mixins: [Navigation, State], + toggleEventLog: function () { + var d = {}; + + if (this.getQuery()[Query.SHOW_EVENTLOG]) { + d[Query.SHOW_EVENTLOG] = undefined; + } else { + d[Query.SHOW_EVENTLOG] = "t"; // any non-false value will do it, keep it short + } + + this.setQuery(d); + }, render: function () { - return <div>Tools Menu</div>; + var showEventLog = this.getQuery()[Query.SHOW_EVENTLOG]; + return ( + <div> + <button + className={"btn " + (showEventLog ? "btn-primary" : "btn-default")} + onClick={this.toggleEventLog}> + <i className="fa fa-database"></i> + Show Eventlog + </button> + <span> </span> + </div> + ); } }); @@ -232,7 +228,9 @@ var FileMenu = React.createClass({ }, handleNewClick: function (e) { e.preventDefault(); - console.error("unimplemented: handleNewClick"); + if (confirm("Delete all flows?")) { + FlowActions.clear(); + } }, handleOpenClick: function (e) { e.preventDefault(); @@ -259,25 +257,34 @@ var FileMenu = React.createClass({ New </a> </li> - <li> - <a href="#" onClick={this.handleOpenClick}> - <i className="fa fa-fw fa-folder-open"></i> - Open - </a> - </li> - <li> - <a href="#" onClick={this.handleSaveClick}> - <i className="fa fa-fw fa-save"></i> - Save - </a> - </li> <li role="presentation" className="divider"></li> <li> - <a href="#" onClick={this.handleShutdownClick}> - <i className="fa fa-fw fa-plug"></i> - Shutdown + <a href="http://mitm.it/" target="_blank"> + <i className="fa fa-fw fa-lock"></i> + Install Certificates... </a> </li> + {/* + <li> + <a href="#" onClick={this.handleOpenClick}> + <i className="fa fa-fw fa-folder-open"></i> + Open + </a> + </li> + <li> + <a href="#" onClick={this.handleSaveClick}> + <i className="fa fa-fw fa-save"></i> + Save + </a> + </li> + <li role="presentation" className="divider"></li> + <li> + <a href="#" onClick={this.handleShutdownClick}> + <i className="fa fa-fw fa-plug"></i> + Shutdown + </a> + </li> + */} </ul> </div> ); @@ -285,7 +292,7 @@ var FileMenu = React.createClass({ }); -var header_entries = [MainMenu, ToolsMenu, ReportsMenu]; +var header_entries = [MainMenu, ViewMenu /*, ReportsMenu */]; var Header = React.createClass({ diff --git a/web/src/js/components/mainview.jsx.js b/web/src/js/components/mainview.jsx.js index 78415ad0..41f22a95 100644 --- a/web/src/js/components/mainview.jsx.js +++ b/web/src/js/components/mainview.jsx.js @@ -44,6 +44,7 @@ var MainView = React.createClass({ view.addListener("recalculate", this.onRecalculate); view.addListener("add update remove", this.onUpdate); + view.addListener("remove", this.onRemove); }, onRecalculate: function () { this.forceUpdate(); @@ -57,6 +58,12 @@ var MainView = React.createClass({ this.forceUpdate(); } }, + onRemove: function (flow_id, index) { + if (flow_id === this.getParams().flowId) { + var flow_to_select = this.state.view.list[Math.min(index, this.state.view.list.length -1)]; + this.selectFlow(flow_to_select); + } + }, closeView: function () { this.state.view.close(); }, @@ -106,7 +113,7 @@ var MainView = React.createClass({ }, onKeyDown: function (e) { var flow = this.getSelected(); - if(e.ctrlKey){ + if (e.ctrlKey) { return; } switch (e.keyCode) { @@ -157,8 +164,6 @@ var MainView = React.createClass({ if (e.shiftKey) { FlowActions.duplicate(flow); } else { - var last_flow = this.state.view.index(flow) === this.state.view.list.length - 1; - this.selectFlowRelative(last_flow ? -1 : +1); FlowActions.delete(flow); } } @@ -171,7 +176,7 @@ var MainView = React.createClass({ } break; case Key.R: - if(!e.shiftKey && flow){ + if (!e.shiftKey && flow) { FlowActions.replay(flow); } break; |