aboutsummaryrefslogtreecommitdiffstats
path: root/libmproxy/web/static/app.js
diff options
context:
space:
mode:
Diffstat (limited to 'libmproxy/web/static/app.js')
-rw-r--r--libmproxy/web/static/app.js159
1 files changed, 135 insertions, 24 deletions
diff --git a/libmproxy/web/static/app.js b/libmproxy/web/static/app.js
index e972e298..efd2bf95 100644
--- a/libmproxy/web/static/app.js
+++ b/libmproxy/web/static/app.js
@@ -676,7 +676,7 @@ var LogMessage = React.createClass({displayName: "LogMessage",
}
return (
React.createElement("div", null,
- indicator, " ", entry.message
+ indicator, " ", entry.message
)
);
},
@@ -1724,15 +1724,77 @@ var utils = require("../../utils.js");
var ContentView = require("./contentview.js");
var Headers = React.createClass({displayName: "Headers",
+ 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 = React.createElement(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 = React.createElement(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 (
React.createElement("tr", {key: i},
- React.createElement("td", {className: "header-name"}, header[0] + ":"),
- React.createElement("td", {className: "header-value"}, header[1])
+ React.createElement("td", {className: "header-name"}, kEdit, ":"),
+ React.createElement("td", {className: "header-value"}, vEdit)
)
);
- });
+ }.bind(this));
return (
React.createElement("table", {className: "header-table"},
React.createElement("tbody", null,
@@ -1743,8 +1805,13 @@ var Headers = React.createClass({displayName: "Headers",
}
});
+
var InlineInput = React.createClass({displayName: "InlineInput",
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
@@ -1781,10 +1848,11 @@ var InlineInput = React.createClass({displayName: "InlineInput",
}
break;
default:
+ this.props.onKeyDown && this.props.onKeyDown(e);
break;
}
},
- blur: function(){
+ blur: function () {
this.getDOMNode().blur();
this.context.returnFocus && this.context.returnFocus();
},
@@ -1814,17 +1882,49 @@ var InlineInput = React.createClass({displayName: "InlineInput",
}
});
+var HeaderInlineInput = React.createClass({displayName: "HeaderInlineInput",
+ render: function () {
+ return React.createElement(InlineInput, React.__spread({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({displayName: "ValidateInlineInput",
+ 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) {
@@ -1841,8 +1941,8 @@ var ValidateInlineInput = React.createClass({displayName: "ValidateInlineInput",
componentWillReceiveProps: function (nextProps) {
if (nextProps.content !== this.state.content) {
this.setState({
- content: ""+nextProps.content,
- originalContent: ""+nextProps.content
+ content: nextProps.content,
+ originalContent: nextProps.content
})
}
},
@@ -1869,16 +1969,13 @@ var RequestLine = React.createClass({displayName: "RequestLine",
var httpver = "HTTP/" + flow.request.httpversion.join(".");
return React.createElement("div", {className: "first-line request-line"},
- React.createElement(ValidateInlineInput, {content: flow.request.method, onChange: this.onMethodChange, isValid: this.isValidMethod}),
+ React.createElement(InlineInput, {content: flow.request.method, onChange: this.onMethodChange}),
" ",
React.createElement(ValidateInlineInput, {content: url, onChange: this.onUrlChange, isValid: this.isValidUrl}),
" ",
- React.createElement(ValidateInlineInput, {content: httpver, onChange: this.onHttpVersionChange, isValid: flowutils.isValidHttpVersion})
+ React.createElement(ValidateInlineInput, {immediate: true, content: httpver, onChange: this.onHttpVersionChange, isValid: flowutils.isValidHttpVersion})
)
},
- isValidMethod: function (method) {
- return true;
- },
isValidUrl: function (url) {
var u = flowutils.parseUrl(url);
return !!u.host;
@@ -1911,20 +2008,17 @@ var ResponseLine = React.createClass({displayName: "ResponseLine",
var flow = this.props.flow;
var httpver = "HTTP/" + flow.response.httpversion.join(".");
return React.createElement("div", {className: "first-line response-line"},
- React.createElement(ValidateInlineInput, {content: httpver, onChange: this.onHttpVersionChange, isValid: flowutils.isValidHttpVersion}),
+ React.createElement(ValidateInlineInput, {immediate: true, content: httpver, onChange: this.onHttpVersionChange, isValid: flowutils.isValidHttpVersion}),
" ",
- React.createElement(ValidateInlineInput, {content: flow.response.code, onChange: this.onCodeChange, isValid: this.isValidCode}),
+ React.createElement(ValidateInlineInput, {immediate: true, content: flow.response.code + "", onChange: this.onCodeChange, isValid: this.isValidCode}),
" ",
- React.createElement(ValidateInlineInput, {content: flow.response.msg, onChange: this.onMsgChange, isValid: this.isValidMsg})
+ React.createElement(InlineInput, {content: flow.response.msg, onChange: this.onMsgChange})
);
},
isValidCode: function (code) {
return /^\d+$/.test(code);
},
- isValidMsg: function () {
- return true;
- },
onHttpVersionChange: function (nextVer) {
var ver = flowutils.parseHttpVersion(nextVer);
actions.FlowActions.update(
@@ -1932,13 +2026,13 @@ var ResponseLine = React.createClass({displayName: "ResponseLine",
{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,
@@ -1954,11 +2048,18 @@ var Request = React.createClass({displayName: "Request",
React.createElement("section", {className: "request"},
React.createElement(RequestLine, {flow: flow}),
/*<ResponseLine flow={flow}/>*/
- React.createElement(Headers, {message: flow.request}),
+ React.createElement(Headers, {message: flow.request, onChange: this.onHeaderChange}),
React.createElement("hr", null),
React.createElement(ContentView, {flow: flow, message: flow.request})
)
);
+ },
+ onHeaderChange: function (nextHeaders) {
+ actions.FlowActions.update(this.props.flow, {
+ request: {
+ headers: nextHeaders
+ }
+ });
}
});
@@ -1969,11 +2070,18 @@ var Response = React.createClass({displayName: "Response",
React.createElement("section", {className: "response"},
/*<RequestLine flow={flow}/>*/
React.createElement(ResponseLine, {flow: flow}),
- React.createElement(Headers, {message: flow.response}),
+ React.createElement(Headers, {message: flow.response, onChange: this.onHeaderChange}),
React.createElement("hr", null),
React.createElement(ContentView, {flow: flow, message: flow.response})
)
);
+ },
+ onHeaderChange: function (nextHeaders) {
+ actions.FlowActions.update(this.props.flow, {
+ response: {
+ headers: nextHeaders
+ }
+ });
}
});
@@ -4826,6 +4934,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],