aboutsummaryrefslogtreecommitdiffstats
path: root/web/src/js/components/flowview/messages.js
diff options
context:
space:
mode:
authorMaximilian Hils <git@maximilianhils.com>2015-03-23 00:24:56 +0100
committerMaximilian Hils <git@maximilianhils.com>2015-03-23 00:24:56 +0100
commit968c7021dfef00c459899520921faf7367e923d9 (patch)
tree71df74f580e277add1f11439ea3ae32019f13789 /web/src/js/components/flowview/messages.js
parent2acd77dea025b489d5d0ca19fc1c84901ac335d4 (diff)
downloadmitmproxy-968c7021dfef00c459899520921faf7367e923d9.tar.gz
mitmproxy-968c7021dfef00c459899520921faf7367e923d9.tar.bz2
mitmproxy-968c7021dfef00c459899520921faf7367e923d9.zip
web: add basic edit capability for first line
Diffstat (limited to 'web/src/js/components/flowview/messages.js')
-rw-r--r--web/src/js/components/flowview/messages.js231
1 files changed, 212 insertions, 19 deletions
diff --git a/web/src/js/components/flowview/messages.js b/web/src/js/components/flowview/messages.js
index fe8fa200..cb8516fc 100644
--- a/web/src/js/components/flowview/messages.js
+++ b/web/src/js/components/flowview/messages.js
@@ -1,5 +1,8 @@
var React = require("react");
+var _ = require("lodash");
+var common = require("../common.js");
+var actions = require("../../actions.js");
var flowutils = require("../../flow/utils.js");
var utils = require("../../utils.js");
var ContentView = require("./contentview.js");
@@ -24,20 +27,217 @@ var Headers = React.createClass({
}
});
-var Request = React.createClass({
+var InlineInput = React.createClass({
+ mixins: [common.ChildFocus],
+ getInitialState: function () {
+ return {
+ editable: false
+ };
+ },
+ render: function () {
+ var Tag = this.props.tag || "span";
+ var className = "inline-input " + (this.props.className || "");
+ var html = {__html: _.escape(this.props.content)};
+ return <Tag
+ {...this.props}
+ tabIndex="0"
+ className={className}
+ contentEditable={this.state.editable || undefined}
+ onInput={this.onInput}
+ onFocus={this.onFocus}
+ onBlur={this.onBlur}
+ onKeyDown={this.onKeyDown}
+ dangerouslySetInnerHTML={html}
+ />;
+ },
+ onKeyDown: function (e) {
+ e.stopPropagation();
+ switch (e.keyCode) {
+ case utils.Key.ESC:
+ this.blur();
+ break;
+ case utils.Key.ENTER:
+ e.preventDefault();
+ if (!e.ctrlKey) {
+ this.blur();
+ } else {
+ this.props.onDone && this.props.onDone();
+ }
+ break;
+ default:
+ break;
+ }
+ },
+ blur: function(){
+ this.getDOMNode().blur();
+ this.context.returnFocus && this.context.returnFocus();
+ },
+ selectContents: function () {
+ var range = document.createRange();
+ range.selectNodeContents(this.getDOMNode());
+ var sel = window.getSelection();
+ sel.removeAllRanges();
+ sel.addRange(range);
+ },
+ onFocus: function () {
+ this.setState({editable: true}, this.selectContents);
+ },
+ onBlur: function (e) {
+ this.setState({editable: false});
+ this.handleChange();
+ this.props.onDone && this.props.onDone();
+ },
+ onInput: function () {
+ this.handleChange();
+ },
+ handleChange: function () {
+ var content = this.getDOMNode().textContent;
+ if (content !== this.props.content) {
+ this.props.onChange(content);
+ }
+ }
+});
+
+var ValidateInlineInput = React.createClass({
+ getInitialState: function () {
+ return {
+ content: ""+this.props.content,
+ originalContent: ""+this.props.content
+ };
+ },
+ onChange: function (val) {
+ this.setState({
+ content: val
+ });
+ },
+ onDone: function () {
+ if (this.state.content === this.state.originalContent) {
+ return true;
+ }
+ if (this.props.isValid(this.state.content)) {
+ this.props.onChange(this.state.content);
+ } else {
+ this.setState({
+ content: this.state.originalContent
+ });
+ }
+ },
+ componentWillReceiveProps: function (nextProps) {
+ if (nextProps.content !== this.state.content) {
+ this.setState({
+ content: ""+nextProps.content,
+ originalContent: ""+nextProps.content
+ })
+ }
+ },
+ render: function () {
+ var className = this.props.className || "";
+ if (this.props.isValid(this.state.content)) {
+ className += " has-success";
+ } else {
+ className += " has-warning"
+ }
+ return <InlineInput {...this.props}
+ className={className}
+ content={this.state.content}
+ onChange={this.onChange}
+ onDone={this.onDone}
+ />;
+ }
+});
+
+var RequestLine = React.createClass({
render: function () {
var flow = this.props.flow;
- var first_line = [
- flow.request.method,
- flowutils.RequestUtils.pretty_url(flow.request),
- "HTTP/" + flow.request.httpversion.join(".")
- ].join(" ");
+ var url = flowutils.RequestUtils.pretty_url(flow.request);
+ var httpver = "HTTP/" + flow.request.httpversion.join(".");
- //TODO: Styling
+ return <div className="first-line request-line">
+ <ValidateInlineInput content={flow.request.method} onChange={this.onMethodChange} isValid={this.isValidMethod}/>
+ &nbsp;
+ <ValidateInlineInput content={url} onChange={this.onUrlChange} isValid={this.isValidUrl} />
+ &nbsp;
+ <ValidateInlineInput 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;
+ },
+ onMethodChange: function (nextMethod) {
+ actions.FlowActions.update(
+ this.props.flow,
+ {request: {method: nextMethod}}
+ );
+ },
+ onUrlChange: function (nextUrl) {
+ var props = flowutils.parseUrl(nextUrl);
+ props.path = props.path || "";
+ actions.FlowActions.update(
+ this.props.flow,
+ {request: props}
+ );
+ },
+ onHttpVersionChange: function (nextVer) {
+ var ver = flowutils.parseHttpVersion(nextVer);
+ actions.FlowActions.update(
+ this.props.flow,
+ {request: {httpversion: ver}}
+ );
+ }
+});
+var ResponseLine = React.createClass({
+ render: function () {
+ 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} />
+ &nbsp;
+ <ValidateInlineInput content={flow.response.code} onChange={this.onCodeChange} isValid={this.isValidCode} />
+ &nbsp;
+ <ValidateInlineInput content={flow.response.msg} onChange={this.onMsgChange} isValid={this.isValidMsg} />
+
+ </div>;
+ },
+ isValidCode: function (code) {
+ return /^\d+$/.test(code);
+ },
+ isValidMsg: function () {
+ return true;
+ },
+ onHttpVersionChange: function (nextVer) {
+ var ver = flowutils.parseHttpVersion(nextVer);
+ actions.FlowActions.update(
+ this.props.flow,
+ {response: {httpversion: ver}}
+ );
+ },
+ onMsgChange: function(nextMsg){
+ actions.FlowActions.update(
+ this.props.flow,
+ {response: {msg: nextMsg}}
+ );
+ },
+ onCodeChange: function(nextCode){
+ nextCode = parseInt(nextCode);
+ actions.FlowActions.update(
+ this.props.flow,
+ {response: {code: nextCode}}
+ );
+ }
+});
+
+var Request = React.createClass({
+ render: function () {
+ var flow = this.props.flow;
return (
- <section>
- <div className="first-line">{ first_line }</div>
+ <section className="request">
+ <RequestLine flow={flow}/>
+ {/*<ResponseLine flow={flow}/>*/}
<Headers message={flow.request}/>
<hr/>
<ContentView flow={flow} message={flow.request}/>
@@ -49,17 +249,10 @@ var Request = React.createClass({
var Response = React.createClass({
render: function () {
var flow = this.props.flow;
- var first_line = [
- "HTTP/" + flow.response.httpversion.join("."),
- flow.response.code,
- flow.response.msg
- ].join(" ");
-
- //TODO: Styling
-
return (
- <section>
- <div className="first-line">{ first_line }</div>
+ <section className="response">
+ {/*<RequestLine flow={flow}/>*/}
+ <ResponseLine flow={flow}/>
<Headers message={flow.response}/>
<hr/>
<ContentView flow={flow} message={flow.response}/>