diff options
author | Maximilian Hils <git@maximilianhils.com> | 2014-12-13 01:56:04 +0100 |
---|---|---|
committer | Maximilian Hils <git@maximilianhils.com> | 2014-12-13 01:56:04 +0100 |
commit | 955c5c87a261ad4083e4b3f1579625a2b53eb912 (patch) | |
tree | c33ab7eb3be5865d1ea38768d3404db524f4413e /web/src/js/components | |
parent | a09eb2bef42e241c6ff1698606f4a0decbca174a (diff) | |
download | mitmproxy-955c5c87a261ad4083e4b3f1579625a2b53eb912.tar.gz mitmproxy-955c5c87a261ad4083e4b3f1579625a2b53eb912.tar.bz2 mitmproxy-955c5c87a261ad4083e4b3f1579625a2b53eb912.zip |
web: filter/highlight ui, many fixes
Diffstat (limited to 'web/src/js/components')
-rw-r--r-- | web/src/js/components/header.jsx.js | 195 | ||||
-rw-r--r-- | web/src/js/components/mainview.jsx.js | 8 |
2 files changed, 184 insertions, 19 deletions
diff --git a/web/src/js/components/header.jsx.js b/web/src/js/components/header.jsx.js index b181b670..d9eefce9 100644 --- a/web/src/js/components/header.jsx.js +++ b/web/src/js/components/header.jsx.js @@ -1,11 +1,124 @@ +var FilterInput = React.createClass({ + getInitialState: function () { + // Focus: Show popover + // Mousefocus: Mouse over Tooltip + // onBlur is triggered before click on tooltip, + // hiding the tooltip before link is clicked. + 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 + }); + try { + Filt.parse(nextValue); + } catch (err) { + return; + } + this.props.onChange(nextValue); + }, + isValid: function () { + try { + Filt.parse(this.state.value); + return true; + } catch (e) { + return false; + } + }, + getDesc: function () { + var desc; + try { + desc = Filt.parse(this.state.value).desc; + } catch (e) { + desc = "" + e; + } + if (desc !== "true") { + return desc; + } else { + return ( + <a href="https://mitmproxy.org/doc/features/filters.html" target="_blank"> + <i className="fa fa-external-link"></i> + Filter Documentation + </a> + ); + } + }, + 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.target.value === "" && + e.keyCode === Key.BACKSPACE) { + e.preventDefault(); + this.remove(); + } + }, + remove: function () { + if(this.props.onRemove) { + this.props.onRemove(); + } + }, + focus: function () { + this.refs.input.getDOMNode().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 = ( + <div className="popover bottom" onMouseEnter={this.onMouseEnter} onMouseLeave={this.onMouseLeave}> + <div className="arrow"></div> + <div className="popover-content"> + {this.getDesc()} + </div> + </div> + ); + } + + return ( + <div className={groupClassName}> + <span className="input-group-addon"> + <i className={icon} style={{color: this.props.color}}></i> + </span> + <input type="text" placeholder="filter expression" className="form-control" + ref="input" + onChange={this.onChange} + onFocus={this.onFocus} + onBlur={this.onBlur} + onKeyDown={this.onKeyDown} + value={this.state.value}/> + {popover} + </div> + ); + } +}); + var MainMenu = React.createClass({ mixins: [Navigation, State], - getInitialState: function(){ - this.onQueryChange(Query.FILTER, function(oldVal, nextVal){ - this.setState({filter: nextVal}); - }.bind(this)); + getInitialState: function () { return { - filter: this.getQuery()[Query.FILTER] + filter: this.getQuery()[Query.FILTER] || "", + highlight: (this.getQuery()[Query.HIGHLIGHT] || "").split("&").map(decodeURIComponent) }; }, statics: { @@ -18,16 +131,64 @@ var MainMenu = React.createClass({ }); }, clearFlows: function () { - $.post("/flows/clear"); + $.post("/clear"); }, - setFilter: function(e){ - e.preventDefault(); - this.setQuery(Query.FILTER, this.state.filter); + applyFilter: function (filter, highlight) { + var d = {}; + d[Query.FILTER] = filter; + d[Query.HIGHLIGHT] = highlight.map(encodeURIComponent).join("&"); + this.setQuery(d); }, - onFilterChange: function(e){ - this.setState({filter: e.target.value}); + onFilterChange: function (val) { + this.setState({filter: val}); + this.applyFilter(val, this.state.highlight); + }, + onHighlightChange: function (index, val) { + var highlight = this.state.highlight.slice(); + highlight[index] = val; + if (highlight[highlight.length - 1] !== "" && highlight.length < 14) { + highlight.push(""); + } + this.setState({ + highlight: highlight + }); + this.applyFilter(this.state.filter, highlight); + }, + onHighlightRemove: function (index) { + if (this.state.highlight.length > 1 && index < this.state.highlight.length - 1) { + var highlight = this.state.highlight.slice(); + highlight.splice(index, 1); + this.setState({ + highlight: highlight + }); + } + this.refs["highlight-" + Math.max(0, index - 1)].focus(); + }, + getColor: function (index) { + var colors = [ + "#ff7f0e", "#2ca02c", "#d62728", "#9467bd", "#1f77b4", "#bcbd22", "#17becf", + "#ffbb78", "#98df8a", "#ff9896", "#c5b0d5", "#aec7e8", "#dbdb8d", "#9edae5" + ]; + return colors[index % colors.length]; }, render: function () { + var highlightFilterInputs = []; + for (var i = 0; i < this.state.highlight.length; i++) { + highlightFilterInputs.push(<span key={"placeholder-" + i}> </span>); + highlightFilterInputs.push( + <FilterInput + key={"highlight-" + i} + ref={"highlight-" + i} + type="tag" + color={this.getColor(i)} + value={this.state.highlight[i]} + onChange={this.onHighlightChange.bind(this, i)} + onRemove={this.onHighlightRemove.bind(this, i)} + /> + ); + + } + return ( <div> <button className={"btn " + (this.props.settings.showEventLog ? "btn-primary" : "btn-default")} onClick={this.toggleEventLog}> @@ -39,13 +200,11 @@ var MainMenu = React.createClass({ <i className="fa fa-eraser"></i> Clear Flows </button> - - <form className="form-inline" onSubmit={this.setFilter} style={{display:"inline-block"}}> - <input type="text" placeholder="filter expression" - onChange={this.onFilterChange} value={this.state.filter} - className="form-control" - /> - </form> + + <form className="form-inline" style={{display:"inline"}}> + <FilterInput type="filter" color="black" value={this.state.filter} onChange={this.onFilterChange} /> + { highlightFilterInputs } + </form> </div> ); diff --git a/web/src/js/components/mainview.jsx.js b/web/src/js/components/mainview.jsx.js index acafdf34..e4bbbde2 100644 --- a/web/src/js/components/mainview.jsx.js +++ b/web/src/js/components/mainview.jsx.js @@ -9,7 +9,7 @@ var MainView = React.createClass({ }; }, getViewFilt: function(){ - return Filt.parse(this.getQuery()[Query.FILTER]); + return Filt.parse(this.getQuery()[Query.FILTER] || ""); }, getViewSort: function(){ }, @@ -104,6 +104,12 @@ var MainView = React.createClass({ case Key.PAGE_UP: this.selectFlowRelative(-10); break; + case Key.END: + this.selectFlowRelative(+1e10); + break; + case Key.HOME: + this.selectFlowRelative(-1e10); + break; case Key.ESC: this.selectFlow(null); break; |