import React from "react"; import ReactDOM from 'react-dom'; import $ from "jquery"; import {connect} from 'react-redux' import Filt from "../filt/filt.js"; import {Key} from "../utils.js"; import {ToggleInputButton, ToggleButton} from "./common.js"; import {SettingsActions, FlowActions} from "../actions.js"; import {Query} from "../actions.js"; import {SettingsState} from "./common.js"; import {ToggleEventLog} from "./eventlog" var FilterDocs = React.createClass({ statics: { xhr: false, doc: false }, componentWillMount: function () { if (!FilterDocs.doc) { FilterDocs.xhr = $.getJSON("/filter-help").done(function (doc) { FilterDocs.doc = doc; FilterDocs.xhr = false; }); } if (FilterDocs.xhr) { FilterDocs.xhr.done(function () { this.forceUpdate(); }.bind(this)); } }, render: function () { if (!FilterDocs.doc) { return ; } else { var commands = FilterDocs.doc.commands.map(function (c) { return {c[0].replace(" ", '\u00a0')} {c[1]} ; }); commands.push(   mitmproxy docs ); return {commands}
; } } }); var FilterInput = React.createClass({ contextTypes: { returnFocus: React.PropTypes.func }, getInitialState: function () { // Consider both focus and mouseover for showing/hiding the tooltip, // because onBlur of the input is triggered before the click on the tooltip // finalized, hiding the tooltip just as the user clicks on it. return { value: this.props.value, focus: false, mousefocus: false }; }, componentWillReceiveProps: function (nextProps) { this.setState({value: nextProps.value}); }, onChange: function (e) { var nextValue = e.target.value; this.setState({ value: nextValue }); // Only propagate valid filters upwards. if (this.isValid(nextValue)) { this.props.onChange(nextValue); } }, isValid: function (filt) { try { var str = filt || this.state.value; if(str){ Filt.parse(filt || this.state.value); } return true; } catch (e) { return false; } }, getDesc: function () { if(this.state.value) { try { return Filt.parse(this.state.value).desc; } catch (e) { return "" + e; } } return ; }, onFocus: function () { this.setState({focus: true}); }, onBlur: function () { this.setState({focus: false}); }, onMouseEnter: function () { this.setState({mousefocus: true}); }, onMouseLeave: function () { this.setState({mousefocus: false}); }, onKeyDown: function (e) { if (e.keyCode === Key.ESC || e.keyCode === Key.ENTER) { this.blur(); // If closed using ESC/ENTER, hide the tooltip. this.setState({mousefocus: false}); } e.stopPropagation(); }, blur: function () { ReactDOM.findDOMNode(this.refs.input).blur(); this.context.returnFocus(); }, select: function () { ReactDOM.findDOMNode(this.refs.input).select(); }, render: function () { var isValid = this.isValid(); var icon = "fa fa-fw fa-" + this.props.type; var groupClassName = "filter-input input-group" + (isValid ? "" : " has-error"); var popover; if (this.state.focus || this.state.mousefocus) { popover = (
{this.getDesc()}
); } return (
{popover}
); } }); export var MainMenu = React.createClass({ propTypes: { settings: React.PropTypes.object.isRequired, }, statics: { title: "Start", route: "flows" }, onSearchChange: function (val) { var d = {}; d[Query.SEARCH] = val; this.props.updateLocation(undefined, d); }, onHighlightChange: function (val) { var d = {}; d[Query.HIGHLIGHT] = val; this.props.updateLocation(undefined, d); }, onInterceptChange: function (val) { SettingsActions.update({intercept: val}); }, render: function () { var search = this.props.query[Query.SEARCH] || ""; var highlight = this.props.query[Query.HIGHLIGHT] || ""; var intercept = this.props.settings.intercept || ""; return (
); } }); var ViewMenu = React.createClass({ statics: { title: "View", route: "flows" }, render: function () { return (
); } }); export const OptionMenu = (props) => { const {mode, intercept, showhost, no_upstream_cert, rawtcp, http2, anticache, anticomp, stickycookie, stickyauth, stream} = props.settings; return (
SettingsActions.update({showhost: !showhost})} /> SettingsActions.update({no_upstream_cert: !no_upstream_cert})} /> SettingsActions.update({rawtcp: !rawtcp})} /> SettingsActions.update({http2: !http2})} /> SettingsActions.update({anticache: !anticache})} /> SettingsActions.update({anticomp: !anticomp})} /> SettingsActions.update({stickyauth: (!stickyauth ? txt : null)})} /> SettingsActions.update({stickycookie: (!stickycookie ? txt : null)})} /> SettingsActions.update({stream: (!stream ? txt : null)})} />
); }; OptionMenu.title = "Options"; OptionMenu.propTypes = { settings: React.PropTypes.object.isRequired }; var ReportsMenu = React.createClass({ statics: { title: "Visualization", route: "reports" }, render: function () { return
Reports Menu
; } }); var FileMenu = React.createClass({ getInitialState: function () { return { showFileMenu: false }; }, handleFileClick: function (e) { e.preventDefault(); if (!this.state.showFileMenu) { var close = function () { this.setState({showFileMenu: false}); document.removeEventListener("click", close); }.bind(this); document.addEventListener("click", close); this.setState({ showFileMenu: true }); } }, handleNewClick: function (e) { e.preventDefault(); if (confirm("Delete all flows?")) { FlowActions.clear(); } }, handleOpenClick: function (e) { this.fileInput.click(); e.preventDefault(); }, handleOpenFile: function (e) { if (e.target.files.length > 0) { FlowActions.upload(e.target.files[0]); this.fileInput.value = ""; } e.preventDefault(); }, handleSaveClick: function (e) { e.preventDefault(); FlowActions.download(); }, handleShutdownClick: function (e) { e.preventDefault(); console.error("unimplemented: handleShutdownClick"); }, render: function () { var fileMenuClass = "dropdown pull-left" + (this.state.showFileMenu ? " open" : ""); return (
mitmproxy
); } }); var header_entries = [MainMenu, ViewMenu, OptionMenu /*, ReportsMenu */]; export var Header = React.createClass({ propTypes: { settings: React.PropTypes.object.isRequired, }, getInitialState: function () { return { active: header_entries[0] }; }, handleClick: function (active, e) { e.preventDefault(); this.props.updateLocation(active.route); this.setState({active: active}); }, render: function () { var header = header_entries.map(function (entry, i) { var className; if (entry === this.state.active) { className = "active"; } else { className = ""; } return ( {entry.title} ); }.bind(this)); return (
); } });