diff options
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 +            } +        });      }  });  | 
