diff --git a/mitmproxy/web/static/app.js b/mitmproxy/web/static/app.js index f546e5f83..2fc1540b6 100644 --- a/mitmproxy/web/static/app.js +++ b/mitmproxy/web/static/app.js @@ -4,94 +4,121 @@ function EventEmitter(){this._events=this._events||{},this._maxListeners=this._m },{}],2:[function(require,module,exports){ "use strict";function _interopRequireDefault(e){return e&&e.__esModule?e:{"default":e}}Object.defineProperty(exports,"__esModule",{value:!0}),exports.Query=exports.FlowActions=exports.SettingsActions=exports.ConnectionActions=exports.StoreCmds=exports.ActionTypes=void 0;var _jquery=require("jquery"),_jquery2=_interopRequireDefault(_jquery),_dispatcher=require("./dispatcher.js"),_utils=require("./utils.js"),ActionTypes=exports.ActionTypes={CONNECTION_OPEN:"connection_open",CONNECTION_CLOSE:"connection_close",CONNECTION_ERROR:"connection_error",SETTINGS_STORE:"settings",EVENT_STORE:"events",FLOW_STORE:"flows"},StoreCmds=exports.StoreCmds={ADD:"add",UPDATE:"update",REMOVE:"remove",RESET:"reset"},ConnectionActions=exports.ConnectionActions={open:function(){_dispatcher.AppDispatcher.dispatchViewAction({type:ActionTypes.CONNECTION_OPEN})},close:function(){_dispatcher.AppDispatcher.dispatchViewAction({type:ActionTypes.CONNECTION_CLOSE})},error:function(){_dispatcher.AppDispatcher.dispatchViewAction({type:ActionTypes.CONNECTION_ERROR})}},SettingsActions=exports.SettingsActions={update:function(e){_jquery2["default"].ajax({type:"PUT",url:"/settings",contentType:"application/json",data:JSON.stringify(e)})}},FlowActions=exports.FlowActions={accept:function(e){_jquery2["default"].post("/flows/"+e.id+"/accept")},accept_all:function(){_jquery2["default"].post("/flows/accept")},"delete":function(e){_jquery2["default"].ajax({type:"DELETE",url:"/flows/"+e.id})},duplicate:function(e){_jquery2["default"].post("/flows/"+e.id+"/duplicate")},replay:function(e){_jquery2["default"].post("/flows/"+e.id+"/replay")},revert:function(e){_jquery2["default"].post("/flows/"+e.id+"/revert")},update:function(e,t){_jquery2["default"].ajax({type:"PUT",url:"/flows/"+e.id,contentType:"application/json",data:JSON.stringify(t)})},clear:function(){_jquery2["default"].post("/clear")},download:function(){return window.location="/flows/dump"},upload:function(e){var t=new FormData;t.append("file",e),(0,_utils.fetchApi)("/flows/dump",{method:"post",body:t})}},Query=exports.Query={SEARCH:"s",HIGHLIGHT:"h",SHOW_EVENTLOG:"e"}; -},{"./dispatcher.js":22,"./utils.js":32,"jquery":"jquery"}],3:[function(require,module,exports){ +},{"./dispatcher.js":31,"./utils.js":41,"jquery":"jquery"}],3:[function(require,module,exports){ "use strict";function _interopRequireDefault(e){return e&&e.__esModule?e:{"default":e}}var _react=require("react"),_react2=_interopRequireDefault(_react),_reactDom=require("react-dom"),_redux=require("redux"),_reactRedux=require("react-redux"),_reduxLogger=require("redux-logger"),_reduxLogger2=_interopRequireDefault(_reduxLogger),_reduxThunk=require("redux-thunk"),_reduxThunk2=_interopRequireDefault(_reduxThunk),_reactRouter=require("react-router"),_connection=require("./connection"),_connection2=_interopRequireDefault(_connection),_ProxyApp=require("./components/ProxyApp"),_ProxyApp2=_interopRequireDefault(_ProxyApp),_MainView=require("./components/MainView"),_MainView2=_interopRequireDefault(_MainView),_index=require("./ducks/index"),_index2=_interopRequireDefault(_index),_eventLog=require("./ducks/eventLog"),store=(0,_redux.createStore)(_index2["default"],(0,_redux.applyMiddleware)(_reduxThunk2["default"],(0,_reduxLogger2["default"])()));window.addEventListener("error",function(e){store.dispatch((0,_eventLog.addLogEntry)(e))}),document.addEventListener("DOMContentLoaded",function(){window.ws=new _connection2["default"]("/updates",store.dispatch),(0,_reactDom.render)(_react2["default"].createElement(_reactRedux.Provider,{store:store},_react2["default"].createElement(_reactRouter.Router,{history:_reactRouter.hashHistory},_react2["default"].createElement(_reactRouter.Redirect,{from:"/",to:"/flows"}),_react2["default"].createElement(_reactRouter.Route,{path:"/",component:_ProxyApp2["default"]},_react2["default"].createElement(_reactRouter.Route,{path:"flows",component:_MainView2["default"]}),_react2["default"].createElement(_reactRouter.Route,{path:"flows/:flowId/:detailTab",component:_MainView2["default"]})))),document.getElementById("mitmproxy"))}); -},{"./components/MainView":4,"./components/ProxyApp":5,"./connection":21,"./ducks/eventLog":23,"./ducks/index":25,"react":"react","react-dom":"react-dom","react-redux":"react-redux","react-router":"react-router","redux":"redux","redux-logger":"redux-logger","redux-thunk":"redux-thunk"}],4:[function(require,module,exports){ -"use strict";function _interopRequireDefault(e){return e&&e.__esModule?e:{"default":e}}function _classCallCheck(e,t){if(!(e instanceof t))throw new TypeError("Cannot call a class as a function")}function _possibleConstructorReturn(e,t){if(!e)throw new ReferenceError("this hasn't been initialised - super() hasn't been called");return!t||"object"!=typeof t&&"function"!=typeof t?e:t}function _inherits(e,t){if("function"!=typeof t&&null!==t)throw new TypeError("Super expression must either be null or a function, not "+typeof t);e.prototype=Object.create(t&&t.prototype,{constructor:{value:e,enumerable:!1,writable:!0,configurable:!0}}),t&&(Object.setPrototypeOf?Object.setPrototypeOf(e,t):e.__proto__=t)}Object.defineProperty(exports,"__esModule",{value:!0});var _createClass=function(){function e(e,t){for(var s=0;se&&(o=s.length-1),this.selectFlow(s[o])}},{key:"onMainKeyDown",value:function(e){var t=this.props.selectedFlow;if(!e.ctrlKey){switch(e.keyCode){case _utils.Key.K:case _utils.Key.UP:this.selectFlowRelative(-1);break;case _utils.Key.J:case _utils.Key.DOWN:this.selectFlowRelative(1);break;case _utils.Key.SPACE:case _utils.Key.PAGE_DOWN:this.selectFlowRelative(10);break;case _utils.Key.PAGE_UP:this.selectFlowRelative(-10);break;case _utils.Key.END:this.selectFlowRelative(1e10);break;case _utils.Key.HOME:this.selectFlowRelative(-1e10);break;case _utils.Key.ESC:this.selectFlow(null);break;case _utils.Key.H:case _utils.Key.LEFT:this.refs.flowDetails&&this.refs.flowDetails.nextTab(-1);break;case _utils.Key.L:case _utils.Key.TAB:case _utils.Key.RIGHT:this.refs.flowDetails&&this.refs.flowDetails.nextTab(1);break;case _utils.Key.C:e.shiftKey&&_actions.FlowActions.clear();break;case _utils.Key.D:t&&(e.shiftKey?_actions.FlowActions.duplicate(t):_actions.FlowActions["delete"](t));break;case _utils.Key.A:e.shiftKey?_actions.FlowActions.accept_all():t&&t.intercepted&&_actions.FlowActions.accept(t);break;case _utils.Key.R:!e.shiftKey&&t&&_actions.FlowActions.replay(t);break;case _utils.Key.V:e.shiftKey&&t&&t.modified&&_actions.FlowActions.revert(t);break;case _utils.Key.E:this.refs.flowDetails&&this.refs.flowDetails.promptEdit();break;case _utils.Key.SHIFT:break;default:return void console.debug("keydown",e.keyCode)}e.preventDefault()}}},{key:"render",value:function(){var e=this,t=this.props.selectedFlow;return _react2["default"].createElement("div",{className:"main-view"},_react2["default"].createElement(_flowtable2["default"],{ref:"flowTable",selectFlow:function(t){return e.selectFlow(t)},selected:t}),t&&[_react2["default"].createElement(_common.Splitter,{key:"splitter"}),_react2["default"].createElement(_index2["default"],{key:"flowDetails",ref:"flowDetails",tab:this.props.routeParams.detailTab,query:this.props.query,updateLocation:this.props.updateLocation,flow:t})])}}]),t}(_react.Component);exports["default"]=(0,_reactRedux.connect)(function(e){return{flows:e.flows.view,filter:e.flows.filter,highlight:e.flows.highlight,selectedFlow:e.flows.all.byId[e.flows.selected[0]]}},function(e){return{selectFlow:function(t){return e((0,_flows.selectFlow)(t))},setFilter:function(t){return e((0,_flows.setFilter)(t))},setHighlight:function(t){return e((0,_flows.setHighlight)(t))}}},void 0,{withRef:!0})(MainView); +},{"./components/MainView":18,"./components/ProxyApp":19,"./connection":30,"./ducks/eventLog":32,"./ducks/index":34,"react":"react","react-dom":"react-dom","react-redux":"react-redux","react-router":"react-router","redux":"redux","redux-logger":"redux-logger","redux-thunk":"redux-thunk"}],4:[function(require,module,exports){ +"use strict";function _interopRequireDefault(e){return e&&e.__esModule?e:{"default":e}}function EventLog(e){var t=e.filters,r=e.events,n=e.onToggleFilter,o=e.onClose;return _react2["default"].createElement("div",{className:"eventlog"},_react2["default"].createElement("div",null,"Eventlog",_react2["default"].createElement("div",{className:"pull-right"},["debug","info","web"].map(function(e){return _react2["default"].createElement(_common.ToggleButton,{key:e,text:e,checked:t[e],onToggle:function(){return n(e)}})}),_react2["default"].createElement("i",{onClick:o,className:"fa fa-close"}))),_react2["default"].createElement(_EventList2["default"],{events:r}))}Object.defineProperty(exports,"__esModule",{value:!0});var _react=require("react"),_react2=_interopRequireDefault(_react),_redux=require("redux"),_reactRedux=require("react-redux"),_eventLog=require("../ducks/eventLog"),_common=require("./common"),_EventList=require("./EventLog/EventList"),_EventList2=_interopRequireDefault(_EventList);EventLog.propTypes={filters:_react.PropTypes.object.isRequired,events:_react.PropTypes.array.isRequired,onToggleFilter:_react.PropTypes.func.isRequired,onClose:_react.PropTypes.func.isRequired},exports["default"]=(0,_reactRedux.connect)(function(e){return{filters:e.eventLog.filter,events:e.eventLog.filteredEvents}},function(e){return(0,_redux.bindActionCreators)({onClose:_eventLog.toggleEventLogVisibility,onToggleFilter:_eventLog.toggleEventLogFilter},e)})(EventLog); -},{"../actions.js":2,"../ducks/flows":24,"../utils.js":32,"./common.js":6,"./flowtable.js":10,"./flowview/index.js":13,"react":"react","react-redux":"react-redux"}],5:[function(require,module,exports){ -"use strict";function _interopRequireDefault(e){return e&&e.__esModule?e:{"default":e}}function _classCallCheck(e,t){if(!(e instanceof t))throw new TypeError("Cannot call a class as a function")}function _possibleConstructorReturn(e,t){if(!e)throw new ReferenceError("this hasn't been initialised - super() hasn't been called");return!t||"object"!=typeof t&&"function"!=typeof t?e:t}function _inherits(e,t){if("function"!=typeof t&&null!==t)throw new TypeError("Super expression must either be null or a function, not "+typeof t);e.prototype=Object.create(t&&t.prototype,{constructor:{value:e,enumerable:!1,writable:!0,configurable:!0}}),t&&(Object.setPrototypeOf?Object.setPrototypeOf(e,t):e.__proto__=t)}Object.defineProperty(exports,"__esModule",{value:!0});var _createClass=function(){function e(e,t){for(var r=0;rn-i?l.scrollTop=n-i:u>c+s&&(l.scrollTop=u-s)}}},{key:"componentWillReceiveProps",value:function(e){e.selected&&e.selected!==this.props.selected&&(this.shouldScrollIntoView=!0)}},{key:"onViewportUpdate",value:function(){var e=_reactDom2["default"].findDOMNode(this),t=e.scrollTop,o=(0,_VirtualScroll.calcVScroll)({viewportTop:t,viewportHeight:e.offsetHeight,itemCount:this.props.flows.length,rowHeight:this.props.rowHeight});this.state.viewportTop===t&&(0,_shallowequal2["default"])(this.state.vScroll,o)||this.setState({vScroll:o,viewportTop:t})}},{key:"render",value:function(){var e=this,t=this.state,o=t.vScroll,r=t.viewportTop,l=this.props,a=l.flows,i=l.selected,n=l.highlight,u=n?_filt2["default"].parse(n):function(){return!1};return _react2["default"].createElement("div",{className:"flow-table",onScroll:this.onViewportUpdate},_react2["default"].createElement("table",null,_react2["default"].createElement("thead",{ref:"head",style:{transform:"translateY("+r+"px)"}},_react2["default"].createElement(_FlowTableHead2["default"],null)),_react2["default"].createElement("tbody",null,_react2["default"].createElement("tr",{style:{height:o.paddingTop}}),a.slice(o.start,o.end).map(function(t){return _react2["default"].createElement(_FlowRow2["default"],{key:t.id,flow:t,selected:t===i,highlighted:u(t),onSelect:e.props.onSelect})}),_react2["default"].createElement("tr",{style:{height:o.paddingBottom}}))))}}]),t}(_react2["default"].Component);FlowTable.propTypes={onSelect:_react.PropTypes.func.isRequired,flows:_react.PropTypes.array.isRequired,rowHeight:_react.PropTypes.number,highlight:_react.PropTypes.string,selected:_react.PropTypes.object},FlowTable.defaultProps={rowHeight:32},exports["default"]=(0,_AutoScroll2["default"])(FlowTable); + +},{"../filt/filt":38,"./FlowTable/FlowRow":8,"./FlowTable/FlowTableHead":9,"./helpers/AutoScroll":27,"./helpers/VirtualScroll":28,"react":"react","react-dom":"react-dom","shallowequal":"shallowequal"}],7:[function(require,module,exports){ +"use strict";function _interopRequireDefault(e){return e&&e.__esModule?e:{"default":e}}function TLSColumn(e){var t=e.flow;return _react2["default"].createElement("td",{className:(0,_classnames2["default"])("col-tls","https"===t.request.scheme?"col-tls-https":"col-tls-http")})}function IconColumn(e){var t=e.flow;return _react2["default"].createElement("td",{className:"col-icon"},_react2["default"].createElement("div",{className:(0,_classnames2["default"])("resource-icon",IconColumn.getIcon(t))}))}function PathColumn(e){var t=e.flow;return _react2["default"].createElement("td",{className:"col-path"},t.request.is_replay&&_react2["default"].createElement("i",{className:"fa fa-fw fa-repeat pull-right"}),t.intercepted&&_react2["default"].createElement("i",{className:"fa fa-fw fa-pause pull-right"}),_utils.RequestUtils.pretty_url(t.request))}function MethodColumn(e){var t=e.flow;return _react2["default"].createElement("td",{className:"col-method"},t.request.method)}function StatusColumn(e){var t=e.flow;return _react2["default"].createElement("td",{className:"col-status"},t.response&&t.response.status_code)}function SizeColumn(e){var t=e.flow;return _react2["default"].createElement("td",{className:"col-size"},(0,_utils2.formatSize)(SizeColumn.getTotalSize(t)))}function TimeColumn(e){var t=e.flow;return _react2["default"].createElement("td",{className:"col-time"},t.response?(0,_utils2.formatTimeDelta)(1e3*(t.response.timestamp_end-t.request.timestamp_start)):"...")}Object.defineProperty(exports,"__esModule",{value:!0}),exports.TLSColumn=TLSColumn,exports.IconColumn=IconColumn,exports.PathColumn=PathColumn,exports.MethodColumn=MethodColumn,exports.StatusColumn=StatusColumn,exports.SizeColumn=SizeColumn,exports.TimeColumn=TimeColumn;var _react=require("react"),_react2=_interopRequireDefault(_react),_classnames=require("classnames"),_classnames2=_interopRequireDefault(_classnames),_utils=require("../../flow/utils.js"),_utils2=require("../../utils.js");TLSColumn.sortKeyFun=function(e){return e.request.scheme},TLSColumn.headerClass="col-tls",TLSColumn.headerName="",IconColumn.headerClass="col-icon",IconColumn.headerName="",IconColumn.getIcon=function(e){if(!e.response)return"resource-icon-plain";var t=_utils.ResponseUtils.getContentType(e.response)||"";return 304===e.response.status_code?"resource-icon-not-modified":300<=e.response.status_code&&e.response.status_code<400?"resource-icon-redirect":t.indexOf("image")>=0?"resource-icon-image":t.indexOf("javascript")>=0?"resource-icon-js":t.indexOf("css")>=0?"resource-icon-css":t.indexOf("html")>=0?"resource-icon-document":"resource-icon-plain"},PathColumn.sortKeyFun=function(e){return _utils.RequestUtils.pretty_url(e.request)},PathColumn.headerClass="col-path",PathColumn.headerName="Path",MethodColumn.sortKeyFun=function(e){return e.request.method},MethodColumn.headerClass="col-method",MethodColumn.headerName="Method",StatusColumn.sortKeyFun=function(e){return e.response&&e.response.status_code},StatusColumn.headerClass="col-status",StatusColumn.headerName="Status",SizeColumn.sortKeyFun=function(e){var t=e.request.contentLength;return e.response&&(t+=e.response.contentLength||0),t},SizeColumn.getTotalSize=SizeColumn.sortKeyFun,SizeColumn.headerClass="col-size",SizeColumn.headerName="Size",TimeColumn.sortKeyFun=function(e){return e.response&&e.response.timestamp_end-e.request.timestamp_start},TimeColumn.headerClass="col-time",TimeColumn.headerName="Time",exports["default"]=[TLSColumn,IconColumn,PathColumn,MethodColumn,StatusColumn,SizeColumn,TimeColumn]; + +},{"../../flow/utils.js":39,"../../utils.js":41,"classnames":"classnames","react":"react"}],8:[function(require,module,exports){ +"use strict";function _interopRequireDefault(e){return e&&e.__esModule?e:{"default":e}}function FlowRow(e){var t=e.flow,r=e.selected,l=e.highlighted,o=e.onSelect,s=(0,_classnames2["default"])({selected:r,highlighted:l,intercepted:t.intercepted,"has-request":t.request,"has-response":t.response});return _react2["default"].createElement("tr",{className:s,onClick:function(){return o(t)}},_FlowColumns2["default"].map(function(e){return _react2["default"].createElement(e,{key:e.name,flow:t})}))}Object.defineProperty(exports,"__esModule",{value:!0}),exports["default"]=FlowRow;var _react=require("react"),_react2=_interopRequireDefault(_react),_classnames=require("classnames"),_classnames2=_interopRequireDefault(_classnames),_FlowColumns=require("./FlowColumns"),_FlowColumns2=_interopRequireDefault(_FlowColumns);FlowRow.propTypes={onSelect:_react.PropTypes.func.isRequired,flow:_react.PropTypes.object.isRequired,highlighted:_react.PropTypes.bool,selected:_react.PropTypes.bool}; + +},{"./FlowColumns":7,"classnames":"classnames","react":"react"}],9:[function(require,module,exports){ +"use strict";function _interopRequireDefault(e){return e&&e.__esModule?e:{"default":e}}function FlowTableHead(e){function r(e){s({sortColumn:e.name,sortDesc:e.name!==t?!1:!o})}var t=e.sortColumn,o=e.sortDesc,s=e.onSort,u=o?"sort-desc":"sort-asc";return _react2["default"].createElement("tr",null,_FlowColumns2["default"].map(function(e){return _react2["default"].createElement("th",{className:(0,_classnames2["default"])(e.headerClass,t===e.name&&u),key:e.name,onClick:function(){return r(e)}},e.headerName)}))}Object.defineProperty(exports,"__esModule",{value:!0});var _react=require("react"),_react2=_interopRequireDefault(_react),_redux=require("redux"),_reactRedux=require("react-redux"),_classnames=require("classnames"),_classnames2=_interopRequireDefault(_classnames),_FlowColumns=require("./FlowColumns"),_FlowColumns2=_interopRequireDefault(_FlowColumns),_flows=require("../../ducks/flows");FlowTableHead.propTypes={onSort:_react.PropTypes.func.isRequired,sortDesc:_react2["default"].PropTypes.bool.isRequired,sortColumn:_react2["default"].PropTypes.string},exports["default"]=(0,_reactRedux.connect)(function(e){return{sortDesc:e.flows.sort.sortDesc,sortColumn:e.flows.sort.sortColumn}},function(e){return(0,_redux.bindActionCreators)({onSort:_flows.setSort},e)})(FlowTableHead); + +},{"../../ducks/flows":33,"./FlowColumns":7,"classnames":"classnames","react":"react","react-redux":"react-redux","redux":"redux"}],10:[function(require,module,exports){ +"use strict";function _interopRequireDefault(e){return e&&e.__esModule?e:{"default":e}}function Footer(e){var a=e.settings;return _react2["default"].createElement("footer",null,a.mode&&"regular"!=a.mode&&_react2["default"].createElement("span",{className:"label label-success"},a.mode," mode"),a.intercept&&_react2["default"].createElement("span",{className:"label label-success"},"Intercept: ",a.intercept),a.showhost&&_react2["default"].createElement("span",{className:"label label-success"},"showhost"),a.no_upstream_cert&&_react2["default"].createElement("span",{className:"label label-success"},"no-upstream-cert"),a.rawtcp&&_react2["default"].createElement("span",{className:"label label-success"},"raw-tcp"),!a.http2&&_react2["default"].createElement("span",{className:"label label-success"},"no-http2"),a.anticache&&_react2["default"].createElement("span",{className:"label label-success"},"anticache"),a.anticomp&&_react2["default"].createElement("span",{className:"label label-success"},"anticomp"),a.stickyauth&&_react2["default"].createElement("span",{className:"label label-success"},"stickyauth: ",a.stickyauth),a.stickycookie&&_react2["default"].createElement("span",{className:"label label-success"},"stickycookie: ",a.stickycookie),a.stream&&_react2["default"].createElement("span",{className:"label label-success"},"stream: ",(0,_utils.formatSize)(a.stream)))}Object.defineProperty(exports,"__esModule",{value:!0}),exports["default"]=Footer;var _react=require("react"),_react2=_interopRequireDefault(_react),_utils=require("../utils.js"),_common=require("./common.js");Footer.propTypes={settings:_react2["default"].PropTypes.object.isRequired}; + +},{"../utils.js":41,"./common.js":20,"react":"react"}],11:[function(require,module,exports){ +"use strict";function _interopRequireDefault(e){return e&&e.__esModule?e:{"default":e}}function _classCallCheck(e,t){if(!(e instanceof t))throw new TypeError("Cannot call a class as a function")}function _possibleConstructorReturn(e,t){if(!e)throw new ReferenceError("this hasn't been initialised - super() hasn't been called");return!t||"object"!=typeof t&&"function"!=typeof t?e:t}function _inherits(e,t){if("function"!=typeof t&&null!==t)throw new TypeError("Super expression must either be null or a function, not "+typeof t);e.prototype=Object.create(t&&t.prototype,{constructor:{value:e,enumerable:!1,writable:!0,configurable:!0}}),t&&(Object.setPrototypeOf?Object.setPrototypeOf(e,t):e.__proto__=t)}Object.defineProperty(exports,"__esModule",{value:!0});var _createClass=function(){function e(e,t){for(var n=0;n0&&(_actions.FlowActions.upload(e.target.files[0]),this.fileInput.value="")}},{key:"onSaveClick",value:function(e){e.preventDefault(),_actions.FlowActions.download()}},{key:"render",value:function(){var e=this;return _react2["default"].createElement("div",{className:(0,_classnames2["default"])("dropdown pull-left",{open:this.state.show})},_react2["default"].createElement("a",{href:"#",className:"special",onClick:this.onFileClick},"mitmproxy"),_react2["default"].createElement("ul",{className:"dropdown-menu",role:"menu"},_react2["default"].createElement("li",null,_react2["default"].createElement("a",{href:"#",onClick:this.onNewClick},_react2["default"].createElement("i",{className:"fa fa-fw fa-file"}),"New")),_react2["default"].createElement("li",null,_react2["default"].createElement("a",{href:"#",onClick:this.onOpenClick},_react2["default"].createElement("i",{className:"fa fa-fw fa-folder-open"}),"Open..."),_react2["default"].createElement("input",{ref:function(t){return e.fileInput=t},className:"hidden",type:"file",onChange:this.onOpenFile})),_react2["default"].createElement("li",null,_react2["default"].createElement("a",{href:"#",onClick:this.onSaveClick},_react2["default"].createElement("i",{className:"fa fa-fw fa-floppy-o"}),"Save...")),_react2["default"].createElement("li",{role:"presentation",className:"divider"}),_react2["default"].createElement("li",null,_react2["default"].createElement("a",{href:"http://mitm.it/",target:"_blank"},_react2["default"].createElement("i",{className:"fa fa-fw fa-external-link"}),"Install Certificates..."))))}}]),t}(_react.Component);exports["default"]=FileMenu; + +},{"../../actions.js":2,"classnames":"classnames","react":"react"}],13:[function(require,module,exports){ +"use strict";function _interopRequireDefault(e){return e&&e.__esModule?e:{"default":e}}function _classCallCheck(e,t){if(!(e instanceof t))throw new TypeError("Cannot call a class as a function")}function _possibleConstructorReturn(e,t){if(!e)throw new ReferenceError("this hasn't been initialised - super() hasn't been called");return!t||"object"!=typeof t&&"function"!=typeof t?e:t}function _inherits(e,t){if("function"!=typeof t&&null!==t)throw new TypeError("Super expression must either be null or a function, not "+typeof t);e.prototype=Object.create(t&&t.prototype,{constructor:{value:e,enumerable:!1,writable:!0,configurable:!0}}),t&&(Object.setPrototypeOf?Object.setPrototypeOf(e,t):e.__proto__=t)}Object.defineProperty(exports,"__esModule",{value:!0});var _createClass=function(){function e(e,t){for(var r=0;re&&(o=s.length-1),this.selectFlow(s[o])}},{key:"onMainKeyDown",value:function(e){var t=this.props.selectedFlow;if(!e.ctrlKey){switch(e.keyCode){case _utils.Key.K:case _utils.Key.UP:this.selectFlowRelative(-1);break;case _utils.Key.J:case _utils.Key.DOWN:this.selectFlowRelative(1);break;case _utils.Key.SPACE:case _utils.Key.PAGE_DOWN:this.selectFlowRelative(10);break;case _utils.Key.PAGE_UP:this.selectFlowRelative(-10);break;case _utils.Key.END:this.selectFlowRelative(1e10);break;case _utils.Key.HOME:this.selectFlowRelative(-1e10);break;case _utils.Key.ESC:this.selectFlow(null);break;case _utils.Key.H:case _utils.Key.LEFT:this.refs.flowDetails&&this.refs.flowDetails.nextTab(-1);break;case _utils.Key.L:case _utils.Key.TAB:case _utils.Key.RIGHT:this.refs.flowDetails&&this.refs.flowDetails.nextTab(1);break;case _utils.Key.C:e.shiftKey&&_actions.FlowActions.clear();break;case _utils.Key.D:t&&(e.shiftKey?_actions.FlowActions.duplicate(t):_actions.FlowActions["delete"](t));break;case _utils.Key.A:e.shiftKey?_actions.FlowActions.accept_all():t&&t.intercepted&&_actions.FlowActions.accept(t);break;case _utils.Key.R:!e.shiftKey&&t&&_actions.FlowActions.replay(t);break;case _utils.Key.V:e.shiftKey&&t&&t.modified&&_actions.FlowActions.revert(t);break;case _utils.Key.E:this.refs.flowDetails&&this.refs.flowDetails.promptEdit();break;case _utils.Key.SHIFT:break;default:return void console.debug("keydown",e.keyCode)}e.preventDefault()}}},{key:"render",value:function(){var e=this,t=this.props,s=t.flows,i=t.selectedFlow,l=t.highlight;t.sort;return _react2["default"].createElement("div",{className:"main-view"},_react2["default"].createElement(_FlowTable2["default"],{ref:"flowTable",flows:s,selected:i,highlight:l,onSelect:function(t){return e.selectFlow(t)}}),i&&[_react2["default"].createElement(_common.Splitter,{key:"splitter"}),_react2["default"].createElement(_index2["default"],{key:"flowDetails",ref:"flowDetails",tab:this.props.routeParams.detailTab,query:this.props.query,updateLocation:this.props.updateLocation,flow:i})])}}]),t}(_react.Component);MainView.propTypes={highlight:_react.PropTypes.string,sort:_react.PropTypes.object},exports["default"]=(0,_reactRedux.connect)(function(e){return{flows:e.flows.view,filter:e.flows.filter,sort:e.flows.sort,highlight:e.flows.highlight,selectedFlow:e.flows.all.byId[e.flows.selected[0]]}},function(e){return(0,_redux.bindActionCreators)({selectFlow:_flows.selectFlow,setFilter:_flows.setFilter,setHighlight:_flows.setHighlight},e)},void 0,{withRef:!0})(MainView); + +},{"../actions.js":2,"../ducks/flows":33,"../utils.js":41,"./FlowTable":6,"./common.js":20,"./flowview/index.js":24,"react":"react","react-redux":"react-redux","redux":"redux"}],19:[function(require,module,exports){ +"use strict";function _interopRequireDefault(e){return e&&e.__esModule?e:{"default":e}}function _classCallCheck(e,t){if(!(e instanceof t))throw new TypeError("Cannot call a class as a function")}function _possibleConstructorReturn(e,t){if(!e)throw new ReferenceError("this hasn't been initialised - super() hasn't been called");return!t||"object"!=typeof t&&"function"!=typeof t?e:t}function _inherits(e,t){if("function"!=typeof t&&null!==t)throw new TypeError("Super expression must either be null or a function, not "+typeof t);e.prototype=Object.create(t&&t.prototype,{constructor:{value:e,enumerable:!1,writable:!0,configurable:!0}}),t&&(Object.setPrototypeOf?Object.setPrototypeOf(e,t):e.__proto__=t)}Object.defineProperty(exports,"__esModule",{value:!0});var _createClass=function(){function e(e,t){for(var r=0;r0)e=o.getRangeAt(0);else if(document.caretPositionFromPoint&&t.clientX&&t.clientY){var n=document.caretPositionFromPoint(t.clientX,t.clientY);e=document.createRange(),e.setStart(n.offsetNode,n.offset)}else document.caretRangeFromPoint&&t.clientX&&t.clientY?e=document.caretRangeFromPoint(t.clientX,t.clientY):(e=document.createRange(),e.selectNodeContents(_reactDom2["default"].findDOMNode(this)));this._ignore_events=!0,this.setState({editable:!0},function(){var t=_reactDom2["default"].findDOMNode(this);t.blur(),t.focus(),this._ignore_events=!1})}},stop:function(){_reactDom2["default"].findDOMNode(this).blur(),this.props.onStop&&this.props.onStop()},_stop:function(t){if(!this._ignore_events){console.log("_stop",_.extend({},t)),window.getSelection().removeAllRanges();var e=_reactDom2["default"].findDOMNode(this),o=this.props.nodeToContent(e);this.setState({editable:!1}),this.props.onDone(o),this.props.onBlur&&this.props.onBlur(t)}},reset:function(){_reactDom2["default"].findDOMNode(this).innerHTML=this.props.contentToHtml(this.props.content)},onKeyDown:function(t){switch(t.stopPropagation(),t.keyCode){case _utils.Key.ESC:t.preventDefault(),this.reset(),this.stop();break;case _utils.Key.ENTER:this.props.submitOnEnter&&!t.shiftKey&&(t.preventDefault(),this.stop())}},onInput:function(){var t=_reactDom2["default"].findDOMNode(this),e=this.props.nodeToContent(t);this.props.onInput&&this.props.onInput(e)}}),ValidateEditor=_react2["default"].createClass({displayName:"ValidateEditor",propTypes:{content:_react2["default"].PropTypes.string.isRequired,onDone:_react2["default"].PropTypes.func.isRequired,onInput:_react2["default"].PropTypes.func,isValid:_react2["default"].PropTypes.func,className:_react2["default"].PropTypes.string},getInitialState:function(){return{currentContent:this.props.content}},componentWillReceiveProps:function(){this.setState({currentContent:this.props.content})},onInput:function(t){this.setState({currentContent:t}),this.props.onInput&&this.props.onInput(t)},render:function(){var t=this.props.className||"";return this.props.isValid&&(t+=this.props.isValid(this.state.currentContent)?" has-success":" has-warning"),_react2["default"].createElement(EditorBase,_extends({},this.props,{ref:"editor",className:t,onDone:this.onDone,onInput:this.onInput}))},onDone:function(t){this.props.isValid&&!this.props.isValid(t)&&(this.refs.editor.reset(),t=this.props.content),this.props.onDone(t)}}),ValueEditor=exports.ValueEditor=_react2["default"].createClass({displayName:"ValueEditor",contextTypes:{returnFocus:_react2["default"].PropTypes.func},propTypes:{content:_react2["default"].PropTypes.string.isRequired,onDone:_react2["default"].PropTypes.func.isRequired,inline:_react2["default"].PropTypes.bool},render:function(){var t=this.props.inline?"span":"div";return _react2["default"].createElement(ValidateEditor,_extends({},this.props,{onStop:this.onStop,tag:t}))},focus:function(){_reactDom2["default"].findDOMNode(this).focus()},onStop:function(){this.context.returnFocus()}}); -},{"../utils.js":32,"react":"react","react-dom":"react-dom"}],8:[function(require,module,exports){ -"use strict";function _interopRequireDefault(e){return e&&e.__esModule?e:{"default":e}}function _classCallCheck(e,t){if(!(e instanceof t))throw new TypeError("Cannot call a class as a function")}function _possibleConstructorReturn(e,t){if(!e)throw new ReferenceError("this hasn't been initialised - super() hasn't been called");return!t||"object"!=typeof t&&"function"!=typeof t?e:t}function _inherits(e,t){if("function"!=typeof t&&null!==t)throw new TypeError("Super expression must either be null or a function, not "+typeof t);e.prototype=Object.create(t&&t.prototype,{constructor:{value:e,enumerable:!1,writable:!0,configurable:!0}}),t&&(Object.setPrototypeOf?Object.setPrototypeOf(e,t):e.__proto__=t)}function LogIcon(e){var t=e.event,n={web:"html5",debug:"bug"}[t.level]||"info";return _react2["default"].createElement("i",{className:"fa fa-fw fa-"+n})}function LogEntry(e){var t=e.event,n=e.registerHeight;return _react2["default"].createElement("div",{ref:n},_react2["default"].createElement(LogIcon,{event:t}),t.message)}Object.defineProperty(exports,"__esModule",{value:!0}),exports.ToggleEventLog=void 0;var _createClass=function(){function e(e,t){for(var n=0;n=0||Object.prototype.hasOwnProperty.call(e,r)&&(o[r]=e[r]);return o}function TLSColumn(e){var t=e.flow,o="https"===t.request.scheme,r=void 0;return r=o?"col-tls col-tls-https":"col-tls col-tls-http",_react2["default"].createElement("td",{className:r})}function IconColumn(e){var t=e.flow,o=void 0;if(t.response){var r=_utils.ResponseUtils.getContentType(t.response);304===t.response.status_code?o="resource-icon-not-modified":300<=t.response.status_code&&t.response.status_code<400?o="resource-icon-redirect":r&&r.indexOf("image")>=0?o="resource-icon-image":r&&r.indexOf("javascript")>=0?o="resource-icon-js":r&&r.indexOf("css")>=0?o="resource-icon-css":r&&r.indexOf("html")>=0&&(o="resource-icon-document")}return o||(o="resource-icon-plain"),o+=" resource-icon",_react2["default"].createElement("td",{className:"col-icon"},_react2["default"].createElement("div",{className:o}))}function PathColumn(e){var t=e.flow;return _react2["default"].createElement("td",{className:"col-path"},t.request.is_replay?_react2["default"].createElement("i",{className:"fa fa-fw fa-repeat pull-right"}):null,t.intercepted?_react2["default"].createElement("i",{className:"fa fa-fw fa-pause pull-right"}):null,_utils.RequestUtils.pretty_url(t.request))}function MethodColumn(e){var t=e.flow;return _react2["default"].createElement("td",{className:"col-method"},t.request.method)}function StatusColumn(e){var t=e.flow,o=void 0;return o=t.response?t.response.status_code:null,_react2["default"].createElement("td",{className:"col-status"},o)}function SizeColumn(e){var t=e.flow,o=t.request.contentLength;t.response&&(o+=t.response.contentLength||0);var r=(0,_utils2.formatSize)(o);return _react2["default"].createElement("td",{className:"col-size"},r)}function TimeColumn(e){var t=e.flow,o=void 0;return o=t.response?(0,_utils2.formatTimeDelta)(1e3*(t.response.timestamp_end-t.request.timestamp_start)):"...",_react2["default"].createElement("td",{className:"col-time"},o)}Object.defineProperty(exports,"__esModule",{value:!0});var _extends=Object.assign||function(e){for(var t=1;t=0}})(FlowRow);FlowTableHead.propTypes={setSort:_react2["default"].PropTypes.func.isRequired,sort:_react2["default"].PropTypes.object.isRequired,columns:_react2["default"].PropTypes.array.isRequired};var FlowTableHeadContainer=(0,_reactRedux.connect)(function(e){return{sort:e.flows.sort}},function(e){return{setSort:function(t){return e((0,_flows.setSort)(t))}}})(FlowTableHead),FlowTable=function(e){function t(e,r){_classCallCheck(this,t);var o=_possibleConstructorReturn(this,Object.getPrototypeOf(t).call(this,e,r));return o.state={vScroll:(0,_VirtualScroll.calcVScroll)()},o.onViewportUpdate=o.onViewportUpdate.bind(o),o}return _inherits(t,e),_createClass(t,[{key:"componentWillMount",value:function(){window.addEventListener("resize",this.onViewportUpdate)}},{key:"componentWillUnmount",value:function(){window.removeEventListener("resize",this.onViewportUpdate)}},{key:"componentWillReceiveProps",value:function(e){var t=this;e.selected&&e.selected!==this.props.selected&&window.setTimeout(function(){return t.scrollIntoView(e.selected)},1)}},{key:"componentDidUpdate",value:function(){this.onViewportUpdate()}},{key:"onViewportUpdate",value:function(){var e=_reactDom2["default"].findDOMNode(this),t=e.scrollTop,r=(0,_VirtualScroll.calcVScroll)({viewportTop:t,viewportHeight:e.offsetHeight,itemCount:this.props.flows.length,rowHeight:this.props.rowHeight});(0,_shallowequal2["default"])(this.state.vScroll,r)&&this.state.viewportTop===t||this.setState({vScroll:r,viewportTop:t})}},{key:"scrollIntoView",value:function(e){var t=_reactDom2["default"].findDOMNode(this),r=this.props.flows.indexOf(e),o=this.props.rowHeight,l=_reactDom2["default"].findDOMNode(this.refs.head),a=l?l.offsetHeight:0,n=r*o+a,s=n+o,i=t.scrollTop,u=t.offsetHeight;i>n-a?t.scrollTop=n-a:s>i+u&&(t.scrollTop=s-u)}},{key:"render",value:function(){var e=this,t=this.state.vScroll,r=this.props.flows.slice(t.start,t.end),o="translate(0,"+this.state.viewportTop+"px)";return _react2["default"].createElement("div",{className:"flow-table",onScroll:this.onViewportUpdate},_react2["default"].createElement("table",null,_react2["default"].createElement("thead",{ref:"head",style:{transform:o}},_react2["default"].createElement(FlowTableHeadContainer,{columns:_flowtableColumns2["default"],setSortKeyFun:this.props.setSortKeyFun,setSort:this.props.setSort})),_react2["default"].createElement("tbody",null,_react2["default"].createElement("tr",{style:{height:t.paddingTop}}),r.map(function(t){return _react2["default"].createElement(FlowRowContainer,{key:t.id,flowId:t.id,columns:_flowtableColumns2["default"],selectFlow:e.props.selectFlow})}),_react2["default"].createElement("tr",{style:{height:t.paddingBottom}}))))}}]),t}(_react2["default"].Component);FlowTable.propTypes={rowHeight:_react2["default"].PropTypes.number},FlowTable.defaultProps={rowHeight:32},FlowTable=(0,_AutoScroll2["default"])(FlowTable);var parseFilter=_lodash2["default"].memoize(_filt2["default"].parse),FlowTableContainer=(0,_reactRedux.connect)(function(e){return{flows:e.flows.view}})(FlowTable);exports["default"]=FlowTableContainer; - -},{"../ducks/flows":24,"../filt/filt":29,"./flowtable-columns.js":9,"./helpers/AutoScroll":18,"./helpers/VirtualScroll":19,"classnames":"classnames","lodash":"lodash","react":"react","react-dom":"react-dom","react-redux":"react-redux","shallowequal":"shallowequal"}],11:[function(require,module,exports){ +},{"../utils.js":41,"react":"react","react-dom":"react-dom"}],22:[function(require,module,exports){ "use strict";function _interopRequireDefault(e){return e&&e.__esModule?e:{"default":e}}Object.defineProperty(exports,"__esModule",{value:!0});var _extends=Object.assign||function(e){for(var t=1;t1048576*t}},render:function(){var e=(0,_utils2.formatSize)(this.props.message.contentLength);return _react2["default"].createElement("div",{className:"alert alert-warning"},_react2["default"].createElement("button",{onClick:this.props.onClick,className:"btn btn-xs btn-warning pull-right"},"Display anyway"),e," content size.")}}),ViewSelector=_react2["default"].createClass({displayName:"ViewSelector",render:function(){for(var e=[],t=0;t0&&(r.preventDefault(),this.refs[e-1+"-value"].focus())},render:function(){var e=this.props.message.headers.map(function(e,t){var r=_react2["default"].createElement(HeaderEditor,{ref:t+"-key",content:e[0],onDone:this.onChange.bind(null,t,0),onRemove:this.onRemove.bind(null,t,0),onTab:this.onTab.bind(null,t,0)}),s=_react2["default"].createElement(HeaderEditor,{ref:t+"-value",content:e[1],onDone:this.onChange.bind(null,t,1),onRemove:this.onRemove.bind(null,t,1),onTab:this.onTab.bind(null,t,1)});return _react2["default"].createElement("tr",{key:t},_react2["default"].createElement("td",{className:"header-name"},r,":"),_react2["default"].createElement("td",{className:"header-value"},s))}.bind(this));return _react2["default"].createElement("table",{className:"header-table"},_react2["default"].createElement("tbody",null,e))}}),HeaderEditor=_react2["default"].createClass({displayName:"HeaderEditor",render:function(){return _react2["default"].createElement(_editor.ValueEditor,_extends({ref:"input"},this.props,{onKeyDown:this.onKeyDown,inline:!0}))},focus:function(){_reactDom2["default"].findDOMNode(this).focus()},onKeyDown:function(e){switch(e.keyCode){case _utils2.Key.BACKSPACE:var t=window.getSelection().getRangeAt(0);0===t.startOffset&&0===t.endOffset&&this.props.onRemove(e);break;case _utils2.Key.TAB:e.shiftKey||this.props.onTab(e)}}}),RequestLine=_react2["default"].createClass({displayName:"RequestLine",render:function(){var e=this.props.flow,t=_utils.RequestUtils.pretty_url(e.request),r=e.request.http_version;return _react2["default"].createElement("div",{className:"first-line request-line"},_react2["default"].createElement(_editor.ValueEditor,{ref:"method",content:e.request.method,onDone:this.onMethodChange,inline:!0})," ",_react2["default"].createElement(_editor.ValueEditor,{ref:"url",content:t,onDone:this.onUrlChange,isValid:this.isValidUrl,inline:!0})," ",_react2["default"].createElement(_editor.ValueEditor,{ref:"httpVersion",content:r,onDone:this.onHttpVersionChange,isValid:_utils.isValidHttpVersion,inline:!0}))},isValidUrl:function(e){var t=(0,_utils.parseUrl)(e);return!!t.host},onMethodChange:function(e){_actions.FlowActions.update(this.props.flow,{request:{method:e}})},onUrlChange:function(e){var t=(0,_utils.parseUrl)(e);t.path=t.path||"",_actions.FlowActions.update(this.props.flow,{request:t})},onHttpVersionChange:function(e){var t=(0,_utils.parseHttpVersion)(e);_actions.FlowActions.update(this.props.flow,{request:{http_version:t}})}}),ResponseLine=_react2["default"].createClass({displayName:"ResponseLine",render:function(){var e=this.props.flow,t=e.response.http_version;return _react2["default"].createElement("div",{className:"first-line response-line"},_react2["default"].createElement(_editor.ValueEditor,{ref:"httpVersion",content:t,onDone:this.onHttpVersionChange,isValid:_utils.isValidHttpVersion,inline:!0})," ",_react2["default"].createElement(_editor.ValueEditor,{ref:"code",content:e.response.status_code+"",onDone:this.onCodeChange,isValid:this.isValidCode,inline:!0})," ",_react2["default"].createElement(_editor.ValueEditor,{ref:"msg",content:e.response.reason,onDone:this.onMsgChange,inline:!0}))},isValidCode:function(e){return/^\d+$/.test(e)},onHttpVersionChange:function(e){var t=(0,_utils.parseHttpVersion)(e);_actions.FlowActions.update(this.props.flow,{response:{http_version:t}})},onMsgChange:function(e){_actions.FlowActions.update(this.props.flow,{response:{msg:e}})},onCodeChange:function(e){e=parseInt(e),_actions.FlowActions.update(this.props.flow,{response:{code:e}})}}),Request=exports.Request=_react2["default"].createClass({displayName:"Request",render:function(){var e=this.props.flow;return _react2["default"].createElement("section",{className:"request"},_react2["default"].createElement(RequestLine,{ref:"requestLine",flow:e}),_react2["default"].createElement(Headers,{ref:"headers",message:e.request,onChange:this.onHeaderChange}),_react2["default"].createElement("hr",null),_react2["default"].createElement(_contentview2["default"],{flow:e,message:e.request}))},edit:function(e){switch(e){case"m":this.refs.requestLine.refs.method.focus();break;case"u":this.refs.requestLine.refs.url.focus();break;case"v":this.refs.requestLine.refs.httpVersion.focus();break;case"h":this.refs.headers.edit();break;default:throw"Unimplemented: "+e}},onHeaderChange:function(e){_actions.FlowActions.update(this.props.flow,{request:{headers:e}})}}),Response=exports.Response=_react2["default"].createClass({displayName:"Response",render:function(){var e=this.props.flow;return _react2["default"].createElement("section",{className:"response"},_react2["default"].createElement(ResponseLine,{ref:"responseLine",flow:e}),_react2["default"].createElement(Headers,{ref:"headers",message:e.response,onChange:this.onHeaderChange}),_react2["default"].createElement("hr",null),_react2["default"].createElement(_contentview2["default"],{flow:e,message:e.response}))},edit:function(e){switch(e){case"c":this.refs.responseLine.refs.status_code.focus();break;case"m":this.refs.responseLine.refs.msg.focus();break;case"v":this.refs.responseLine.refs.httpVersion.focus();break;case"h":this.refs.headers.edit();break;default:throw"Unimplemented: "+e}},onHeaderChange:function(e){_actions.FlowActions.update(this.props.flow,{response:{headers:e}})}}),Error=exports.Error=_react2["default"].createClass({displayName:"Error",render:function(){var e=this.props.flow;return _react2["default"].createElement("section",null,_react2["default"].createElement("div",{className:"alert alert-warning"},e.error.msg,_react2["default"].createElement("div",null,_react2["default"].createElement("small",null,(0,_utils2.formatTimeStamp)(e.error.timestamp)))))}}); -},{"../../actions.js":2,"../../flow/utils.js":30,"../../utils.js":32,"../editor.js":7,"./contentview.js":11,"lodash":"lodash","react":"react","react-dom":"react-dom"}],15:[function(require,module,exports){ +},{"../../actions.js":2,"../../flow/utils.js":39,"../../utils.js":41,"../editor.js":21,"./contentview.js":22,"lodash":"lodash","react":"react","react-dom":"react-dom"}],26:[function(require,module,exports){ "use strict";function _interopRequireDefault(e){return e&&e.__esModule?e:{"default":e}}Object.defineProperty(exports,"__esModule",{value:!0});var _react=require("react"),_react2=_interopRequireDefault(_react),_actions=require("../../actions.js"),NavAction=_react2["default"].createClass({displayName:"NavAction",onClick:function(e){e.preventDefault(),this.props.onClick()},render:function(){return _react2["default"].createElement("a",{title:this.props.title,href:"#",className:"nav-action",onClick:this.onClick},_react2["default"].createElement("i",{className:"fa fa-fw "+this.props.icon}))}}),Nav=_react2["default"].createClass({displayName:"Nav",render:function(){var e=this.props.flow,t=this.props.tabs.map(function(e){var t=e.charAt(0).toUpperCase()+e.slice(1),a=this.props.active===e?"active":"",c=function(t){this.props.selectTab(e),t.preventDefault()}.bind(this);return _react2["default"].createElement("a",{key:e,href:"#",className:a,onClick:c},t)}.bind(this)),a=null;e.intercepted&&(a=_react2["default"].createElement(NavAction,{title:"[a]ccept intercepted flow",icon:"fa-play",onClick:_actions.FlowActions.accept.bind(null,e)}));var c=null;return e.modified&&(c=_react2["default"].createElement(NavAction,{title:"revert changes to flow [V]",icon:"fa-history",onClick:_actions.FlowActions.revert.bind(null,e)})),_react2["default"].createElement("nav",{ref:"head",className:"nav-tabs nav-tabs-sm"},t,_react2["default"].createElement(NavAction,{title:"[d]elete flow",icon:"fa-trash",onClick:_actions.FlowActions["delete"].bind(null,e)}),_react2["default"].createElement(NavAction,{title:"[D]uplicate flow",icon:"fa-copy",onClick:_actions.FlowActions.duplicate.bind(null,e)}),_react2["default"].createElement(NavAction,{disabled:!0,title:"[r]eplay flow",icon:"fa-repeat",onClick:_actions.FlowActions.replay.bind(null,e)}),a,c)}});exports["default"]=Nav; -},{"../../actions.js":2,"react":"react"}],16:[function(require,module,exports){ -"use strict";function _interopRequireDefault(e){return e&&e.__esModule?e:{"default":e}}function Footer(e){var a=e.settings,t=a.mode,s=a.intercept,l=a.showhost,c=a.no_upstream_cert,r=a.rawtcp,u=a.http2,n=a.anticache,o=a.anticomp,m=a.stickyauth,p=a.stickycookie,i=a.stream;return _react2["default"].createElement("footer",null,t&&"regular"!=t&&_react2["default"].createElement("span",{className:"label label-success"},t," mode"),s&&_react2["default"].createElement("span",{className:"label label-success"},"Intercept: ",s),l&&_react2["default"].createElement("span",{className:"label label-success"},"showhost"),c&&_react2["default"].createElement("span",{className:"label label-success"},"no-upstream-cert"),r&&_react2["default"].createElement("span",{className:"label label-success"},"raw-tcp"),!u&&_react2["default"].createElement("span",{className:"label label-success"},"no-http2"),n&&_react2["default"].createElement("span",{className:"label label-success"},"anticache"),o&&_react2["default"].createElement("span",{className:"label label-success"},"anticomp"),m&&_react2["default"].createElement("span",{className:"label label-success"},"stickyauth: ",m),p&&_react2["default"].createElement("span",{className:"label label-success"},"stickycookie: ",p),i&&_react2["default"].createElement("span",{className:"label label-success"},"stream: ",(0,_utils.formatSize)(i)))}Object.defineProperty(exports,"__esModule",{value:!0}),exports["default"]=Footer;var _react=require("react"),_react2=_interopRequireDefault(_react),_utils=require("../utils.js"),_common=require("./common.js");Footer.propTypes={settings:_react2["default"].PropTypes.object.isRequired}; - -},{"../utils.js":32,"./common.js":6,"react":"react"}],17:[function(require,module,exports){ -"use strict";function _interopRequireDefault(e){return e&&e.__esModule?e:{"default":e}}Object.defineProperty(exports,"__esModule",{value:!0}),exports.Header=exports.OptionMenu=exports.MainMenu=void 0;var _react=require("react"),_react2=_interopRequireDefault(_react),_reactDom=require("react-dom"),_reactDom2=_interopRequireDefault(_reactDom),_jquery=require("jquery"),_jquery2=_interopRequireDefault(_jquery),_reactRedux=require("react-redux"),_filt=require("../filt/filt.js"),_filt2=_interopRequireDefault(_filt),_utils=require("../utils.js"),_common=require("./common.js"),_actions=require("../actions.js"),_eventlog=require("./eventlog"),FilterDocs=_react2["default"].createClass({displayName:"FilterDocs",statics:{xhr:!1,doc:!1},componentWillMount:function(){FilterDocs.doc||(FilterDocs.xhr=_jquery2["default"].getJSON("/filter-help").done(function(e){FilterDocs.doc=e,FilterDocs.xhr=!1})),FilterDocs.xhr&&FilterDocs.xhr.done(function(){this.forceUpdate()}.bind(this))},render:function(){if(FilterDocs.doc){var e=FilterDocs.doc.commands.map(function(e){return _react2["default"].createElement("tr",{key:e[1]},_react2["default"].createElement("td",null,e[0].replace(" "," ")),_react2["default"].createElement("td",null,e[1]))});return e.push(_react2["default"].createElement("tr",{key:"docs-link"},_react2["default"].createElement("td",{colSpan:"2"},_react2["default"].createElement("a",{href:"http://docs.mitmproxy.org/en/stable/features/filters.html",target:"_blank"},_react2["default"].createElement("i",{className:"fa fa-external-link"}),"  mitmproxy docs")))),_react2["default"].createElement("table",{className:"table table-condensed"},_react2["default"].createElement("tbody",null,e))}return _react2["default"].createElement("i",{className:"fa fa-spinner fa-spin"})}}),FilterInput=_react2["default"].createClass({displayName:"FilterInput",contextTypes:{returnFocus:_react2["default"].PropTypes.func},getInitialState:function(){return{value:this.props.value,focus:!1,mousefocus:!1}},componentWillReceiveProps:function(e){this.setState({value:e.value})},onChange:function(e){var t=e.target.value;this.setState({value:t}),this.isValid(t)&&this.props.onChange(t)},isValid:function(e){try{var t=e||this.state.value;return t&&_filt2["default"].parse(e||this.state.value),!0}catch(a){return!1}},getDesc:function(){if(this.state.value)try{return _filt2["default"].parse(this.state.value).desc}catch(e){return""+e}return _react2["default"].createElement(FilterDocs,null)},onFocus:function(){this.setState({focus:!0})},onBlur:function(){this.setState({focus:!1})},onMouseEnter:function(){this.setState({mousefocus:!0})},onMouseLeave:function(){this.setState({mousefocus:!1})},onKeyDown:function(e){e.keyCode!==_utils.Key.ESC&&e.keyCode!==_utils.Key.ENTER||(this.blur(),this.setState({mousefocus:!1})),e.stopPropagation()},blur:function(){_reactDom2["default"].findDOMNode(this.refs.input).blur(),this.context.returnFocus()},select:function(){_reactDom2["default"].findDOMNode(this.refs.input).select()},render:function(){var e,t=this.isValid(),a="fa fa-fw fa-"+this.props.type,n="filter-input input-group"+(t?"":" has-error");return(this.state.focus||this.state.mousefocus)&&(e=_react2["default"].createElement("div",{className:"popover bottom",onMouseEnter:this.onMouseEnter,onMouseLeave:this.onMouseLeave},_react2["default"].createElement("div",{className:"arrow"}),_react2["default"].createElement("div",{className:"popover-content"},this.getDesc()))),_react2["default"].createElement("div",{className:n},_react2["default"].createElement("span",{className:"input-group-addon"},_react2["default"].createElement("i",{className:a,style:{color:this.props.color}})),_react2["default"].createElement("input",{type:"text",placeholder:this.props.placeholder,className:"form-control",ref:"input",onChange:this.onChange,onFocus:this.onFocus,onBlur:this.onBlur,onKeyDown:this.onKeyDown,value:this.state.value}),e)}}),MainMenu=exports.MainMenu=_react2["default"].createClass({displayName:"MainMenu",propTypes:{settings:_react2["default"].PropTypes.object.isRequired},statics:{title:"Start",route:"flows"},onSearchChange:function(e){var t={};t[_actions.Query.SEARCH]=e,this.props.updateLocation(void 0,t)},onHighlightChange:function(e){var t={};t[_actions.Query.HIGHLIGHT]=e,this.props.updateLocation(void 0,t)},onInterceptChange:function(e){_actions.SettingsActions.update({intercept:e})},render:function(){var e=this.props.query[_actions.Query.SEARCH]||"",t=this.props.query[_actions.Query.HIGHLIGHT]||"",a=this.props.settings.intercept||"";return _react2["default"].createElement("div",null,_react2["default"].createElement("div",{className:"menu-row"},_react2["default"].createElement(FilterInput,{ref:"search",placeholder:"Search",type:"search",color:"black",value:e,onChange:this.onSearchChange}),_react2["default"].createElement(FilterInput,{ref:"highlight",placeholder:"Highlight",type:"tag",color:"hsl(48, 100%, 50%)",value:t,onChange:this.onHighlightChange}),_react2["default"].createElement(FilterInput,{ref:"intercept",placeholder:"Intercept",type:"pause",color:"hsl(208, 56%, 53%)",value:a,onChange:this.onInterceptChange})),_react2["default"].createElement("div",{className:"clearfix"}))}}),ViewMenu=_react2["default"].createClass({displayName:"ViewMenu",statics:{title:"View",route:"flows"},render:function(){return _react2["default"].createElement("div",null,_react2["default"].createElement("div",{className:"menu-row"},_react2["default"].createElement(_eventlog.ToggleEventLog,{text:"Show Event Log"})),_react2["default"].createElement("div",{className:"clearfix"}))}}),OptionMenu=exports.OptionMenu=function(e){var t=e.settings,a=(t.mode,t.intercept,t.showhost),n=t.no_upstream_cert,r=t.rawtcp,l=t.http2,c=t.anticache,i=t.anticomp,o=t.stickycookie,s=t.stickyauth,u=t.stream;return _react2["default"].createElement("div",null,_react2["default"].createElement("div",{className:"menu-row"},_react2["default"].createElement(_common.ToggleButton,{text:"showhost",checked:a,onToggle:function(){return _actions.SettingsActions.update({showhost:!a})}}),_react2["default"].createElement(_common.ToggleButton,{text:"no_upstream_cert",checked:n,onToggle:function(){return _actions.SettingsActions.update({no_upstream_cert:!n})}}),_react2["default"].createElement(_common.ToggleButton,{text:"rawtcp",checked:r,onToggle:function(){return _actions.SettingsActions.update({rawtcp:!r})}}),_react2["default"].createElement(_common.ToggleButton,{text:"http2",checked:l,onToggle:function(){return _actions.SettingsActions.update({http2:!l})}}),_react2["default"].createElement(_common.ToggleButton,{text:"anticache",checked:c,onToggle:function(){return _actions.SettingsActions.update({anticache:!c})}}),_react2["default"].createElement(_common.ToggleButton,{text:"anticomp",checked:i,onToggle:function(){return _actions.SettingsActions.update({anticomp:!i})}}),_react2["default"].createElement(_common.ToggleInputButton,{name:"stickyauth",placeholder:"Sticky auth filter",checked:Boolean(s),txt:s||"",onToggleChanged:function(e){return _actions.SettingsActions.update({stickyauth:s?null:e})}}),_react2["default"].createElement(_common.ToggleInputButton,{name:"stickycookie",placeholder:"Sticky cookie filter",checked:Boolean(o),txt:o||"",onToggleChanged:function(e){return _actions.SettingsActions.update({stickycookie:o?null:e})}}),_react2["default"].createElement(_common.ToggleInputButton,{name:"stream",placeholder:"stream...",checked:Boolean(u),txt:u||"",inputType:"number",onToggleChanged:function(e){return _actions.SettingsActions.update({stream:u?null:e})}})),_react2["default"].createElement("div",{className:"clearfix"}))};OptionMenu.title="Options",OptionMenu.propTypes={settings:_react2["default"].PropTypes.object.isRequired};var ReportsMenu=_react2["default"].createClass({displayName:"ReportsMenu",statics:{title:"Visualization",route:"reports"},render:function(){return _react2["default"].createElement("div",null,"Reports Menu")}}),FileMenu=_react2["default"].createClass({displayName:"FileMenu",getInitialState:function(){return{showFileMenu:!1}},handleFileClick:function(e){if(e.preventDefault(),!this.state.showFileMenu){var t=function(){this.setState({showFileMenu:!1}),document.removeEventListener("click",t)}.bind(this);document.addEventListener("click",t),this.setState({showFileMenu:!0})}},handleNewClick:function(e){e.preventDefault(),confirm("Delete all flows?")&&_actions.FlowActions.clear()},handleOpenClick:function(e){this.fileInput.click(),e.preventDefault()},handleOpenFile:function(e){e.target.files.length>0&&(_actions.FlowActions.upload(e.target.files[0]),this.fileInput.value=""),e.preventDefault()},handleSaveClick:function(e){e.preventDefault(),_actions.FlowActions.download()},handleShutdownClick:function(e){e.preventDefault(),console.error("unimplemented: handleShutdownClick")},render:function(){var e=this,t="dropdown pull-left"+(this.state.showFileMenu?" open":"");return _react2["default"].createElement("div",{className:t},_react2["default"].createElement("a",{href:"#",className:"special",onClick:this.handleFileClick}," mitmproxy "),_react2["default"].createElement("ul",{className:"dropdown-menu",role:"menu"},_react2["default"].createElement("li",null,_react2["default"].createElement("a",{href:"#",onClick:this.handleNewClick},_react2["default"].createElement("i",{className:"fa fa-fw fa-file"}),"New")),_react2["default"].createElement("li",null,_react2["default"].createElement("a",{href:"#",onClick:this.handleOpenClick},_react2["default"].createElement("i",{className:"fa fa-fw fa-folder-open"}),"Open..."),_react2["default"].createElement("input",{ref:function(t){return e.fileInput=t},className:"hidden",type:"file",onChange:this.handleOpenFile})),_react2["default"].createElement("li",null,_react2["default"].createElement("a",{href:"#",onClick:this.handleSaveClick},_react2["default"].createElement("i",{className:"fa fa-fw fa-floppy-o"}),"Save...")),_react2["default"].createElement("li",{role:"presentation",className:"divider"}),_react2["default"].createElement("li",null,_react2["default"].createElement("a",{href:"http://mitm.it/",target:"_blank"},_react2["default"].createElement("i",{className:"fa fa-fw fa-external-link"}),"Install Certificates..."))))}}),header_entries=[MainMenu,ViewMenu,OptionMenu],Header=exports.Header=_react2["default"].createClass({displayName:"Header",propTypes:{settings:_react2["default"].PropTypes.object.isRequired},getInitialState:function(){return{active:header_entries[0]}},handleClick:function(e,t){t.preventDefault(),this.props.updateLocation(e.route),this.setState({active:e})},render:function(){var e=header_entries.map(function(e,t){var a;return a=e===this.state.active?"active":"",_react2["default"].createElement("a",{key:t,href:"#",className:a,onClick:this.handleClick.bind(this,e)},e.title)}.bind(this));return _react2["default"].createElement("header",null,_react2["default"].createElement("nav",{className:"nav-tabs nav-tabs-lg"},_react2["default"].createElement(FileMenu,null),e),_react2["default"].createElement("div",{className:"menu"},_react2["default"].createElement(this.state.active,{settings:this.props.settings,updateLocation:this.props.updateLocation,query:this.props.query})))}}); - -},{"../actions.js":2,"../filt/filt.js":29,"../utils.js":32,"./common.js":6,"./eventlog":8,"jquery":"jquery","react":"react","react-dom":"react-dom","react-redux":"react-redux"}],18:[function(require,module,exports){ +},{"../../actions.js":2,"react":"react"}],27:[function(require,module,exports){ "use strict";function _interopRequireDefault(t){return t&&t.__esModule?t:{"default":t}}function _classCallCheck(t,e){if(!(t instanceof e))throw new TypeError("Cannot call a class as a function")}function _possibleConstructorReturn(t,e){if(!t)throw new ReferenceError("this hasn't been initialised - super() hasn't been called");return!e||"object"!=typeof e&&"function"!=typeof e?t:e}function _inherits(t,e){if("function"!=typeof e&&null!==e)throw new TypeError("Super expression must either be null or a function, not "+typeof e);t.prototype=Object.create(e&&e.prototype,{constructor:{value:t,enumerable:!1,writable:!0,configurable:!0}}),e&&(Object.setPrototypeOf?Object.setPrototypeOf(t,e):t.__proto__=e)}Object.defineProperty(exports,"__esModule",{value:!0});var _createClass=function(){function t(t,e){for(var o=0;oh;h++){var m=i[h]||o;r>=s&&h%2===0&&(d=s,n=h),l>=s?c=h+1:p+=m,s+=m}else n=-2&Math.max(0,Math.floor(r/o)-1),c=Math.min(e,n+Math.ceil(a/o)+2),d=Math.min(n,e)*o,p=Math.max(0,e-c)*o;return{start:n,end:c,paddingTop:d,paddingBottom:p}}Object.defineProperty(exports,"__esModule",{value:!0}),exports.calcVScroll=calcVScroll; -},{}],20:[function(require,module,exports){ -"use strict";function _interopRequireDefault(e){return e&&e.__esModule?e:{"default":e}}Object.defineProperty(exports,"__esModule",{value:!0});var _react=require("react"),_react2=_interopRequireDefault(_react),_reactDom=require("react-dom"),_reactDom2=_interopRequireDefault(_reactDom),_lodash=require("lodash"),_lodash2=_interopRequireDefault(_lodash),_utils=require("../utils.js"),Prompt=_react2["default"].createClass({displayName:"Prompt",contextTypes:{returnFocus:_react2["default"].PropTypes.func},propTypes:{options:_react2["default"].PropTypes.array.isRequired,done:_react2["default"].PropTypes.func.isRequired,prompt:_react2["default"].PropTypes.string},componentDidMount:function(){_reactDom2["default"].findDOMNode(this).focus()},onKeyDown:function(e){e.stopPropagation(),e.preventDefault();for(var t=this.getOptions(),r=0;r0&&t(n[0]);)n=n.substr(1);o={text:o,key:n[0]}}if(!o.text||!o.key||t(o.key))throw"invalid options";e.push(o)}return e},render:function(){var e=this.getOptions();return e=_lodash2["default"].map(e,function(e){var t,r,o=e.text.indexOf(e.key);-1!==o?(t=e.text.substring(0,o),r=e.text.substring(o+1)):(t=e.text+" (",r=")");var n=function(t){this.done(e.key),t.stopPropagation()}.bind(this);return _react2["default"].createElement("span",{key:e.key,className:"option",onClick:n},t,_react2["default"].createElement("strong",{className:"text-primary"},e.key),r)}.bind(this)),_react2["default"].createElement("div",{tabIndex:"0",onKeyDown:this.onKeyDown,onClick:this.onClick,className:"prompt-dialog"},_react2["default"].createElement("div",{className:"prompt-content"},this.props.prompt||_react2["default"].createElement("strong",null,"Select: "),e))}});exports["default"]=Prompt; +},{}],29:[function(require,module,exports){ +"use strict";function _interopRequireDefault(e){return e&&e.__esModule?e:{"default":e}}Object.defineProperty(exports,"__esModule",{value:!0});var _react=require("react"),_react2=_interopRequireDefault(_react),_reactDom=require("react-dom"),_reactDom2=_interopRequireDefault(_reactDom),_lodash=require("lodash"),_lodash2=_interopRequireDefault(_lodash),_utils=require("../utils.js"),Prompt=_react2["default"].createClass({displayName:"Prompt",contextTypes:{returnFocus:_react2["default"].PropTypes.func},propTypes:{options:_react2["default"].PropTypes.array.isRequired,done:_react2["default"].PropTypes.func.isRequired,prompt:_react2["default"].PropTypes.string},componentDidMount:function(){_reactDom2["default"].findDOMNode(this).focus()},onKeyDown:function(e){e.stopPropagation(),e.preventDefault();for(var t=this.getOptions(),r=0;r0&&t(n[0]);)n=n.substr(1);o={text:o,key:n[0]}}if(!o.text||!o.key||t(o.key))throw"invalid options";e.push(o)}return e},render:function(){var e=this.getOptions();return e=_lodash2["default"].map(e,function(e){var t,r,o=e.text.indexOf(e.key);-1!==o?(t=e.text.substring(0,o),r=e.text.substring(o+1)):(t=e.text+" (",r=")");var n=function(t){this.done(e.key),t.stopPropagation()}.bind(this);return _react2["default"].createElement("span",{key:e.key,className:"option",onClick:n},t,_react2["default"].createElement("strong",{className:"text-primary"},e.key),r)}.bind(this)),_react2["default"].createElement("div",{tabIndex:"0",onKeyDown:this.onKeyDown,onClick:this.onClick,className:"prompt-dialog"},_react2["default"].createElement("div",{className:"prompt-content"},this.props.prompt||_react2["default"].createElement("strong",null,"Select: "),e))}});exports["default"]=Prompt; -},{"../utils.js":32,"lodash":"lodash","react":"react","react-dom":"react-dom"}],21:[function(require,module,exports){ +},{"../utils.js":41,"lodash":"lodash","react":"react","react-dom":"react-dom"}],30:[function(require,module,exports){ "use strict";function _interopRequireWildcard(e){if(e&&e.__esModule)return e;var o={};if(null!=e)for(var n in e)Object.prototype.hasOwnProperty.call(e,n)&&(o[n]=e[n]);return o["default"]=e,o}function Connection(e,o){"/"===e[0]&&(e=location.origin.replace("http","ws")+e);var n=new WebSocket(e);return n.onopen=function(){o(webSocketActions.connected()),o(flowActions.fetchFlows()).then(function(){console.log("flows are loaded now"),_actions.ConnectionActions.open()}),o(eventLogActions.fetchLogEntries())},n.onmessage=function(e){var n=JSON.parse(e.data);switch(_dispatcher.AppDispatcher.dispatchServerAction(n),n.type){case eventLogActions.UPDATE_LOG:return o(eventLogActions.updateLogEntries(n));case flowActions.UPDATE_FLOWS:return o(flowActions.updateFlows(n));default:console.warn("unknown message",n)}},n.onerror=function(){_actions.ConnectionActions.error(),o(eventLogActions.addLogEntry("WebSocket connection error."))},n.onclose=function(){_actions.ConnectionActions.close(),o(eventLogActions.addLogEntry("WebSocket connection closed.")),o(webSocketActions.disconnected())},n}Object.defineProperty(exports,"__esModule",{value:!0}),exports["default"]=Connection;var _actions=require("./actions.js"),_dispatcher=require("./dispatcher.js"),_websocket=require("./ducks/websocket"),webSocketActions=_interopRequireWildcard(_websocket),_eventLog=require("./ducks/eventLog"),eventLogActions=_interopRequireWildcard(_eventLog),_flows=require("./ducks/flows"),flowActions=_interopRequireWildcard(_flows); -},{"./actions.js":2,"./dispatcher.js":22,"./ducks/eventLog":23,"./ducks/flows":24,"./ducks/websocket":28}],22:[function(require,module,exports){ +},{"./actions.js":2,"./dispatcher.js":31,"./ducks/eventLog":32,"./ducks/flows":33,"./ducks/websocket":37}],31:[function(require,module,exports){ "use strict";function _interopRequireDefault(e){return e&&e.__esModule?e:{"default":e}}Object.defineProperty(exports,"__esModule",{value:!0}),exports.AppDispatcher=void 0;var _flux=require("flux"),_flux2=_interopRequireDefault(_flux),PayloadSources={VIEW:"view",SERVER:"server"},AppDispatcher=exports.AppDispatcher=new _flux2["default"].Dispatcher;AppDispatcher.dispatchViewAction=function(e){e.source=PayloadSources.VIEW,this.dispatch(e)},AppDispatcher.dispatchServerAction=function(e){e.source=PayloadSources.SERVER,this.dispatch(e)}; -},{"flux":"flux"}],23:[function(require,module,exports){ +},{"flux":"flux"}],32:[function(require,module,exports){ "use strict";function _interopRequireDefault(e){return e&&e.__esModule?e:{"default":e}}function _defineProperty(e,t,i){return t in e?Object.defineProperty(e,t,{value:i,enumerable:!0,configurable:!0,writable:!0}):e[t]=i,e}function reducer(){var e=arguments.length<=0||void 0===arguments[0]?defaultState:arguments[0],t=arguments[1];switch(t.type){case TOGGLE_FILTER:var i=_extends({},e.filter,_defineProperty({},t.filter,!e.filter[t.filter]));return _extends({},e,{filter:i,filteredEvents:(0,_view.updateViewFilter)(e.events,function(e){return i[e.level]})});case TOGGLE_VISIBILITY:return _extends({},e,{visible:!e.visible});case UPDATE_LOG:var r=reduceList(e.events,t);return _extends({},e,{events:r,filteredEvents:(0,_view.updateViewList)(e.filteredEvents,e.events,r,t,function(t){return e.filter[t.level]})});default:return e}}function toggleEventLogFilter(e){return{type:TOGGLE_FILTER,filter:e}}function toggleEventLogVisibility(){return{type:TOGGLE_VISIBILITY}}function addLogEntry(e){var t=arguments.length<=1||void 0===arguments[1]?"web":arguments[1];return addItem({message:e,level:t,id:"log-"+id++})}Object.defineProperty(exports,"__esModule",{value:!0}),exports.fetchLogEntries=exports.updateLogEntries=exports.UPDATE_LOG=void 0;var _extends=Object.assign||function(e){for(var t=1;tn;)u=n+o>>>1,r(e[u])n?-1:n>o?1:0};return t},sortedInsert=function(e,t,r){var n=[].concat(_toConsumableArray(e),[r]);n.indexOf=function(e){return sortedIndexOf(n,e,t)};var o=makeCompareFn(t);return t&&o(e[e.length-1],r)>0&&(console.debug("sorting view..."),n.sort(o)),n},sortedRemove=function(e,t,r){var n=r.id,o=e.filter(function(e){return e.id!==n});return o.indexOf=function(e){return sortedIndexOf(o,e,t)},o}; -},{"./list":26}],28:[function(require,module,exports){ +},{"./list":35}],37:[function(require,module,exports){ "use strict";function reducer(){var e=arguments.length<=0||void 0===arguments[0]?defaultState:arguments[0],t=arguments[1];switch(t.type){case CONNECTED:return{connected:!0};case DISCONNECTED:return{connected:!1};default:return e}}function connected(){return{type:CONNECTED}}function disconnected(){return{type:DISCONNECTED}}Object.defineProperty(exports,"__esModule",{value:!0}),exports["default"]=reducer,exports.connected=connected,exports.disconnected=disconnected;var CONNECTED="WEBSOCKET_CONNECTED",DISCONNECTED="WEBSOCKET_DISCONNECTED",defaultState={connected:!1}; -},{}],29:[function(require,module,exports){ +},{}],38:[function(require,module,exports){ "use strict";module.exports=function(){function e(e,t){function r(){this.constructor=e}r.prototype=t.prototype,e.prototype=new r}function t(e,r,n,i){this.message=e,this.expected=r,this.found=n,this.location=i,this.name="SyntaxError","function"==typeof Error.captureStackTrace&&Error.captureStackTrace(this,t)}function r(e){function r(t){var r,n,i=Ot[t];if(i)return i;for(r=t-1;!Ot[r];)r--;for(i=Ot[r],i={line:i.line,column:i.column,seenCR:i.seenCR};t>r;)n=e.charAt(r),"\n"===n?(i.seenCR||i.line++,i.column=1,i.seenCR=!1):"\r"===n||"\u2028"===n||"\u2029"===n?(i.line++,i.column=1,i.seenCR=!0):(i.column++,i.seenCR=!1),r++;return Ot[t]=i,i}function n(e,t){var n=r(e),i=r(t);return{start:{offset:e,line:n.line,column:n.column},end:{offset:t,line:i.line,column:i.column}}}function i(e){Pt>Mt||(Mt>Pt&&(Pt=Mt,Qt=[]),Qt.push(e))}function s(e,r,n,i){function s(e){var t=1;for(e.sort(function(e,t){return e.descriptiont.description?1:0});t1?u.slice(0,-1).join(", ")+" or "+u[e.length-1]:u[0],i=t?'"'+r(t)+'"':"end of input","Expected "+n+" but "+i+" found."}return null!==r&&s(r),new t(null!==e?e:u(r,n),r,n,i)}function u(){var e,t,r,n;return Vt++,e=Mt,t=a(),t!==P?(r=l(),r!==P?(n=a(),n!==P?(Nt=e,t=X(r),e=t):(Mt=e,e=P)):(Mt=e,e=P)):(Mt=e,e=P),Vt--,e===P&&(t=P,0===Vt&&i(W)),e}function c(){var t,r;return Vt++,Z.test(e.charAt(Mt))?(t=e.charAt(Mt),Mt++):(t=P,0===Vt&&i($)),Vt--,t===P&&(r=P,0===Vt&&i(Y)),t}function o(){var t,r;return Vt++,te.test(e.charAt(Mt))?(t=e.charAt(Mt),Mt++):(t=P,0===Vt&&i(re)),Vt--,t===P&&(r=P,0===Vt&&i(ee)),t}function a(){var e,t;for(Vt++,e=[],t=c();t!==P;)e.push(t),t=c();return Vt--,e===P&&(t=P,0===Vt&&i(ne)),e}function l(){var t,r,n,s,u,c;return t=Mt,r=p(),r!==P?(n=a(),n!==P?(124===e.charCodeAt(Mt)?(s=ie,Mt++):(s=P,0===Vt&&i(se)),s!==P?(u=a(),u!==P?(c=l(),c!==P?(Nt=t,r=ue(r,c),t=r):(Mt=t,t=P)):(Mt=t,t=P)):(Mt=t,t=P)):(Mt=t,t=P)):(Mt=t,t=P),t===P&&(t=p()),t}function p(){var t,r,n,s,u,o;if(t=Mt,r=f(),r!==P?(n=a(),n!==P?(38===e.charCodeAt(Mt)?(s=ce,Mt++):(s=P,0===Vt&&i(oe)),s!==P?(u=a(),u!==P?(o=p(),o!==P?(Nt=t,r=ae(r,o),t=r):(Mt=t,t=P)):(Mt=t,t=P)):(Mt=t,t=P)):(Mt=t,t=P)):(Mt=t,t=P),t===P){if(t=Mt,r=f(),r!==P){if(n=[],s=c(),s!==P)for(;s!==P;)n.push(s),s=c();else n=P;n!==P?(s=p(),s!==P?(Nt=t,r=ae(r,s),t=r):(Mt=t,t=P)):(Mt=t,t=P)}else Mt=t,t=P;t===P&&(t=f())}return t}function f(){var t,r,n,s;return t=Mt,33===e.charCodeAt(Mt)?(r=le,Mt++):(r=P,0===Vt&&i(pe)),r!==P?(n=a(),n!==P?(s=f(),s!==P?(Nt=t,r=fe(s),t=r):(Mt=t,t=P)):(Mt=t,t=P)):(Mt=t,t=P),t===P&&(t=h()),t}function h(){var t,r,n,s,u,c;return t=Mt,40===e.charCodeAt(Mt)?(r=he,Mt++):(r=P,0===Vt&&i(de)),r!==P?(n=a(),n!==P?(s=l(),s!==P?(u=a(),u!==P?(41===e.charCodeAt(Mt)?(c=ve,Mt++):(c=P,0===Vt&&i(ye)),c!==P?(Nt=t,r=ge(s),t=r):(Mt=t,t=P)):(Mt=t,t=P)):(Mt=t,t=P)):(Mt=t,t=P)):(Mt=t,t=P),t===P&&(t=d()),t}function d(){var e;return e=v(),e===P&&(e=g()),e}function v(){var t,r;return t=y(),t===P&&(t=Mt,e.substr(Mt,2)===Ae?(r=Ae,Mt+=2):(r=P,0===Vt&&i(xe)),r!==P&&(Nt=t,r=Re()),t=r,t===P&&(t=Mt,e.substr(Mt,2)===me?(r=me,Mt+=2):(r=P,0===Vt&&i(qe)),r!==P&&(Nt=t,r=Ce()),t=r,t===P&&(t=Mt,e.substr(Mt,2)===we?(r=we,Mt+=2):(r=P,0===Vt&&i(Ee)),r!==P&&(Nt=t,r=be()),t=r,t===P&&(t=Mt,e.substr(Mt,2)===Fe?(r=Fe,Mt+=2):(r=P,0===Vt&&i(Ue)),r!==P&&(Nt=t,r=je()),t=r)))),t}function y(){var t,r;return t=Mt,e.substr(Mt,4)===Te?(r=Te,Mt+=4):(r=P,0===Vt&&i(_e)),r!==P&&(Nt=t,r=Se()),t=r,t===P&&(t=Mt,e.substr(Mt,5)===ke?(r=ke,Mt+=5):(r=P,0===Vt&&i(Be)),r!==P&&(Nt=t,r=Ie()),t=r),t}function g(){var t,r,n,s;if(t=Mt,e.substr(Mt,2)===ze?(r=ze,Mt+=2):(r=P,0===Vt&&i(De)),r!==P){if(n=[],s=c(),s!==P)for(;s!==P;)n.push(s),s=c();else n=P;n!==P?(s=A(),s!==P?(Nt=t,r=Ge(s),t=r):(Mt=t,t=P)):(Mt=t,t=P)}else Mt=t,t=P;if(t===P){if(t=Mt,e.substr(Mt,2)===He?(r=He,Mt+=2):(r=P,0===Vt&&i(Je)),r!==P){if(n=[],s=c(),s!==P)for(;s!==P;)n.push(s),s=c();else n=P;n!==P?(s=x(),s!==P?(Nt=t,r=Ke(s),t=r):(Mt=t,t=P)):(Mt=t,t=P)}else Mt=t,t=P;if(t===P){if(t=Mt,e.substr(Mt,2)===Le?(r=Le,Mt+=2):(r=P,0===Vt&&i(Me)),r!==P){if(n=[],s=c(),s!==P)for(;s!==P;)n.push(s),s=c();else n=P;n!==P?(s=x(),s!==P?(Nt=t,r=Ne(s),t=r):(Mt=t,t=P)):(Mt=t,t=P)}else Mt=t,t=P;if(t===P){if(t=Mt,e.substr(Mt,3)===Oe?(r=Oe,Mt+=3):(r=P,0===Vt&&i(Pe)),r!==P){if(n=[],s=c(),s!==P)for(;s!==P;)n.push(s),s=c();else n=P;n!==P?(s=x(),s!==P?(Nt=t,r=Qe(s),t=r):(Mt=t,t=P)):(Mt=t,t=P)}else Mt=t,t=P;if(t===P){if(t=Mt,e.substr(Mt,3)===Ve?(r=Ve,Mt+=3):(r=P,0===Vt&&i(We)),r!==P){if(n=[],s=c(),s!==P)for(;s!==P;)n.push(s),s=c();else n=P;n!==P?(s=x(),s!==P?(Nt=t,r=Xe(s),t=r):(Mt=t,t=P)):(Mt=t,t=P)}else Mt=t,t=P;if(t===P){if(t=Mt,e.substr(Mt,2)===Ye?(r=Ye,Mt+=2):(r=P,0===Vt&&i(Ze)),r!==P){if(n=[],s=c(),s!==P)for(;s!==P;)n.push(s),s=c();else n=P;n!==P?(s=x(),s!==P?(Nt=t,r=$e(s),t=r):(Mt=t,t=P)):(Mt=t,t=P)}else Mt=t,t=P;if(t===P){if(t=Mt,e.substr(Mt,2)===et?(r=et,Mt+=2):(r=P,0===Vt&&i(tt)),r!==P){if(n=[],s=c(),s!==P)for(;s!==P;)n.push(s),s=c();else n=P;n!==P?(s=x(),s!==P?(Nt=t,r=rt(s),t=r):(Mt=t,t=P)):(Mt=t,t=P)}else Mt=t,t=P;if(t===P){if(t=Mt,e.substr(Mt,3)===nt?(r=nt,Mt+=3):(r=P,0===Vt&&i(it)),r!==P){if(n=[],s=c(),s!==P)for(;s!==P;)n.push(s),s=c();else n=P;n!==P?(s=x(),s!==P?(Nt=t,r=st(s),t=r):(Mt=t,t=P)):(Mt=t,t=P)}else Mt=t,t=P;if(t===P){if(t=Mt,e.substr(Mt,3)===ut?(r=ut,Mt+=3):(r=P,0===Vt&&i(ct)),r!==P){if(n=[],s=c(),s!==P)for(;s!==P;)n.push(s),s=c();else n=P;n!==P?(s=x(),s!==P?(Nt=t,r=ot(s),t=r):(Mt=t,t=P)):(Mt=t,t=P)}else Mt=t,t=P;if(t===P){if(t=Mt,e.substr(Mt,2)===at?(r=at,Mt+=2):(r=P,0===Vt&&i(lt)),r!==P){if(n=[],s=c(),s!==P)for(;s!==P;)n.push(s),s=c();else n=P;n!==P?(s=x(),s!==P?(Nt=t,r=pt(s),t=r):(Mt=t,t=P)):(Mt=t,t=P)}else Mt=t,t=P;t===P&&(t=Mt,r=x(),r!==P&&(Nt=t,r=pt(r)),t=r)}}}}}}}}}return t}function A(){var t,r,n,s;if(Vt++,t=Mt,ht.test(e.charAt(Mt))?(r=e.charAt(Mt),Mt++):(r=P,0===Vt&&i(dt)),r===P&&(r=null),r!==P){if(n=[],vt.test(e.charAt(Mt))?(s=e.charAt(Mt),Mt++):(s=P,0===Vt&&i(yt)),s!==P)for(;s!==P;)n.push(s),vt.test(e.charAt(Mt))?(s=e.charAt(Mt),Mt++):(s=P,0===Vt&&i(yt));else n=P;n!==P?(ht.test(e.charAt(Mt))?(s=e.charAt(Mt),Mt++):(s=P,0===Vt&&i(dt)),s===P&&(s=null),s!==P?(Nt=t,r=gt(n),t=r):(Mt=t,t=P)):(Mt=t,t=P)}else Mt=t,t=P;return Vt--,t===P&&(r=P,0===Vt&&i(ft)),t}function x(){var t,r,n,s;if(Vt++,t=Mt,34===e.charCodeAt(Mt)?(r=xt,Mt++):(r=P,0===Vt&&i(Rt)),r!==P){for(n=[],s=R();s!==P;)n.push(s),s=R();n!==P?(34===e.charCodeAt(Mt)?(s=xt,Mt++):(s=P,0===Vt&&i(Rt)),s!==P?(Nt=t,r=mt(n),t=r):(Mt=t,t=P)):(Mt=t,t=P)}else Mt=t,t=P;if(t===P){if(t=Mt,39===e.charCodeAt(Mt)?(r=qt,Mt++):(r=P,0===Vt&&i(Ct)),r!==P){for(n=[],s=m();s!==P;)n.push(s),s=m();n!==P?(39===e.charCodeAt(Mt)?(s=qt,Mt++):(s=P,0===Vt&&i(Ct)),s!==P?(Nt=t,r=mt(n),t=r):(Mt=t,t=P)):(Mt=t,t=P)}else Mt=t,t=P;if(t===P)if(t=Mt,r=Mt,Vt++,n=o(),Vt--,n===P?r=void 0:(Mt=r,r=P),r!==P){if(n=[],s=q(),s!==P)for(;s!==P;)n.push(s),s=q();else n=P;n!==P?(Nt=t,r=mt(n),t=r):(Mt=t,t=P)}else Mt=t,t=P}return Vt--,t===P&&(r=P,0===Vt&&i(At)),t}function R(){var t,r,n;return t=Mt,r=Mt,Vt++,wt.test(e.charAt(Mt))?(n=e.charAt(Mt),Mt++):(n=P,0===Vt&&i(Et)),Vt--,n===P?r=void 0:(Mt=r,r=P),r!==P?(e.length>Mt?(n=e.charAt(Mt),Mt++):(n=P,0===Vt&&i(bt)),n!==P?(Nt=t,r=Ft(n),t=r):(Mt=t,t=P)):(Mt=t,t=P),t===P&&(t=Mt,92===e.charCodeAt(Mt)?(r=Ut,Mt++):(r=P,0===Vt&&i(jt)),r!==P?(n=C(),n!==P?(Nt=t,r=Ft(n),t=r):(Mt=t,t=P)):(Mt=t,t=P)),t}function m(){var t,r,n;return t=Mt,r=Mt,Vt++,Tt.test(e.charAt(Mt))?(n=e.charAt(Mt),Mt++):(n=P,0===Vt&&i(_t)),Vt--,n===P?r=void 0:(Mt=r,r=P),r!==P?(e.length>Mt?(n=e.charAt(Mt),Mt++):(n=P,0===Vt&&i(bt)),n!==P?(Nt=t,r=Ft(n),t=r):(Mt=t,t=P)):(Mt=t,t=P),t===P&&(t=Mt,92===e.charCodeAt(Mt)?(r=Ut,Mt++):(r=P,0===Vt&&i(jt)),r!==P?(n=C(),n!==P?(Nt=t,r=Ft(n),t=r):(Mt=t,t=P)):(Mt=t,t=P)),t}function q(){var t,r,n;return t=Mt,r=Mt,Vt++,n=c(),Vt--,n===P?r=void 0:(Mt=r,r=P),r!==P?(e.length>Mt?(n=e.charAt(Mt),Mt++):(n=P,0===Vt&&i(bt)),n!==P?(Nt=t,r=Ft(n),t=r):(Mt=t,t=P)):(Mt=t,t=P),t}function C(){var t,r;return St.test(e.charAt(Mt))?(t=e.charAt(Mt),Mt++):(t=P,0===Vt&&i(kt)),t===P&&(t=Mt,110===e.charCodeAt(Mt)?(r=Bt,Mt++):(r=P,0===Vt&&i(It)),r!==P&&(Nt=t,r=zt()),t=r,t===P&&(t=Mt,114===e.charCodeAt(Mt)?(r=Dt,Mt++):(r=P,0===Vt&&i(Gt)),r!==P&&(Nt=t,r=Ht()),t=r,t===P&&(t=Mt,116===e.charCodeAt(Mt)?(r=Jt,Mt++):(r=P,0===Vt&&i(Kt)),r!==P&&(Nt=t,r=Lt()),t=r))),t}function w(e,t){function r(){return e.apply(this,arguments)||t.apply(this,arguments)}return r.desc=e.desc+" or "+t.desc,r}function E(e,t){function r(){return e.apply(this,arguments)&&t.apply(this,arguments)}return r.desc=e.desc+" and "+t.desc,r}function b(e){function t(){return!e.apply(this,arguments)}return t.desc="not "+e.desc,t}function F(e){function t(){return e.apply(this,arguments)}return t.desc="("+e.desc+")",t}function U(e){return!0}function j(e){return!1}function T(e){if(e.response)for(var t=Wt.ResponseUtils.getContentType(e.response),r=Xt.length;r--;)if(Xt[r].test(t))return!0;return!1}function _(e){function t(t){return t.response&&t.response.status_code===e}return t.desc="resp. code is "+e,t}function S(e){function t(t){return t.request&&e.test(t.request.host)}return e=new RegExp(e,"i"),t.desc="domain matches "+e,t}function k(e){return!!e.error}function B(e){function t(t){return t.request&&Wt.RequestUtils.match_header(t.request,e)||t.response&&Wt.ResponseUtils.match_header(t.response,e)}return e=new RegExp(e,"i"),t.desc="header matches "+e,t}function I(e){function t(t){return t.request&&Wt.RequestUtils.match_header(t.request,e)}return e=new RegExp(e,"i"),t.desc="req. header matches "+e,t}function z(e){function t(t){return t.response&&Wt.ResponseUtils.match_header(t.response,e)}return e=new RegExp(e,"i"),t.desc="resp. header matches "+e,t}function D(e){function t(t){return t.request&&e.test(t.request.method)}return e=new RegExp(e,"i"),t.desc="method matches "+e,t}function G(e){return e.request&&!e.response}function H(e){return!!e.response}function J(e){function t(t){return t.request&&e.test(Wt.RequestUtils.getContentType(t.request))||t.response&&e.test(Wt.ResponseUtils.getContentType(t.response))}return e=new RegExp(e,"i"),t.desc="content type matches "+e,t}function K(e){function t(t){return t.request&&e.test(Wt.RequestUtils.getContentType(t.request))}return e=new RegExp(e,"i"),t.desc="req. content type matches "+e,t}function L(e){function t(t){return t.response&&e.test(Wt.ResponseUtils.getContentType(t.response))}return e=new RegExp(e,"i"),t.desc="resp. content type matches "+e,t}function M(e){function t(t){return t.request&&e.test(Wt.RequestUtils.pretty_url(t.request))}return e=new RegExp(e,"i"),t.desc="url matches "+e,t}var N,O=arguments.length>1?arguments[1]:{},P={},Q={start:u},V=u,W={type:"other",description:"filter expression"},X=function(e){return e},Y={type:"other",description:"whitespace"},Z=/^[ \t\n\r]/,$={type:"class",value:"[ \\t\\n\\r]",description:"[ \\t\\n\\r]"},ee={type:"other",description:"control character"},te=/^[|&!()~"]/,re={type:"class",value:'[|&!()~"]',description:'[|&!()~"]'},ne={type:"other",description:"optional whitespace"},ie="|",se={type:"literal",value:"|",description:'"|"'},ue=function(e,t){return w(e,t)},ce="&",oe={type:"literal",value:"&",description:'"&"'},ae=function(e,t){return E(e,t)},le="!",pe={type:"literal",value:"!",description:'"!"'},fe=function(e){return b(e)},he="(",de={type:"literal",value:"(",description:'"("'},ve=")",ye={type:"literal",value:")",description:'")"'},ge=function(e){return F(e)},Ae="~a",xe={type:"literal",value:"~a",description:'"~a"'},Re=function(){return T},me="~e",qe={type:"literal",value:"~e",description:'"~e"'},Ce=function(){return k},we="~q",Ee={type:"literal",value:"~q",description:'"~q"'},be=function(){return G},Fe="~s",Ue={type:"literal",value:"~s",description:'"~s"'},je=function(){return H},Te="true",_e={type:"literal",value:"true",description:'"true"'},Se=function(){return U},ke="false",Be={type:"literal",value:"false",description:'"false"'},Ie=function(){return j},ze="~c",De={type:"literal",value:"~c",description:'"~c"'},Ge=function(e){return _(e)},He="~d",Je={type:"literal",value:"~d",description:'"~d"'},Ke=function(e){return S(e)},Le="~h",Me={type:"literal",value:"~h",description:'"~h"'},Ne=function(e){return B(e)},Oe="~hq",Pe={type:"literal",value:"~hq",description:'"~hq"'},Qe=function(e){return I(e)},Ve="~hs",We={type:"literal",value:"~hs",description:'"~hs"'},Xe=function(e){return z(e)},Ye="~m",Ze={type:"literal",value:"~m",description:'"~m"'},$e=function(e){return D(e)},et="~t",tt={type:"literal",value:"~t",description:'"~t"'},rt=function(e){return J(e)},nt="~tq",it={type:"literal",value:"~tq",description:'"~tq"'},st=function(e){return K(e)},ut="~ts",ct={type:"literal",value:"~ts",description:'"~ts"'},ot=function(e){return L(e)},at="~u",lt={type:"literal",value:"~u",description:'"~u"'},pt=function(e){return M(e)},ft={type:"other",description:"integer"},ht=/^['"]/,dt={type:"class",value:"['\"]",description:"['\"]"},vt=/^[0-9]/,yt={type:"class",value:"[0-9]",description:"[0-9]"},gt=function(e){return parseInt(e.join(""),10)},At={type:"other",description:"string"},xt='"',Rt={type:"literal",value:'"',description:'"\\""'},mt=function(e){return e.join("")},qt="'",Ct={type:"literal",value:"'",description:'"\'"'},wt=/^["\\]/,Et={type:"class",value:'["\\\\]',description:'["\\\\]'},bt={type:"any",description:"any character"},Ft=function(e){return e},Ut="\\",jt={type:"literal",value:"\\",description:'"\\\\"'},Tt=/^['\\]/,_t={type:"class",value:"['\\\\]",description:"['\\\\]"},St=/^['"\\]/,kt={type:"class",value:"['\"\\\\]",description:"['\"\\\\]"},Bt="n",It={type:"literal",value:"n",description:'"n"'},zt=function(){return"\n"},Dt="r",Gt={type:"literal",value:"r",description:'"r"'},Ht=function(){return"\r"},Jt="t",Kt={type:"literal",value:"t",description:'"t"'},Lt=function(){return" "},Mt=0,Nt=0,Ot=[{line:1,column:1,seenCR:!1}],Pt=0,Qt=[],Vt=0;if("startRule"in O){if(!(O.startRule in Q))throw new Error("Can't start parsing from rule \""+O.startRule+'".');V=Q[O.startRule]}var Wt=require("../flow/utils.js");U.desc="true",j.desc="false";var Xt=[new RegExp("text/javascript"),new RegExp("application/x-javascript"),new RegExp("application/javascript"),new RegExp("text/css"),new RegExp("image/.*"),new RegExp("application/x-shockwave-flash")];if(T.desc="is asset",k.desc="has error",G.desc="has no response",H.desc="has response",N=V(),N!==P&&Mt===e.length)return N;throw N!==P&&Mt=i;i++)Key[String.fromCharCode(i)]=i;var formatSize=exports.formatSize=function(e){if(0===e)return"0";for(var r=["b","kb","mb","gb","tb"],t=0;te);t++);var o;return o=e%Math.pow(1024,t)===0?0:1,(e/Math.pow(1024,t)).toFixed(o)+r[t]},formatTimeDelta=exports.formatTimeDelta=function(e){for(var r=e,t=["ms","s","min","h"],o=[1e3,60,60],a=0;Math.abs(r)>=o[a]&&a=0&&"/"===e.url[0]&&(-1===e.url.indexOf("?")?e.url+="?"+xsrf:e.url+="&"+xsrf)}),(0,_jquery2["default"])(document).ajaxError(function(e,r,t,o){if("abort"!==o){var a=r.responseText;console.error(o,a,arguments),alert(a)}}); },{"./actions.js":2,"jquery":"jquery","lodash":"lodash","react":"react"}]},{},[3]) diff --git a/web/conf.js b/web/conf.js index 7fe75da48..cc616a637 100644 --- a/web/conf.js +++ b/web/conf.js @@ -11,7 +11,7 @@ var conf = { // Package these as well as the dependencies vendor_includes: [ ], - app: 'src/js/app.js', + app: 'src/js/app', eslint: ["src/js/**/*.js", "!src/js/filt/filt.js"] }, css: { @@ -27,4 +27,4 @@ var conf = { peg: ["src/js/filt/filt.peg"] }; -module.exports = conf; \ No newline at end of file +module.exports = conf; diff --git a/web/gulpfile.js b/web/gulpfile.js index ffa5387ad..4a8c765f3 100644 --- a/web/gulpfile.js +++ b/web/gulpfile.js @@ -142,6 +142,7 @@ function app_stream(dev) { var bundler = browserify({ entries: [conf.js.app], debug: true, + extensions: ['.jsx'], cache: {}, // required for watchify packageCache: {} // required for watchify }); diff --git a/web/src/js/app.js b/web/src/js/app.jsx similarity index 100% rename from web/src/js/app.js rename to web/src/js/app.jsx diff --git a/web/src/js/components/EventLog.jsx b/web/src/js/components/EventLog.jsx new file mode 100644 index 000000000..24b3c2bf8 --- /dev/null +++ b/web/src/js/components/EventLog.jsx @@ -0,0 +1,41 @@ +import React, { PropTypes } from 'react' +import { bindActionCreators } from 'redux' +import { connect } from 'react-redux' +import { toggleEventLogFilter, toggleEventLogVisibility } from '../ducks/eventLog' +import { ToggleButton } from './common' +import EventList from './EventLog/EventList' + +EventLog.propTypes = { + filters: PropTypes.object.isRequired, + events: PropTypes.array.isRequired, + onToggleFilter: PropTypes.func.isRequired, + onClose: PropTypes.func.isRequired +} + +function EventLog({ filters, events, onToggleFilter, onClose }) { + return ( +
+
+ Eventlog +
+ {['debug', 'info', 'web'].map(type => ( + onToggleFilter(type)}/> + ))} + +
+
+ +
+ ) +} + +export default connect( + state => ({ + filters: state.eventLog.filter, + events: state.eventLog.filteredEvents, + }), + dispatch => bindActionCreators({ + onClose: toggleEventLogVisibility, + onToggleFilter: toggleEventLogFilter, + }, dispatch) +)(EventLog) diff --git a/web/src/js/components/EventLog/EventList.jsx b/web/src/js/components/EventLog/EventList.jsx new file mode 100644 index 000000000..d0b036e7c --- /dev/null +++ b/web/src/js/components/EventLog/EventList.jsx @@ -0,0 +1,90 @@ +import React, { Component, PropTypes } from 'react' +import ReactDOM from 'react-dom' +import shallowEqual from 'shallowequal' +import AutoScroll from '../helpers/AutoScroll' +import { calcVScroll } from '../helpers/VirtualScroll' + +class EventLogList extends Component { + + static propTypes = { + events: PropTypes.array.isRequired, + rowHeight: PropTypes.number, + } + + static defaultProps = { + rowHeight: 18, + } + + constructor(props) { + super(props) + + this.heights = {} + this.state = { vScroll: calcVScroll() } + + this.onViewportUpdate = this.onViewportUpdate.bind(this) + } + + componentDidMount() { + window.addEventListener('resize', this.onViewportUpdate) + this.onViewportUpdate() + } + + componentWillUnmount() { + window.removeEventListener('resize', this.onViewportUpdate) + } + + componentDidUpdate() { + this.onViewportUpdate() + } + + onViewportUpdate() { + const viewport = ReactDOM.findDOMNode(this) + + const vScroll = calcVScroll({ + itemCount: this.props.events.length, + rowHeight: this.props.rowHeight, + viewportTop: viewport.scrollTop, + viewportHeight: viewport.offsetHeight, + itemHeights: this.props.events.map(entry => this.heights[entry.id]), + }) + + if (!shallowEqual(this.state.vScroll, vScroll)) { + this.setState({vScroll}) + } + } + + setHeight(id, node) { + if (node && !this.heights[id]) { + const height = node.offsetHeight + if (this.heights[id] !== height) { + this.heights[id] = height + this.onViewportUpdate() + } + } + } + + render() { + const { vScroll } = this.state + const { events } = this.props + + return ( +
+                
+ {events.slice(vScroll.start, vScroll.end).map(event => ( +
this.setHeight(event.id, node)}> + + {event.message} +
+ ))} +
+
+ ) + } +} + +function LogIcon({ event }) { + const icon = { web: 'html5', debug: 'bug' }[event.level] || 'info' + return +} + +export default AutoScroll(EventLogList) diff --git a/web/src/js/components/FlowTable.jsx b/web/src/js/components/FlowTable.jsx new file mode 100644 index 000000000..eddeed62e --- /dev/null +++ b/web/src/js/components/FlowTable.jsx @@ -0,0 +1,120 @@ +import React, { PropTypes } from 'react' +import ReactDOM from 'react-dom' +import shallowEqual from 'shallowequal' +import AutoScroll from './helpers/AutoScroll' +import { calcVScroll } from './helpers/VirtualScroll' +import FlowTableHead from './FlowTable/FlowTableHead' +import FlowRow from './FlowTable/FlowRow' +import Filt from "../filt/filt" + +class FlowTable extends React.Component { + + static propTypes = { + onSelect: PropTypes.func.isRequired, + flows: PropTypes.array.isRequired, + rowHeight: PropTypes.number, + highlight: PropTypes.string, + selected: PropTypes.object, + } + + static defaultProps = { + rowHeight: 32, + } + + constructor(props, context) { + super(props, context) + + this.state = { vScroll: calcVScroll() } + this.onViewportUpdate = this.onViewportUpdate.bind(this) + } + + componentWillMount() { + window.addEventListener('resize', this.onViewportUpdate) + } + + componentWillUnmount() { + window.removeEventListener('resize', this.onViewportUpdate) + } + + componentDidUpdate() { + this.onViewportUpdate() + + if (!this.shouldScrollIntoView) { + return + } + + this.shouldScrollIntoView = false + + const { rowHeight, flows, selected } = this.props + const viewport = ReactDOM.findDOMNode(this) + const head = ReactDOM.findDOMNode(this.refs.head) + + const headHeight = head ? head.offsetHeight : 0 + + const rowTop = (flows.indexOf(selected) * rowHeight) + headHeight + const rowBottom = rowTop + rowHeight + + const viewportTop = viewport.scrollTop + const viewportHeight = viewport.offsetHeight + + // Account for pinned thead + if (rowTop - headHeight < viewportTop) { + viewport.scrollTop = rowTop - headHeight + } else if (rowBottom > viewportTop + viewportHeight) { + viewport.scrollTop = rowBottom - viewportHeight + } + } + + componentWillReceiveProps(nextProps) { + if (nextProps.selected && nextProps.selected !== this.props.selected) { + this.shouldScrollIntoView = true + } + } + + onViewportUpdate() { + const viewport = ReactDOM.findDOMNode(this) + const viewportTop = viewport.scrollTop + + const vScroll = calcVScroll({ + viewportTop, + viewportHeight: viewport.offsetHeight, + itemCount: this.props.flows.length, + rowHeight: this.props.rowHeight, + }) + + if (this.state.viewportTop !== viewportTop || !shallowEqual(this.state.vScroll, vScroll)) { + this.setState({ vScroll, viewportTop }) + } + } + + render() { + const { vScroll, viewportTop } = this.state + const { flows, selected, highlight } = this.props + const isHighlighted = highlight ? Filt.parse(highlight) : () => false + + return ( +
+ + + + + + + {flows.slice(vScroll.start, vScroll.end).map(flow => ( + + ))} + + +
+
+ ) + } +} + +export default AutoScroll(FlowTable) diff --git a/web/src/js/components/FlowTable/FlowColumns.jsx b/web/src/js/components/FlowTable/FlowColumns.jsx new file mode 100644 index 000000000..11c0796c3 --- /dev/null +++ b/web/src/js/components/FlowTable/FlowColumns.jsx @@ -0,0 +1,137 @@ +import React, { Component } from 'react' +import classnames from 'classnames' +import { RequestUtils, ResponseUtils } from '../../flow/utils.js' +import { formatSize, formatTimeDelta } from '../../utils.js' + +export function TLSColumn({ flow }) { + return ( + + ) +} + +TLSColumn.sortKeyFun = flow => flow.request.scheme +TLSColumn.headerClass = 'col-tls' +TLSColumn.headerName = '' + +export function IconColumn({ flow }) { + return ( + +
+ + ) +} + +IconColumn.headerClass = 'col-icon' +IconColumn.headerName = '' + +IconColumn.getIcon = flow => { + if (!flow.response) { + return 'resource-icon-plain' + } + + var contentType = ResponseUtils.getContentType(flow.response) || '' + + // @todo We should assign a type to the flow somewhere else. + if (flow.response.status_code === 304) { + return 'resource-icon-not-modified' + } + if (300 <= flow.response.status_code && flow.response.status_code < 400) { + return 'resource-icon-redirect' + } + if (contentType.indexOf('image') >= 0) { + return 'resource-icon-image' + } + if (contentType.indexOf('javascript') >= 0) { + return 'resource-icon-js' + } + if (contentType.indexOf('css') >= 0) { + return 'resource-icon-css' + } + if (contentType.indexOf('html') >= 0) { + return 'resource-icon-document' + } + + return 'resource-icon-plain' +} + +export function PathColumn({ flow }) { + return ( + + {flow.request.is_replay && ( + + )} + {flow.intercepted && ( + + )} + {RequestUtils.pretty_url(flow.request)} + + ) +} + +PathColumn.sortKeyFun = flow => RequestUtils.pretty_url(flow.request) +PathColumn.headerClass = 'col-path' +PathColumn.headerName = 'Path' + +export function MethodColumn({ flow }) { + return ( + {flow.request.method} + ) +} + +MethodColumn.sortKeyFun = flow => flow.request.method +MethodColumn.headerClass = 'col-method' +MethodColumn.headerName = 'Method' + +export function StatusColumn({ flow }) { + return ( + {flow.response && flow.response.status_code} + ) +} + +StatusColumn.sortKeyFun = flow => flow.response && flow.response.status_code +StatusColumn.headerClass = 'col-status' +StatusColumn.headerName = 'Status' + +export function SizeColumn({ flow }) { + return ( + {formatSize(SizeColumn.getTotalSize(flow))} + ) +} + +SizeColumn.sortKeyFun = flow => { + let total = flow.request.contentLength + if (flow.response) { + total += flow.response.contentLength || 0 + } + return total +} + +SizeColumn.getTotalSize = SizeColumn.sortKeyFun +SizeColumn.headerClass = 'col-size' +SizeColumn.headerName = 'Size' + +export function TimeColumn({ flow }) { + return ( + + {flow.response ? ( + formatTimeDelta(1000 * (flow.response.timestamp_end - flow.request.timestamp_start)) + ) : ( + '...' + )} + + ) +} + +TimeColumn.sortKeyFun = flow => flow.response && flow.response.timestamp_end - flow.request.timestamp_start +TimeColumn.headerClass = 'col-time' +TimeColumn.headerName = 'Time' + +export default [ + TLSColumn, + IconColumn, + PathColumn, + MethodColumn, + StatusColumn, + SizeColumn, + TimeColumn, +] diff --git a/web/src/js/components/FlowTable/FlowRow.jsx b/web/src/js/components/FlowTable/FlowRow.jsx new file mode 100644 index 000000000..749bc0ce4 --- /dev/null +++ b/web/src/js/components/FlowTable/FlowRow.jsx @@ -0,0 +1,28 @@ +import React, { PropTypes } from 'react' +import classnames from 'classnames' +import columns from './FlowColumns' + +FlowRow.propTypes = { + onSelect: PropTypes.func.isRequired, + flow: PropTypes.object.isRequired, + highlighted: PropTypes.bool, + selected: PropTypes.bool, +} + +export default function FlowRow({ flow, selected, highlighted, onSelect }) { + const className = classnames({ + 'selected': selected, + 'highlighted': highlighted, + 'intercepted': flow.intercepted, + 'has-request': flow.request, + 'has-response': flow.response, + }) + + return ( + onSelect(flow)}> + {columns.map(Column => ( + + ))} + + ) +} diff --git a/web/src/js/components/FlowTable/FlowTableHead.jsx b/web/src/js/components/FlowTable/FlowTableHead.jsx new file mode 100644 index 000000000..a46219d11 --- /dev/null +++ b/web/src/js/components/FlowTable/FlowTableHead.jsx @@ -0,0 +1,43 @@ +import React, { PropTypes } from 'react' +import { bindActionCreators } from 'redux' +import { connect } from 'react-redux' +import classnames from 'classnames' +import columns from './FlowColumns' + +import { setSort } from "../../ducks/flows" + +FlowTableHead.propTypes = { + onSort: PropTypes.func.isRequired, + sortDesc: React.PropTypes.bool.isRequired, + sortColumn: React.PropTypes.string, +} + +function FlowTableHead({ sortColumn, sortDesc, onSort }) { + const sortType = sortDesc ? 'sort-desc' : 'sort-asc' + + return ( + + {columns.map(Column => ( + onClick(Column)}> + {Column.headerName} + + ))} + + ) + + function onClick(Column) { + onSort({ sortColumn: Column.name, sortDesc: Column.name !== sortColumn ? false : !sortDesc }) + } +} + +export default connect( + state => ({ + sortDesc: state.flows.sort.sortDesc, + sortColumn: state.flows.sort.sortColumn, + }), + dispatch => bindActionCreators({ + onSort: setSort, + }, dispatch) +)(FlowTableHead) diff --git a/web/src/js/components/footer.js b/web/src/js/components/Footer.jsx similarity index 57% rename from web/src/js/components/footer.js rename to web/src/js/components/Footer.jsx index 8fe1081b0..903522f48 100644 --- a/web/src/js/components/footer.js +++ b/web/src/js/components/Footer.jsx @@ -1,50 +1,47 @@ -import React from "react"; -import {formatSize} from "../utils.js" -import {SettingsState} from "./common.js"; +import React from 'react' +import { formatSize } from '../utils.js' +import { SettingsState } from './common.js' Footer.propTypes = { settings: React.PropTypes.object.isRequired, -}; +} export default function Footer({ settings }) { - const {mode, intercept, showhost, no_upstream_cert, rawtcp, http2, anticache, anticomp, stickyauth, stickycookie, stream} = settings; return (
- {mode && mode != "regular" && ( - {mode} mode + {settings.mode && settings.mode != "regular" && ( + {settings.mode} mode )} - {intercept && ( - Intercept: {intercept} + {settings.intercept && ( + Intercept: {settings.intercept} )} - {showhost && ( + {settings.showhost && ( showhost )} - {no_upstream_cert && ( + {settings.no_upstream_cert && ( no-upstream-cert )} - {rawtcp && ( + {settings.rawtcp && ( raw-tcp )} - {!http2 && ( + {!settings.http2 && ( no-http2 )} - {anticache && ( + {settings.anticache && ( anticache )} - {anticomp && ( + {settings.anticomp && ( anticomp )} - {stickyauth && ( - stickyauth: {stickyauth} + {settings.stickyauth && ( + stickyauth: {settings.stickyauth} )} - {stickycookie && ( - stickycookie: {stickycookie} + {settings.stickycookie && ( + stickycookie: {settings.stickycookie} )} - {stream && ( - stream: {formatSize(stream)} + {settings.stream && ( + stream: {formatSize(settings.stream)} )} - -
- ); + ) } diff --git a/web/src/js/components/Header.js b/web/src/js/components/Header.js new file mode 100644 index 000000000..7134f7d99 --- /dev/null +++ b/web/src/js/components/Header.js @@ -0,0 +1,56 @@ +import React, { Component, PropTypes } from 'react' +import classnames from 'classnames' +import { toggleEventLogVisibility } from '../ducks/eventLog' +import MainMenu from './Header/MainMenu' +import ViewMenu from './Header/ViewMenu' +import OptionMenu from './Header/OptionMenu' +import FileMenu from './Header/FileMenu' + +export default class Header extends Component { + + static entries = [MainMenu, ViewMenu, OptionMenu] + + static propTypes = { + settings: PropTypes.object.isRequired, + } + + constructor(props, context) { + super(props, context) + this.state = { active: Header.entries[0] } + } + + handleClick(active, e) { + e.preventDefault() + this.props.updateLocation(active.route) + this.setState({ active }) + } + + render() { + const { active: Active } = this.state + const { settings, updateLocation, query } = this.props + + return ( +
+ +
+ +
+
+ ) + } +} diff --git a/web/src/js/components/Header/FileMenu.jsx b/web/src/js/components/Header/FileMenu.jsx new file mode 100644 index 000000000..b075b3c80 --- /dev/null +++ b/web/src/js/components/Header/FileMenu.jsx @@ -0,0 +1,100 @@ +import React, { Component } from 'react' +import classnames from 'classnames' +import { FlowActions } from '../../actions.js' + +export default class FileMenu extends Component { + + constructor(props, context) { + super(props, context) + this.state = { show: false } + + this.close = this.close.bind(this) + this.onFileClick = this.onFileClick.bind(this) + this.onNewClick = this.onNewClick.bind(this) + this.onOpenClick = this.onOpenClick.bind(this) + this.onOpenFile = this.onOpenFile.bind(this) + this.onSaveClick = this.onSaveClick.bind(this) + } + + close() { + this.setState({ show: false }) + document.removeEventListener('click', this.close) + } + + onFileClick(e) { + e.preventDefault() + + if (this.state.show) { + return + } + + document.addEventListener('click', this.close) + this.setState({ show: true }) + } + + onNewClick(e) { + e.preventDefault() + if (confirm('Delete all flows?')) { + FlowActions.clear() + } + } + + onOpenClick(e) { + e.preventDefault() + this.fileInput.click() + } + + onOpenFile(e) { + e.preventDefault() + if (e.target.files.length > 0) { + FlowActions.upload(e.target.files[0]) + this.fileInput.value = '' + } + } + + onSaveClick(e) { + e.preventDefault() + FlowActions.download() + } + + render() { + return ( +
+ mitmproxy + +
+ ) + } +} diff --git a/web/src/js/components/Header/FilterDocs.jsx b/web/src/js/components/Header/FilterDocs.jsx new file mode 100644 index 000000000..efb4818ca --- /dev/null +++ b/web/src/js/components/Header/FilterDocs.jsx @@ -0,0 +1,56 @@ +import React, { Component } from 'react' +import $ from 'jquery' + +export default class FilterDocs extends Component { + + // @todo move to redux + + static xhr = null + static doc = null + + constructor(props, context) { + super(props, context) + this.state = { doc: FilterDocs.doc } + } + + componentWillMount() { + if (!FilterDocs.xhr) { + FilterDocs.xhr = $.getJSON('/filter-help') + FilterDocs.xhr.fail(() => { + FilterDocs.xhr = null + }) + } + if (!this.state.doc) { + FilterDocs.xhr.done(doc => { + FilterDocs.doc = doc + this.setState({ doc }) + }) + } + } + + render() { + const { doc } = this.state + return !doc ? ( + + ) : ( + + + {doc.commands.map(cmd => ( + + + + + ))} + + + + +
{cmd[0].replace(' ', '\u00a0')}{cmd[1]}
+ + +   mitmproxy docs +
+ ) + } +} diff --git a/web/src/js/components/Header/FilterInput.jsx b/web/src/js/components/Header/FilterInput.jsx new file mode 100644 index 000000000..5b49b7884 --- /dev/null +++ b/web/src/js/components/Header/FilterInput.jsx @@ -0,0 +1,133 @@ +import React, { PropTypes, Component } from 'react' +import ReactDOM from 'react-dom' +import classnames from 'classnames' +import { Key } from '../../utils.js' +import Filt from '../../filt/filt' +import FilterDocs from './FilterDocs' + +export default class FilterInput extends Component { + + static contextTypes = { + returnFocus: React.PropTypes.func, + } + + constructor(props, context) { + super(props, context) + + // Consider both focus and mouseover for showing/hiding the tooltip, + // because onBlur of the input is triggered before the click on the tooltip + // finalized, hiding the tooltip just as the user clicks on it. + this.state = { value: this.props.value, focus: false, mousefocus: false } + + this.onChange = this.onChange.bind(this) + this.onFocus = this.onFocus.bind(this) + this.onBlur = this.onBlur.bind(this) + this.onKeyDown = this.onKeyDown.bind(this) + this.onMouseEnter = this.onMouseEnter.bind(this) + this.onMouseLeave = this.onMouseLeave.bind(this) + } + + componentWillReceiveProps(nextProps) { + this.setState({ value: nextProps.value }) + } + + isValid(filt) { + try { + const str = filt == null ? this.state.value : filt + if (str) { + Filt.parse(str) + } + return true + } catch (e) { + return false + } + } + + getDesc() { + if (!this.state.value) { + return + } + try { + return Filt.parse(this.state.value).desc + } catch (e) { + return '' + e + } + } + + onChange(e) { + const value = e.target.value + this.setState({ value }) + + // Only propagate valid filters upwards. + if (this.isValid(value)) { + this.props.onChange(value) + } + } + + onFocus() { + this.setState({ focus: true }) + } + + onBlur() { + this.setState({ focus: false }) + } + + onMouseEnter() { + this.setState({ mousefocus: true }) + } + + onMouseLeave() { + this.setState({ mousefocus: false }) + } + + onKeyDown(e) { + if (e.keyCode === Key.ESC || e.keyCode === Key.ENTER) { + this.blur() + // If closed using ESC/ENTER, hide the tooltip. + this.setState({mousefocus: false}) + } + e.stopPropagation() + } + + blur() { + ReactDOM.findDOMNode(this.refs.input).blur() + this.context.returnFocus() + } + + select() { + ReactDOM.findDOMNode(this.refs.input).select() + } + + render() { + const { type, color, placeholder } = this.props + const { value, focus, mousefocus } = this.state + return ( +
+ + + + + {(focus || mousefocus) && ( +
+
+
+ {this.getDesc()} +
+
+ )} +
+ ) + } +} diff --git a/web/src/js/components/Header/MainMenu.jsx b/web/src/js/components/Header/MainMenu.jsx new file mode 100644 index 000000000..86bf961a3 --- /dev/null +++ b/web/src/js/components/Header/MainMenu.jsx @@ -0,0 +1,73 @@ +import React, { Component, PropTypes } from 'react' +import { SettingsActions } from "../../actions.js" +import FilterInput from './FilterInput' +import { Query } from '../../actions.js' + +export default class MainMenu extends Component { + + static title = 'Start' + static route = 'flows' + + static propTypes = { + settings: React.PropTypes.object.isRequired, + } + + constructor(props, context) { + super(props, context) + this.onSearchChange = this.onSearchChange.bind(this) + this.onHighlightChange = this.onHighlightChange.bind(this) + this.onInterceptChange = this.onInterceptChange.bind(this) + } + + onSearchChange(val) { + this.props.updateLocation(undefined, { [Query.SEARCH]: val }) + } + + onHighlightChange(val) { + this.props.updateLocation(undefined, { [Query.HIGHLIGHT]: val }) + } + + onInterceptChange(val) { + SettingsActions.update({ intercept: val }) + } + + render() { + const { query, settings } = this.props + + const search = query[Query.SEARCH] || '' + const highlight = query[Query.HIGHLIGHT] || '' + const intercept = settings.intercept || '' + + return ( +
+
+ + + +
+
+
+ ) + } +} diff --git a/web/src/js/components/Header/OptionMenu.jsx b/web/src/js/components/Header/OptionMenu.jsx new file mode 100644 index 000000000..6bbf15d56 --- /dev/null +++ b/web/src/js/components/Header/OptionMenu.jsx @@ -0,0 +1,60 @@ +import React, { PropTypes } from 'react' +import { ToggleInputButton, ToggleButton } from '../common.js' +import { SettingsActions } from '../../actions.js' + +OptionMenu.title = "Options" + +OptionMenu.propTypes = { + settings: PropTypes.object.isRequired, +} + +export default function OptionMenu({ settings }) { + // @todo use settings.map + return ( +
+
+ SettingsActions.update({ showhost: !settings.showhost })} + /> + SettingsActions.update({ no_upstream_cert: !settings.no_upstream_cert })} + /> + SettingsActions.update({ rawtcp: !settings.rawtcp })} + /> + SettingsActions.update({ http2: !settings.http2 })} + /> + SettingsActions.update({ anticache: !settings.anticache })} + /> + SettingsActions.update({ anticomp: !settings.anticomp })} + /> + SettingsActions.update({ stickyauth: !settings.stickyauth ? txt : null })} + /> + SettingsActions.update({ stickycookie: !settings.stickycookie ? txt : null })} + /> + SettingsActions.update({ stream: !settings.stream ? txt : null })} + /> +
+
+
+ ) +} diff --git a/web/src/js/components/Header/ViewMenu.jsx b/web/src/js/components/Header/ViewMenu.jsx new file mode 100644 index 000000000..45359a836 --- /dev/null +++ b/web/src/js/components/Header/ViewMenu.jsx @@ -0,0 +1,33 @@ +import React, { PropTypes } from 'react' +import { bindActionCreators } from 'redux' +import { connect } from 'react-redux' +import { ToggleButton } from '../common.js' +import { toggleEventLogVisibility } from '../../ducks/eventLog' + +ViewMenu.title = 'View' +ViewMenu.route = 'flows' + +ViewMenu.propTypes = { + visible: PropTypes.bool.isRequired, + onToggle: PropTypes.func.isRequired, +} + +function ViewMenu({ visible, onToggle }) { + return ( +
+
+ +
+
+
+ ) +} + +export default connect( + state => ({ + visible: state.eventLog.visible, + }), + dispatch => bindActionCreators({ + onToggle: toggleEventLogVisibility, + }, dispatch) +)(ViewMenu) diff --git a/web/src/js/components/MainView.js b/web/src/js/components/MainView.jsx similarity index 83% rename from web/src/js/components/MainView.js rename to web/src/js/components/MainView.jsx index 6172ce77a..dbea76e58 100644 --- a/web/src/js/components/MainView.js +++ b/web/src/js/components/MainView.jsx @@ -1,16 +1,22 @@ -import React, { Component } from "react" - -import { FlowActions } from "../actions.js" -import { Query } from "../actions.js" -import { Key } from "../utils.js" -import { Splitter } from "./common.js" -import FlowTable from "./flowtable.js" -import FlowView from "./flowview/index.js" +import React, { Component, PropTypes } from 'react' import { connect } from 'react-redux' -import { selectFlow, setFilter, setHighlight } from "../ducks/flows" +import { bindActionCreators } from 'redux' + +import { FlowActions } from '../actions.js' +import { Query } from '../actions.js' +import { Key } from '../utils.js' +import { Splitter } from './common.js' +import FlowTable from './FlowTable' +import FlowView from './flowview/index.js' +import { selectFlow, setFilter, setHighlight } from '../ducks/flows' class MainView extends Component { + static propTypes = { + highlight: PropTypes.string, + sort: PropTypes.object, + } + /** * @todo move to actions * @todo replace with mapStateToProps @@ -33,9 +39,9 @@ class MainView extends Component { */ selectFlow(flow) { if (flow) { - this.props.updateLocation(`/flows/${flow.id}/${this.props.routeParams.detailTab || "request"}`) + this.props.updateLocation(`/flows/${flow.id}/${this.props.routeParams.detailTab || 'request'}`) } else { - this.props.updateLocation("/flows") + this.props.updateLocation('/flows') } } @@ -143,20 +149,22 @@ class MainView extends Component { case Key.SHIFT: break default: - console.debug("keydown", e.keyCode) + console.debug('keydown', e.keyCode) return } e.preventDefault() } render() { - const { selectedFlow } = this.props + const { flows, selectedFlow, highlight, sort } = this.props return (
this.selectFlow(flow)} + flows={flows} selected={selectedFlow} + highlight={highlight} + onSelect={flow => this.selectFlow(flow)} /> {selectedFlow && [ , @@ -178,14 +186,15 @@ export default connect( state => ({ flows: state.flows.view, filter: state.flows.filter, + sort: state.flows.sort, highlight: state.flows.highlight, selectedFlow: state.flows.all.byId[state.flows.selected[0]] }), - dispatch => ({ - selectFlow: flowId => dispatch(selectFlow(flowId)), - setFilter: filter => dispatch(setFilter(filter)), - setHighlight: highlight => dispatch(setHighlight(highlight)) - }), + dispatch => bindActionCreators({ + selectFlow, + setFilter, + setHighlight, + }, dispatch), undefined, { withRef: true } )(MainView) diff --git a/web/src/js/components/ProxyApp.js b/web/src/js/components/ProxyApp.jsx similarity index 94% rename from web/src/js/components/ProxyApp.js rename to web/src/js/components/ProxyApp.jsx index 71a7bf9b4..81272268a 100644 --- a/web/src/js/components/ProxyApp.js +++ b/web/src/js/components/ProxyApp.jsx @@ -4,9 +4,9 @@ import _ from "lodash" import { connect } from 'react-redux' import { Splitter } from "./common.js" -import { Header, MainMenu } from "./header.js" -import EventLog from "./eventlog.js" -import Footer from "./footer.js" +import Header from "./Header" +import EventLog from "./EventLog" +import Footer from "./Footer" import { SettingsStore } from "../store/store.js" import { Key } from "../utils.js" @@ -31,6 +31,7 @@ class ProxyAppMain extends Component { this.state = { settings: this.settingsStore.dict } + this.focus = this.focus.bind(this) this.onKeyDown = this.onKeyDown.bind(this) this.updateLocation = this.updateLocation.bind(this) this.onSettingsChange = this.onSettingsChange.bind(this) @@ -45,7 +46,7 @@ class ProxyAppMain extends Component { } const query = this.props.location.query for (const key of Object.keys(queryUpdate || {})) { - query[i] = queryUpdate[i] || undefined + query[key] = queryUpdate[key] || undefined } this.context.router.replace({ pathname, query }) } @@ -133,7 +134,7 @@ class ProxyAppMain extends Component { if (name) { const headerComponent = this.refs.header - headerComponent.setState({active: MainMenu}, function () { + headerComponent.setState({ active: Header.entries.MainMenu }, () => { headerComponent.refs.active.refs[name].select() }) } diff --git a/web/src/js/components/eventlog.js b/web/src/js/components/eventlog.js deleted file mode 100644 index 6ada58ffe..000000000 --- a/web/src/js/components/eventlog.js +++ /dev/null @@ -1,153 +0,0 @@ -import React from "react" -import ReactDOM from "react-dom" -import {connect} from 'react-redux' -import shallowEqual from "shallowequal" -import {toggleEventLogFilter, toggleEventLogVisibility} from "../ducks/eventLog" -import AutoScroll from "./helpers/AutoScroll"; -import {calcVScroll} from "./helpers/VirtualScroll" -import {ToggleButton} from "./common"; - -function LogIcon({event}) { - let icon = {web: "html5", debug: "bug"}[event.level] || "info"; - return -} - -function LogEntry({event, registerHeight}) { - return
- - {event.message} -
; -} - -class EventLogContents extends React.Component { - - static defaultProps = { - rowHeight: 18, - }; - - constructor(props) { - super(props); - - this.heights = {}; - this.state = {vScroll: calcVScroll()}; - - this.onViewportUpdate = this.onViewportUpdate.bind(this); - } - - componentDidMount() { - window.addEventListener("resize", this.onViewportUpdate); - this.onViewportUpdate(); - } - - componentWillUnmount() { - window.removeEventListener("resize", this.onViewportUpdate); - } - - componentDidUpdate() { - this.onViewportUpdate(); - } - - onViewportUpdate() { - const viewport = ReactDOM.findDOMNode(this); - - const vScroll = calcVScroll({ - itemCount: this.props.events.length, - rowHeight: this.props.rowHeight, - viewportTop: viewport.scrollTop, - viewportHeight: viewport.offsetHeight, - itemHeights: this.props.events.map(entry => this.heights[entry.id]), - }); - - if (!shallowEqual(this.state.vScroll, vScroll)) { - this.setState({vScroll}); - } - } - - setHeight(id, node) { - if (node && !this.heights[id]) { - const height = node.offsetHeight; - if (this.heights[id] !== height) { - this.heights[id] = height; - this.onViewportUpdate(); - } - } - } - - render() { - const vScroll = this.state.vScroll; - const events = this.props.events - .slice(vScroll.start, vScroll.end) - .map(event => - this.setHeight(event.id, node)} - /> - ); - - return ( -
-                
- {events} -
-
- ); - } -} - -EventLogContents = AutoScroll(EventLogContents); - - -const EventLogContentsContainer = connect( - state => ({ - events: state.eventLog.filteredEvents - }) -)(EventLogContents); - - -export const ToggleEventLog = connect( - state => ({ - checked: state.eventLog.visible - }), - dispatch => ({ - onToggle: () => dispatch(toggleEventLogVisibility()) - }) -)(ToggleButton); - - -const ToggleFilter = connect( - (state, ownProps) => ({ - checked: state.eventLog.filter[ownProps.text] - }), - (dispatch, ownProps) => ({ - onToggle: () => dispatch(toggleEventLogFilter(ownProps.text)) - }) -)(ToggleButton); - - -const EventLog = ({close}) => -
-
- Eventlog -
- - - - -
-
- -
; - -EventLog.propTypes = { - close: React.PropTypes.func.isRequired -}; - -const EventLogContainer = connect( - undefined, - dispatch => ({ - close: () => dispatch(toggleEventLogVisibility()) - }) -)(EventLog); - -export default EventLogContainer; diff --git a/web/src/js/components/flowtable-columns.js b/web/src/js/components/flowtable-columns.js deleted file mode 100644 index 799b3f9f6..000000000 --- a/web/src/js/components/flowtable-columns.js +++ /dev/null @@ -1,131 +0,0 @@ -import React from "react" -import {RequestUtils, ResponseUtils} from "../flow/utils.js" -import {formatSize, formatTimeDelta} from "../utils.js" - - -export function TLSColumn({flow}) { - let ssl = (flow.request.scheme === "https") - let classes - if (ssl) { - classes = "col-tls col-tls-https" - } else { - classes = "col-tls col-tls-http" - } - return -} -TLSColumn.Title = ({className = "", ...props}) => -TLSColumn.sortKeyFun = flow => flow.request.scheme - - -export function IconColumn({flow}) { - let icon - if (flow.response) { - var contentType = ResponseUtils.getContentType(flow.response) - - //TODO: We should assign a type to the flow somewhere else. - if (flow.response.status_code === 304) { - icon = "resource-icon-not-modified" - } else if (300 <= flow.response.status_code && flow.response.status_code < 400) { - icon = "resource-icon-redirect" - } else if (contentType && contentType.indexOf("image") >= 0) { - icon = "resource-icon-image" - } else if (contentType && contentType.indexOf("javascript") >= 0) { - icon = "resource-icon-js" - } else if (contentType && contentType.indexOf("css") >= 0) { - icon = "resource-icon-css" - } else if (contentType && contentType.indexOf("html") >= 0) { - icon = "resource-icon-document" - } - } - if (!icon) { - icon = "resource-icon-plain" - } - - icon += " resource-icon" - return -
- -} -IconColumn.Title = ({className = "", ...props}) => - - -export function PathColumn({flow}) { - return - {flow.request.is_replay ? : null} - {flow.intercepted ? : null} - { RequestUtils.pretty_url(flow.request) } - -} -PathColumn.Title = ({className = "", ...props}) => - Path -PathColumn.sortKeyFun = flow => RequestUtils.pretty_url(flow.request) - - -export function MethodColumn({flow}) { - return {flow.request.method} -} -MethodColumn.Title = ({className = "", ...props}) => - Method -MethodColumn.sortKeyFun = flow => flow.request.method - - -export function StatusColumn({flow}) { - let status - if (flow.response) { - status = flow.response.status_code - } else { - status = null - } - return {status} - -} -StatusColumn.Title = ({className = "", ...props}) => - Status -StatusColumn.sortKeyFun = flow => flow.response ? flow.response.status_code : undefined - - -export function SizeColumn({flow}) { - let total = flow.request.contentLength - if (flow.response) { - total += flow.response.contentLength || 0 - } - let size = formatSize(total) - return {size} - -} -SizeColumn.Title = ({className = "", ...props}) => - Size -SizeColumn.sortKeyFun = flow => { - let total = flow.request.contentLength - if (flow.response) { - total += flow.response.contentLength || 0 - } - return total -} - - -export function TimeColumn({flow}) { - let time - if (flow.response) { - time = formatTimeDelta(1000 * (flow.response.timestamp_end - flow.request.timestamp_start)) - } else { - time = "..." - } - return {time} -} -TimeColumn.Title = ({className = "", ...props}) => - Time -TimeColumn.sortKeyFun = flow => flow.response.timestamp_end - flow.request.timestamp_start - - -var all_columns = [ - TLSColumn, - IconColumn, - PathColumn, - MethodColumn, - StatusColumn, - SizeColumn, - TimeColumn -] - -export default all_columns diff --git a/web/src/js/components/flowtable.js b/web/src/js/components/flowtable.js deleted file mode 100644 index 5a0793bac..000000000 --- a/web/src/js/components/flowtable.js +++ /dev/null @@ -1,201 +0,0 @@ -import React from "react"; -import ReactDOM from "react-dom"; -import {connect} from 'react-redux' -import classNames from "classnames"; -import _ from "lodash"; -import shallowEqual from "shallowequal"; -import AutoScroll from "./helpers/AutoScroll"; -import {calcVScroll} from "./helpers/VirtualScroll"; -import flowtable_columns from "./flowtable-columns.js"; -import Filt from "../filt/filt"; -import {setSort} from "../ducks/flows"; - - -FlowRow.propTypes = { - selectFlow: React.PropTypes.func.isRequired, - columns: React.PropTypes.array.isRequired, - flow: React.PropTypes.object.isRequired, - highlight: React.PropTypes.string, - selected: React.PropTypes.bool, -}; - -function FlowRow({flow, selected, highlight, columns, selectFlow}) { - - const className = classNames({ - "selected": selected, - "highlighted": highlight && parseFilter(highlight)(flow), - "intercepted": flow.intercepted, - "has-request": flow.request, - "has-response": flow.response, - }); - - return ( - selectFlow(flow)}> - {columns.map(Column => ( - - ))} - - ); -} - -const FlowRowContainer = connect( - (state, ownProps) => ({ - flow: state.flows.all.byId[ownProps.flowId], - highlight: state.flows.highlight, - selected: state.flows.selected.indexOf(ownProps.flowId) >= 0 - }) -)(FlowRow) - -function FlowTableHead({setSort, columns, sort}) { - const sortColumn = sort.sortColumn; - const sortType = sort.sortDesc ? "sort-desc" : "sort-asc"; - - return ( - - {columns.map(Column => ( - setSort({sortColumn: Column.name, sortDesc: Column.name != sort.sortColumn ? false : !sort.sortDesc})} - className={sortColumn === Column.name ? sortType : undefined} - /> - ))} - - ); -} - -FlowTableHead.propTypes = { - setSort: React.PropTypes.func.isRequired, - sort: React.PropTypes.object.isRequired, - columns: React.PropTypes.array.isRequired -}; - -const FlowTableHeadContainer = connect( - state => ({ - sort: state.flows.sort - }), - dispatch => ({ - setSort: (sort) => dispatch(setSort(sort)), - }) -)(FlowTableHead) - -class FlowTable extends React.Component { - - static propTypes = { - rowHeight: React.PropTypes.number, - }; - - static defaultProps = { - rowHeight: 32, - }; - - constructor(props, context) { - super(props, context); - - this.state = {vScroll: calcVScroll()}; - - this.onViewportUpdate = this.onViewportUpdate.bind(this); - } - - componentWillMount() { - window.addEventListener("resize", this.onViewportUpdate); - } - - componentWillUnmount() { - window.removeEventListener("resize", this.onViewportUpdate); - } - - componentWillReceiveProps(nextProps) { - if (nextProps.selected && nextProps.selected !== this.props.selected) { - window.setTimeout(() => this.scrollIntoView(nextProps.selected), 1) - } - } - - componentDidUpdate() { - this.onViewportUpdate(); - } - - onViewportUpdate() { - const viewport = ReactDOM.findDOMNode(this); - const viewportTop = viewport.scrollTop; - - const vScroll = calcVScroll({ - viewportTop, - viewportHeight: viewport.offsetHeight, - itemCount: this.props.flows.length, - rowHeight: this.props.rowHeight, - }); - - if (!shallowEqual(this.state.vScroll, vScroll) || - this.state.viewportTop !== viewportTop) { - this.setState({vScroll, viewportTop}); - } - } - - scrollIntoView(flow) { - const viewport = ReactDOM.findDOMNode(this); - const index = this.props.flows.indexOf(flow); - const rowHeight = this.props.rowHeight; - const head = ReactDOM.findDOMNode(this.refs.head); - - const headHeight = head ? head.offsetHeight : 0; - - const rowTop = (index * rowHeight) + headHeight; - const rowBottom = rowTop + rowHeight; - - const viewportTop = viewport.scrollTop; - const viewportHeight = viewport.offsetHeight; - - // Account for pinned thead - if (rowTop - headHeight < viewportTop) { - viewport.scrollTop = rowTop - headHeight; - } else if (rowBottom > viewportTop + viewportHeight) { - viewport.scrollTop = rowBottom - viewportHeight; - } - } - - render() { - const vScroll = this.state.vScroll; - const flows = this.props.flows.slice(vScroll.start, vScroll.end); - - const transform = `translate(0,${this.state.viewportTop}px)`; - - return ( -
- - - - - - - {flows.map(flow => ( - - ))} - - -
-
- ); - } -} - -FlowTable = AutoScroll(FlowTable) - - -const parseFilter = _.memoize(Filt.parse) - -const FlowTableContainer = connect( - state => ({ - flows: state.flows.view - }) -)(FlowTable) - -export default FlowTableContainer; diff --git a/web/src/js/components/header.js b/web/src/js/components/header.js deleted file mode 100644 index 4152e95c2..000000000 --- a/web/src/js/components/header.js +++ /dev/null @@ -1,453 +0,0 @@ -import React from "react"; -import ReactDOM from 'react-dom'; -import $ from "jquery"; -import {connect} from 'react-redux' - -import Filt from "../filt/filt.js"; -import {Key} from "../utils.js"; -import {ToggleInputButton, ToggleButton} from "./common.js"; -import {SettingsActions, FlowActions} from "../actions.js"; -import {Query} from "../actions.js"; -import {SettingsState} from "./common.js"; -import {ToggleEventLog} from "./eventlog" - -var FilterDocs = React.createClass({ - statics: { - xhr: false, - doc: false - }, - componentWillMount: function () { - if (!FilterDocs.doc) { - FilterDocs.xhr = $.getJSON("/filter-help").done(function (doc) { - FilterDocs.doc = doc; - FilterDocs.xhr = false; - }); - } - if (FilterDocs.xhr) { - FilterDocs.xhr.done(function () { - this.forceUpdate(); - }.bind(this)); - } - }, - render: function () { - if (!FilterDocs.doc) { - return ; - } else { - var commands = FilterDocs.doc.commands.map(function (c) { - return - {c[0].replace(" ", '\u00a0')} - {c[1]} - ; - }); - commands.push( - - - -   mitmproxy docs - - ); - return - {commands} -
; - } - } -}); -var FilterInput = React.createClass({ - contextTypes: { - returnFocus: React.PropTypes.func - }, - getInitialState: function () { - // Consider both focus and mouseover for showing/hiding the tooltip, - // because onBlur of the input is triggered before the click on the tooltip - // finalized, hiding the tooltip just as the user clicks on it. - return { - value: this.props.value, - focus: false, - mousefocus: false - }; - }, - componentWillReceiveProps: function (nextProps) { - this.setState({value: nextProps.value}); - }, - onChange: function (e) { - var nextValue = e.target.value; - this.setState({ - value: nextValue - }); - // Only propagate valid filters upwards. - if (this.isValid(nextValue)) { - this.props.onChange(nextValue); - } - }, - isValid: function (filt) { - try { - var str = filt || this.state.value; - if(str){ - Filt.parse(filt || this.state.value); - } - return true; - } catch (e) { - return false; - } - }, - getDesc: function () { - if(this.state.value) { - try { - return Filt.parse(this.state.value).desc; - } catch (e) { - return "" + e; - } - } - return ; - }, - onFocus: function () { - this.setState({focus: true}); - }, - onBlur: function () { - this.setState({focus: false}); - }, - onMouseEnter: function () { - this.setState({mousefocus: true}); - }, - onMouseLeave: function () { - this.setState({mousefocus: false}); - }, - onKeyDown: function (e) { - if (e.keyCode === Key.ESC || e.keyCode === Key.ENTER) { - this.blur(); - // If closed using ESC/ENTER, hide the tooltip. - this.setState({mousefocus: false}); - } - e.stopPropagation(); - }, - blur: function () { - ReactDOM.findDOMNode(this.refs.input).blur(); - this.context.returnFocus(); - }, - select: function () { - ReactDOM.findDOMNode(this.refs.input).select(); - }, - render: function () { - var isValid = this.isValid(); - var icon = "fa fa-fw fa-" + this.props.type; - var groupClassName = "filter-input input-group" + (isValid ? "" : " has-error"); - - var popover; - if (this.state.focus || this.state.mousefocus) { - popover = ( -
-
-
- {this.getDesc()} -
-
- ); - } - - return ( -
- - - - - {popover} -
- ); - } -}); - -export var MainMenu = React.createClass({ - propTypes: { - settings: React.PropTypes.object.isRequired, - }, - statics: { - title: "Start", - route: "flows" - }, - onSearchChange: function (val) { - var d = {}; - d[Query.SEARCH] = val; - this.props.updateLocation(undefined, d); - }, - onHighlightChange: function (val) { - var d = {}; - d[Query.HIGHLIGHT] = val; - this.props.updateLocation(undefined, d); - }, - onInterceptChange: function (val) { - SettingsActions.update({intercept: val}); - }, - render: function () { - var search = this.props.query[Query.SEARCH] || ""; - var highlight = this.props.query[Query.HIGHLIGHT] || ""; - var intercept = this.props.settings.intercept || ""; - - return ( -
-
- - - -
-
-
- ); - } -}); - - -var ViewMenu = React.createClass({ - statics: { - title: "View", - route: "flows" - }, - render: function () { - return ( -
-
- -
-
-
- ); - } -}); - -export const OptionMenu = (props) => { - const {mode, intercept, showhost, no_upstream_cert, rawtcp, http2, anticache, anticomp, stickycookie, stickyauth, stream} = props.settings; - return ( -
-
- SettingsActions.update({showhost: !showhost})} - /> - SettingsActions.update({no_upstream_cert: !no_upstream_cert})} - /> - SettingsActions.update({rawtcp: !rawtcp})} - /> - SettingsActions.update({http2: !http2})} - /> - SettingsActions.update({anticache: !anticache})} - /> - SettingsActions.update({anticomp: !anticomp})} - /> - SettingsActions.update({stickyauth: (!stickyauth ? txt : null)})} - /> - SettingsActions.update({stickycookie: (!stickycookie ? txt : null)})} - /> - SettingsActions.update({stream: (!stream ? txt : null)})} - /> -
-
-
- ); -}; -OptionMenu.title = "Options"; - -OptionMenu.propTypes = { - settings: React.PropTypes.object.isRequired -}; - -var ReportsMenu = React.createClass({ - statics: { - title: "Visualization", - route: "reports" - }, - render: function () { - return
Reports Menu
; - } -}); - -var FileMenu = React.createClass({ - getInitialState: function () { - return { - showFileMenu: false - }; - }, - handleFileClick: function (e) { - e.preventDefault(); - if (!this.state.showFileMenu) { - var close = function () { - this.setState({showFileMenu: false}); - document.removeEventListener("click", close); - }.bind(this); - document.addEventListener("click", close); - - this.setState({ - showFileMenu: true - }); - } - }, - handleNewClick: function (e) { - e.preventDefault(); - if (confirm("Delete all flows?")) { - FlowActions.clear(); - } - }, - handleOpenClick: function (e) { - this.fileInput.click(); - e.preventDefault(); - }, - handleOpenFile: function (e) { - if (e.target.files.length > 0) { - FlowActions.upload(e.target.files[0]); - this.fileInput.value = ""; - } - e.preventDefault(); - }, - handleSaveClick: function (e) { - e.preventDefault(); - FlowActions.download(); - }, - handleShutdownClick: function (e) { - e.preventDefault(); - console.error("unimplemented: handleShutdownClick"); - }, - render: function () { - var fileMenuClass = "dropdown pull-left" + (this.state.showFileMenu ? " open" : ""); - - return ( -
- mitmproxy - -
- ); - } -}); - - -var header_entries = [MainMenu, ViewMenu, OptionMenu /*, ReportsMenu */]; - - -export var Header = React.createClass({ - propTypes: { - settings: React.PropTypes.object.isRequired, - }, - getInitialState: function () { - return { - active: header_entries[0] - }; - }, - handleClick: function (active, e) { - e.preventDefault(); - this.props.updateLocation(active.route); - this.setState({active: active}); - }, - render: function () { - var header = header_entries.map(function (entry, i) { - var className; - if (entry === this.state.active) { - className = "active"; - } else { - className = ""; - } - return ( - - {entry.title} - - ); - }.bind(this)); - - return ( -
- -
- -
-
- ); - } -}); diff --git a/web/src/js/components/prompt.js b/web/src/js/components/prompt.js index e324f7d48..5ab26b82d 100644 --- a/web/src/js/components/prompt.js +++ b/web/src/js/components/prompt.js @@ -42,7 +42,7 @@ var Prompt = React.createClass({ var opts = []; var keyTaken = function (k) { - return _.includes(_.pluck(opts, "key"), k); + return _.includes(_.map(opts, "key"), k); }; for (var i = 0; i < this.props.options.length; i++) { @@ -99,4 +99,4 @@ var Prompt = React.createClass({ } }); -export default Prompt; \ No newline at end of file +export default Prompt; diff --git a/web/src/js/ducks/flows.js b/web/src/js/ducks/flows.js index 6adde71c4..f7a5538ac 100644 --- a/web/src/js/ducks/flows.js +++ b/web/src/js/ducks/flows.js @@ -2,7 +2,7 @@ import makeList from "./utils/list" import Filt from "../filt/filt" import {updateViewFilter, updateViewList, updateViewSort} from "./utils/view" import {reverseString} from "../utils.js"; -import * as flow_table_columns from "../components/flowtable-columns.js"; +import * as columns from "../components/FlowTable/FlowColumns"; export const UPDATE_FLOWS = "UPDATE_FLOWS" export const SET_FILTER = "SET_FLOW_FILTER" @@ -32,7 +32,7 @@ function makeFilterFn(filter) { function makeSortFn(sort){ - let column = flow_table_columns[sort.sortColumn]; + let column = columns[sort.sortColumn]; if (!column) return; let sortKeyFun = column.sortKeyFun; @@ -108,4 +108,4 @@ export function selectFlow(flowId) { } -export {updateList as updateFlows, fetchList as fetchFlows} \ No newline at end of file +export {updateList as updateFlows, fetchList as fetchFlows}