diff options
Diffstat (limited to 'web/src/js/components/flowview/messages.js')
| -rw-r--r-- | web/src/js/components/flowview/messages.js | 326 | 
1 files changed, 326 insertions, 0 deletions
diff --git a/web/src/js/components/flowview/messages.js b/web/src/js/components/flowview/messages.js new file mode 100644 index 00000000..7ac95d85 --- /dev/null +++ b/web/src/js/components/flowview/messages.js @@ -0,0 +1,326 @@ +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"); +var ValueEditor = require("../editor.js").ValueEditor; + +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); +    }, +    edit: function () { +        this.refs["0-key"].focus(); +    }, +    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 = <HeaderEditor +                ref={i + "-key"} +                content={header[0]} +                onDone={this.onChange.bind(null, i, 0)} +                onRemove={this.onRemove.bind(null, i, 0)} +                onTab={this.onTab.bind(null, i, 0)}/>; +            var vEdit = <HeaderEditor +                ref={i + "-value"} +                content={header[1]} +                onDone={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">{kEdit}:</td> +                    <td className="header-value">{vEdit}</td> +                </tr> +            ); +        }.bind(this)); +        return ( +            <table className="header-table"> +                <tbody> +                    {rows} +                </tbody> +            </table> +        ); +    } +}); + +var HeaderEditor = React.createClass({ +    render: function () { +        return <ValueEditor ref="input" {...this.props} onKeyDown={this.onKeyDown} inline/>; +    }, +    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 RequestLine = React.createClass({ +    render: function () { +        var flow = this.props.flow; +        var url = flowutils.RequestUtils.pretty_url(flow.request); +        var httpver = flow.request.http_version; + +        return <div className="first-line request-line"> +            <ValueEditor +                ref="method" +                content={flow.request.method} +                onDone={this.onMethodChange} +                inline/> +          +            <ValueEditor +                ref="url" +                content={url} +                onDone={this.onUrlChange} +                isValid={this.isValidUrl} +                inline/> +          +            <ValueEditor +                ref="httpVersion" +                content={httpver} +                onDone={this.onHttpVersionChange} +                isValid={flowutils.isValidHttpVersion} +                inline/> +        </div> +    }, +    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: {http_version: ver}} +        ); +    } +}); + +var ResponseLine = React.createClass({ +    render: function () { +        var flow = this.props.flow; +        var httpver = flow.response.http_version; +        return <div className="first-line response-line"> +            <ValueEditor +                ref="httpVersion" +                content={httpver} +                onDone={this.onHttpVersionChange} +                isValid={flowutils.isValidHttpVersion} +                inline/> +          +            <ValueEditor +                ref="code" +                content={flow.response.status_code + ""} +                onDone={this.onCodeChange} +                isValid={this.isValidCode} +                inline/> +          +            <ValueEditor +                ref="msg" +                content={flow.response.msg} +                onDone={this.onMsgChange} +                inline/> +        </div>; +    }, +    isValidCode: function (code) { +        return /^\d+$/.test(code); +    }, +    onHttpVersionChange: function (nextVer) { +        var ver = flowutils.parseHttpVersion(nextVer); +        actions.FlowActions.update( +            this.props.flow, +            {response: {http_version: 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 className="request"> +                <RequestLine ref="requestLine" flow={flow}/> +                {/*<ResponseLine flow={flow}/>*/} +                <Headers ref="headers" message={flow.request} onChange={this.onHeaderChange}/> +                <hr/> +                <ContentView flow={flow} message={flow.request}/> +            </section> +        ); +    }, +    edit: function (k) { +        switch (k) { +            case "m": +                this.refs.requestLine.refs.method.focus(); +                break; +            case "u": +                this.refs.requestLine.refs.url.focus(); +                break; +            case "v": +                this.refs.requestLine.refs.httpVersion.focus(); +                break; +            case "h": +                this.refs.headers.edit(); +                break; +            default: +                throw "Unimplemented: " + k; +        } +    }, +    onHeaderChange: function (nextHeaders) { +        actions.FlowActions.update(this.props.flow, { +            request: { +                headers: nextHeaders +            } +        }); +    } +}); + +var Response = React.createClass({ +    render: function () { +        var flow = this.props.flow; +        return ( +            <section className="response"> +                {/*<RequestLine flow={flow}/>*/} +                <ResponseLine ref="responseLine" flow={flow}/> +                <Headers ref="headers" message={flow.response} onChange={this.onHeaderChange}/> +                <hr/> +                <ContentView flow={flow} message={flow.response}/> +            </section> +        ); +    }, +    edit: function (k) { +        switch (k) { +            case "c": +                this.refs.responseLine.refs.status_code.focus(); +                break; +            case "m": +                this.refs.responseLine.refs.msg.focus(); +                break; +            case "v": +                this.refs.responseLine.refs.httpVersion.focus(); +                break; +            case "h": +                this.refs.headers.edit(); +                break; +            default: +                throw "Unimplemented: " + k; +        } +    }, +    onHeaderChange: function (nextHeaders) { +        actions.FlowActions.update(this.props.flow, { +            response: { +                headers: nextHeaders +            } +        }); +    } +}); + +var Error = React.createClass({ +    render: function () { +        var flow = this.props.flow; +        return ( +            <section> +                <div className="alert alert-warning"> +                {flow.error.msg} +                    <div> +                        <small>{ utils.formatTimeStamp(flow.error.timestamp) }</small> +                    </div> +                </div> +            </section> +        ); +    } +}); + +module.exports = { +    Request: Request, +    Response: Response, +    Error: Error +};  | 
