From 42cd942b64e53db9feb5f6c8b2a95669e97b1230 Mon Sep 17 00:00:00 2001 From: Maximilian Hils Date: Fri, 27 Mar 2015 15:30:19 +0100 Subject: web: initial attempt at header editor --- web/src/js/components/flowview/messages.js | 154 ++++++++++++++++++++++++----- web/src/js/flow/utils.js | 3 + 2 files changed, 134 insertions(+), 23 deletions(-) (limited to 'web') 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 = ; + var vEdit = ; return ( - {header[0] + ":"} - {header[1]} + {kEdit}: + {vEdit} ); - }); + }.bind(this)); return ( @@ -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 ; + }, + 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
- +     - +
}, - 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
- +   - +   - +
; }, 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({
{/**/} - +
); + }, + onHeaderChange: function (nextHeaders) { + actions.FlowActions.update(this.props.flow, { + request: { + headers: nextHeaders + } + }); } }); @@ -253,11 +354,18 @@ var Response = React.createClass({
{/**/} - +
); + }, + onHeaderChange: function (nextHeaders) { + actions.FlowActions.update(this.props.flow, { + response: { + headers: nextHeaders + } + }); } }); diff --git a/web/src/js/flow/utils.js b/web/src/js/flow/utils.js index aa91e3b6..d72febaa 100644 --- a/web/src/js/flow/utils.js +++ b/web/src/js/flow/utils.js @@ -80,6 +80,9 @@ var parseUrl = function (url) { //there are many correct ways to parse a URL, //however, a mitmproxy user may also wish to generate a not-so-correct URL. ;-) var parts = parseUrl_regex.exec(url); + if(!parts){ + return false; + } var scheme = parts[1], host = parts[2], -- cgit v1.2.3