aboutsummaryrefslogtreecommitdiffstats
path: root/web/src
diff options
context:
space:
mode:
Diffstat (limited to 'web/src')
-rw-r--r--web/src/css/header.less18
-rw-r--r--web/src/js/components/common.js84
-rw-r--r--web/src/js/components/eventlog.js4
-rw-r--r--web/src/js/components/flowview/contentview.js64
-rw-r--r--web/src/js/components/flowview/index.js6
-rw-r--r--web/src/js/components/footer.js32
-rw-r--r--web/src/js/components/header.js133
-rw-r--r--web/src/js/components/mainview.js13
-rw-r--r--web/src/js/components/proxyapp.js57
9 files changed, 269 insertions, 142 deletions
diff --git a/web/src/css/header.less b/web/src/css/header.less
index 065471d1..b1bd9c04 100644
--- a/web/src/css/header.less
+++ b/web/src/css/header.less
@@ -18,6 +18,7 @@ header {
.filter-input {
.make-sm-column(3, @menu-row-gutter-width);
+ margin-bottom:5px;
}
.filter-input .popover {
@@ -32,6 +33,19 @@ header {
}
}
-.menu .btn {
- margin: 2px 2px 2px 2px;
+.menu .toggle-btn {
+ .make-xs-column(4, @menu-row-gutter-width);
+ .make-sm-column(3, @menu-row-gutter-width);
+ .make-lg-column(2, @menu-row-gutter-width);
+ margin-bottom:5px;
+}
+
+.menu .toggle-btn .btn {
+ width: 100%;
+}
+
+.menu .toggle-input-btn {
+ .make-sm-column(6, @menu-row-gutter-width);
+ .make-lg-column(4, @menu-row-gutter-width);
+ margin-bottom:5px;
} \ No newline at end of file
diff --git a/web/src/js/components/common.js b/web/src/js/components/common.js
index 21ca454f..87c34ffc 100644
--- a/web/src/js/components/common.js
+++ b/web/src/js/components/common.js
@@ -1,33 +1,8 @@
import React from "react"
import ReactDOM from "react-dom"
+import {Key} from "../utils.js";
import _ from "lodash"
-export var Router = {
- contextTypes: {
- location: React.PropTypes.object,
- router: React.PropTypes.object.isRequired
- },
- updateLocation: function (pathname, queryUpdate) {
- if (pathname === undefined) {
- pathname = this.context.location.pathname;
- }
- var query = this.context.location.query;
- if (queryUpdate !== undefined) {
- for (var i in queryUpdate) {
- if (queryUpdate.hasOwnProperty(i)) {
- query[i] = queryUpdate[i] || undefined; //falsey values shall be removed.
- }
- }
- }
- this.context.router.replace({pathname, query});
- },
- getQuery: function () {
- // For whatever reason, react-router always returns the same object, which makes comparing
- // the current props with nextProps impossible. As a workaround, we just clone the query object.
- return _.clone(this.context.location.query);
- }
-};
-
export var Splitter = React.createClass({
getDefaultProps: function () {
return {
@@ -133,14 +108,55 @@ export var Splitter = React.createClass({
}
});
-export const ToggleComponent = (props) =>
- <div
- className={"btn " + (props.checked ? "btn-primary" : "btn-default")}
- onClick={props.onToggleChanged}>
- <span><i className={"fa " + (props.checked ? "fa-check-square-o" : "fa-square-o")}></i> {props.name}</span>
- </div>
+export const ToggleButton = (props) =>
+ <div className="input-group toggle-btn">
+ <div
+ className={"btn " + (props.checked ? "btn-primary" : "btn-default")}
+ onClick={props.onToggleChanged}>
+ <span className={"fa " + (props.checked ? "fa-check-square-o" : "fa-square-o")}>&nbsp;{props.name}</span>
+ </div>
+ </div>;
+
+ToggleButton.propTypes = {
+ name: React.PropTypes.string.isRequired,
+ onToggleChanged: React.PropTypes.func.isRequired
+};
+
+export class ToggleInputButton extends React.Component {
+ constructor(props) {
+ super(props);
+ this.state = {txt: props.txt};
+ }
-ToggleComponent.propTypes = {
+ render() {
+ return (
+ <div className="input-group toggle-input-btn">
+ <span
+ className="input-group-btn"
+ onClick={() => this.props.onToggleChanged(this.state.txt)}>
+ <div className={"btn " + (this.props.checked ? "btn-primary" : "btn-default")}>
+ <span className={"fa " + (this.props.checked ? "fa-check-square-o" : "fa-square-o")}/>
+ &nbsp;{this.props.name}
+ </div>
+ </span>
+ <input
+ className="form-control"
+ placeholder={this.props.placeholder}
+ disabled={this.props.checked}
+ value={this.state.txt}
+ type={this.props.inputType}
+ onChange={e => this.setState({txt: e.target.value})}
+ onKeyDown={e => {if (e.keyCode === Key.ENTER) this.props.onToggleChanged(this.state.txt); e.stopPropagation()}}/>
+ </div>
+ );
+ }
+}
+
+ToggleInputButton.propTypes = {
name: React.PropTypes.string.isRequired,
+ txt: React.PropTypes.string.isRequired,
onToggleChanged: React.PropTypes.func.isRequired
-} \ No newline at end of file
+};
+
+
+
diff --git a/web/src/js/components/eventlog.js b/web/src/js/components/eventlog.js
index d1b23ace..6e4f9096 100644
--- a/web/src/js/components/eventlog.js
+++ b/web/src/js/components/eventlog.js
@@ -1,7 +1,6 @@
import React from "react"
import ReactDOM from "react-dom"
import shallowEqual from "shallowequal"
-import {Router} from "./common.js"
import {Query} from "../actions.js"
import AutoScroll from "./helpers/AutoScroll";
import {calcVScroll} from "./helpers/VirtualScroll"
@@ -144,7 +143,6 @@ function ToggleFilter ({ name, active, toggleLevel }) {
const AutoScrollEventLog = AutoScroll(EventLogContents);
var EventLog = React.createClass({
- mixins: [Router],
getInitialState() {
return {
filter: {
@@ -157,7 +155,7 @@ var EventLog = React.createClass({
close() {
var d = {};
d[Query.SHOW_EVENTLOG] = undefined;
- this.updateLocation(undefined, d);
+ this.props.updateLocation(undefined, d);
},
toggleLevel(level) {
var filter = _.extend({}, this.state.filter);
diff --git a/web/src/js/components/flowview/contentview.js b/web/src/js/components/flowview/contentview.js
index 2743eec3..cbac9a75 100644
--- a/web/src/js/components/flowview/contentview.js
+++ b/web/src/js/components/flowview/contentview.js
@@ -4,11 +4,15 @@ import _ from "lodash";
import {MessageUtils} from "../../flow/utils.js";
import {formatSize} from "../../utils.js";
-var image_regex = /^image\/(png|jpe?g|gif|vnc.microsoft.icon|x-icon)$/i;
var ViewImage = React.createClass({
+ propTypes: {
+ flow: React.PropTypes.object.isRequired,
+ message: React.PropTypes.object.isRequired,
+ },
statics: {
+ regex: /^image\/(png|jpe?g|gif|vnc.microsoft.icon|x-icon)$/i,
matches: function (message) {
- return image_regex.test(MessageUtils.getContentType(message));
+ return ViewImage.regex.test(MessageUtils.getContentType(message));
}
},
render: function () {
@@ -19,7 +23,11 @@ var ViewImage = React.createClass({
}
});
-var RawMixin = {
+var ContentLoader = React.createClass({
+ propTypes: {
+ flow: React.PropTypes.object.isRequired,
+ message: React.PropTypes.object.isRequired,
+ },
getInitialState: function () {
return {
content: undefined,
@@ -66,41 +74,54 @@ var RawMixin = {
<i className="fa fa-spinner fa-spin"></i>
</div>;
}
- return this.renderContent();
+ return React.cloneElement(this.props.children, {
+ content: this.state.content
+ })
}
-};
+});
var ViewRaw = React.createClass({
- mixins: [RawMixin],
+ propTypes: {
+ content: React.PropTypes.string.isRequired,
+ },
statics: {
+ textView: true,
matches: function (message) {
return true;
}
},
- renderContent: function () {
- return <pre>{this.state.content}</pre>;
+ render: function () {
+ return <pre>{this.props.content}</pre>;
}
});
-var json_regex = /^application\/json$/i;
var ViewJSON = React.createClass({
- mixins: [RawMixin],
+ propTypes: {
+ content: React.PropTypes.string.isRequired,
+ },
statics: {
+ textView: true,
+ regex: /^application\/json$/i,
matches: function (message) {
- return json_regex.test(MessageUtils.getContentType(message));
+ return ViewJSON.regex.test(MessageUtils.getContentType(message));
}
},
- renderContent: function () {
- var json = this.state.content;
+ render: function () {
+ var json = this.props.content;
try {
json = JSON.stringify(JSON.parse(json), null, 2);
} catch (e) {
+ // @noop
}
return <pre>{json}</pre>;
}
});
var ViewAuto = React.createClass({
+ propTypes: {
+ message: React.PropTypes.object.isRequired,
+ flow: React.PropTypes.object.isRequired,
+ },
statics: {
matches: function () {
return false; // don't match itself
@@ -115,14 +136,18 @@ var ViewAuto = React.createClass({
}
},
render: function () {
+ var { message, flow } = this.props
var View = ViewAuto.findView(this.props.message);
- return <View {...this.props}/>;
+ if (View.textView) {
+ return <ContentLoader message={message} flow={flow}><View content="" /></ContentLoader>
+ } else {
+ return <View message={message} flow={flow} />
+ }
}
});
var all = [ViewAuto, ViewImage, ViewJSON, ViewRaw];
-
var ContentEmpty = React.createClass({
render: function () {
var message_name = this.props.flow.request === this.props.message ? "request" : "response";
@@ -210,6 +235,7 @@ var ContentView = React.createClass({
}
},
render: function () {
+ var { flow, message } = this.props
var message = this.props.message;
if (message.contentLength === 0) {
return <ContentEmpty {...this.props}/>;
@@ -222,7 +248,11 @@ var ContentView = React.createClass({
var downloadUrl = MessageUtils.getContentURL(this.props.flow, message);
return <div>
- <this.state.View {...this.props} />
+ {this.state.View.textView ? (
+ <ContentLoader flow={flow} message={message}><this.state.View content="" /></ContentLoader>
+ ) : (
+ <this.state.View flow={flow} message={message} />
+ )}
<div className="view-options text-center">
<ViewSelector selectView={this.selectView} active={this.state.View} message={message}/>
&nbsp;
@@ -234,4 +264,4 @@ var ContentView = React.createClass({
}
});
-export default ContentView; \ No newline at end of file
+export default ContentView;
diff --git a/web/src/js/components/flowview/index.js b/web/src/js/components/flowview/index.js
index 47531f58..6f4f7395 100644
--- a/web/src/js/components/flowview/index.js
+++ b/web/src/js/components/flowview/index.js
@@ -1,6 +1,5 @@
import React from "react";
-import {Router, StickyHeadMixin} from "../common.js"
import Nav from "./nav.js";
import {Request, Response, Error} from "./messages.js";
import Details from "./details.js";
@@ -15,7 +14,6 @@ var allTabs = {
};
var FlowView = React.createClass({
- mixins: [StickyHeadMixin, Router],
getInitialState: function () {
return {
prompt: false
@@ -39,7 +37,7 @@ var FlowView = React.createClass({
this.selectTab(tabs[nextIndex]);
},
selectTab: function (panel) {
- this.updateLocation(`/flows/${this.props.flow.id}/${panel}`);
+ this.props.updateLocation(`/flows/${this.props.flow.id}/${panel}`);
},
promptEdit: function () {
var options;
@@ -114,4 +112,4 @@ var FlowView = React.createClass({
}
});
-export default FlowView; \ No newline at end of file
+export default FlowView;
diff --git a/web/src/js/components/footer.js b/web/src/js/components/footer.js
index e2d96288..8fe1081b 100644
--- a/web/src/js/components/footer.js
+++ b/web/src/js/components/footer.js
@@ -1,4 +1,5 @@
import React from "react";
+import {formatSize} from "../utils.js"
import {SettingsState} from "./common.js";
Footer.propTypes = {
@@ -6,7 +7,7 @@ Footer.propTypes = {
};
export default function Footer({ settings }) {
- const {mode, intercept} = settings;
+ const {mode, intercept, showhost, no_upstream_cert, rawtcp, http2, anticache, anticomp, stickyauth, stickycookie, stream} = settings;
return (
<footer>
{mode && mode != "regular" && (
@@ -15,6 +16,35 @@ export default function Footer({ settings }) {
{intercept && (
<span className="label label-success">Intercept: {intercept}</span>
)}
+ {showhost && (
+ <span className="label label-success">showhost</span>
+ )}
+ {no_upstream_cert && (
+ <span className="label label-success">no-upstream-cert</span>
+ )}
+ {rawtcp && (
+ <span className="label label-success">raw-tcp</span>
+ )}
+ {!http2 && (
+ <span className="label label-success">no-http2</span>
+ )}
+ {anticache && (
+ <span className="label label-success">anticache</span>
+ )}
+ {anticomp && (
+ <span className="label label-success">anticomp</span>
+ )}
+ {stickyauth && (
+ <span className="label label-success">stickyauth: {stickyauth}</span>
+ )}
+ {stickycookie && (
+ <span className="label label-success">stickycookie: {stickycookie}</span>
+ )}
+ {stream && (
+ <span className="label label-success">stream: {formatSize(stream)}</span>
+ )}
+
+
</footer>
);
}
diff --git a/web/src/js/components/header.js b/web/src/js/components/header.js
index 226cb61f..643659c3 100644
--- a/web/src/js/components/header.js
+++ b/web/src/js/components/header.js
@@ -4,9 +4,10 @@ import $ from "jquery";
import Filt from "../filt/filt.js";
import {Key} from "../utils.js";
-import {Router, ToggleComponent} from "./common.js";
+import {ToggleInputButton, ToggleButton} from "./common.js";
import {SettingsActions, FlowActions} from "../actions.js";
import {Query} from "../actions.js";
+import {SettingsState} from "./common.js";
var FilterDocs = React.createClass({
statics: {
@@ -161,7 +162,6 @@ var FilterInput = React.createClass({
});
export var MainMenu = React.createClass({
- mixins: [Router],
propTypes: {
settings: React.PropTypes.object.isRequired,
},
@@ -172,19 +172,19 @@ export var MainMenu = React.createClass({
onSearchChange: function (val) {
var d = {};
d[Query.SEARCH] = val;
- this.updateLocation(undefined, d);
+ this.props.updateLocation(undefined, d);
},
onHighlightChange: function (val) {
var d = {};
d[Query.HIGHLIGHT] = val;
- this.updateLocation(undefined, d);
+ this.props.updateLocation(undefined, d);
},
onInterceptChange: function (val) {
SettingsActions.update({intercept: val});
},
render: function () {
- var search = this.getQuery()[Query.SEARCH] || "";
- var highlight = this.getQuery()[Query.HIGHLIGHT] || "";
+ var search = this.props.query[Query.SEARCH] || "";
+ var highlight = this.props.query[Query.HIGHLIGHT] || "";
var intercept = this.props.settings.intercept || "";
return (
@@ -224,72 +224,88 @@ var ViewMenu = React.createClass({
title: "View",
route: "flows"
},
- mixins: [Router],
toggleEventLog: function () {
var d = {};
- if (this.getQuery()[Query.SHOW_EVENTLOG]) {
+ if (this.props.query[Query.SHOW_EVENTLOG]) {
d[Query.SHOW_EVENTLOG] = undefined;
} else {
d[Query.SHOW_EVENTLOG] = "t"; // any non-false value will do it, keep it short
}
- this.updateLocation(undefined, d);
+ this.props.updateLocation(undefined, d);
console.log('toggleevent');
},
render: function () {
- var showEventLog = this.getQuery()[Query.SHOW_EVENTLOG];
+ var showEventLog = this.props.query[Query.SHOW_EVENTLOG];
return (
- <div>
- <ToggleComponent
- checked={showEventLog}
- name = "Show Eventlog"
- onToggleChanged={this.toggleEventLog}/>
- </div>
+ <div>
+ <div className="menu-row">
+ <ToggleButton
+ checked={showEventLog}
+ name = "Show Eventlog"
+ onToggleChanged={this.toggleEventLog}/>
+ </div>
+ <div className="clearfix"></div>
+ </div>
);
}
});
-
-class OptionMenu extends React.Component{
- static title = "Options";
- constructor(props){
- super(props);
- this.state = {
- options :
- [
- {name: "--host", checked: true},
- {name: "--no-upstream-cert", checked: false},
- {name: "--http2", checked: false},
- {name: "--anticache", checked: false},
- {name: "--anticomp", checked: false},
- {name: "--stickycookie", checked: true},
- {name: "--stickyauth", checked: false},
- {name: "--stream", checked: false}
- ]
- }
- }
- setOption(entry){
- console.log(entry.name);//TODO: get options from outside and remove state
- entry.checked = !entry.checked;
- this.setState({options: this.state.options});
- }
- render() {
- return (
+export const OptionMenu = (props) => {
+ const {mode, intercept, showhost, no_upstream_cert, rawtcp, http2, anticache, anticomp, stickycookie, stickyauth, stream} = props.settings;
+ return (
<div>
- {this.state.options.map((entry, i) => {
- return (
- <ToggleComponent
- key={i}
- checked={entry.checked}
- name = {entry.name}
- onToggleChanged={() => this.setOption(entry)}/>
- );
- })}
+ <div className="menu-row">
+ <ToggleButton name="showhost"
+ checked={showhost}
+ onToggleChanged={() => SettingsActions.update({showhost: !showhost})}
+ />
+ <ToggleButton name="no_upstream_cert"
+ checked={no_upstream_cert}
+ onToggleChanged={() => SettingsActions.update({no_upstream_cert: !no_upstream_cert})}
+ />
+ <ToggleButton name="rawtcp"
+ checked={rawtcp}
+ onToggleChanged={() => SettingsActions.update({rawtcp: !rawtcp})}
+ />
+ <ToggleButton name="http2"
+ checked={http2}
+ onToggleChanged={() => SettingsActions.update({http2: !http2})}
+ />
+ <ToggleButton name="anticache"
+ checked={anticache}
+ onToggleChanged={() => SettingsActions.update({anticache: !anticache})}
+ />
+ <ToggleButton name="anticomp"
+ checked={anticomp}
+ onToggleChanged={() => SettingsActions.update({anticomp: !anticomp})}
+ />
+ <ToggleInputButton name="stickyauth" placeholder="Sticky auth filter"
+ checked={Boolean(stickyauth)}
+ txt={stickyauth || ""}
+ onToggleChanged={txt => SettingsActions.update({stickyauth: (!stickyauth ? txt : null)})}
+ />
+ <ToggleInputButton name="stickycookie" placeholder="Sticky cookie filter"
+ checked={Boolean(stickycookie)}
+ txt={stickycookie || ""}
+ onToggleChanged={txt => SettingsActions.update({stickycookie: (!stickycookie ? txt : null)})}
+ />
+ <ToggleInputButton name="stream" placeholder="stream..."
+ checked={Boolean(stream)}
+ txt={stream || ""}
+ inputType = "number"
+ onToggleChanged={txt => SettingsActions.update({stream: (!stream ? txt : null)})}
+ />
+ </div>
+ <div className="clearfix"/>
</div>
- );
- }
-}
+ );
+};
+OptionMenu.title = "Options";
+OptionMenu.propTypes = {
+ settings: React.PropTypes.object.isRequired
+};
var ReportsMenu = React.createClass({
statics: {
@@ -391,7 +407,6 @@ var header_entries = [MainMenu, ViewMenu, OptionMenu /*, ReportsMenu */];
export var Header = React.createClass({
- mixins: [Router],
propTypes: {
settings: React.PropTypes.object.isRequired,
},
@@ -402,7 +417,7 @@ export var Header = React.createClass({
},
handleClick: function (active, e) {
e.preventDefault();
- this.updateLocation(active.route);
+ this.props.updateLocation(active.route);
this.setState({active: active});
},
render: function () {
@@ -430,7 +445,11 @@ export var Header = React.createClass({
{header}
</nav>
<div className="menu">
- <this.state.active ref="active" settings={this.props.settings}/>
+ <this.state.active
+ settings={this.props.settings}
+ updateLocation={this.props.updateLocation}
+ query={this.props.query}
+ />
</div>
</header>
);
diff --git a/web/src/js/components/mainview.js b/web/src/js/components/mainview.js
index 87c0c4bd..964e82db 100644
--- a/web/src/js/components/mainview.js
+++ b/web/src/js/components/mainview.js
@@ -5,12 +5,11 @@ import {Query} from "../actions.js";
import {Key} from "../utils.js";
import {StoreView} from "../store/view.js";
import Filt from "../filt/filt.js";
-import { Router, Splitter} from "./common.js"
+import {Splitter} from "./common.js"
import FlowTable from "./flowtable.js";
import FlowView from "./flowview/index.js";
var MainView = React.createClass({
- mixins: [Router],
contextTypes: {
flowStore: React.PropTypes.object.isRequired,
},
@@ -41,9 +40,9 @@ var MainView = React.createClass({
},
getViewFilt: function () {
try {
- var filtStr = this.getQuery()[Query.SEARCH];
+ var filtStr = this.props.query[Query.SEARCH];
var filt = filtStr ? Filt.parse(filtStr) : () => true;
- var highlightStr = this.getQuery()[Query.HIGHLIGHT];
+ var highlightStr = this.props.query[Query.HIGHLIGHT];
var highlight = highlightStr ? Filt.parse(highlightStr) : () => false;
} catch (e) {
console.error("Error when processing filter: " + e);
@@ -94,10 +93,10 @@ var MainView = React.createClass({
selectFlow: function (flow) {
if (flow) {
var tab = this.props.routeParams.detailTab || "request";
- this.updateLocation(`/flows/${flow.id}/${tab}`);
+ this.props.updateLocation(`/flows/${flow.id}/${tab}`);
this.refs.flowTable.scrollIntoView(flow);
} else {
- this.updateLocation("/flows");
+ this.props.updateLocation("/flows");
}
},
selectFlowRelative: function (shift) {
@@ -225,6 +224,8 @@ var MainView = React.createClass({
key="flowDetails"
ref="flowDetails"
tab={this.props.routeParams.detailTab}
+ query={this.props.query}
+ updateLocation={this.props.updateLocation}
flow={selected}/>
];
} else {
diff --git a/web/src/js/components/proxyapp.js b/web/src/js/components/proxyapp.js
index d17a1522..f47c5bb4 100644
--- a/web/src/js/components/proxyapp.js
+++ b/web/src/js/components/proxyapp.js
@@ -2,7 +2,7 @@ import React from "react";
import ReactDOM from "react-dom";
import _ from "lodash";
-import {Router, Splitter} from "./common.js"
+import {Splitter} from "./common.js"
import MainView from "./mainview.js";
import Footer from "./footer.js";
import {Header, MainMenu} from "./header.js";
@@ -21,13 +21,34 @@ var Reports = React.createClass({
var ProxyAppMain = React.createClass({
- mixins: [Router],
childContextTypes: {
flowStore: React.PropTypes.object.isRequired,
eventStore: React.PropTypes.object.isRequired,
returnFocus: React.PropTypes.func.isRequired,
location: React.PropTypes.object.isRequired,
},
+ contextTypes: {
+ router: React.PropTypes.object.isRequired
+ },
+ updateLocation: function (pathname, queryUpdate) {
+ if (pathname === undefined) {
+ pathname = this.props.location.pathname;
+ }
+ var query = this.props.location.query;
+ if (queryUpdate !== undefined) {
+ for (var i in queryUpdate) {
+ if (queryUpdate.hasOwnProperty(i)) {
+ query[i] = queryUpdate[i] || undefined; //falsey values shall be removed.
+ }
+ }
+ }
+ this.context.router.replace({pathname, query});
+ },
+ getQuery: function () {
+ // For whatever reason, react-router always returns the same object, which makes comparing
+ // the current props with nextProps impossible. As a workaround, we just clone the query object.
+ return _.clone(this.props.location.query);
+ },
componentDidMount: function () {
this.focus();
this.settingsStore.addListener("recalculate", this.onSettingsChange);
@@ -97,23 +118,23 @@ var ProxyAppMain = React.createClass({
e.preventDefault();
},
render: function () {
+ var query = this.getQuery();
var eventlog;
if (this.props.location.query[Query.SHOW_EVENTLOG]) {
eventlog = [
<Splitter key="splitter" axis="y"/>,
- <EventLog key="eventlog"/>
+ <EventLog key="eventlog" updateLocation={this.updateLocation}/>
];
} else {
eventlog = null;
}
- var children = React.cloneElement(
- this.props.children,
- { ref: "view", location: this.props.location }
- );
return (
<div id="container" tabIndex="0" onKeyDown={this.onKeydown}>
- <Header ref="header" settings={this.state.settings}/>
- {children}
+ <Header ref="header" settings={this.state.settings} updateLocation={this.updateLocation} query={query} />
+ {React.cloneElement(
+ this.props.children,
+ { ref: "view", location: this.props.location , updateLocation: this.updateLocation, query }
+ )}
{eventlog}
<Footer settings={this.state.settings}/>
</div>
@@ -125,12 +146,12 @@ var ProxyAppMain = React.createClass({
import { Route, Router as ReactRouter, hashHistory, Redirect} from "react-router";
export var app = (
-<ReactRouter history={hashHistory}>
- <Redirect from="/" to="/flows" />
- <Route path="/" component={ProxyAppMain}>
- <Route path="flows" component={MainView}/>
- <Route path="flows/:flowId/:detailTab" component={MainView}/>
- <Route path="reports" component={Reports}/>
- </Route>
-</ReactRouter>
-); \ No newline at end of file
+ <ReactRouter history={hashHistory}>
+ <Redirect from="/" to="/flows" />
+ <Route path="/" component={ProxyAppMain}>
+ <Route path="flows" component={MainView}/>
+ <Route path="flows/:flowId/:detailTab" component={MainView}/>
+ <Route path="reports" component={Reports}/>
+ </Route>
+ </ReactRouter>
+);