diff options
author | Maximilian Hils <git@maximilianhils.com> | 2015-03-27 15:30:19 +0100 |
---|---|---|
committer | Maximilian Hils <git@maximilianhils.com> | 2015-03-27 15:30:19 +0100 |
commit | 42cd942b64e53db9feb5f6c8b2a95669e97b1230 (patch) | |
tree | c0b2714a103ad57a9b9c21cf4d4b4c834e77df0d /web/src/js/components/flowview | |
parent | 04f2467e84905d44483469cf43ada4ec44faa203 (diff) | |
download | mitmproxy-42cd942b64e53db9feb5f6c8b2a95669e97b1230.tar.gz mitmproxy-42cd942b64e53db9feb5f6c8b2a95669e97b1230.tar.bz2 mitmproxy-42cd942b64e53db9feb5f6c8b2a95669e97b1230.zip |
web: initial attempt at header editor
Diffstat (limited to 'web/src/js/components/flowview')
-rw-r--r-- | web/src/js/components/flowview/messages.js | 154 |
1 files changed, 131 insertions, 23 deletions
diff --git a/web/src/js/components/flowview/messages.js b/web/src/js/components/flowview/messages.js index cb8516fc..7feaa634 100644 --- a/web/src/js/components/flowview/messages.js +++ b/web/src/js/components/flowview/messages.js @@ -8,15 +8,77 @@ var utils = require("../../utils.js"); var ContentView = require("./contentview.js"); var Headers = React.createClass({ + propTypes: { + onChange: React.PropTypes.func.isRequired, + message: React.PropTypes.object.isRequired + }, + onChange: function (row, col, val) { + var nextHeaders = _.cloneDeep(this.props.message.headers); + nextHeaders[row][col] = val; + if (!nextHeaders[row][0] && !nextHeaders[row][1]) { + // do not delete last row + if (nextHeaders.length === 1) { + nextHeaders[0][0] = "Name"; + nextHeaders[0][1] = "Value"; + } else { + nextHeaders.splice(row, 1); + // manually move selection target if this has been the last row. + if(row === nextHeaders.length){ + this._nextSel = (row-1)+"-value"; + } + } + } + this.props.onChange(nextHeaders); + }, + onTab: function (row, col, e) { + var headers = this.props.message.headers; + if (row === headers.length - 1 && col === 1) { + e.preventDefault(); + + var nextHeaders = _.cloneDeep(this.props.message.headers); + nextHeaders.push(["Name", "Value"]); + this.props.onChange(nextHeaders); + this._nextSel = (row + 1) + "-key"; + } + }, + componentDidUpdate: function () { + if (this._nextSel && this.refs[this._nextSel]) { + this.refs[this._nextSel].focus(); + this._nextSel = undefined; + } + }, + onRemove: function (row, col, e) { + if (col === 1) { + e.preventDefault(); + this.refs[row + "-key"].focus(); + } else if (row > 0) { + e.preventDefault(); + this.refs[(row - 1) + "-value"].focus(); + } + }, render: function () { + var rows = this.props.message.headers.map(function (header, i) { + + var kEdit = <HeaderInlineInput + ref={i + "-key"} + content={header[0]} + onChange={this.onChange.bind(null, i, 0)} + onRemove={this.onRemove.bind(null, i, 0)} + onTab={this.onTab.bind(null, i, 0)}/>; + var vEdit = <HeaderInlineInput + ref={i + "-value"} + content={header[1]} + onChange={this.onChange.bind(null, i, 1)} + onRemove={this.onRemove.bind(null, i, 1)} + onTab={this.onTab.bind(null, i, 1)}/>; return ( <tr key={i}> - <td className="header-name">{header[0] + ":"}</td> - <td className="header-value">{header[1]}</td> + <td className="header-name">{kEdit}:</td> + <td className="header-value">{vEdit}</td> </tr> ); - }); + }.bind(this)); return ( <table className="header-table"> <tbody> @@ -27,8 +89,13 @@ var Headers = React.createClass({ } }); + var InlineInput = React.createClass({ mixins: [common.ChildFocus], + propTypes: { + content: React.PropTypes.string.isRequired, //must be string to match strict equality. + onChange: React.PropTypes.func.isRequired, + }, getInitialState: function () { return { editable: false @@ -65,10 +132,11 @@ var InlineInput = React.createClass({ } break; default: + this.props.onKeyDown && this.props.onKeyDown(e); break; } }, - blur: function(){ + blur: function () { this.getDOMNode().blur(); this.context.returnFocus && this.context.returnFocus(); }, @@ -98,17 +166,49 @@ var InlineInput = React.createClass({ } }); +var HeaderInlineInput = React.createClass({ + render: function () { + return <InlineInput ref="input" {...this.props} onKeyDown={this.onKeyDown}/>; + }, + focus: function () { + this.getDOMNode().focus(); + }, + onKeyDown: function (e) { + switch (e.keyCode) { + case utils.Key.BACKSPACE: + var s = window.getSelection().getRangeAt(0); + if (s.startOffset === 0 && s.endOffset === 0) { + this.props.onRemove(e); + } + break; + case utils.Key.TAB: + if(!e.shiftKey){ + this.props.onTab(e); + } + break; + } + } +}); + var ValidateInlineInput = React.createClass({ + propTypes: { + onChange: React.PropTypes.func.isRequired, + isValid: React.PropTypes.func.isRequired, + immediate: React.PropTypes.bool + }, getInitialState: function () { return { - content: ""+this.props.content, - originalContent: ""+this.props.content + content: this.props.content, + originalContent: this.props.content }; }, onChange: function (val) { this.setState({ content: val }); + if (this.props.immediate && val !== this.state.originalContent && this.props.isValid(val)) { + this.props.onChange(val); + } }, onDone: function () { if (this.state.content === this.state.originalContent) { @@ -125,8 +225,8 @@ var ValidateInlineInput = React.createClass({ componentWillReceiveProps: function (nextProps) { if (nextProps.content !== this.state.content) { this.setState({ - content: ""+nextProps.content, - originalContent: ""+nextProps.content + content: nextProps.content, + originalContent: nextProps.content }) } }, @@ -153,16 +253,13 @@ var RequestLine = React.createClass({ var httpver = "HTTP/" + flow.request.httpversion.join("."); return <div className="first-line request-line"> - <ValidateInlineInput content={flow.request.method} onChange={this.onMethodChange} isValid={this.isValidMethod}/> + <InlineInput content={flow.request.method} onChange={this.onMethodChange}/> <ValidateInlineInput content={url} onChange={this.onUrlChange} isValid={this.isValidUrl} /> - <ValidateInlineInput content={httpver} onChange={this.onHttpVersionChange} isValid={flowutils.isValidHttpVersion} /> + <ValidateInlineInput immediate content={httpver} onChange={this.onHttpVersionChange} isValid={flowutils.isValidHttpVersion} /> </div> }, - isValidMethod: function (method) { - return true; - }, isValidUrl: function (url) { var u = flowutils.parseUrl(url); return !!u.host; @@ -195,20 +292,17 @@ var ResponseLine = React.createClass({ var flow = this.props.flow; var httpver = "HTTP/" + flow.response.httpversion.join("."); return <div className="first-line response-line"> - <ValidateInlineInput content={httpver} onChange={this.onHttpVersionChange} isValid={flowutils.isValidHttpVersion} /> + <ValidateInlineInput immediate content={httpver} onChange={this.onHttpVersionChange} isValid={flowutils.isValidHttpVersion} /> - <ValidateInlineInput content={flow.response.code} onChange={this.onCodeChange} isValid={this.isValidCode} /> + <ValidateInlineInput immediate content={flow.response.code + ""} onChange={this.onCodeChange} isValid={this.isValidCode} /> - <ValidateInlineInput content={flow.response.msg} onChange={this.onMsgChange} isValid={this.isValidMsg} /> + <InlineInput content={flow.response.msg} onChange={this.onMsgChange}/> </div>; }, isValidCode: function (code) { return /^\d+$/.test(code); }, - isValidMsg: function () { - return true; - }, onHttpVersionChange: function (nextVer) { var ver = flowutils.parseHttpVersion(nextVer); actions.FlowActions.update( @@ -216,13 +310,13 @@ var ResponseLine = React.createClass({ {response: {httpversion: ver}} ); }, - onMsgChange: function(nextMsg){ + onMsgChange: function (nextMsg) { actions.FlowActions.update( this.props.flow, {response: {msg: nextMsg}} ); }, - onCodeChange: function(nextCode){ + onCodeChange: function (nextCode) { nextCode = parseInt(nextCode); actions.FlowActions.update( this.props.flow, @@ -238,11 +332,18 @@ var Request = React.createClass({ <section className="request"> <RequestLine flow={flow}/> {/*<ResponseLine flow={flow}/>*/} - <Headers message={flow.request}/> + <Headers message={flow.request} onChange={this.onHeaderChange}/> <hr/> <ContentView flow={flow} message={flow.request}/> </section> ); + }, + onHeaderChange: function (nextHeaders) { + actions.FlowActions.update(this.props.flow, { + request: { + headers: nextHeaders + } + }); } }); @@ -253,11 +354,18 @@ var Response = React.createClass({ <section className="response"> {/*<RequestLine flow={flow}/>*/} <ResponseLine flow={flow}/> - <Headers message={flow.response}/> + <Headers message={flow.response} onChange={this.onHeaderChange}/> <hr/> <ContentView flow={flow} message={flow.response}/> </section> ); + }, + onHeaderChange: function (nextHeaders) { + actions.FlowActions.update(this.props.flow, { + response: { + headers: nextHeaders + } + }); } }); |