diff options
Diffstat (limited to 'mitmproxy/libmproxy/web/static/app.js')
-rw-r--r-- | mitmproxy/libmproxy/web/static/app.js | 6271 |
1 files changed, 6271 insertions, 0 deletions
diff --git a/mitmproxy/libmproxy/web/static/app.js b/mitmproxy/libmproxy/web/static/app.js new file mode 100644 index 00000000..27f356f7 --- /dev/null +++ b/mitmproxy/libmproxy/web/static/app.js @@ -0,0 +1,6271 @@ +(function e(t,n,r){function s(o,u){if(!n[o]){if(!t[o]){var a=typeof require=="function"&&require;if(!u&&a)return a(o,!0);if(i)return i(o,!0);var f=new Error("Cannot find module '"+o+"'");throw f.code="MODULE_NOT_FOUND",f}var l=n[o]={exports:{}};t[o][0].call(l.exports,function(e){var n=t[o][1][e];return s(n?n:e)},l,l.exports,e,t,n,r)}return n[o].exports}var i=typeof require=="function"&&require;for(var o=0;o<r.length;o++)s(r[o]);return s})({1:[function(require,module,exports){ +// Copyright Joyent, Inc. and other Node contributors. +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the +// "Software"), to deal in the Software without restriction, including +// without limitation the rights to use, copy, modify, merge, publish, +// distribute, sublicense, and/or sell copies of the Software, and to permit +// persons to whom the Software is furnished to do so, subject to the +// following conditions: +// +// The above copyright notice and this permission notice shall be included +// in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN +// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, +// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR +// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE +// USE OR OTHER DEALINGS IN THE SOFTWARE. + +function EventEmitter() { + this._events = this._events || {}; + this._maxListeners = this._maxListeners || undefined; +} +module.exports = EventEmitter; + +// Backwards-compat with node 0.10.x +EventEmitter.EventEmitter = EventEmitter; + +EventEmitter.prototype._events = undefined; +EventEmitter.prototype._maxListeners = undefined; + +// By default EventEmitters will print a warning if more than 10 listeners are +// added to it. This is a useful default which helps finding memory leaks. +EventEmitter.defaultMaxListeners = 10; + +// Obviously not all Emitters should be limited to 10. This function allows +// that to be increased. Set to zero for unlimited. +EventEmitter.prototype.setMaxListeners = function(n) { + if (!isNumber(n) || n < 0 || isNaN(n)) + throw TypeError('n must be a positive number'); + this._maxListeners = n; + return this; +}; + +EventEmitter.prototype.emit = function(type) { + var er, handler, len, args, i, listeners; + + if (!this._events) + this._events = {}; + + // If there is no 'error' event listener then throw. + if (type === 'error') { + if (!this._events.error || + (isObject(this._events.error) && !this._events.error.length)) { + er = arguments[1]; + if (er instanceof Error) { + throw er; // Unhandled 'error' event + } + throw TypeError('Uncaught, unspecified "error" event.'); + } + } + + handler = this._events[type]; + + if (isUndefined(handler)) + return false; + + if (isFunction(handler)) { + switch (arguments.length) { + // fast cases + case 1: + handler.call(this); + break; + case 2: + handler.call(this, arguments[1]); + break; + case 3: + handler.call(this, arguments[1], arguments[2]); + break; + // slower + default: + len = arguments.length; + args = new Array(len - 1); + for (i = 1; i < len; i++) + args[i - 1] = arguments[i]; + handler.apply(this, args); + } + } else if (isObject(handler)) { + len = arguments.length; + args = new Array(len - 1); + for (i = 1; i < len; i++) + args[i - 1] = arguments[i]; + + listeners = handler.slice(); + len = listeners.length; + for (i = 0; i < len; i++) + listeners[i].apply(this, args); + } + + return true; +}; + +EventEmitter.prototype.addListener = function(type, listener) { + var m; + + if (!isFunction(listener)) + throw TypeError('listener must be a function'); + + if (!this._events) + this._events = {}; + + // To avoid recursion in the case that type === "newListener"! Before + // adding it to the listeners, first emit "newListener". + if (this._events.newListener) + this.emit('newListener', type, + isFunction(listener.listener) ? + listener.listener : listener); + + if (!this._events[type]) + // Optimize the case of one listener. Don't need the extra array object. + this._events[type] = listener; + else if (isObject(this._events[type])) + // If we've already got an array, just append. + this._events[type].push(listener); + else + // Adding the second element, need to change to array. + this._events[type] = [this._events[type], listener]; + + // Check for listener leak + if (isObject(this._events[type]) && !this._events[type].warned) { + var m; + if (!isUndefined(this._maxListeners)) { + m = this._maxListeners; + } else { + m = EventEmitter.defaultMaxListeners; + } + + if (m && m > 0 && this._events[type].length > m) { + this._events[type].warned = true; + console.error('(node) warning: possible EventEmitter memory ' + + 'leak detected. %d listeners added. ' + + 'Use emitter.setMaxListeners() to increase limit.', + this._events[type].length); + if (typeof console.trace === 'function') { + // not supported in IE 10 + console.trace(); + } + } + } + + return this; +}; + +EventEmitter.prototype.on = EventEmitter.prototype.addListener; + +EventEmitter.prototype.once = function(type, listener) { + if (!isFunction(listener)) + throw TypeError('listener must be a function'); + + var fired = false; + + function g() { + this.removeListener(type, g); + + if (!fired) { + fired = true; + listener.apply(this, arguments); + } + } + + g.listener = listener; + this.on(type, g); + + return this; +}; + +// emits a 'removeListener' event iff the listener was removed +EventEmitter.prototype.removeListener = function(type, listener) { + var list, position, length, i; + + if (!isFunction(listener)) + throw TypeError('listener must be a function'); + + if (!this._events || !this._events[type]) + return this; + + list = this._events[type]; + length = list.length; + position = -1; + + if (list === listener || + (isFunction(list.listener) && list.listener === listener)) { + delete this._events[type]; + if (this._events.removeListener) + this.emit('removeListener', type, listener); + + } else if (isObject(list)) { + for (i = length; i-- > 0;) { + if (list[i] === listener || + (list[i].listener && list[i].listener === listener)) { + position = i; + break; + } + } + + if (position < 0) + return this; + + if (list.length === 1) { + list.length = 0; + delete this._events[type]; + } else { + list.splice(position, 1); + } + + if (this._events.removeListener) + this.emit('removeListener', type, listener); + } + + return this; +}; + +EventEmitter.prototype.removeAllListeners = function(type) { + var key, listeners; + + if (!this._events) + return this; + + // not listening for removeListener, no need to emit + if (!this._events.removeListener) { + if (arguments.length === 0) + this._events = {}; + else if (this._events[type]) + delete this._events[type]; + return this; + } + + // emit removeListener for all listeners on all events + if (arguments.length === 0) { + for (key in this._events) { + if (key === 'removeListener') continue; + this.removeAllListeners(key); + } + this.removeAllListeners('removeListener'); + this._events = {}; + return this; + } + + listeners = this._events[type]; + + if (isFunction(listeners)) { + this.removeListener(type, listeners); + } else { + // LIFO order + while (listeners.length) + this.removeListener(type, listeners[listeners.length - 1]); + } + delete this._events[type]; + + return this; +}; + +EventEmitter.prototype.listeners = function(type) { + var ret; + if (!this._events || !this._events[type]) + ret = []; + else if (isFunction(this._events[type])) + ret = [this._events[type]]; + else + ret = this._events[type].slice(); + return ret; +}; + +EventEmitter.listenerCount = function(emitter, type) { + var ret; + if (!emitter._events || !emitter._events[type]) + ret = 0; + else if (isFunction(emitter._events[type])) + ret = 1; + else + ret = emitter._events[type].length; + return ret; +}; + +function isFunction(arg) { + return typeof arg === 'function'; +} + +function isNumber(arg) { + return typeof arg === 'number'; +} + +function isObject(arg) { + return typeof arg === 'object' && arg !== null; +} + +function isUndefined(arg) { + return arg === void 0; +} + +},{}],2:[function(require,module,exports){ +"use strict"; + +var $ = require("jquery"); +var _ = require("lodash"); +var AppDispatcher = require("./dispatcher.js").AppDispatcher; + +var ActionTypes = { + // Connection + CONNECTION_OPEN: "connection_open", + CONNECTION_CLOSE: "connection_close", + CONNECTION_ERROR: "connection_error", + + // Stores + SETTINGS_STORE: "settings", + EVENT_STORE: "events", + FLOW_STORE: "flows" +}; + +var StoreCmds = { + ADD: "add", + UPDATE: "update", + REMOVE: "remove", + RESET: "reset" +}; + +var ConnectionActions = { + open: function open() { + AppDispatcher.dispatchViewAction({ + type: ActionTypes.CONNECTION_OPEN + }); + }, + close: function close() { + AppDispatcher.dispatchViewAction({ + type: ActionTypes.CONNECTION_CLOSE + }); + }, + error: function error() { + AppDispatcher.dispatchViewAction({ + type: ActionTypes.CONNECTION_ERROR + }); + } +}; + +var SettingsActions = { + update: function update(settings) { + + $.ajax({ + type: "PUT", + url: "/settings", + contentType: 'application/json', + data: JSON.stringify(settings) + }); + + /* + //Facebook Flux: We do an optimistic update on the client already. + AppDispatcher.dispatchViewAction({ + type: ActionTypes.SETTINGS_STORE, + cmd: StoreCmds.UPDATE, + data: settings + }); + */ + } +}; + +var EventLogActions_event_id = 0; +var EventLogActions = { + add_event: function add_event(message) { + AppDispatcher.dispatchViewAction({ + type: ActionTypes.EVENT_STORE, + cmd: StoreCmds.ADD, + data: { + message: message, + level: "web", + id: "viewAction-" + EventLogActions_event_id++ + } + }); + } +}; + +var FlowActions = { + accept: function accept(flow) { + $.post("/flows/" + flow.id + "/accept"); + }, + accept_all: function accept_all() { + $.post("/flows/accept"); + }, + "delete": function _delete(flow) { + $.ajax({ + type: "DELETE", + url: "/flows/" + flow.id + }); + }, + duplicate: function duplicate(flow) { + $.post("/flows/" + flow.id + "/duplicate"); + }, + replay: function replay(flow) { + $.post("/flows/" + flow.id + "/replay"); + }, + revert: function revert(flow) { + $.post("/flows/" + flow.id + "/revert"); + }, + update: function update(flow, nextProps) { + /* + //Facebook Flux: We do an optimistic update on the client already. + var nextFlow = _.cloneDeep(flow); + _.merge(nextFlow, nextProps); + AppDispatcher.dispatchViewAction({ + type: ActionTypes.FLOW_STORE, + cmd: StoreCmds.UPDATE, + data: nextFlow + }); + */ + $.ajax({ + type: "PUT", + url: "/flows/" + flow.id, + contentType: 'application/json', + data: JSON.stringify(nextProps) + }); + }, + clear: function clear() { + $.post("/clear"); + } +}; + +var Query = { + SEARCH: "s", + HIGHLIGHT: "h", + SHOW_EVENTLOG: "e" +}; + +module.exports = { + ActionTypes: ActionTypes, + ConnectionActions: ConnectionActions, + FlowActions: FlowActions, + StoreCmds: StoreCmds, + SettingsActions: SettingsActions, + EventLogActions: EventLogActions, + Query: Query +}; + + +},{"./dispatcher.js":21,"jquery":"jquery","lodash":"lodash"}],3:[function(require,module,exports){ +"use strict"; + +var React = require("react"); +var ReactRouter = require("react-router"); +var $ = require("jquery"); +var Connection = require("./connection"); +var proxyapp = require("./components/proxyapp.js"); +var EventLogActions = require("./actions.js").EventLogActions; + +$(function () { + window.ws = new Connection("/updates"); + + window.onerror = function (msg) { + EventLogActions.add_event(msg); + }; + + ReactRouter.run(proxyapp.routes, function (Handler, state) { + React.render(React.createElement(Handler, null), document.body); + }); +}); + + +},{"./actions.js":2,"./components/proxyapp.js":18,"./connection":20,"jquery":"jquery","react":"react","react-router":"react-router"}],4:[function(require,module,exports){ +"use strict"; + +var React = require("react"); +var ReactRouter = require("react-router"); +var _ = require("lodash"); + +// http://blog.vjeux.com/2013/javascript/scroll-position-with-react.html (also contains inverse example) +var AutoScrollMixin = { + componentWillUpdate: function componentWillUpdate() { + var node = this.getDOMNode(); + this._shouldScrollBottom = node.scrollTop !== 0 && node.scrollTop + node.clientHeight === node.scrollHeight; + }, + componentDidUpdate: function componentDidUpdate() { + if (this._shouldScrollBottom) { + var node = this.getDOMNode(); + node.scrollTop = node.scrollHeight; + } + } +}; + +var StickyHeadMixin = { + adjustHead: function adjustHead() { + // Abusing CSS transforms to set the element + // referenced as head into some kind of position:sticky. + var head = this.refs.head.getDOMNode(); + head.style.transform = "translate(0," + this.getDOMNode().scrollTop + "px)"; + } +}; + +var SettingsState = { + contextTypes: { + settingsStore: React.PropTypes.object.isRequired + }, + getInitialState: function getInitialState() { + return { + settings: this.context.settingsStore.dict + }; + }, + componentDidMount: function componentDidMount() { + this.context.settingsStore.addListener("recalculate", this.onSettingsChange); + }, + componentWillUnmount: function componentWillUnmount() { + this.context.settingsStore.removeListener("recalculate", this.onSettingsChange); + }, + onSettingsChange: function onSettingsChange() { + this.setState({ + settings: this.context.settingsStore.dict + }); + } +}; + +var ChildFocus = { + contextTypes: { + returnFocus: React.PropTypes.func + }, + returnFocus: function returnFocus() { + React.findDOMNode(this).blur(); + window.getSelection().removeAllRanges(); + this.context.returnFocus(); + } +}; + +var Navigation = _.extend({}, ReactRouter.Navigation, { + setQuery: function setQuery(dict) { + var q = this.context.router.getCurrentQuery(); + for (var i in dict) { + if (dict.hasOwnProperty(i)) { + q[i] = dict[i] || undefined; //falsey values shall be removed. + } + } + this.replaceWith(this.context.router.getCurrentPath(), this.context.router.getCurrentParams(), q); + }, + replaceWith: function replaceWith(routeNameOrPath, params, query) { + if (routeNameOrPath === undefined) { + routeNameOrPath = this.context.router.getCurrentPath(); + } + if (params === undefined) { + params = this.context.router.getCurrentParams(); + } + if (query === undefined) { + query = this.context.router.getCurrentQuery(); + } + + this.context.router.replaceWith(routeNameOrPath, params, query); + } +}); + +// react-router is fairly good at changing its API regularly. +// We keep the old method for now - if it should turn out that their changes are permanent, +// we may remove this mixin and access react-router directly again. +var RouterState = _.extend({}, ReactRouter.State, { + getQuery: function getQuery() { + // 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.router.getCurrentQuery()); + }, + getParams: function getParams() { + return _.clone(this.context.router.getCurrentParams()); + } +}); + +var Splitter = React.createClass({ + displayName: "Splitter", + + getDefaultProps: function getDefaultProps() { + return { + axis: "x" + }; + }, + getInitialState: function getInitialState() { + return { + applied: false, + startX: false, + startY: false + }; + }, + onMouseDown: function onMouseDown(e) { + this.setState({ + startX: e.pageX, + startY: e.pageY + }); + window.addEventListener("mousemove", this.onMouseMove); + window.addEventListener("mouseup", this.onMouseUp); + // Occasionally, only a dragEnd event is triggered, but no mouseUp. + window.addEventListener("dragend", this.onDragEnd); + }, + onDragEnd: function onDragEnd() { + this.getDOMNode().style.transform = ""; + window.removeEventListener("dragend", this.onDragEnd); + window.removeEventListener("mouseup", this.onMouseUp); + window.removeEventListener("mousemove", this.onMouseMove); + }, + onMouseUp: function onMouseUp(e) { + this.onDragEnd(); + + var node = this.getDOMNode(); + var prev = node.previousElementSibling; + var next = node.nextElementSibling; + + var dX = e.pageX - this.state.startX; + var dY = e.pageY - this.state.startY; + var flexBasis; + if (this.props.axis === "x") { + flexBasis = prev.offsetWidth + dX; + } else { + flexBasis = prev.offsetHeight + dY; + } + + prev.style.flex = "0 0 " + Math.max(0, flexBasis) + "px"; + next.style.flex = "1 1 auto"; + + this.setState({ + applied: true + }); + this.onResize(); + }, + onMouseMove: function onMouseMove(e) { + var dX = 0, + dY = 0; + if (this.props.axis === "x") { + dX = e.pageX - this.state.startX; + } else { + dY = e.pageY - this.state.startY; + } + this.getDOMNode().style.transform = "translate(" + dX + "px," + dY + "px)"; + }, + onResize: function onResize() { + // Trigger a global resize event. This notifies components that employ virtual scrolling + // that their viewport may have changed. + window.setTimeout(function () { + window.dispatchEvent(new CustomEvent("resize")); + }, 1); + }, + reset: function reset(willUnmount) { + if (!this.state.applied) { + return; + } + var node = this.getDOMNode(); + var prev = node.previousElementSibling; + var next = node.nextElementSibling; + + prev.style.flex = ""; + next.style.flex = ""; + + if (!willUnmount) { + this.setState({ + applied: false + }); + } + this.onResize(); + }, + componentWillUnmount: function componentWillUnmount() { + this.reset(true); + }, + render: function render() { + var className = "splitter"; + if (this.props.axis === "x") { + className += " splitter-x"; + } else { + className += " splitter-y"; + } + return React.createElement( + "div", + { className: className }, + React.createElement("div", { onMouseDown: this.onMouseDown, draggable: "true" }) + ); + } +}); + +module.exports = { + ChildFocus: ChildFocus, + RouterState: RouterState, + Navigation: Navigation, + StickyHeadMixin: StickyHeadMixin, + AutoScrollMixin: AutoScrollMixin, + Splitter: Splitter, + SettingsState: SettingsState +}; + + +},{"lodash":"lodash","react":"react","react-router":"react-router"}],5:[function(require,module,exports){ +"use strict"; + +var _extends = Object.assign || function (target) { for (var i = 1; i < arguments.length; i++) { var source = arguments[i]; for (var key in source) { if (Object.prototype.hasOwnProperty.call(source, key)) { target[key] = source[key]; } } } return target; }; + +var React = require("react"); +var common = require("./common.js"); +var utils = require("../utils.js"); + +var contentToHtml = function contentToHtml(content) { + return _.escape(content); +}; +var nodeToContent = function nodeToContent(node) { + return node.textContent; +}; + +/* + Basic Editor Functionality + */ +var EditorBase = React.createClass({ + displayName: "EditorBase", + + propTypes: { + content: React.PropTypes.string.isRequired, + onDone: React.PropTypes.func.isRequired, + contentToHtml: React.PropTypes.func, + nodeToContent: React.PropTypes.func, // content === nodeToContent( Node<innerHTML=contentToHtml(content)> ) + onStop: React.PropTypes.func, + submitOnEnter: React.PropTypes.bool, + className: React.PropTypes.string, + tag: React.PropTypes.string + }, + getDefaultProps: function getDefaultProps() { + return { + contentToHtml: contentToHtml, + nodeToContent: nodeToContent, + submitOnEnter: true, + className: "", + tag: "div" + }; + }, + getInitialState: function getInitialState() { + return { + editable: false + }; + }, + render: function render() { + var className = "inline-input " + this.props.className; + var html = { __html: this.props.contentToHtml(this.props.content) }; + var Tag = this.props.tag; + return React.createElement(Tag, _extends({}, this.props, { + tabIndex: "0", + className: className, + contentEditable: this.state.editable || undefined, // workaround: use undef instead of false to remove attr + onFocus: this.onFocus, + onMouseDown: this.onMouseDown, + onClick: this.onClick, + onBlur: this._stop, + onKeyDown: this.onKeyDown, + onInput: this.onInput, + onPaste: this.onPaste, + dangerouslySetInnerHTML: html + })); + }, + onPaste: function onPaste(e) { + e.preventDefault(); + var content = e.clipboardData.getData("text/plain"); + document.execCommand("insertHTML", false, content); + }, + onMouseDown: function onMouseDown(e) { + this._mouseDown = true; + window.addEventListener("mouseup", this.onMouseUp); + this.props.onMouseDown && this.props.onMouseDown(e); + }, + onMouseUp: function onMouseUp() { + if (this._mouseDown) { + this._mouseDown = false; + window.removeEventListener("mouseup", this.onMouseUp); + } + }, + onClick: function onClick(e) { + this.onMouseUp(); + this.onFocus(e); + }, + onFocus: function onFocus(e) { + console.log("onFocus", this._mouseDown, this._ignore_events, this.state.editable); + if (this._mouseDown || this._ignore_events || this.state.editable) { + return; + } + + //contenteditable in FireFox is more or less broken. + // - we need to blur() and then focus(), otherwise the caret is not shown. + // - blur() + focus() == we need to save the caret position before + // Firefox sometimes just doesn't set a caret position => use caretPositionFromPoint + var sel = window.getSelection(); + var range; + if (sel.rangeCount > 0) { + range = sel.getRangeAt(0); + } else if (document.caretPositionFromPoint && e.clientX && e.clientY) { + var pos = document.caretPositionFromPoint(e.clientX, e.clientY); + range = document.createRange(); + range.setStart(pos.offsetNode, pos.offset); + } else if (document.caretRangeFromPoint && e.clientX && e.clientY) { + range = document.caretRangeFromPoint(e.clientX, e.clientY); + } else { + range = document.createRange(); + range.selectNodeContents(React.findDOMNode(this)); + } + + this._ignore_events = true; + this.setState({ editable: true }, function () { + var node = React.findDOMNode(this); + node.blur(); + node.focus(); + this._ignore_events = false; + //sel.removeAllRanges(); + //sel.addRange(range); + }); + }, + stop: function stop() { + // a stop would cause a blur as a side-effect. + // but a blur event must trigger a stop as well. + // to fix this, make stop = blur and do the actual stop in the onBlur handler. + React.findDOMNode(this).blur(); + this.props.onStop && this.props.onStop(); + }, + _stop: function _stop(e) { + if (this._ignore_events) { + return; + } + console.log("_stop", _.extend({}, e)); + window.getSelection().removeAllRanges(); //make sure that selection is cleared on blur + var node = React.findDOMNode(this); + var content = this.props.nodeToContent(node); + this.setState({ editable: false }); + this.props.onDone(content); + this.props.onBlur && this.props.onBlur(e); + }, + reset: function reset() { + React.findDOMNode(this).innerHTML = this.props.contentToHtml(this.props.content); + }, + onKeyDown: function onKeyDown(e) { + e.stopPropagation(); + switch (e.keyCode) { + case utils.Key.ESC: + e.preventDefault(); + this.reset(); + this.stop(); + break; + case utils.Key.ENTER: + if (this.props.submitOnEnter && !e.shiftKey) { + e.preventDefault(); + this.stop(); + } + break; + default: + break; + } + }, + onInput: function onInput() { + var node = React.findDOMNode(this); + var content = this.props.nodeToContent(node); + this.props.onInput && this.props.onInput(content); + } +}); + +/* + Add Validation to EditorBase + */ +var ValidateEditor = React.createClass({ + displayName: "ValidateEditor", + + propTypes: { + content: React.PropTypes.string.isRequired, + onDone: React.PropTypes.func.isRequired, + onInput: React.PropTypes.func, + isValid: React.PropTypes.func, + className: React.PropTypes.string + }, + getInitialState: function getInitialState() { + return { + currentContent: this.props.content + }; + }, + componentWillReceiveProps: function componentWillReceiveProps() { + this.setState({ currentContent: this.props.content }); + }, + onInput: function onInput(content) { + this.setState({ currentContent: content }); + this.props.onInput && this.props.onInput(content); + }, + render: function render() { + var className = this.props.className || ""; + if (this.props.isValid) { + if (this.props.isValid(this.state.currentContent)) { + className += " has-success"; + } else { + className += " has-warning"; + } + } + return React.createElement(EditorBase, _extends({}, this.props, { + ref: "editor", + className: className, + onDone: this.onDone, + onInput: this.onInput + })); + }, + onDone: function onDone(content) { + if (this.props.isValid && !this.props.isValid(content)) { + this.refs.editor.reset(); + content = this.props.content; + } + this.props.onDone(content); + } +}); + +/* + Text Editor with mitmweb-specific convenience features + */ +var ValueEditor = React.createClass({ + displayName: "ValueEditor", + + mixins: [common.ChildFocus], + propTypes: { + content: React.PropTypes.string.isRequired, + onDone: React.PropTypes.func.isRequired, + inline: React.PropTypes.bool + }, + render: function render() { + var tag = this.props.inline ? "span" : "div"; + return React.createElement(ValidateEditor, _extends({}, this.props, { + onStop: this.onStop, + tag: tag + })); + }, + focus: function focus() { + React.findDOMNode(this).focus(); + }, + onStop: function onStop() { + this.returnFocus(); + } +}); + +module.exports = { + ValueEditor: ValueEditor +}; + + +},{"../utils.js":26,"./common.js":4,"react":"react"}],6:[function(require,module,exports){ +"use strict"; + +var React = require("react"); +var common = require("./common.js"); +var Query = require("../actions.js").Query; +var VirtualScrollMixin = require("./virtualscroll.js"); +var views = require("../store/view.js"); +var _ = require("lodash"); + +var LogMessage = React.createClass({ + displayName: "LogMessage", + + render: function render() { + var entry = this.props.entry; + var indicator; + switch (entry.level) { + case "web": + indicator = React.createElement("i", { className: "fa fa-fw fa-html5" }); + break; + case "debug": + indicator = React.createElement("i", { className: "fa fa-fw fa-bug" }); + break; + default: + indicator = React.createElement("i", { className: "fa fa-fw fa-info" }); + } + return React.createElement( + "div", + null, + indicator, + " ", + entry.message + ); + }, + shouldComponentUpdate: function shouldComponentUpdate() { + return false; // log entries are immutable. + } +}); + +var EventLogContents = React.createClass({ + displayName: "EventLogContents", + + contextTypes: { + eventStore: React.PropTypes.object.isRequired + }, + mixins: [common.AutoScrollMixin, VirtualScrollMixin], + getInitialState: function getInitialState() { + var filterFn = function filterFn(entry) { + return this.props.filter[entry.level]; + }; + var view = new views.StoreView(this.context.eventStore, filterFn.bind(this)); + view.addListener("add", this.onEventLogChange); + view.addListener("recalculate", this.onEventLogChange); + + return { + view: view + }; + }, + componentWillUnmount: function componentWillUnmount() { + this.state.view.close(); + }, + filter: function filter(entry) { + return this.props.filter[entry.level]; + }, + onEventLogChange: function onEventLogChange() { + this.forceUpdate(); + }, + componentWillReceiveProps: function componentWillReceiveProps(nextProps) { + if (nextProps.filter !== this.props.filter) { + this.props.filter = nextProps.filter; // Dirty: Make sure that view filter sees the update. + this.state.view.recalculate(); + } + }, + getDefaultProps: function getDefaultProps() { + return { + rowHeight: 45, + rowHeightMin: 15, + placeholderTagName: "div" + }; + }, + renderRow: function renderRow(elem) { + return React.createElement(LogMessage, { key: elem.id, entry: elem }); + }, + render: function render() { + var entries = this.state.view.list; + var rows = this.renderRows(entries); + + return React.createElement( + "pre", + { onScroll: this.onScroll }, + this.getPlaceholderTop(entries.length), + rows, + this.getPlaceholderBottom(entries.length) + ); + } +}); + +var ToggleFilter = React.createClass({ + displayName: "ToggleFilter", + + toggle: function toggle(e) { + e.preventDefault(); + return this.props.toggleLevel(this.props.name); + }, + render: function render() { + var className = "label "; + if (this.props.active) { + className += "label-primary"; + } else { + className += "label-default"; + } + return React.createElement( + "a", + { + href: "#", + className: className, + onClick: this.toggle }, + this.props.name + ); + } +}); + +var EventLog = React.createClass({ + displayName: "EventLog", + + mixins: [common.Navigation], + getInitialState: function getInitialState() { + return { + filter: { + "debug": false, + "info": true, + "web": true + } + }; + }, + close: function close() { + var d = {}; + d[Query.SHOW_EVENTLOG] = undefined; + this.setQuery(d); + }, + toggleLevel: function toggleLevel(level) { + var filter = _.extend({}, this.state.filter); + filter[level] = !filter[level]; + this.setState({ filter: filter }); + }, + render: function render() { + return React.createElement( + "div", + { className: "eventlog" }, + React.createElement( + "div", + null, + "Eventlog", + React.createElement( + "div", + { className: "pull-right" }, + React.createElement(ToggleFilter, { name: "debug", active: this.state.filter.debug, toggleLevel: this.toggleLevel }), + React.createElement(ToggleFilter, { name: "info", active: this.state.filter.info, toggleLevel: this.toggleLevel }), + React.createElement(ToggleFilter, { name: "web", active: this.state.filter.web, toggleLevel: this.toggleLevel }), + React.createElement("i", { onClick: this.close, className: "fa fa-close" }) + ) + ), + React.createElement(EventLogContents, { filter: this.state.filter }) + ); + } +}); + +module.exports = EventLog; + + +},{"../actions.js":2,"../store/view.js":25,"./common.js":4,"./virtualscroll.js":19,"lodash":"lodash","react":"react"}],7:[function(require,module,exports){ +"use strict"; + +var _extends = Object.assign || function (target) { for (var i = 1; i < arguments.length; i++) { var source = arguments[i]; for (var key in source) { if (Object.prototype.hasOwnProperty.call(source, key)) { target[key] = source[key]; } } } return target; }; + +var React = require("react"); +var RequestUtils = require("../flow/utils.js").RequestUtils; +var ResponseUtils = require("../flow/utils.js").ResponseUtils; +var utils = require("../utils.js"); + +var TLSColumn = React.createClass({ + displayName: "TLSColumn", + + statics: { + Title: React.createClass({ + displayName: "Title", + + render: function render() { + return React.createElement("th", _extends({}, this.props, { className: "col-tls " + (this.props.className || "") })); + } + }), + sortKeyFun: function sortKeyFun(flow) { + return flow.request.scheme; + } + }, + render: function render() { + var flow = this.props.flow; + var ssl = flow.request.scheme === "https"; + var classes; + if (ssl) { + classes = "col-tls col-tls-https"; + } else { + classes = "col-tls col-tls-http"; + } + return React.createElement("td", { className: classes }); + } +}); + +var IconColumn = React.createClass({ + displayName: "IconColumn", + + statics: { + Title: React.createClass({ + displayName: "Title", + + render: function render() { + return React.createElement("th", _extends({}, this.props, { className: "col-icon " + (this.props.className || "") })); + } + }) + }, + render: function render() { + var flow = this.props.flow; + + var icon; + if (flow.response) { + var contentType = ResponseUtils.getContentType(flow.response); + + //TODO: We should assign a type to the flow somewhere else. + if (flow.response.status_code === 304) { + icon = "resource-icon-not-modified"; + } else if (300 <= flow.response.status_code && flow.response.status_code < 400) { + icon = "resource-icon-redirect"; + } else if (contentType && contentType.indexOf("image") >= 0) { + icon = "resource-icon-image"; + } else if (contentType && contentType.indexOf("javascript") >= 0) { + icon = "resource-icon-js"; + } else if (contentType && contentType.indexOf("css") >= 0) { + icon = "resource-icon-css"; + } else if (contentType && contentType.indexOf("html") >= 0) { + icon = "resource-icon-document"; + } + } + if (!icon) { + icon = "resource-icon-plain"; + } + + icon += " resource-icon"; + return React.createElement( + "td", + { className: "col-icon" }, + React.createElement("div", { className: icon }) + ); + } +}); + +var PathColumn = React.createClass({ + displayName: "PathColumn", + + statics: { + Title: React.createClass({ + displayName: "Title", + + render: function render() { + return React.createElement( + "th", + _extends({}, this.props, { className: "col-path " + (this.props.className || "") }), + "Path" + ); + } + }), + sortKeyFun: function sortKeyFun(flow) { + return RequestUtils.pretty_url(flow.request); + } + }, + render: function render() { + var flow = this.props.flow; + return React.createElement( + "td", + { className: "col-path" }, + flow.request.is_replay ? React.createElement("i", { className: "fa fa-fw fa-repeat pull-right" }) : null, + flow.intercepted ? React.createElement("i", { className: "fa fa-fw fa-pause pull-right" }) : null, + RequestUtils.pretty_url(flow.request) + ); + } +}); + +var MethodColumn = React.createClass({ + displayName: "MethodColumn", + + statics: { + Title: React.createClass({ + displayName: "Title", + + render: function render() { + return React.createElement( + "th", + _extends({}, this.props, { className: "col-method " + (this.props.className || "") }), + "Method" + ); + } + }), + sortKeyFun: function sortKeyFun(flow) { + return flow.request.method; + } + }, + render: function render() { + var flow = this.props.flow; + return React.createElement( + "td", + { className: "col-method" }, + flow.request.method + ); + } +}); + +var StatusColumn = React.createClass({ + displayName: "StatusColumn", + + statics: { + Title: React.createClass({ + displayName: "Title", + + render: function render() { + return React.createElement( + "th", + _extends({}, this.props, { className: "col-status " + (this.props.className || "") }), + "Status" + ); + } + }), + sortKeyFun: function sortKeyFun(flow) { + return flow.response ? flow.response.status_code : undefined; + } + }, + render: function render() { + var flow = this.props.flow; + var status; + if (flow.response) { + status = flow.response.status_code; + } else { + status = null; + } + return React.createElement( + "td", + { className: "col-status" }, + status + ); + } +}); + +var SizeColumn = React.createClass({ + displayName: "SizeColumn", + + statics: { + Title: React.createClass({ + displayName: "Title", + + render: function render() { + return React.createElement( + "th", + _extends({}, this.props, { className: "col-size " + (this.props.className || "") }), + "Size" + ); + } + }), + sortKeyFun: function sortKeyFun(flow) { + var total = flow.request.contentLength; + if (flow.response) { + total += flow.response.contentLength || 0; + } + return total; + } + }, + render: function render() { + var flow = this.props.flow; + + var total = flow.request.contentLength; + if (flow.response) { + total += flow.response.contentLength || 0; + } + var size = utils.formatSize(total); + return React.createElement( + "td", + { className: "col-size" }, + size + ); + } +}); + +var TimeColumn = React.createClass({ + displayName: "TimeColumn", + + statics: { + Title: React.createClass({ + displayName: "Title", + + render: function render() { + return React.createElement( + "th", + _extends({}, this.props, { className: "col-time " + (this.props.className || "") }), + "Time" + ); + } + }), + sortKeyFun: function sortKeyFun(flow) { + if (flow.response) { + return flow.response.timestamp_end - flow.request.timestamp_start; + } + } + }, + render: function render() { + var flow = this.props.flow; + var time; + if (flow.response) { + time = utils.formatTimeDelta(1000 * (flow.response.timestamp_end - flow.request.timestamp_start)); + } else { + time = "..."; + } + return React.createElement( + "td", + { className: "col-time" }, + time + ); + } +}); + +var all_columns = [TLSColumn, IconColumn, PathColumn, MethodColumn, StatusColumn, SizeColumn, TimeColumn]; + +module.exports = all_columns; + + +},{"../flow/utils.js":23,"../utils.js":26,"react":"react"}],8:[function(require,module,exports){ +"use strict"; + +var React = require("react"); +var common = require("./common.js"); +var utils = require("../utils.js"); +var _ = require("lodash"); + +var VirtualScrollMixin = require("./virtualscroll.js"); +var flowtable_columns = require("./flowtable-columns.js"); + +var FlowRow = React.createClass({ + displayName: "FlowRow", + + render: function render() { + var flow = this.props.flow; + var columns = this.props.columns.map((function (Column) { + return React.createElement(Column, { key: Column.displayName, flow: flow }); + }).bind(this)); + var className = ""; + if (this.props.selected) { + className += " selected"; + } + if (this.props.highlighted) { + className += " highlighted"; + } + if (flow.intercepted) { + className += " intercepted"; + } + if (flow.request) { + className += " has-request"; + } + if (flow.response) { + className += " has-response"; + } + + return React.createElement( + "tr", + { className: className, onClick: this.props.selectFlow.bind(null, flow) }, + columns + ); + }, + shouldComponentUpdate: function shouldComponentUpdate(nextProps) { + return true; + // Further optimization could be done here + // by calling forceUpdate on flow updates, selection changes and column changes. + //return ( + //(this.props.columns.length !== nextProps.columns.length) || + //(this.props.selected !== nextProps.selected) + //); + } +}); + +var FlowTableHead = React.createClass({ + displayName: "FlowTableHead", + + getInitialState: function getInitialState() { + return { + sortColumn: undefined, + sortDesc: false + }; + }, + onClick: function onClick(Column) { + var sortDesc = this.state.sortDesc; + var hasSort = Column.sortKeyFun; + if (Column === this.state.sortColumn) { + sortDesc = !sortDesc; + this.setState({ + sortDesc: sortDesc + }); + } else { + this.setState({ + sortColumn: hasSort && Column, + sortDesc: false + }); + } + var sortKeyFun; + if (!sortDesc) { + sortKeyFun = Column.sortKeyFun; + } else { + sortKeyFun = hasSort && function () { + var k = Column.sortKeyFun.apply(this, arguments); + if (_.isString(k)) { + return utils.reverseString("" + k); + } else { + return -k; + } + }; + } + this.props.setSortKeyFun(sortKeyFun); + }, + render: function render() { + var columns = this.props.columns.map((function (Column) { + var onClick = this.onClick.bind(this, Column); + var className; + if (this.state.sortColumn === Column) { + if (this.state.sortDesc) { + className = "sort-desc"; + } else { + className = "sort-asc"; + } + } + return React.createElement(Column.Title, { + key: Column.displayName, + onClick: onClick, + className: className }); + }).bind(this)); + return React.createElement( + "thead", + null, + React.createElement( + "tr", + null, + columns + ) + ); + } +}); + +var ROW_HEIGHT = 32; + +var FlowTable = React.createClass({ + displayName: "FlowTable", + + mixins: [common.StickyHeadMixin, common.AutoScrollMixin, VirtualScrollMixin], + contextTypes: { + view: React.PropTypes.object.isRequired + }, + getInitialState: function getInitialState() { + return { + columns: flowtable_columns + }; + }, + componentWillMount: function componentWillMount() { + this.context.view.addListener("add", this.onChange); + this.context.view.addListener("update", this.onChange); + this.context.view.addListener("remove", this.onChange); + this.context.view.addListener("recalculate", this.onChange); + }, + componentWillUnmount: function componentWillUnmount() { + this.context.view.removeListener("add", this.onChange); + this.context.view.removeListener("update", this.onChange); + this.context.view.removeListener("remove", this.onChange); + this.context.view.removeListener("recalculate", this.onChange); + }, + getDefaultProps: function getDefaultProps() { + return { + rowHeight: ROW_HEIGHT + }; + }, + onScrollFlowTable: function onScrollFlowTable() { + this.adjustHead(); + this.onScroll(); + }, + onChange: function onChange() { + this.forceUpdate(); + }, + scrollIntoView: function scrollIntoView(flow) { + this.scrollRowIntoView(this.context.view.index(flow), this.refs.body.getDOMNode().offsetTop); + }, + renderRow: function renderRow(flow) { + var selected = flow === this.props.selected; + var highlighted = this.context.view._highlight && this.context.view._highlight[flow.id]; + + return React.createElement(FlowRow, { key: flow.id, + ref: flow.id, + flow: flow, + columns: this.state.columns, + selected: selected, + highlighted: highlighted, + selectFlow: this.props.selectFlow + }); + }, + render: function render() { + var flows = this.context.view.list; + var rows = this.renderRows(flows); + + return React.createElement( + "div", + { className: "flow-table", onScroll: this.onScrollFlowTable }, + React.createElement( + "table", + null, + React.createElement(FlowTableHead, { ref: "head", + columns: this.state.columns, + setSortKeyFun: this.props.setSortKeyFun }), + React.createElement( + "tbody", + { ref: "body" }, + this.getPlaceholderTop(flows.length), + rows, + this.getPlaceholderBottom(flows.length) + ) + ) + ); + } +}); + +module.exports = FlowTable; + + +},{"../utils.js":26,"./common.js":4,"./flowtable-columns.js":7,"./virtualscroll.js":19,"lodash":"lodash","react":"react"}],9:[function(require,module,exports){ +"use strict"; + +var _extends = Object.assign || function (target) { for (var i = 1; i < arguments.length; i++) { var source = arguments[i]; for (var key in source) { if (Object.prototype.hasOwnProperty.call(source, key)) { target[key] = source[key]; } } } return target; }; + +var React = require("react"); +var _ = require("lodash"); + +var MessageUtils = require("../../flow/utils.js").MessageUtils; +var utils = require("../../utils.js"); + +var image_regex = /^image\/(png|jpe?g|gif|vnc.microsoft.icon|x-icon)$/i; +var ViewImage = React.createClass({ + displayName: "ViewImage", + + statics: { + matches: function matches(message) { + return image_regex.test(MessageUtils.getContentType(message)); + } + }, + render: function render() { + var url = MessageUtils.getContentURL(this.props.flow, this.props.message); + return React.createElement( + "div", + { className: "flowview-image" }, + React.createElement("img", { src: url, alt: "preview", className: "img-thumbnail" }) + ); + } +}); + +var RawMixin = { + getInitialState: function getInitialState() { + return { + content: undefined, + request: undefined + }; + }, + requestContent: function requestContent(nextProps) { + if (this.state.request) { + this.state.request.abort(); + } + var request = MessageUtils.getContent(nextProps.flow, nextProps.message); + this.setState({ + content: undefined, + request: request + }); + request.done((function (data) { + this.setState({ content: data }); + }).bind(this)).fail((function (jqXHR, textStatus, errorThrown) { + if (textStatus === "abort") { + return; + } + this.setState({ content: "AJAX Error: " + textStatus + "\r\n" + errorThrown }); + }).bind(this)).always((function () { + this.setState({ request: undefined }); + }).bind(this)); + }, + componentWillMount: function componentWillMount() { + this.requestContent(this.props); + }, + componentWillReceiveProps: function componentWillReceiveProps(nextProps) { + if (nextProps.message !== this.props.message) { + this.requestContent(nextProps); + } + }, + componentWillUnmount: function componentWillUnmount() { + if (this.state.request) { + this.state.request.abort(); + } + }, + render: function render() { + if (!this.state.content) { + return React.createElement( + "div", + { className: "text-center" }, + React.createElement("i", { className: "fa fa-spinner fa-spin" }) + ); + } + return this.renderContent(); + } +}; + +var ViewRaw = React.createClass({ + displayName: "ViewRaw", + + mixins: [RawMixin], + statics: { + matches: function matches(message) { + return true; + } + }, + renderContent: function renderContent() { + return React.createElement( + "pre", + null, + this.state.content + ); + } +}); + +var json_regex = /^application\/json$/i; +var ViewJSON = React.createClass({ + displayName: "ViewJSON", + + mixins: [RawMixin], + statics: { + matches: function matches(message) { + return json_regex.test(MessageUtils.getContentType(message)); + } + }, + renderContent: function renderContent() { + var json = this.state.content; + try { + json = JSON.stringify(JSON.parse(json), null, 2); + } catch (e) {} + return React.createElement( + "pre", + null, + json + ); + } +}); + +var ViewAuto = React.createClass({ + displayName: "ViewAuto", + + statics: { + matches: function matches() { + return false; // don't match itself + }, + findView: function findView(message) { + for (var i = 0; i < all.length; i++) { + if (all[i].matches(message)) { + return all[i]; + } + } + return all[all.length - 1]; + } + }, + render: function render() { + var View = ViewAuto.findView(this.props.message); + return React.createElement(View, this.props); + } +}); + +var all = [ViewAuto, ViewImage, ViewJSON, ViewRaw]; + +var ContentEmpty = React.createClass({ + displayName: "ContentEmpty", + + render: function render() { + var message_name = this.props.flow.request === this.props.message ? "request" : "response"; + return React.createElement( + "div", + { className: "alert alert-info" }, + "No ", + message_name, + " content." + ); + } +}); + +var ContentMissing = React.createClass({ + displayName: "ContentMissing", + + render: function render() { + var message_name = this.props.flow.request === this.props.message ? "Request" : "Response"; + return React.createElement( + "div", + { className: "alert alert-info" }, + message_name, + " content missing." + ); + } +}); + +var TooLarge = React.createClass({ + displayName: "TooLarge", + + statics: { + isTooLarge: function isTooLarge(message) { + var max_mb = ViewImage.matches(message) ? 10 : 0.2; + return message.contentLength > 1024 * 1024 * max_mb; + } + }, + render: function render() { + var size = utils.formatSize(this.props.message.contentLength); + return React.createElement( + "div", + { className: "alert alert-warning" }, + React.createElement( + "button", + { onClick: this.props.onClick, className: "btn btn-xs btn-warning pull-right" }, + "Display anyway" + ), + size, + " content size." + ); + } +}); + +var ViewSelector = React.createClass({ + displayName: "ViewSelector", + + render: function render() { + var views = []; + for (var i = 0; i < all.length; i++) { + var view = all[i]; + var className = "btn btn-default"; + if (view === this.props.active) { + className += " active"; + } + var text; + if (view === ViewAuto) { + text = "auto: " + ViewAuto.findView(this.props.message).displayName.toLowerCase().replace("view", ""); + } else { + text = view.displayName.toLowerCase().replace("view", ""); + } + views.push(React.createElement( + "button", + { + key: view.displayName, + onClick: this.props.selectView.bind(null, view), + className: className }, + text + )); + } + + return React.createElement( + "div", + { className: "view-selector btn-group btn-group-xs" }, + views + ); + } +}); + +var ContentView = React.createClass({ + displayName: "ContentView", + + getInitialState: function getInitialState() { + return { + displayLarge: false, + View: ViewAuto + }; + }, + propTypes: { + // It may seem a bit weird at the first glance: + // Every view takes the flow and the message as props, e.g. + // <Auto flow={flow} message={flow.request}/> + flow: React.PropTypes.object.isRequired, + message: React.PropTypes.object.isRequired + }, + selectView: function selectView(view) { + this.setState({ + View: view + }); + }, + displayLarge: function displayLarge() { + this.setState({ displayLarge: true }); + }, + componentWillReceiveProps: function componentWillReceiveProps(nextProps) { + if (nextProps.message !== this.props.message) { + this.setState(this.getInitialState()); + } + }, + render: function render() { + var message = this.props.message; + if (message.contentLength === 0) { + return React.createElement(ContentEmpty, this.props); + } else if (message.contentLength === null) { + return React.createElement(ContentMissing, this.props); + } else if (!this.state.displayLarge && TooLarge.isTooLarge(message)) { + return React.createElement(TooLarge, _extends({}, this.props, { onClick: this.displayLarge })); + } + + var downloadUrl = MessageUtils.getContentURL(this.props.flow, message); + + return React.createElement( + "div", + null, + React.createElement(this.state.View, this.props), + React.createElement( + "div", + { className: "view-options text-center" }, + React.createElement(ViewSelector, { selectView: this.selectView, active: this.state.View, message: message }), + "Â ", + React.createElement( + "a", + { className: "btn btn-default btn-xs", href: downloadUrl }, + React.createElement("i", { className: "fa fa-download" }) + ) + ) + ); + } +}); + +module.exports = ContentView; + + +},{"../../flow/utils.js":23,"../../utils.js":26,"lodash":"lodash","react":"react"}],10:[function(require,module,exports){ +"use strict"; + +var React = require("react"); +var _ = require("lodash"); + +var utils = require("../../utils.js"); + +var TimeStamp = React.createClass({ + displayName: "TimeStamp", + + render: function render() { + + if (!this.props.t) { + //should be return null, but that triggers a React bug. + return React.createElement("tr", null); + } + + var ts = utils.formatTimeStamp(this.props.t); + + var delta; + if (this.props.deltaTo) { + delta = utils.formatTimeDelta(1000 * (this.props.t - this.props.deltaTo)); + delta = React.createElement( + "span", + { className: "text-muted" }, + "(" + delta + ")" + ); + } else { + delta = null; + } + + return React.createElement( + "tr", + null, + React.createElement( + "td", + null, + this.props.title + ":" + ), + React.createElement( + "td", + null, + ts, + " ", + delta + ) + ); + } +}); + +var ConnectionInfo = React.createClass({ + displayName: "ConnectionInfo", + + render: function render() { + var conn = this.props.conn; + var address = conn.address.address.join(":"); + + var sni = React.createElement("tr", { key: "sni" }); //should be null, but that triggers a React bug. + if (conn.sni) { + sni = React.createElement( + "tr", + { key: "sni" }, + React.createElement( + "td", + null, + React.createElement( + "abbr", + { title: "TLS Server Name Indication" }, + "TLS SNI:" + ) + ), + React.createElement( + "td", + null, + conn.sni + ) + ); + } + return React.createElement( + "table", + { className: "connection-table" }, + React.createElement( + "tbody", + null, + React.createElement( + "tr", + { key: "address" }, + React.createElement( + "td", + null, + "Address:" + ), + React.createElement( + "td", + null, + address + ) + ), + sni + ) + ); + } +}); + +var CertificateInfo = React.createClass({ + displayName: "CertificateInfo", + + render: function render() { + //TODO: We should fetch human-readable certificate representation + // from the server + var flow = this.props.flow; + var client_conn = flow.client_conn; + var server_conn = flow.server_conn; + + var preStyle = { maxHeight: 100 }; + return React.createElement( + "div", + null, + client_conn.cert ? React.createElement( + "h4", + null, + "Client Certificate" + ) : null, + client_conn.cert ? React.createElement( + "pre", + { style: preStyle }, + client_conn.cert + ) : null, + server_conn.cert ? React.createElement( + "h4", + null, + "Server Certificate" + ) : null, + server_conn.cert ? React.createElement( + "pre", + { style: preStyle }, + server_conn.cert + ) : null + ); + } +}); + +var Timing = React.createClass({ + displayName: "Timing", + + render: function render() { + var flow = this.props.flow; + var sc = flow.server_conn; + var cc = flow.client_conn; + var req = flow.request; + var resp = flow.response; + + var timestamps = [{ + title: "Server conn. initiated", + t: sc.timestamp_start, + deltaTo: req.timestamp_start + }, { + title: "Server conn. TCP handshake", + t: sc.timestamp_tcp_setup, + deltaTo: req.timestamp_start + }, { + title: "Server conn. SSL handshake", + t: sc.timestamp_ssl_setup, + deltaTo: req.timestamp_start + }, { + title: "Client conn. established", + t: cc.timestamp_start, + deltaTo: req.timestamp_start + }, { + title: "Client conn. SSL handshake", + t: cc.timestamp_ssl_setup, + deltaTo: req.timestamp_start + }, { + title: "First request byte", + t: req.timestamp_start + }, { + title: "Request complete", + t: req.timestamp_end, + deltaTo: req.timestamp_start + }]; + + if (flow.response) { + timestamps.push({ + title: "First response byte", + t: resp.timestamp_start, + deltaTo: req.timestamp_start + }, { + title: "Response complete", + t: resp.timestamp_end, + deltaTo: req.timestamp_start + }); + } + + //Add unique key for each row. + timestamps.forEach(function (e) { + e.key = e.title; + }); + + timestamps = _.sortBy(timestamps, 't'); + + var rows = timestamps.map(function (e) { + return React.createElement(TimeStamp, e); + }); + + return React.createElement( + "div", + null, + React.createElement( + "h4", + null, + "Timing" + ), + React.createElement( + "table", + { className: "timing-table" }, + React.createElement( + "tbody", + null, + rows + ) + ) + ); + } +}); + +var Details = React.createClass({ + displayName: "Details", + + render: function render() { + var flow = this.props.flow; + var client_conn = flow.client_conn; + var server_conn = flow.server_conn; + return React.createElement( + "section", + null, + React.createElement( + "h4", + null, + "Client Connection" + ), + React.createElement(ConnectionInfo, { conn: client_conn }), + React.createElement( + "h4", + null, + "Server Connection" + ), + React.createElement(ConnectionInfo, { conn: server_conn }), + React.createElement(CertificateInfo, { flow: flow }), + React.createElement(Timing, { flow: flow }) + ); + } +}); + +module.exports = Details; + + +},{"../../utils.js":26,"lodash":"lodash","react":"react"}],11:[function(require,module,exports){ +"use strict"; + +var React = require("react"); +var _ = require("lodash"); + +var common = require("../common.js"); +var Nav = require("./nav.js"); +var Messages = require("./messages.js"); +var Details = require("./details.js"); +var Prompt = require("../prompt.js"); + +var allTabs = { + request: Messages.Request, + response: Messages.Response, + error: Messages.Error, + details: Details +}; + +var FlowView = React.createClass({ + displayName: "FlowView", + + mixins: [common.StickyHeadMixin, common.Navigation, common.RouterState], + getInitialState: function getInitialState() { + return { + prompt: false + }; + }, + getTabs: function getTabs(flow) { + var tabs = []; + ["request", "response", "error"].forEach(function (e) { + if (flow[e]) { + tabs.push(e); + } + }); + tabs.push("details"); + return tabs; + }, + nextTab: function nextTab(i) { + var tabs = this.getTabs(this.props.flow); + var currentIndex = tabs.indexOf(this.getActive()); + // JS modulo operator doesn't correct negative numbers, make sure that we are positive. + var nextIndex = (currentIndex + i + tabs.length) % tabs.length; + this.selectTab(tabs[nextIndex]); + }, + selectTab: function selectTab(panel) { + this.replaceWith("flow", { + flowId: this.getParams().flowId, + detailTab: panel + }); + }, + getActive: function getActive() { + return this.getParams().detailTab; + }, + promptEdit: function promptEdit() { + var options; + switch (this.getActive()) { + case "request": + options = ["method", "url", { text: "http version", key: "v" }, "header" + /*, "content"*/]; + break; + case "response": + options = [{ text: "http version", key: "v" }, "code", "message", "header" + /*, "content"*/]; + break; + case "details": + return; + default: + throw "Unknown tab for edit: " + this.getActive(); + } + + this.setState({ + prompt: { + done: (function (k) { + this.setState({ prompt: false }); + if (k) { + this.refs.tab.edit(k); + } + }).bind(this), + options: options + } + }); + }, + render: function render() { + var flow = this.props.flow; + var tabs = this.getTabs(flow); + var active = this.getActive(); + + if (!_.contains(tabs, active)) { + if (active === "response" && flow.error) { + active = "error"; + } else if (active === "error" && flow.response) { + active = "response"; + } else { + active = tabs[0]; + } + this.selectTab(active); + } + + var prompt = null; + if (this.state.prompt) { + prompt = React.createElement(Prompt, this.state.prompt); + } + + var Tab = allTabs[active]; + return React.createElement( + "div", + { className: "flow-detail", onScroll: this.adjustHead }, + React.createElement(Nav, { ref: "head", + flow: flow, + tabs: tabs, + active: active, + selectTab: this.selectTab }), + React.createElement(Tab, { ref: "tab", flow: flow }), + prompt + ); + } +}); + +module.exports = FlowView; + + +},{"../common.js":4,"../prompt.js":17,"./details.js":10,"./messages.js":12,"./nav.js":13,"lodash":"lodash","react":"react"}],12:[function(require,module,exports){ +"use strict"; + +var _extends = Object.assign || function (target) { for (var i = 1; i < arguments.length; i++) { var source = arguments[i]; for (var key in source) { if (Object.prototype.hasOwnProperty.call(source, key)) { target[key] = source[key]; } } } return target; }; + +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({ + displayName: "Headers", + + propTypes: { + onChange: React.PropTypes.func.isRequired, + message: React.PropTypes.object.isRequired + }, + onChange: function onChange(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 edit() { + this.refs["0-key"].focus(); + }, + onTab: function onTab(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 componentDidUpdate() { + if (this._nextSel && this.refs[this._nextSel]) { + this.refs[this._nextSel].focus(); + this._nextSel = undefined; + } + }, + onRemove: function onRemove(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 render() { + + var rows = this.props.message.headers.map((function (header, i) { + + var kEdit = React.createElement(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 = React.createElement(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 React.createElement( + "tr", + { key: i }, + React.createElement( + "td", + { className: "header-name" }, + kEdit, + ":" + ), + React.createElement( + "td", + { className: "header-value" }, + vEdit + ) + ); + }).bind(this)); + return React.createElement( + "table", + { className: "header-table" }, + React.createElement( + "tbody", + null, + rows + ) + ); + } +}); + +var HeaderEditor = React.createClass({ + displayName: "HeaderEditor", + + render: function render() { + return React.createElement(ValueEditor, _extends({ ref: "input" }, this.props, { onKeyDown: this.onKeyDown, inline: true })); + }, + focus: function focus() { + this.getDOMNode().focus(); + }, + onKeyDown: function onKeyDown(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({ + displayName: "RequestLine", + + render: function render() { + var flow = this.props.flow; + var url = flowutils.RequestUtils.pretty_url(flow.request); + var httpver = flow.request.http_version; + + return React.createElement( + "div", + { className: "first-line request-line" }, + React.createElement(ValueEditor, { + ref: "method", + content: flow.request.method, + onDone: this.onMethodChange, + inline: true }), + "Â ", + React.createElement(ValueEditor, { + ref: "url", + content: url, + onDone: this.onUrlChange, + isValid: this.isValidUrl, + inline: true }), + "Â ", + React.createElement(ValueEditor, { + ref: "httpVersion", + content: httpver, + onDone: this.onHttpVersionChange, + isValid: flowutils.isValidHttpVersion, + inline: true }) + ); + }, + isValidUrl: function isValidUrl(url) { + var u = flowutils.parseUrl(url); + return !!u.host; + }, + onMethodChange: function onMethodChange(nextMethod) { + actions.FlowActions.update(this.props.flow, { request: { method: nextMethod } }); + }, + onUrlChange: function onUrlChange(nextUrl) { + var props = flowutils.parseUrl(nextUrl); + props.path = props.path || ""; + actions.FlowActions.update(this.props.flow, { request: props }); + }, + onHttpVersionChange: function onHttpVersionChange(nextVer) { + var ver = flowutils.parseHttpVersion(nextVer); + actions.FlowActions.update(this.props.flow, { request: { http_version: ver } }); + } +}); + +var ResponseLine = React.createClass({ + displayName: "ResponseLine", + + render: function render() { + var flow = this.props.flow; + var httpver = flow.response.http_version; + return React.createElement( + "div", + { className: "first-line response-line" }, + React.createElement(ValueEditor, { + ref: "httpVersion", + content: httpver, + onDone: this.onHttpVersionChange, + isValid: flowutils.isValidHttpVersion, + inline: true }), + "Â ", + React.createElement(ValueEditor, { + ref: "code", + content: flow.response.status_code + "", + onDone: this.onCodeChange, + isValid: this.isValidCode, + inline: true }), + "Â ", + React.createElement(ValueEditor, { + ref: "msg", + content: flow.response.msg, + onDone: this.onMsgChange, + inline: true }) + ); + }, + isValidCode: function isValidCode(code) { + return (/^\d+$/.test(code) + ); + }, + onHttpVersionChange: function onHttpVersionChange(nextVer) { + var ver = flowutils.parseHttpVersion(nextVer); + actions.FlowActions.update(this.props.flow, { response: { http_version: ver } }); + }, + onMsgChange: function onMsgChange(nextMsg) { + actions.FlowActions.update(this.props.flow, { response: { msg: nextMsg } }); + }, + onCodeChange: function onCodeChange(nextCode) { + nextCode = parseInt(nextCode); + actions.FlowActions.update(this.props.flow, { response: { code: nextCode } }); + } +}); + +var Request = React.createClass({ + displayName: "Request", + + render: function render() { + var flow = this.props.flow; + return React.createElement( + "section", + { className: "request" }, + React.createElement(RequestLine, { ref: "requestLine", flow: flow }), + React.createElement(Headers, { ref: "headers", message: flow.request, onChange: this.onHeaderChange }), + React.createElement("hr", null), + React.createElement(ContentView, { flow: flow, message: flow.request }) + ); + }, + edit: function edit(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 onHeaderChange(nextHeaders) { + actions.FlowActions.update(this.props.flow, { + request: { + headers: nextHeaders + } + }); + } +}); + +var Response = React.createClass({ + displayName: "Response", + + render: function render() { + var flow = this.props.flow; + return React.createElement( + "section", + { className: "response" }, + React.createElement(ResponseLine, { ref: "responseLine", flow: flow }), + React.createElement(Headers, { ref: "headers", message: flow.response, onChange: this.onHeaderChange }), + React.createElement("hr", null), + React.createElement(ContentView, { flow: flow, message: flow.response }) + ); + }, + edit: function edit(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 onHeaderChange(nextHeaders) { + actions.FlowActions.update(this.props.flow, { + response: { + headers: nextHeaders + } + }); + } +}); + +var Error = React.createClass({ + displayName: "Error", + + render: function render() { + var flow = this.props.flow; + return React.createElement( + "section", + null, + React.createElement( + "div", + { className: "alert alert-warning" }, + flow.error.msg, + React.createElement( + "div", + null, + React.createElement( + "small", + null, + utils.formatTimeStamp(flow.error.timestamp) + ) + ) + ) + ); + } +}); + +module.exports = { + Request: Request, + Response: Response, + Error: Error +}; +/*<ResponseLine flow={flow}/>*/ /*<RequestLine flow={flow}/>*/ + + +},{"../../actions.js":2,"../../flow/utils.js":23,"../../utils.js":26,"../common.js":4,"../editor.js":5,"./contentview.js":9,"lodash":"lodash","react":"react"}],13:[function(require,module,exports){ +"use strict"; + +var React = require("react"); + +var actions = require("../../actions.js"); + +var NavAction = React.createClass({ + displayName: "NavAction", + + onClick: function onClick(e) { + e.preventDefault(); + this.props.onClick(); + }, + render: function render() { + return React.createElement( + "a", + { title: this.props.title, + href: "#", + className: "nav-action", + onClick: this.onClick }, + React.createElement("i", { className: "fa fa-fw " + this.props.icon }) + ); + } +}); + +var Nav = React.createClass({ + displayName: "Nav", + + render: function render() { + var flow = this.props.flow; + + var tabs = this.props.tabs.map((function (e) { + var str = e.charAt(0).toUpperCase() + e.slice(1); + var className = this.props.active === e ? "active" : ""; + var onClick = (function (event) { + this.props.selectTab(e); + event.preventDefault(); + }).bind(this); + return React.createElement( + "a", + { key: e, + href: "#", + className: className, + onClick: onClick }, + str + ); + }).bind(this)); + + var acceptButton = null; + if (flow.intercepted) { + acceptButton = React.createElement(NavAction, { title: "[a]ccept intercepted flow", icon: "fa-play", onClick: actions.FlowActions.accept.bind(null, flow) }); + } + var revertButton = null; + if (flow.modified) { + revertButton = React.createElement(NavAction, { title: "revert changes to flow [V]", icon: "fa-history", onClick: actions.FlowActions.revert.bind(null, flow) }); + } + + return React.createElement( + "nav", + { ref: "head", className: "nav-tabs nav-tabs-sm" }, + tabs, + React.createElement(NavAction, { title: "[d]elete flow", icon: "fa-trash", onClick: actions.FlowActions["delete"].bind(null, flow) }), + React.createElement(NavAction, { title: "[D]uplicate flow", icon: "fa-copy", onClick: actions.FlowActions.duplicate.bind(null, flow) }), + React.createElement(NavAction, { disabled: true, title: "[r]eplay flow", icon: "fa-repeat", onClick: actions.FlowActions.replay.bind(null, flow) }), + acceptButton, + revertButton + ); + } +}); + +module.exports = Nav; + + +},{"../../actions.js":2,"react":"react"}],14:[function(require,module,exports){ +"use strict"; + +var React = require("react"); +var common = require("./common.js"); + +var Footer = React.createClass({ + displayName: "Footer", + + mixins: [common.SettingsState], + render: function render() { + var mode = this.state.settings.mode; + var intercept = this.state.settings.intercept; + return React.createElement( + "footer", + null, + mode && mode != "regular" ? React.createElement( + "span", + { className: "label label-success" }, + mode, + " mode" + ) : null, + "Â ", + intercept ? React.createElement( + "span", + { className: "label label-success" }, + "Intercept: ", + intercept + ) : null + ); + } +}); + +module.exports = Footer; + + +},{"./common.js":4,"react":"react"}],15:[function(require,module,exports){ +"use strict"; + +var React = require("react"); +var $ = require("jquery"); + +var Filt = require("../filt/filt.js"); +var utils = require("../utils.js"); +var common = require("./common.js"); +var actions = require("../actions.js"); +var Query = require("../actions.js").Query; + +var FilterDocs = React.createClass({ + displayName: "FilterDocs", + + statics: { + xhr: false, + doc: false + }, + componentWillMount: function componentWillMount() { + 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 render() { + if (!FilterDocs.doc) { + return React.createElement("i", { className: "fa fa-spinner fa-spin" }); + } else { + var commands = FilterDocs.doc.commands.map(function (c) { + return React.createElement( + "tr", + { key: c[1] }, + React.createElement( + "td", + null, + c[0].replace(" ", "Â ") + ), + React.createElement( + "td", + null, + c[1] + ) + ); + }); + commands.push(React.createElement( + "tr", + { key: "docs-link" }, + React.createElement( + "td", + { colSpan: "2" }, + React.createElement( + "a", + { href: "https://mitmproxy.org/doc/features/filters.html", + target: "_blank" }, + React.createElement("i", { className: "fa fa-external-link" }), + "Â mitmproxy docs" + ) + ) + )); + return React.createElement( + "table", + { className: "table table-condensed" }, + React.createElement( + "tbody", + null, + commands + ) + ); + } + } +}); +var FilterInput = React.createClass({ + displayName: "FilterInput", + + mixins: [common.ChildFocus], + getInitialState: function getInitialState() { + // 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 componentWillReceiveProps(nextProps) { + this.setState({ value: nextProps.value }); + }, + onChange: function onChange(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 isValid(filt) { + try { + Filt.parse(filt || this.state.value); + return true; + } catch (e) { + return false; + } + }, + getDesc: function getDesc() { + var desc; + try { + desc = Filt.parse(this.state.value).desc; + } catch (e) { + desc = "" + e; + } + if (desc !== "true") { + return desc; + } else { + return React.createElement(FilterDocs, null); + } + }, + onFocus: function onFocus() { + this.setState({ focus: true }); + }, + onBlur: function onBlur() { + this.setState({ focus: false }); + }, + onMouseEnter: function onMouseEnter() { + this.setState({ mousefocus: true }); + }, + onMouseLeave: function onMouseLeave() { + this.setState({ mousefocus: false }); + }, + onKeyDown: function onKeyDown(e) { + if (e.keyCode === utils.Key.ESC || e.keyCode === utils.Key.ENTER) { + this.blur(); + // If closed using ESC/ENTER, hide the tooltip. + this.setState({ mousefocus: false }); + } + e.stopPropagation(); + }, + blur: function blur() { + this.refs.input.getDOMNode().blur(); + this.returnFocus(); + }, + select: function select() { + this.refs.input.getDOMNode().select(); + }, + render: function render() { + 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 = React.createElement( + "div", + { className: "popover bottom", onMouseEnter: this.onMouseEnter, onMouseLeave: this.onMouseLeave }, + React.createElement("div", { className: "arrow" }), + React.createElement( + "div", + { className: "popover-content" }, + this.getDesc() + ) + ); + } + + return React.createElement( + "div", + { className: groupClassName }, + React.createElement( + "span", + { className: "input-group-addon" }, + React.createElement("i", { className: icon, style: { color: this.props.color } }) + ), + React.createElement("input", { type: "text", placeholder: this.props.placeholder, className: "form-control", + ref: "input", + onChange: this.onChange, + onFocus: this.onFocus, + onBlur: this.onBlur, + onKeyDown: this.onKeyDown, + value: this.state.value }), + popover + ); + } +}); + +var MainMenu = React.createClass({ + displayName: "MainMenu", + + mixins: [common.Navigation, common.RouterState, common.SettingsState], + statics: { + title: "Start", + route: "flows" + }, + onSearchChange: function onSearchChange(val) { + var d = {}; + d[Query.SEARCH] = val; + this.setQuery(d); + }, + onHighlightChange: function onHighlightChange(val) { + var d = {}; + d[Query.HIGHLIGHT] = val; + this.setQuery(d); + }, + onInterceptChange: function onInterceptChange(val) { + actions.SettingsActions.update({ intercept: val }); + }, + render: function render() { + var search = this.getQuery()[Query.SEARCH] || ""; + var highlight = this.getQuery()[Query.HIGHLIGHT] || ""; + var intercept = this.state.settings.intercept || ""; + + return React.createElement( + "div", + null, + React.createElement( + "div", + { className: "menu-row" }, + React.createElement(FilterInput, { + ref: "search", + placeholder: "Search", + type: "search", + color: "black", + value: search, + onChange: this.onSearchChange }), + React.createElement(FilterInput, { + ref: "highlight", + placeholder: "Highlight", + type: "tag", + color: "hsl(48, 100%, 50%)", + value: highlight, + onChange: this.onHighlightChange }), + React.createElement(FilterInput, { + ref: "intercept", + placeholder: "Intercept", + type: "pause", + color: "hsl(208, 56%, 53%)", + value: intercept, + onChange: this.onInterceptChange }) + ), + React.createElement("div", { className: "clearfix" }) + ); + } +}); + +var ViewMenu = React.createClass({ + displayName: "ViewMenu", + + statics: { + title: "View", + route: "flows" + }, + mixins: [common.Navigation, common.RouterState], + toggleEventLog: function toggleEventLog() { + var d = {}; + + if (this.getQuery()[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.setQuery(d); + }, + render: function render() { + var showEventLog = this.getQuery()[Query.SHOW_EVENTLOG]; + return React.createElement( + "div", + null, + React.createElement( + "button", + { + className: "btn " + (showEventLog ? "btn-primary" : "btn-default"), + onClick: this.toggleEventLog }, + React.createElement("i", { className: "fa fa-database" }), + "Â Show Eventlog" + ), + React.createElement( + "span", + null, + " " + ) + ); + } +}); + +var ReportsMenu = React.createClass({ + displayName: "ReportsMenu", + + statics: { + title: "Visualization", + route: "reports" + }, + render: function render() { + return React.createElement( + "div", + null, + "Reports Menu" + ); + } +}); + +var FileMenu = React.createClass({ + displayName: "FileMenu", + + getInitialState: function getInitialState() { + return { + showFileMenu: false + }; + }, + handleFileClick: function handleFileClick(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 handleNewClick(e) { + e.preventDefault(); + if (confirm("Delete all flows?")) { + actions.FlowActions.clear(); + } + }, + handleOpenClick: function handleOpenClick(e) { + e.preventDefault(); + console.error("unimplemented: handleOpenClick"); + }, + handleSaveClick: function handleSaveClick(e) { + e.preventDefault(); + console.error("unimplemented: handleSaveClick"); + }, + handleShutdownClick: function handleShutdownClick(e) { + e.preventDefault(); + console.error("unimplemented: handleShutdownClick"); + }, + render: function render() { + var fileMenuClass = "dropdown pull-left" + (this.state.showFileMenu ? " open" : ""); + + return React.createElement( + "div", + { className: fileMenuClass }, + React.createElement( + "a", + { href: "#", className: "special", onClick: this.handleFileClick }, + " mitmproxy " + ), + React.createElement( + "ul", + { className: "dropdown-menu", role: "menu" }, + React.createElement( + "li", + null, + React.createElement( + "a", + { href: "#", onClick: this.handleNewClick }, + React.createElement("i", { className: "fa fa-fw fa-file" }), + "New" + ) + ), + React.createElement("li", { role: "presentation", className: "divider" }), + React.createElement( + "li", + null, + React.createElement( + "a", + { href: "http://mitm.it/", target: "_blank" }, + React.createElement("i", { className: "fa fa-fw fa-external-link" }), + "Install Certificates..." + ) + ) + ) + ); + } +}); + +var header_entries = [MainMenu, ViewMenu /*, ReportsMenu */]; + +var Header = React.createClass({ + displayName: "Header", + + mixins: [common.Navigation], + getInitialState: function getInitialState() { + return { + active: header_entries[0] + }; + }, + handleClick: function handleClick(active, e) { + e.preventDefault(); + this.replaceWith(active.route); + this.setState({ active: active }); + }, + render: function render() { + var header = header_entries.map((function (entry, i) { + var className; + if (entry === this.state.active) { + className = "active"; + } else { + className = ""; + } + return React.createElement( + "a", + { key: i, + href: "#", + className: className, + onClick: this.handleClick.bind(this, entry) }, + entry.title + ); + }).bind(this)); + + return React.createElement( + "header", + null, + React.createElement( + "nav", + { className: "nav-tabs nav-tabs-lg" }, + React.createElement(FileMenu, null), + header + ), + React.createElement( + "div", + { className: "menu" }, + React.createElement(this.state.active, { ref: "active" }) + ) + ); + } +}); + +module.exports = { + Header: Header, + MainMenu: MainMenu +}; +/* +<li> +<a href="#" onClick={this.handleOpenClick}> +<i className="fa fa-fw fa-folder-open"></i> +Open +</a> +</li> +<li> +<a href="#" onClick={this.handleSaveClick}> +<i className="fa fa-fw fa-save"></i> +Save +</a> +</li> +<li role="presentation" className="divider"></li> +<li> +<a href="#" onClick={this.handleShutdownClick}> +<i className="fa fa-fw fa-plug"></i> +Shutdown +</a> +</li> +*/ + + +},{"../actions.js":2,"../filt/filt.js":22,"../utils.js":26,"./common.js":4,"jquery":"jquery","react":"react"}],16:[function(require,module,exports){ +"use strict"; + +var React = require("react"); + +var actions = require("../actions.js"); +var Query = require("../actions.js").Query; +var utils = require("../utils.js"); +var views = require("../store/view.js"); +var Filt = require("../filt/filt.js"); + +var common = require("./common.js"); +var FlowTable = require("./flowtable.js"); +var FlowView = require("./flowview/index.js"); + +var MainView = React.createClass({ + displayName: "MainView", + + mixins: [common.Navigation, common.RouterState], + contextTypes: { + flowStore: React.PropTypes.object.isRequired + }, + childContextTypes: { + view: React.PropTypes.object.isRequired + }, + getChildContext: function getChildContext() { + return { + view: this.state.view + }; + }, + getInitialState: function getInitialState() { + var sortKeyFun = false; + var view = new views.StoreView(this.context.flowStore, this.getViewFilt(), sortKeyFun); + view.addListener("recalculate", this.onRecalculate); + view.addListener("add", this.onUpdate); + view.addListener("update", this.onUpdate); + view.addListener("remove", this.onUpdate); + view.addListener("remove", this.onRemove); + + return { + view: view, + sortKeyFun: sortKeyFun + }; + }, + componentWillUnmount: function componentWillUnmount() { + this.state.view.close(); + }, + getViewFilt: function getViewFilt() { + try { + var filt = Filt.parse(this.getQuery()[Query.SEARCH] || ""); + var highlightStr = this.getQuery()[Query.HIGHLIGHT]; + var highlight = highlightStr ? Filt.parse(highlightStr) : false; + } catch (e) { + console.error("Error when processing filter: " + e); + } + + return function filter_and_highlight(flow) { + if (!this._highlight) { + this._highlight = {}; + } + this._highlight[flow.id] = highlight && highlight(flow); + return filt(flow); + }; + }, + componentWillReceiveProps: function componentWillReceiveProps(nextProps) { + var filterChanged = this.props.query[Query.SEARCH] !== nextProps.query[Query.SEARCH]; + var highlightChanged = this.props.query[Query.HIGHLIGHT] !== nextProps.query[Query.HIGHLIGHT]; + if (filterChanged || highlightChanged) { + this.state.view.recalculate(this.getViewFilt(), this.state.sortKeyFun); + } + }, + onRecalculate: function onRecalculate() { + this.forceUpdate(); + var selected = this.getSelected(); + if (selected) { + this.refs.flowTable.scrollIntoView(selected); + } + }, + onUpdate: function onUpdate(flow) { + if (flow.id === this.getParams().flowId) { + this.forceUpdate(); + } + }, + onRemove: function onRemove(flow_id, index) { + if (flow_id === this.getParams().flowId) { + var flow_to_select = this.state.view.list[Math.min(index, this.state.view.list.length - 1)]; + this.selectFlow(flow_to_select); + } + }, + setSortKeyFun: function setSortKeyFun(sortKeyFun) { + this.setState({ + sortKeyFun: sortKeyFun + }); + this.state.view.recalculate(this.getViewFilt(), sortKeyFun); + }, + selectFlow: function selectFlow(flow) { + if (flow) { + this.replaceWith("flow", { + flowId: flow.id, + detailTab: this.getParams().detailTab || "request" + }); + this.refs.flowTable.scrollIntoView(flow); + } else { + this.replaceWith("flows", {}); + } + }, + selectFlowRelative: function selectFlowRelative(shift) { + var flows = this.state.view.list; + var index; + if (!this.getParams().flowId) { + if (shift < 0) { + index = flows.length - 1; + } else { + index = 0; + } + } else { + var currFlowId = this.getParams().flowId; + var i = flows.length; + while (i--) { + if (flows[i].id === currFlowId) { + index = i; + break; + } + } + index = Math.min(Math.max(0, index + shift), flows.length - 1); + } + this.selectFlow(flows[index]); + }, + onMainKeyDown: function onMainKeyDown(e) { + var flow = this.getSelected(); + if (e.ctrlKey) { + return; + } + switch (e.keyCode) { + case utils.Key.K: + case utils.Key.UP: + this.selectFlowRelative(-1); + break; + case utils.Key.J: + case utils.Key.DOWN: + this.selectFlowRelative(+1); + break; + case utils.Key.SPACE: + case utils.Key.PAGE_DOWN: + this.selectFlowRelative(+10); + break; + case utils.Key.PAGE_UP: + this.selectFlowRelative(-10); + break; + case utils.Key.END: + this.selectFlowRelative(+1e10); + break; + case utils.Key.HOME: + this.selectFlowRelative(-1e10); + break; + case utils.Key.ESC: + this.selectFlow(null); + break; + case utils.Key.H: + case utils.Key.LEFT: + if (this.refs.flowDetails) { + this.refs.flowDetails.nextTab(-1); + } + break; + case utils.Key.L: + case utils.Key.TAB: + case utils.Key.RIGHT: + if (this.refs.flowDetails) { + this.refs.flowDetails.nextTab(+1); + } + break; + case utils.Key.C: + if (e.shiftKey) { + actions.FlowActions.clear(); + } + break; + case utils.Key.D: + if (flow) { + if (e.shiftKey) { + actions.FlowActions.duplicate(flow); + } else { + actions.FlowActions["delete"](flow); + } + } + break; + case utils.Key.A: + if (e.shiftKey) { + actions.FlowActions.accept_all(); + } else if (flow && flow.intercepted) { + actions.FlowActions.accept(flow); + } + break; + case utils.Key.R: + if (!e.shiftKey && flow) { + actions.FlowActions.replay(flow); + } + break; + case utils.Key.V: + if (e.shiftKey && flow && flow.modified) { + actions.FlowActions.revert(flow); + } + break; + case utils.Key.E: + if (this.refs.flowDetails) { + this.refs.flowDetails.promptEdit(); + } + break; + case utils.Key.SHIFT: + break; + default: + console.debug("keydown", e.keyCode); + return; + } + e.preventDefault(); + }, + getSelected: function getSelected() { + return this.context.flowStore.get(this.getParams().flowId); + }, + render: function render() { + var selected = this.getSelected(); + + var details; + if (selected) { + details = [React.createElement(common.Splitter, { key: "splitter" }), React.createElement(FlowView, { key: "flowDetails", ref: "flowDetails", flow: selected })]; + } else { + details = null; + } + + return React.createElement( + "div", + { className: "main-view" }, + React.createElement(FlowTable, { ref: "flowTable", + selectFlow: this.selectFlow, + setSortKeyFun: this.setSortKeyFun, + selected: selected }), + details + ); + } +}); + +module.exports = MainView; + + +},{"../actions.js":2,"../filt/filt.js":22,"../store/view.js":25,"../utils.js":26,"./common.js":4,"./flowtable.js":8,"./flowview/index.js":11,"react":"react"}],17:[function(require,module,exports){ +"use strict"; + +var React = require("react"); +var _ = require("lodash"); + +var utils = require("../utils.js"); +var common = require("./common.js"); + +var Prompt = React.createClass({ + displayName: "Prompt", + + mixins: [common.ChildFocus], + propTypes: { + options: React.PropTypes.array.isRequired, + done: React.PropTypes.func.isRequired, + prompt: React.PropTypes.string + }, + componentDidMount: function componentDidMount() { + React.findDOMNode(this).focus(); + }, + onKeyDown: function onKeyDown(e) { + e.stopPropagation(); + e.preventDefault(); + var opts = this.getOptions(); + for (var i = 0; i < opts.length; i++) { + var k = opts[i].key; + if (utils.Key[k.toUpperCase()] === e.keyCode) { + this.done(k); + return; + } + } + if (e.keyCode === utils.Key.ESC || e.keyCode === utils.Key.ENTER) { + this.done(false); + } + }, + onClick: function onClick(e) { + this.done(false); + }, + done: function done(ret) { + this.props.done(ret); + this.returnFocus(); + }, + getOptions: function getOptions() { + var opts = []; + + var keyTaken = function keyTaken(k) { + return _.includes(_.pluck(opts, "key"), k); + }; + + for (var i = 0; i < this.props.options.length; i++) { + var opt = this.props.options[i]; + if (_.isString(opt)) { + var str = opt; + while (str.length > 0 && keyTaken(str[0])) { + str = str.substr(1); + } + opt = { + text: opt, + key: str[0] + }; + } + if (!opt.text || !opt.key || keyTaken(opt.key)) { + throw "invalid options"; + } else { + opts.push(opt); + } + } + return opts; + }, + render: function render() { + var opts = this.getOptions(); + opts = _.map(opts, (function (o) { + var prefix, suffix; + var idx = o.text.indexOf(o.key); + if (idx !== -1) { + prefix = o.text.substring(0, idx); + suffix = o.text.substring(idx + 1); + } else { + prefix = o.text + " ("; + suffix = ")"; + } + var onClick = (function (e) { + this.done(o.key); + e.stopPropagation(); + }).bind(this); + return React.createElement( + "span", + { + key: o.key, + className: "option", + onClick: onClick }, + prefix, + React.createElement( + "strong", + { className: "text-primary" }, + o.key + ), + suffix + ); + }).bind(this)); + return React.createElement( + "div", + { tabIndex: "0", onKeyDown: this.onKeyDown, onClick: this.onClick, className: "prompt-dialog" }, + React.createElement( + "div", + { className: "prompt-content" }, + this.props.prompt || React.createElement( + "strong", + null, + "Select: " + ), + opts + ) + ); + } +}); + +module.exports = Prompt; + + +},{"../utils.js":26,"./common.js":4,"lodash":"lodash","react":"react"}],18:[function(require,module,exports){ +"use strict"; + +var React = require("react"); +var ReactRouter = require("react-router"); +var _ = require("lodash"); + +var common = require("./common.js"); +var MainView = require("./mainview.js"); +var Footer = require("./footer.js"); +var header = require("./header.js"); +var EventLog = require("./eventlog.js"); +var store = require("../store/store.js"); +var Query = require("../actions.js").Query; +var Key = require("../utils.js").Key; + +//TODO: Move out of here, just a stub. +var Reports = React.createClass({ + displayName: "Reports", + + render: function render() { + return React.createElement( + "div", + null, + "ReportEditor" + ); + } +}); + +var ProxyAppMain = React.createClass({ + displayName: "ProxyAppMain", + + mixins: [common.RouterState], + childContextTypes: { + settingsStore: React.PropTypes.object.isRequired, + flowStore: React.PropTypes.object.isRequired, + eventStore: React.PropTypes.object.isRequired, + returnFocus: React.PropTypes.func.isRequired + }, + componentDidMount: function componentDidMount() { + this.focus(); + }, + getChildContext: function getChildContext() { + return { + settingsStore: this.state.settingsStore, + flowStore: this.state.flowStore, + eventStore: this.state.eventStore, + returnFocus: this.focus + }; + }, + getInitialState: function getInitialState() { + var eventStore = new store.EventLogStore(); + var flowStore = new store.FlowStore(); + var settingsStore = new store.SettingsStore(); + + // Default Settings before fetch + _.extend(settingsStore.dict, {}); + return { + settingsStore: settingsStore, + flowStore: flowStore, + eventStore: eventStore + }; + }, + focus: function focus() { + React.findDOMNode(this).focus(); + }, + getMainComponent: function getMainComponent() { + return this.refs.view.refs.__routeHandler__; + }, + onKeydown: function onKeydown(e) { + + var selectFilterInput = (function (name) { + var headerComponent = this.refs.header; + headerComponent.setState({ active: header.MainMenu }, function () { + headerComponent.refs.active.refs[name].select(); + }); + }).bind(this); + + switch (e.keyCode) { + case Key.I: + selectFilterInput("intercept"); + break; + case Key.L: + selectFilterInput("search"); + break; + case Key.H: + selectFilterInput("highlight"); + break; + default: + var main = this.getMainComponent(); + if (main.onMainKeyDown) { + main.onMainKeyDown(e); + } + return; // don't prevent default then + } + e.preventDefault(); + }, + render: function render() { + var eventlog; + if (this.getQuery()[Query.SHOW_EVENTLOG]) { + eventlog = [React.createElement(common.Splitter, { key: "splitter", axis: "y" }), React.createElement(EventLog, { key: "eventlog" })]; + } else { + eventlog = null; + } + return React.createElement( + "div", + { id: "container", tabIndex: "0", onKeyDown: this.onKeydown }, + React.createElement(header.Header, { ref: "header" }), + React.createElement(RouteHandler, { ref: "view", query: this.getQuery() }), + eventlog, + React.createElement(Footer, null) + ); + } +}); + +var Route = ReactRouter.Route; +var RouteHandler = ReactRouter.RouteHandler; +var Redirect = ReactRouter.Redirect; +var DefaultRoute = ReactRouter.DefaultRoute; +var NotFoundRoute = ReactRouter.NotFoundRoute; + +var routes = React.createElement( + Route, + { path: "/", handler: ProxyAppMain }, + React.createElement(Route, { name: "flows", path: "flows", handler: MainView }), + React.createElement(Route, { name: "flow", path: "flows/:flowId/:detailTab", handler: MainView }), + React.createElement(Route, { name: "reports", handler: Reports }), + React.createElement(Redirect, { path: "/", to: "flows" }) +); + +module.exports = { + routes: routes +}; + + +},{"../actions.js":2,"../store/store.js":24,"../utils.js":26,"./common.js":4,"./eventlog.js":6,"./footer.js":14,"./header.js":15,"./mainview.js":16,"lodash":"lodash","react":"react","react-router":"react-router"}],19:[function(require,module,exports){ +"use strict"; + +var React = require("react"); + +var VirtualScrollMixin = { + getInitialState: function getInitialState() { + return { + start: 0, + stop: 0 + }; + }, + componentWillMount: function componentWillMount() { + if (!this.props.rowHeight) { + console.warn("VirtualScrollMixin: No rowHeight specified", this); + } + }, + getPlaceholderTop: function getPlaceholderTop(total) { + var Tag = this.props.placeholderTagName || "tr"; + // When a large trunk of elements is removed from the button, start may be far off the viewport. + // To make this issue less severe, limit the top placeholder to the total number of rows. + var style = { + height: Math.min(this.state.start, total) * this.props.rowHeight + }; + var spacer = React.createElement(Tag, { key: "placeholder-top", style: style }); + + if (this.state.start % 2 === 1) { + // fix even/odd rows + return [spacer, React.createElement(Tag, { key: "placeholder-top-2" })]; + } else { + return spacer; + } + }, + getPlaceholderBottom: function getPlaceholderBottom(total) { + var Tag = this.props.placeholderTagName || "tr"; + var style = { + height: Math.max(0, total - this.state.stop) * this.props.rowHeight + }; + return React.createElement(Tag, { key: "placeholder-bottom", style: style }); + }, + componentDidMount: function componentDidMount() { + this.onScroll(); + window.addEventListener('resize', this.onScroll); + }, + componentWillUnmount: function componentWillUnmount() { + window.removeEventListener('resize', this.onScroll); + }, + onScroll: function onScroll() { + var viewport = this.getDOMNode(); + var top = viewport.scrollTop; + var height = viewport.offsetHeight; + var start = Math.floor(top / this.props.rowHeight); + var stop = start + Math.ceil(height / (this.props.rowHeightMin || this.props.rowHeight)); + + this.setState({ + start: start, + stop: stop + }); + }, + renderRows: function renderRows(elems) { + var rows = []; + var max = Math.min(elems.length, this.state.stop); + + for (var i = this.state.start; i < max; i++) { + var elem = elems[i]; + rows.push(this.renderRow(elem)); + } + return rows; + }, + scrollRowIntoView: function scrollRowIntoView(index, head_height) { + + var row_top = index * this.props.rowHeight + head_height; + var row_bottom = row_top + this.props.rowHeight; + + var viewport = this.getDOMNode(); + var viewport_top = viewport.scrollTop; + var viewport_bottom = viewport_top + viewport.offsetHeight; + + // Account for pinned thead + if (row_top - head_height < viewport_top) { + viewport.scrollTop = row_top - head_height; + } else if (row_bottom > viewport_bottom) { + viewport.scrollTop = row_bottom - viewport.offsetHeight; + } + } +}; + +module.exports = VirtualScrollMixin; + + +},{"react":"react"}],20:[function(require,module,exports){ +"use strict"; + +var actions = require("./actions.js"); +var AppDispatcher = require("./dispatcher.js").AppDispatcher; + +function Connection(url) { + if (url[0] === "/") { + url = location.origin.replace("http", "ws") + url; + } + + var ws = new WebSocket(url); + ws.onopen = function () { + actions.ConnectionActions.open(); + }; + ws.onmessage = function (message) { + var m = JSON.parse(message.data); + AppDispatcher.dispatchServerAction(m); + }; + ws.onerror = function () { + actions.ConnectionActions.error(); + actions.EventLogActions.add_event("WebSocket connection error."); + }; + ws.onclose = function () { + actions.ConnectionActions.close(); + actions.EventLogActions.add_event("WebSocket connection closed."); + }; + return ws; +} + +module.exports = Connection; + + +},{"./actions.js":2,"./dispatcher.js":21}],21:[function(require,module,exports){ +"use strict"; + +var flux = require("flux"); + +var PayloadSources = { + VIEW: "view", + SERVER: "server" +}; + +var AppDispatcher = new flux.Dispatcher(); +AppDispatcher.dispatchViewAction = function (action) { + action.source = PayloadSources.VIEW; + this.dispatch(action); +}; +AppDispatcher.dispatchServerAction = function (action) { + action.source = PayloadSources.SERVER; + this.dispatch(action); +}; + +module.exports = { + AppDispatcher: AppDispatcher +}; + + +},{"flux":"flux"}],22:[function(require,module,exports){ +"use strict"; + +module.exports = (function () { + /* + * Generated by PEG.js 0.8.0. + * + * http://pegjs.majda.cz/ + */ + + function peg$subclass(child, parent) { + function ctor() { + this.constructor = child; + } + ctor.prototype = parent.prototype; + child.prototype = new ctor(); + } + + function SyntaxError(message, expected, found, offset, line, column) { + this.message = message; + this.expected = expected; + this.found = found; + this.offset = offset; + this.line = line; + this.column = column; + + this.name = "SyntaxError"; + } + + peg$subclass(SyntaxError, Error); + + function parse(input) { + var options = arguments.length > 1 ? arguments[1] : {}, + peg$FAILED = {}, + peg$startRuleFunctions = { start: peg$parsestart }, + peg$startRuleFunction = peg$parsestart, + peg$c0 = { type: "other", description: "filter expression" }, + peg$c1 = peg$FAILED, + peg$c2 = function peg$c2(orExpr) { + return orExpr; + }, + peg$c3 = [], + peg$c4 = function peg$c4() { + return trueFilter; + }, + peg$c5 = { type: "other", description: "whitespace" }, + peg$c6 = /^[ \t\n\r]/, + peg$c7 = { type: "class", value: "[ \\t\\n\\r]", description: "[ \\t\\n\\r]" }, + peg$c8 = { type: "other", description: "control character" }, + peg$c9 = /^[|&!()~"]/, + peg$c10 = { type: "class", value: "[|&!()~\"]", description: "[|&!()~\"]" }, + peg$c11 = { type: "other", description: "optional whitespace" }, + peg$c12 = "|", + peg$c13 = { type: "literal", value: "|", description: "\"|\"" }, + peg$c14 = function peg$c14(first, second) { + return or(first, second); + }, + peg$c15 = "&", + peg$c16 = { type: "literal", value: "&", description: "\"&\"" }, + peg$c17 = function peg$c17(first, second) { + return and(first, second); + }, + peg$c18 = "!", + peg$c19 = { type: "literal", value: "!", description: "\"!\"" }, + peg$c20 = function peg$c20(expr) { + return not(expr); + }, + peg$c21 = "(", + peg$c22 = { type: "literal", value: "(", description: "\"(\"" }, + peg$c23 = ")", + peg$c24 = { type: "literal", value: ")", description: "\")\"" }, + peg$c25 = function peg$c25(expr) { + return binding(expr); + }, + peg$c26 = "~a", + peg$c27 = { type: "literal", value: "~a", description: "\"~a\"" }, + peg$c28 = function peg$c28() { + return assetFilter; + }, + peg$c29 = "~e", + peg$c30 = { type: "literal", value: "~e", description: "\"~e\"" }, + peg$c31 = function peg$c31() { + return errorFilter; + }, + peg$c32 = "~q", + peg$c33 = { type: "literal", value: "~q", description: "\"~q\"" }, + peg$c34 = function peg$c34() { + return noResponseFilter; + }, + peg$c35 = "~s", + peg$c36 = { type: "literal", value: "~s", description: "\"~s\"" }, + peg$c37 = function peg$c37() { + return responseFilter; + }, + peg$c38 = "true", + peg$c39 = { type: "literal", value: "true", description: "\"true\"" }, + peg$c40 = function peg$c40() { + return trueFilter; + }, + peg$c41 = "false", + peg$c42 = { type: "literal", value: "false", description: "\"false\"" }, + peg$c43 = function peg$c43() { + return falseFilter; + }, + peg$c44 = "~c", + peg$c45 = { type: "literal", value: "~c", description: "\"~c\"" }, + peg$c46 = function peg$c46(s) { + return responseCode(s); + }, + peg$c47 = "~d", + peg$c48 = { type: "literal", value: "~d", description: "\"~d\"" }, + peg$c49 = function peg$c49(s) { + return domain(s); + }, + peg$c50 = "~h", + peg$c51 = { type: "literal", value: "~h", description: "\"~h\"" }, + peg$c52 = function peg$c52(s) { + return header(s); + }, + peg$c53 = "~hq", + peg$c54 = { type: "literal", value: "~hq", description: "\"~hq\"" }, + peg$c55 = function peg$c55(s) { + return requestHeader(s); + }, + peg$c56 = "~hs", + peg$c57 = { type: "literal", value: "~hs", description: "\"~hs\"" }, + peg$c58 = function peg$c58(s) { + return responseHeader(s); + }, + peg$c59 = "~m", + peg$c60 = { type: "literal", value: "~m", description: "\"~m\"" }, + peg$c61 = function peg$c61(s) { + return method(s); + }, + peg$c62 = "~t", + peg$c63 = { type: "literal", value: "~t", description: "\"~t\"" }, + peg$c64 = function peg$c64(s) { + return contentType(s); + }, + peg$c65 = "~tq", + peg$c66 = { type: "literal", value: "~tq", description: "\"~tq\"" }, + peg$c67 = function peg$c67(s) { + return requestContentType(s); + }, + peg$c68 = "~ts", + peg$c69 = { type: "literal", value: "~ts", description: "\"~ts\"" }, + peg$c70 = function peg$c70(s) { + return responseContentType(s); + }, + peg$c71 = "~u", + peg$c72 = { type: "literal", value: "~u", description: "\"~u\"" }, + peg$c73 = function peg$c73(s) { + return url(s); + }, + peg$c74 = { type: "other", description: "integer" }, + peg$c75 = null, + peg$c76 = /^['"]/, + peg$c77 = { type: "class", value: "['\"]", description: "['\"]" }, + peg$c78 = /^[0-9]/, + peg$c79 = { type: "class", value: "[0-9]", description: "[0-9]" }, + peg$c80 = function peg$c80(digits) { + return parseInt(digits.join(""), 10); + }, + peg$c81 = { type: "other", description: "string" }, + peg$c82 = "\"", + peg$c83 = { type: "literal", value: "\"", description: "\"\\\"\"" }, + peg$c84 = function peg$c84(chars) { + return chars.join(""); + }, + peg$c85 = "'", + peg$c86 = { type: "literal", value: "'", description: "\"'\"" }, + peg$c87 = void 0, + peg$c88 = /^["\\]/, + peg$c89 = { type: "class", value: "[\"\\\\]", description: "[\"\\\\]" }, + peg$c90 = { type: "any", description: "any character" }, + peg$c91 = function peg$c91(char) { + return char; + }, + peg$c92 = "\\", + peg$c93 = { type: "literal", value: "\\", description: "\"\\\\\"" }, + peg$c94 = /^['\\]/, + peg$c95 = { type: "class", value: "['\\\\]", description: "['\\\\]" }, + peg$c96 = /^['"\\]/, + peg$c97 = { type: "class", value: "['\"\\\\]", description: "['\"\\\\]" }, + peg$c98 = "n", + peg$c99 = { type: "literal", value: "n", description: "\"n\"" }, + peg$c100 = function peg$c100() { + return "\n"; + }, + peg$c101 = "r", + peg$c102 = { type: "literal", value: "r", description: "\"r\"" }, + peg$c103 = function peg$c103() { + return "\r"; + }, + peg$c104 = "t", + peg$c105 = { type: "literal", value: "t", description: "\"t\"" }, + peg$c106 = function peg$c106() { + return "\t"; + }, + peg$currPos = 0, + peg$reportedPos = 0, + peg$cachedPos = 0, + peg$cachedPosDetails = { line: 1, column: 1, seenCR: false }, + peg$maxFailPos = 0, + peg$maxFailExpected = [], + peg$silentFails = 0, + peg$result; + + if ("startRule" in options) { + if (!(options.startRule in peg$startRuleFunctions)) { + throw new Error("Can't start parsing from rule \"" + options.startRule + "\"."); + } + + peg$startRuleFunction = peg$startRuleFunctions[options.startRule]; + } + + function text() { + return input.substring(peg$reportedPos, peg$currPos); + } + + function offset() { + return peg$reportedPos; + } + + function line() { + return peg$computePosDetails(peg$reportedPos).line; + } + + function column() { + return peg$computePosDetails(peg$reportedPos).column; + } + + function expected(description) { + throw peg$buildException(null, [{ type: "other", description: description }], peg$reportedPos); + } + + function error(message) { + throw peg$buildException(message, null, peg$reportedPos); + } + + function peg$computePosDetails(pos) { + function advance(details, startPos, endPos) { + var p, ch; + + for (p = startPos; p < endPos; p++) { + ch = input.charAt(p); + if (ch === "\n") { + if (!details.seenCR) { + details.line++; + } + details.column = 1; + details.seenCR = false; + } else if (ch === "\r" || ch === "\u2028" || ch === "\u2029") { + details.line++; + details.column = 1; + details.seenCR = true; + } else { + details.column++; + details.seenCR = false; + } + } + } + + if (peg$cachedPos !== pos) { + if (peg$cachedPos > pos) { + peg$cachedPos = 0; + peg$cachedPosDetails = { line: 1, column: 1, seenCR: false }; + } + advance(peg$cachedPosDetails, peg$cachedPos, pos); + peg$cachedPos = pos; + } + + return peg$cachedPosDetails; + } + + function peg$fail(expected) { + if (peg$currPos < peg$maxFailPos) { + return; + } + + if (peg$currPos > peg$maxFailPos) { + peg$maxFailPos = peg$currPos; + peg$maxFailExpected = []; + } + + peg$maxFailExpected.push(expected); + } + + function peg$buildException(message, expected, pos) { + function cleanupExpected(expected) { + var i = 1; + + expected.sort(function (a, b) { + if (a.description < b.description) { + return -1; + } else if (a.description > b.description) { + return 1; + } else { + return 0; + } + }); + + while (i < expected.length) { + if (expected[i - 1] === expected[i]) { + expected.splice(i, 1); + } else { + i++; + } + } + } + + function buildMessage(expected, found) { + function stringEscape(s) { + function hex(ch) { + return ch.charCodeAt(0).toString(16).toUpperCase(); + } + + return s.replace(/\\/g, '\\\\').replace(/"/g, '\\"').replace(/\x08/g, '\\b').replace(/\t/g, '\\t').replace(/\n/g, '\\n').replace(/\f/g, '\\f').replace(/\r/g, '\\r').replace(/[\x00-\x07\x0B\x0E\x0F]/g, function (ch) { + return '\\x0' + hex(ch); + }).replace(/[\x10-\x1F\x80-\xFF]/g, function (ch) { + return '\\x' + hex(ch); + }).replace(/[\u0180-\u0FFF]/g, function (ch) { + return "\\u0" + hex(ch); + }).replace(/[\u1080-\uFFFF]/g, function (ch) { + return "\\u" + hex(ch); + }); + } + + var expectedDescs = new Array(expected.length), + expectedDesc, + foundDesc, + i; + + for (i = 0; i < expected.length; i++) { + expectedDescs[i] = expected[i].description; + } + + expectedDesc = expected.length > 1 ? expectedDescs.slice(0, -1).join(", ") + " or " + expectedDescs[expected.length - 1] : expectedDescs[0]; + + foundDesc = found ? "\"" + stringEscape(found) + "\"" : "end of input"; + + return "Expected " + expectedDesc + " but " + foundDesc + " found."; + } + + var posDetails = peg$computePosDetails(pos), + found = pos < input.length ? input.charAt(pos) : null; + + if (expected !== null) { + cleanupExpected(expected); + } + + return new SyntaxError(message !== null ? message : buildMessage(expected, found), expected, found, pos, posDetails.line, posDetails.column); + } + + function peg$parsestart() { + var s0, s1, s2, s3; + + peg$silentFails++; + s0 = peg$currPos; + s1 = peg$parse__(); + if (s1 !== peg$FAILED) { + s2 = peg$parseOrExpr(); + if (s2 !== peg$FAILED) { + s3 = peg$parse__(); + if (s3 !== peg$FAILED) { + peg$reportedPos = s0; + s1 = peg$c2(s2); + s0 = s1; + } else { + peg$currPos = s0; + s0 = peg$c1; + } + } else { + peg$currPos = s0; + s0 = peg$c1; + } + } else { + peg$currPos = s0; + s0 = peg$c1; + } + if (s0 === peg$FAILED) { + s0 = peg$currPos; + s1 = []; + if (s1 !== peg$FAILED) { + peg$reportedPos = s0; + s1 = peg$c4(); + } + s0 = s1; + } + peg$silentFails--; + if (s0 === peg$FAILED) { + s1 = peg$FAILED; + if (peg$silentFails === 0) { + peg$fail(peg$c0); + } + } + + return s0; + } + + function peg$parsews() { + var s0, s1; + + peg$silentFails++; + if (peg$c6.test(input.charAt(peg$currPos))) { + s0 = input.charAt(peg$currPos); + peg$currPos++; + } else { + s0 = peg$FAILED; + if (peg$silentFails === 0) { + peg$fail(peg$c7); + } + } + peg$silentFails--; + if (s0 === peg$FAILED) { + s1 = peg$FAILED; + if (peg$silentFails === 0) { + peg$fail(peg$c5); + } + } + + return s0; + } + + function peg$parsecc() { + var s0, s1; + + peg$silentFails++; + if (peg$c9.test(input.charAt(peg$currPos))) { + s0 = input.charAt(peg$currPos); + peg$currPos++; + } else { + s0 = peg$FAILED; + if (peg$silentFails === 0) { + peg$fail(peg$c10); + } + } + peg$silentFails--; + if (s0 === peg$FAILED) { + s1 = peg$FAILED; + if (peg$silentFails === 0) { + peg$fail(peg$c8); + } + } + + return s0; + } + + function peg$parse__() { + var s0, s1; + + peg$silentFails++; + s0 = []; + s1 = peg$parsews(); + while (s1 !== peg$FAILED) { + s0.push(s1); + s1 = peg$parsews(); + } + peg$silentFails--; + if (s0 === peg$FAILED) { + s1 = peg$FAILED; + if (peg$silentFails === 0) { + peg$fail(peg$c11); + } + } + + return s0; + } + + function peg$parseOrExpr() { + var s0, s1, s2, s3, s4, s5; + + s0 = peg$currPos; + s1 = peg$parseAndExpr(); + if (s1 !== peg$FAILED) { + s2 = peg$parse__(); + if (s2 !== peg$FAILED) { + if (input.charCodeAt(peg$currPos) === 124) { + s3 = peg$c12; + peg$currPos++; + } else { + s3 = peg$FAILED; + if (peg$silentFails === 0) { + peg$fail(peg$c13); + } + } + if (s3 !== peg$FAILED) { + s4 = peg$parse__(); + if (s4 !== peg$FAILED) { + s5 = peg$parseOrExpr(); + if (s5 !== peg$FAILED) { + peg$reportedPos = s0; + s1 = peg$c14(s1, s5); + s0 = s1; + } else { + peg$currPos = s0; + s0 = peg$c1; + } + } else { + peg$currPos = s0; + s0 = peg$c1; + } + } else { + peg$currPos = s0; + s0 = peg$c1; + } + } else { + peg$currPos = s0; + s0 = peg$c1; + } + } else { + peg$currPos = s0; + s0 = peg$c1; + } + if (s0 === peg$FAILED) { + s0 = peg$parseAndExpr(); + } + + return s0; + } + + function peg$parseAndExpr() { + var s0, s1, s2, s3, s4, s5; + + s0 = peg$currPos; + s1 = peg$parseNotExpr(); + if (s1 !== peg$FAILED) { + s2 = peg$parse__(); + if (s2 !== peg$FAILED) { + if (input.charCodeAt(peg$currPos) === 38) { + s3 = peg$c15; + peg$currPos++; + } else { + s3 = peg$FAILED; + if (peg$silentFails === 0) { + peg$fail(peg$c16); + } + } + if (s3 !== peg$FAILED) { + s4 = peg$parse__(); + if (s4 !== peg$FAILED) { + s5 = peg$parseAndExpr(); + if (s5 !== peg$FAILED) { + peg$reportedPos = s0; + s1 = peg$c17(s1, s5); + s0 = s1; + } else { + peg$currPos = s0; + s0 = peg$c1; + } + } else { + peg$currPos = s0; + s0 = peg$c1; + } + } else { + peg$currPos = s0; + s0 = peg$c1; + } + } else { + peg$currPos = s0; + s0 = peg$c1; + } + } else { + peg$currPos = s0; + s0 = peg$c1; + } + if (s0 === peg$FAILED) { + s0 = peg$currPos; + s1 = peg$parseNotExpr(); + if (s1 !== peg$FAILED) { + s2 = []; + s3 = peg$parsews(); + if (s3 !== peg$FAILED) { + while (s3 !== peg$FAILED) { + s2.push(s3); + s3 = peg$parsews(); + } + } else { + s2 = peg$c1; + } + if (s2 !== peg$FAILED) { + s3 = peg$parseAndExpr(); + if (s3 !== peg$FAILED) { + peg$reportedPos = s0; + s1 = peg$c17(s1, s3); + s0 = s1; + } else { + peg$currPos = s0; + s0 = peg$c1; + } + } else { + peg$currPos = s0; + s0 = peg$c1; + } + } else { + peg$currPos = s0; + s0 = peg$c1; + } + if (s0 === peg$FAILED) { + s0 = peg$parseNotExpr(); + } + } + + return s0; + } + + function peg$parseNotExpr() { + var s0, s1, s2, s3; + + s0 = peg$currPos; + if (input.charCodeAt(peg$currPos) === 33) { + s1 = peg$c18; + peg$currPos++; + } else { + s1 = peg$FAILED; + if (peg$silentFails === 0) { + peg$fail(peg$c19); + } + } + if (s1 !== peg$FAILED) { + s2 = peg$parse__(); + if (s2 !== peg$FAILED) { + s3 = peg$parseNotExpr(); + if (s3 !== peg$FAILED) { + peg$reportedPos = s0; + s1 = peg$c20(s3); + s0 = s1; + } else { + peg$currPos = s0; + s0 = peg$c1; + } + } else { + peg$currPos = s0; + s0 = peg$c1; + } + } else { + peg$currPos = s0; + s0 = peg$c1; + } + if (s0 === peg$FAILED) { + s0 = peg$parseBindingExpr(); + } + + return s0; + } + + function peg$parseBindingExpr() { + var s0, s1, s2, s3, s4, s5; + + s0 = peg$currPos; + if (input.charCodeAt(peg$currPos) === 40) { + s1 = peg$c21; + peg$currPos++; + } else { + s1 = peg$FAILED; + if (peg$silentFails === 0) { + peg$fail(peg$c22); + } + } + if (s1 !== peg$FAILED) { + s2 = peg$parse__(); + if (s2 !== peg$FAILED) { + s3 = peg$parseOrExpr(); + if (s3 !== peg$FAILED) { + s4 = peg$parse__(); + if (s4 !== peg$FAILED) { + if (input.charCodeAt(peg$currPos) === 41) { + s5 = peg$c23; + peg$currPos++; + } else { + s5 = peg$FAILED; + if (peg$silentFails === 0) { + peg$fail(peg$c24); + } + } + if (s5 !== peg$FAILED) { + peg$reportedPos = s0; + s1 = peg$c25(s3); + s0 = s1; + } else { + peg$currPos = s0; + s0 = peg$c1; + } + } else { + peg$currPos = s0; + s0 = peg$c1; + } + } else { + peg$currPos = s0; + s0 = peg$c1; + } + } else { + peg$currPos = s0; + s0 = peg$c1; + } + } else { + peg$currPos = s0; + s0 = peg$c1; + } + if (s0 === peg$FAILED) { + s0 = peg$parseExpr(); + } + + return s0; + } + + function peg$parseExpr() { + var s0; + + s0 = peg$parseNullaryExpr(); + if (s0 === peg$FAILED) { + s0 = peg$parseUnaryExpr(); + } + + return s0; + } + + function peg$parseNullaryExpr() { + var s0, s1; + + s0 = peg$parseBooleanLiteral(); + if (s0 === peg$FAILED) { + s0 = peg$currPos; + if (input.substr(peg$currPos, 2) === peg$c26) { + s1 = peg$c26; + peg$currPos += 2; + } else { + s1 = peg$FAILED; + if (peg$silentFails === 0) { + peg$fail(peg$c27); + } + } + if (s1 !== peg$FAILED) { + peg$reportedPos = s0; + s1 = peg$c28(); + } + s0 = s1; + if (s0 === peg$FAILED) { + s0 = peg$currPos; + if (input.substr(peg$currPos, 2) === peg$c29) { + s1 = peg$c29; + peg$currPos += 2; + } else { + s1 = peg$FAILED; + if (peg$silentFails === 0) { + peg$fail(peg$c30); + } + } + if (s1 !== peg$FAILED) { + peg$reportedPos = s0; + s1 = peg$c31(); + } + s0 = s1; + if (s0 === peg$FAILED) { + s0 = peg$currPos; + if (input.substr(peg$currPos, 2) === peg$c32) { + s1 = peg$c32; + peg$currPos += 2; + } else { + s1 = peg$FAILED; + if (peg$silentFails === 0) { + peg$fail(peg$c33); + } + } + if (s1 !== peg$FAILED) { + peg$reportedPos = s0; + s1 = peg$c34(); + } + s0 = s1; + if (s0 === peg$FAILED) { + s0 = peg$currPos; + if (input.substr(peg$currPos, 2) === peg$c35) { + s1 = peg$c35; + peg$currPos += 2; + } else { + s1 = peg$FAILED; + if (peg$silentFails === 0) { + peg$fail(peg$c36); + } + } + if (s1 !== peg$FAILED) { + peg$reportedPos = s0; + s1 = peg$c37(); + } + s0 = s1; + } + } + } + } + + return s0; + } + + function peg$parseBooleanLiteral() { + var s0, s1; + + s0 = peg$currPos; + if (input.substr(peg$currPos, 4) === peg$c38) { + s1 = peg$c38; + peg$currPos += 4; + } else { + s1 = peg$FAILED; + if (peg$silentFails === 0) { + peg$fail(peg$c39); + } + } + if (s1 !== peg$FAILED) { + peg$reportedPos = s0; + s1 = peg$c40(); + } + s0 = s1; + if (s0 === peg$FAILED) { + s0 = peg$currPos; + if (input.substr(peg$currPos, 5) === peg$c41) { + s1 = peg$c41; + peg$currPos += 5; + } else { + s1 = peg$FAILED; + if (peg$silentFails === 0) { + peg$fail(peg$c42); + } + } + if (s1 !== peg$FAILED) { + peg$reportedPos = s0; + s1 = peg$c43(); + } + s0 = s1; + } + + return s0; + } + + function peg$parseUnaryExpr() { + var s0, s1, s2, s3; + + s0 = peg$currPos; + if (input.substr(peg$currPos, 2) === peg$c44) { + s1 = peg$c44; + peg$currPos += 2; + } else { + s1 = peg$FAILED; + if (peg$silentFails === 0) { + peg$fail(peg$c45); + } + } + if (s1 !== peg$FAILED) { + s2 = []; + s3 = peg$parsews(); + if (s3 !== peg$FAILED) { + while (s3 !== peg$FAILED) { + s2.push(s3); + s3 = peg$parsews(); + } + } else { + s2 = peg$c1; + } + if (s2 !== peg$FAILED) { + s3 = peg$parseIntegerLiteral(); + if (s3 !== peg$FAILED) { + peg$reportedPos = s0; + s1 = peg$c46(s3); + s0 = s1; + } else { + peg$currPos = s0; + s0 = peg$c1; + } + } else { + peg$currPos = s0; + s0 = peg$c1; + } + } else { + peg$currPos = s0; + s0 = peg$c1; + } + if (s0 === peg$FAILED) { + s0 = peg$currPos; + if (input.substr(peg$currPos, 2) === peg$c47) { + s1 = peg$c47; + peg$currPos += 2; + } else { + s1 = peg$FAILED; + if (peg$silentFails === 0) { + peg$fail(peg$c48); + } + } + if (s1 !== peg$FAILED) { + s2 = []; + s3 = peg$parsews(); + if (s3 !== peg$FAILED) { + while (s3 !== peg$FAILED) { + s2.push(s3); + s3 = peg$parsews(); + } + } else { + s2 = peg$c1; + } + if (s2 !== peg$FAILED) { + s3 = peg$parseStringLiteral(); + if (s3 !== peg$FAILED) { + peg$reportedPos = s0; + s1 = peg$c49(s3); + s0 = s1; + } else { + peg$currPos = s0; + s0 = peg$c1; + } + } else { + peg$currPos = s0; + s0 = peg$c1; + } + } else { + peg$currPos = s0; + s0 = peg$c1; + } + if (s0 === peg$FAILED) { + s0 = peg$currPos; + if (input.substr(peg$currPos, 2) === peg$c50) { + s1 = peg$c50; + peg$currPos += 2; + } else { + s1 = peg$FAILED; + if (peg$silentFails === 0) { + peg$fail(peg$c51); + } + } + if (s1 !== peg$FAILED) { + s2 = []; + s3 = peg$parsews(); + if (s3 !== peg$FAILED) { + while (s3 !== peg$FAILED) { + s2.push(s3); + s3 = peg$parsews(); + } + } else { + s2 = peg$c1; + } + if (s2 !== peg$FAILED) { + s3 = peg$parseStringLiteral(); + if (s3 !== peg$FAILED) { + peg$reportedPos = s0; + s1 = peg$c52(s3); + s0 = s1; + } else { + peg$currPos = s0; + s0 = peg$c1; + } + } else { + peg$currPos = s0; + s0 = peg$c1; + } + } else { + peg$currPos = s0; + s0 = peg$c1; + } + if (s0 === peg$FAILED) { + s0 = peg$currPos; + if (input.substr(peg$currPos, 3) === peg$c53) { + s1 = peg$c53; + peg$currPos += 3; + } else { + s1 = peg$FAILED; + if (peg$silentFails === 0) { + peg$fail(peg$c54); + } + } + if (s1 !== peg$FAILED) { + s2 = []; + s3 = peg$parsews(); + if (s3 !== peg$FAILED) { + while (s3 !== peg$FAILED) { + s2.push(s3); + s3 = peg$parsews(); + } + } else { + s2 = peg$c1; + } + if (s2 !== peg$FAILED) { + s3 = peg$parseStringLiteral(); + if (s3 !== peg$FAILED) { + peg$reportedPos = s0; + s1 = peg$c55(s3); + s0 = s1; + } else { + peg$currPos = s0; + s0 = peg$c1; + } + } else { + peg$currPos = s0; + s0 = peg$c1; + } + } else { + peg$currPos = s0; + s0 = peg$c1; + } + if (s0 === peg$FAILED) { + s0 = peg$currPos; + if (input.substr(peg$currPos, 3) === peg$c56) { + s1 = peg$c56; + peg$currPos += 3; + } else { + s1 = peg$FAILED; + if (peg$silentFails === 0) { + peg$fail(peg$c57); + } + } + if (s1 !== peg$FAILED) { + s2 = []; + s3 = peg$parsews(); + if (s3 !== peg$FAILED) { + while (s3 !== peg$FAILED) { + s2.push(s3); + s3 = peg$parsews(); + } + } else { + s2 = peg$c1; + } + if (s2 !== peg$FAILED) { + s3 = peg$parseStringLiteral(); + if (s3 !== peg$FAILED) { + peg$reportedPos = s0; + s1 = peg$c58(s3); + s0 = s1; + } else { + peg$currPos = s0; + s0 = peg$c1; + } + } else { + peg$currPos = s0; + s0 = peg$c1; + } + } else { + peg$currPos = s0; + s0 = peg$c1; + } + if (s0 === peg$FAILED) { + s0 = peg$currPos; + if (input.substr(peg$currPos, 2) === peg$c59) { + s1 = peg$c59; + peg$currPos += 2; + } else { + s1 = peg$FAILED; + if (peg$silentFails === 0) { + peg$fail(peg$c60); + } + } + if (s1 !== peg$FAILED) { + s2 = []; + s3 = peg$parsews(); + if (s3 !== peg$FAILED) { + while (s3 !== peg$FAILED) { + s2.push(s3); + s3 = peg$parsews(); + } + } else { + s2 = peg$c1; + } + if (s2 !== peg$FAILED) { + s3 = peg$parseStringLiteral(); + if (s3 !== peg$FAILED) { + peg$reportedPos = s0; + s1 = peg$c61(s3); + s0 = s1; + } else { + peg$currPos = s0; + s0 = peg$c1; + } + } else { + peg$currPos = s0; + s0 = peg$c1; + } + } else { + peg$currPos = s0; + s0 = peg$c1; + } + if (s0 === peg$FAILED) { + s0 = peg$currPos; + if (input.substr(peg$currPos, 2) === peg$c62) { + s1 = peg$c62; + peg$currPos += 2; + } else { + s1 = peg$FAILED; + if (peg$silentFails === 0) { + peg$fail(peg$c63); + } + } + if (s1 !== peg$FAILED) { + s2 = []; + s3 = peg$parsews(); + if (s3 !== peg$FAILED) { + while (s3 !== peg$FAILED) { + s2.push(s3); + s3 = peg$parsews(); + } + } else { + s2 = peg$c1; + } + if (s2 !== peg$FAILED) { + s3 = peg$parseStringLiteral(); + if (s3 !== peg$FAILED) { + peg$reportedPos = s0; + s1 = peg$c64(s3); + s0 = s1; + } else { + peg$currPos = s0; + s0 = peg$c1; + } + } else { + peg$currPos = s0; + s0 = peg$c1; + } + } else { + peg$currPos = s0; + s0 = peg$c1; + } + if (s0 === peg$FAILED) { + s0 = peg$currPos; + if (input.substr(peg$currPos, 3) === peg$c65) { + s1 = peg$c65; + peg$currPos += 3; + } else { + s1 = peg$FAILED; + if (peg$silentFails === 0) { + peg$fail(peg$c66); + } + } + if (s1 !== peg$FAILED) { + s2 = []; + s3 = peg$parsews(); + if (s3 !== peg$FAILED) { + while (s3 !== peg$FAILED) { + s2.push(s3); + s3 = peg$parsews(); + } + } else { + s2 = peg$c1; + } + if (s2 !== peg$FAILED) { + s3 = peg$parseStringLiteral(); + if (s3 !== peg$FAILED) { + peg$reportedPos = s0; + s1 = peg$c67(s3); + s0 = s1; + } else { + peg$currPos = s0; + s0 = peg$c1; + } + } else { + peg$currPos = s0; + s0 = peg$c1; + } + } else { + peg$currPos = s0; + s0 = peg$c1; + } + if (s0 === peg$FAILED) { + s0 = peg$currPos; + if (input.substr(peg$currPos, 3) === peg$c68) { + s1 = peg$c68; + peg$currPos += 3; + } else { + s1 = peg$FAILED; + if (peg$silentFails === 0) { + peg$fail(peg$c69); + } + } + if (s1 !== peg$FAILED) { + s2 = []; + s3 = peg$parsews(); + if (s3 !== peg$FAILED) { + while (s3 !== peg$FAILED) { + s2.push(s3); + s3 = peg$parsews(); + } + } else { + s2 = peg$c1; + } + if (s2 !== peg$FAILED) { + s3 = peg$parseStringLiteral(); + if (s3 !== peg$FAILED) { + peg$reportedPos = s0; + s1 = peg$c70(s3); + s0 = s1; + } else { + peg$currPos = s0; + s0 = peg$c1; + } + } else { + peg$currPos = s0; + s0 = peg$c1; + } + } else { + peg$currPos = s0; + s0 = peg$c1; + } + if (s0 === peg$FAILED) { + s0 = peg$currPos; + if (input.substr(peg$currPos, 2) === peg$c71) { + s1 = peg$c71; + peg$currPos += 2; + } else { + s1 = peg$FAILED; + if (peg$silentFails === 0) { + peg$fail(peg$c72); + } + } + if (s1 !== peg$FAILED) { + s2 = []; + s3 = peg$parsews(); + if (s3 !== peg$FAILED) { + while (s3 !== peg$FAILED) { + s2.push(s3); + s3 = peg$parsews(); + } + } else { + s2 = peg$c1; + } + if (s2 !== peg$FAILED) { + s3 = peg$parseStringLiteral(); + if (s3 !== peg$FAILED) { + peg$reportedPos = s0; + s1 = peg$c73(s3); + s0 = s1; + } else { + peg$currPos = s0; + s0 = peg$c1; + } + } else { + peg$currPos = s0; + s0 = peg$c1; + } + } else { + peg$currPos = s0; + s0 = peg$c1; + } + if (s0 === peg$FAILED) { + s0 = peg$currPos; + s1 = peg$parseStringLiteral(); + if (s1 !== peg$FAILED) { + peg$reportedPos = s0; + s1 = peg$c73(s1); + } + s0 = s1; + } + } + } + } + } + } + } + } + } + } + + return s0; + } + + function peg$parseIntegerLiteral() { + var s0, s1, s2, s3; + + peg$silentFails++; + s0 = peg$currPos; + if (peg$c76.test(input.charAt(peg$currPos))) { + s1 = input.charAt(peg$currPos); + peg$currPos++; + } else { + s1 = peg$FAILED; + if (peg$silentFails === 0) { + peg$fail(peg$c77); + } + } + if (s1 === peg$FAILED) { + s1 = peg$c75; + } + if (s1 !== peg$FAILED) { + s2 = []; + if (peg$c78.test(input.charAt(peg$currPos))) { + s3 = input.charAt(peg$currPos); + peg$currPos++; + } else { + s3 = peg$FAILED; + if (peg$silentFails === 0) { + peg$fail(peg$c79); + } + } + if (s3 !== peg$FAILED) { + while (s3 !== peg$FAILED) { + s2.push(s3); + if (peg$c78.test(input.charAt(peg$currPos))) { + s3 = input.charAt(peg$currPos); + peg$currPos++; + } else { + s3 = peg$FAILED; + if (peg$silentFails === 0) { + peg$fail(peg$c79); + } + } + } + } else { + s2 = peg$c1; + } + if (s2 !== peg$FAILED) { + if (peg$c76.test(input.charAt(peg$currPos))) { + s3 = input.charAt(peg$currPos); + peg$currPos++; + } else { + s3 = peg$FAILED; + if (peg$silentFails === 0) { + peg$fail(peg$c77); + } + } + if (s3 === peg$FAILED) { + s3 = peg$c75; + } + if (s3 !== peg$FAILED) { + peg$reportedPos = s0; + s1 = peg$c80(s2); + s0 = s1; + } else { + peg$currPos = s0; + s0 = peg$c1; + } + } else { + peg$currPos = s0; + s0 = peg$c1; + } + } else { + peg$currPos = s0; + s0 = peg$c1; + } + peg$silentFails--; + if (s0 === peg$FAILED) { + s1 = peg$FAILED; + if (peg$silentFails === 0) { + peg$fail(peg$c74); + } + } + + return s0; + } + + function peg$parseStringLiteral() { + var s0, s1, s2, s3; + + peg$silentFails++; + s0 = peg$currPos; + if (input.charCodeAt(peg$currPos) === 34) { + s1 = peg$c82; + peg$currPos++; + } else { + s1 = peg$FAILED; + if (peg$silentFails === 0) { + peg$fail(peg$c83); + } + } + if (s1 !== peg$FAILED) { + s2 = []; + s3 = peg$parseDoubleStringChar(); + while (s3 !== peg$FAILED) { + s2.push(s3); + s3 = peg$parseDoubleStringChar(); + } + if (s2 !== peg$FAILED) { + if (input.charCodeAt(peg$currPos) === 34) { + s3 = peg$c82; + peg$currPos++; + } else { + s3 = peg$FAILED; + if (peg$silentFails === 0) { + peg$fail(peg$c83); + } + } + if (s3 !== peg$FAILED) { + peg$reportedPos = s0; + s1 = peg$c84(s2); + s0 = s1; + } else { + peg$currPos = s0; + s0 = peg$c1; + } + } else { + peg$currPos = s0; + s0 = peg$c1; + } + } else { + peg$currPos = s0; + s0 = peg$c1; + } + if (s0 === peg$FAILED) { + s0 = peg$currPos; + if (input.charCodeAt(peg$currPos) === 39) { + s1 = peg$c85; + peg$currPos++; + } else { + s1 = peg$FAILED; + if (peg$silentFails === 0) { + peg$fail(peg$c86); + } + } + if (s1 !== peg$FAILED) { + s2 = []; + s3 = peg$parseSingleStringChar(); + while (s3 !== peg$FAILED) { + s2.push(s3); + s3 = peg$parseSingleStringChar(); + } + if (s2 !== peg$FAILED) { + if (input.charCodeAt(peg$currPos) === 39) { + s3 = peg$c85; + peg$currPos++; + } else { + s3 = peg$FAILED; + if (peg$silentFails === 0) { + peg$fail(peg$c86); + } + } + if (s3 !== peg$FAILED) { + peg$reportedPos = s0; + s1 = peg$c84(s2); + s0 = s1; + } else { + peg$currPos = s0; + s0 = peg$c1; + } + } else { + peg$currPos = s0; + s0 = peg$c1; + } + } else { + peg$currPos = s0; + s0 = peg$c1; + } + if (s0 === peg$FAILED) { + s0 = peg$currPos; + s1 = peg$currPos; + peg$silentFails++; + s2 = peg$parsecc(); + peg$silentFails--; + if (s2 === peg$FAILED) { + s1 = peg$c87; + } else { + peg$currPos = s1; + s1 = peg$c1; + } + if (s1 !== peg$FAILED) { + s2 = []; + s3 = peg$parseUnquotedStringChar(); + if (s3 !== peg$FAILED) { + while (s3 !== peg$FAILED) { + s2.push(s3); + s3 = peg$parseUnquotedStringChar(); + } + } else { + s2 = peg$c1; + } + if (s2 !== peg$FAILED) { + peg$reportedPos = s0; + s1 = peg$c84(s2); + s0 = s1; + } else { + peg$currPos = s0; + s0 = peg$c1; + } + } else { + peg$currPos = s0; + s0 = peg$c1; + } + } + } + peg$silentFails--; + if (s0 === peg$FAILED) { + s1 = peg$FAILED; + if (peg$silentFails === 0) { + peg$fail(peg$c81); + } + } + + return s0; + } + + function peg$parseDoubleStringChar() { + var s0, s1, s2; + + s0 = peg$currPos; + s1 = peg$currPos; + peg$silentFails++; + if (peg$c88.test(input.charAt(peg$currPos))) { + s2 = input.charAt(peg$currPos); + peg$currPos++; + } else { + s2 = peg$FAILED; + if (peg$silentFails === 0) { + peg$fail(peg$c89); + } + } + peg$silentFails--; + if (s2 === peg$FAILED) { + s1 = peg$c87; + } else { + peg$currPos = s1; + s1 = peg$c1; + } + if (s1 !== peg$FAILED) { + if (input.length > peg$currPos) { + s2 = input.charAt(peg$currPos); + peg$currPos++; + } else { + s2 = peg$FAILED; + if (peg$silentFails === 0) { + peg$fail(peg$c90); + } + } + if (s2 !== peg$FAILED) { + peg$reportedPos = s0; + s1 = peg$c91(s2); + s0 = s1; + } else { + peg$currPos = s0; + s0 = peg$c1; + } + } else { + peg$currPos = s0; + s0 = peg$c1; + } + if (s0 === peg$FAILED) { + s0 = peg$currPos; + if (input.charCodeAt(peg$currPos) === 92) { + s1 = peg$c92; + peg$currPos++; + } else { + s1 = peg$FAILED; + if (peg$silentFails === 0) { + peg$fail(peg$c93); + } + } + if (s1 !== peg$FAILED) { + s2 = peg$parseEscapeSequence(); + if (s2 !== peg$FAILED) { + peg$reportedPos = s0; + s1 = peg$c91(s2); + s0 = s1; + } else { + peg$currPos = s0; + s0 = peg$c1; + } + } else { + peg$currPos = s0; + s0 = peg$c1; + } + } + + return s0; + } + + function peg$parseSingleStringChar() { + var s0, s1, s2; + + s0 = peg$currPos; + s1 = peg$currPos; + peg$silentFails++; + if (peg$c94.test(input.charAt(peg$currPos))) { + s2 = input.charAt(peg$currPos); + peg$currPos++; + } else { + s2 = peg$FAILED; + if (peg$silentFails === 0) { + peg$fail(peg$c95); + } + } + peg$silentFails--; + if (s2 === peg$FAILED) { + s1 = peg$c87; + } else { + peg$currPos = s1; + s1 = peg$c1; + } + if (s1 !== peg$FAILED) { + if (input.length > peg$currPos) { + s2 = input.charAt(peg$currPos); + peg$currPos++; + } else { + s2 = peg$FAILED; + if (peg$silentFails === 0) { + peg$fail(peg$c90); + } + } + if (s2 !== peg$FAILED) { + peg$reportedPos = s0; + s1 = peg$c91(s2); + s0 = s1; + } else { + peg$currPos = s0; + s0 = peg$c1; + } + } else { + peg$currPos = s0; + s0 = peg$c1; + } + if (s0 === peg$FAILED) { + s0 = peg$currPos; + if (input.charCodeAt(peg$currPos) === 92) { + s1 = peg$c92; + peg$currPos++; + } else { + s1 = peg$FAILED; + if (peg$silentFails === 0) { + peg$fail(peg$c93); + } + } + if (s1 !== peg$FAILED) { + s2 = peg$parseEscapeSequence(); + if (s2 !== peg$FAILED) { + peg$reportedPos = s0; + s1 = peg$c91(s2); + s0 = s1; + } else { + peg$currPos = s0; + s0 = peg$c1; + } + } else { + peg$currPos = s0; + s0 = peg$c1; + } + } + + return s0; + } + + function peg$parseUnquotedStringChar() { + var s0, s1, s2; + + s0 = peg$currPos; + s1 = peg$currPos; + peg$silentFails++; + s2 = peg$parsews(); + peg$silentFails--; + if (s2 === peg$FAILED) { + s1 = peg$c87; + } else { + peg$currPos = s1; + s1 = peg$c1; + } + if (s1 !== peg$FAILED) { + if (input.length > peg$currPos) { + s2 = input.charAt(peg$currPos); + peg$currPos++; + } else { + s2 = peg$FAILED; + if (peg$silentFails === 0) { + peg$fail(peg$c90); + } + } + if (s2 !== peg$FAILED) { + peg$reportedPos = s0; + s1 = peg$c91(s2); + s0 = s1; + } else { + peg$currPos = s0; + s0 = peg$c1; + } + } else { + peg$currPos = s0; + s0 = peg$c1; + } + + return s0; + } + + function peg$parseEscapeSequence() { + var s0, s1; + + if (peg$c96.test(input.charAt(peg$currPos))) { + s0 = input.charAt(peg$currPos); + peg$currPos++; + } else { + s0 = peg$FAILED; + if (peg$silentFails === 0) { + peg$fail(peg$c97); + } + } + if (s0 === peg$FAILED) { + s0 = peg$currPos; + if (input.charCodeAt(peg$currPos) === 110) { + s1 = peg$c98; + peg$currPos++; + } else { + s1 = peg$FAILED; + if (peg$silentFails === 0) { + peg$fail(peg$c99); + } + } + if (s1 !== peg$FAILED) { + peg$reportedPos = s0; + s1 = peg$c100(); + } + s0 = s1; + if (s0 === peg$FAILED) { + s0 = peg$currPos; + if (input.charCodeAt(peg$currPos) === 114) { + s1 = peg$c101; + peg$currPos++; + } else { + s1 = peg$FAILED; + if (peg$silentFails === 0) { + peg$fail(peg$c102); + } + } + if (s1 !== peg$FAILED) { + peg$reportedPos = s0; + s1 = peg$c103(); + } + s0 = s1; + if (s0 === peg$FAILED) { + s0 = peg$currPos; + if (input.charCodeAt(peg$currPos) === 116) { + s1 = peg$c104; + peg$currPos++; + } else { + s1 = peg$FAILED; + if (peg$silentFails === 0) { + peg$fail(peg$c105); + } + } + if (s1 !== peg$FAILED) { + peg$reportedPos = s0; + s1 = peg$c106(); + } + s0 = s1; + } + } + } + + return s0; + } + + var flowutils = require("../flow/utils.js"); + + function or(first, second) { + // Add explicit function names to ease debugging. + function orFilter() { + return first.apply(this, arguments) || second.apply(this, arguments); + } + orFilter.desc = first.desc + " or " + second.desc; + return orFilter; + } + function and(first, second) { + function andFilter() { + return first.apply(this, arguments) && second.apply(this, arguments); + } + andFilter.desc = first.desc + " and " + second.desc; + return andFilter; + } + function not(expr) { + function notFilter() { + return !expr.apply(this, arguments); + } + notFilter.desc = "not " + expr.desc; + return notFilter; + } + function binding(expr) { + function bindingFilter() { + return expr.apply(this, arguments); + } + bindingFilter.desc = "(" + expr.desc + ")"; + return bindingFilter; + } + function trueFilter(flow) { + return true; + } + trueFilter.desc = "true"; + function falseFilter(flow) { + return false; + } + falseFilter.desc = "false"; + + var ASSET_TYPES = [new RegExp("text/javascript"), new RegExp("application/x-javascript"), new RegExp("application/javascript"), new RegExp("text/css"), new RegExp("image/.*"), new RegExp("application/x-shockwave-flash")]; + function assetFilter(flow) { + if (flow.response) { + var ct = flowutils.ResponseUtils.getContentType(flow.response); + var i = ASSET_TYPES.length; + while (i--) { + if (ASSET_TYPES[i].test(ct)) { + return true; + } + } + } + return false; + } + assetFilter.desc = "is asset"; + function responseCode(code) { + function responseCodeFilter(flow) { + return flow.response && flow.response.status_code === code; + } + responseCodeFilter.desc = "resp. code is " + code; + return responseCodeFilter; + } + function domain(regex) { + regex = new RegExp(regex, "i"); + function domainFilter(flow) { + return flow.request && regex.test(flow.request.host); + } + domainFilter.desc = "domain matches " + regex; + return domainFilter; + } + function errorFilter(flow) { + return !!flow.error; + } + errorFilter.desc = "has error"; + function header(regex) { + regex = new RegExp(regex, "i"); + function headerFilter(flow) { + return flow.request && flowutils.RequestUtils.match_header(flow.request, regex) || flow.response && flowutils.ResponseUtils.match_header(flow.response, regex); + } + headerFilter.desc = "header matches " + regex; + return headerFilter; + } + function requestHeader(regex) { + regex = new RegExp(regex, "i"); + function requestHeaderFilter(flow) { + return flow.request && flowutils.RequestUtils.match_header(flow.request, regex); + } + requestHeaderFilter.desc = "req. header matches " + regex; + return requestHeaderFilter; + } + function responseHeader(regex) { + regex = new RegExp(regex, "i"); + function responseHeaderFilter(flow) { + return flow.response && flowutils.ResponseUtils.match_header(flow.response, regex); + } + responseHeaderFilter.desc = "resp. header matches " + regex; + return responseHeaderFilter; + } + function method(regex) { + regex = new RegExp(regex, "i"); + function methodFilter(flow) { + return flow.request && regex.test(flow.request.method); + } + methodFilter.desc = "method matches " + regex; + return methodFilter; + } + function noResponseFilter(flow) { + return flow.request && !flow.response; + } + noResponseFilter.desc = "has no response"; + function responseFilter(flow) { + return !!flow.response; + } + responseFilter.desc = "has response"; + + function contentType(regex) { + regex = new RegExp(regex, "i"); + function contentTypeFilter(flow) { + return flow.request && regex.test(flowutils.RequestUtils.getContentType(flow.request)) || flow.response && regex.test(flowutils.ResponseUtils.getContentType(flow.response)); + } + contentTypeFilter.desc = "content type matches " + regex; + return contentTypeFilter; + } + function requestContentType(regex) { + regex = new RegExp(regex, "i"); + function requestContentTypeFilter(flow) { + return flow.request && regex.test(flowutils.RequestUtils.getContentType(flow.request)); + } + requestContentTypeFilter.desc = "req. content type matches " + regex; + return requestContentTypeFilter; + } + function responseContentType(regex) { + regex = new RegExp(regex, "i"); + function responseContentTypeFilter(flow) { + return flow.response && regex.test(flowutils.ResponseUtils.getContentType(flow.response)); + } + responseContentTypeFilter.desc = "resp. content type matches " + regex; + return responseContentTypeFilter; + } + function url(regex) { + regex = new RegExp(regex, "i"); + function urlFilter(flow) { + return flow.request && regex.test(flowutils.RequestUtils.pretty_url(flow.request)); + } + urlFilter.desc = "url matches " + regex; + return urlFilter; + } + + peg$result = peg$startRuleFunction(); + + if (peg$result !== peg$FAILED && peg$currPos === input.length) { + return peg$result; + } else { + if (peg$result !== peg$FAILED && peg$currPos < input.length) { + peg$fail({ type: "end", description: "end of input" }); + } + + throw peg$buildException(null, peg$maxFailExpected, peg$maxFailPos); + } + } + + return { + SyntaxError: SyntaxError, + parse: parse + }; +})(); + + +},{"../flow/utils.js":23}],23:[function(require,module,exports){ +"use strict"; + +var _ = require("lodash"); +var $ = require("jquery"); + +var defaultPorts = { + "http": 80, + "https": 443 +}; + +var MessageUtils = { + getContentType: function getContentType(message) { + var ct = this.get_first_header(message, /^Content-Type$/i); + if (ct) { + return ct.split(";")[0].trim(); + } + }, + get_first_header: function get_first_header(message, regex) { + //FIXME: Cache Invalidation. + if (!message._headerLookups) Object.defineProperty(message, "_headerLookups", { + value: {}, + configurable: false, + enumerable: false, + writable: false + }); + if (!(regex in message._headerLookups)) { + var header; + for (var i = 0; i < message.headers.length; i++) { + if (!!message.headers[i][0].match(regex)) { + header = message.headers[i]; + break; + } + } + message._headerLookups[regex] = header ? header[1] : undefined; + } + return message._headerLookups[regex]; + }, + match_header: function match_header(message, regex) { + var headers = message.headers; + var i = headers.length; + while (i--) { + if (regex.test(headers[i].join(" "))) { + return headers[i]; + } + } + return false; + }, + getContentURL: function getContentURL(flow, message) { + if (message === flow.request) { + message = "request"; + } else if (message === flow.response) { + message = "response"; + } + return "/flows/" + flow.id + "/" + message + "/content"; + }, + getContent: function getContent(flow, message) { + var url = MessageUtils.getContentURL(flow, message); + return $.get(url); + } +}; + +var RequestUtils = _.extend(MessageUtils, { + pretty_host: function pretty_host(request) { + //FIXME: Add hostheader + return request.host; + }, + pretty_url: function pretty_url(request) { + var port = ""; + if (defaultPorts[request.scheme] !== request.port) { + port = ":" + request.port; + } + return request.scheme + "://" + this.pretty_host(request) + port + request.path; + } +}); + +var ResponseUtils = _.extend(MessageUtils, {}); + +var parseUrl_regex = /^(?:(https?):\/\/)?([^\/:]+)?(?::(\d+))?(\/.*)?$/i; +var parseUrl = function parseUrl(url) { + //there are many correct ways to parse a URL, + //however, a mitmproxy user may also wish to generate a not-so-correct URL. ;-) + var parts = parseUrl_regex.exec(url); + if (!parts) { + return false; + } + + var scheme = parts[1], + host = parts[2], + port = parseInt(parts[3]), + path = parts[4]; + if (scheme) { + port = port || defaultPorts[scheme]; + } + var ret = {}; + if (scheme) { + ret.scheme = scheme; + } + if (host) { + ret.host = host; + } + if (port) { + ret.port = port; + } + if (path) { + ret.path = path; + } + return ret; +}; + +var isValidHttpVersion_regex = /^HTTP\/\d+(\.\d+)*$/i; +var isValidHttpVersion = function isValidHttpVersion(httpVersion) { + return isValidHttpVersion_regex.test(httpVersion); +}; + +var parseHttpVersion = function parseHttpVersion(httpVersion) { + httpVersion = httpVersion.replace("HTTP/", "").split("."); + return _.map(httpVersion, function (x) { + return parseInt(x); + }); +}; + +module.exports = { + ResponseUtils: ResponseUtils, + RequestUtils: RequestUtils, + MessageUtils: MessageUtils, + parseUrl: parseUrl, + parseHttpVersion: parseHttpVersion, + isValidHttpVersion: isValidHttpVersion +}; + + +},{"jquery":"jquery","lodash":"lodash"}],24:[function(require,module,exports){ +"use strict"; + +var _ = require("lodash"); +var $ = require("jquery"); +var EventEmitter = require('events').EventEmitter; + +var utils = require("../utils.js"); +var actions = require("../actions.js"); +var dispatcher = require("../dispatcher.js"); + +function ListStore() { + EventEmitter.call(this); + this.reset(); +} +_.extend(ListStore.prototype, EventEmitter.prototype, { + add: function add(elem) { + if (elem.id in this._pos_map) { + return; + } + this._pos_map[elem.id] = this.list.length; + this.list.push(elem); + this.emit("add", elem); + }, + update: function update(elem) { + if (!(elem.id in this._pos_map)) { + return; + } + this.list[this._pos_map[elem.id]] = elem; + this.emit("update", elem); + }, + remove: function remove(elem_id) { + if (!(elem_id in this._pos_map)) { + return; + } + this.list.splice(this._pos_map[elem_id], 1); + this._build_map(); + this.emit("remove", elem_id); + }, + reset: function reset(elems) { + this.list = elems || []; + this._build_map(); + this.emit("recalculate"); + }, + _build_map: function _build_map() { + this._pos_map = {}; + for (var i = 0; i < this.list.length; i++) { + var elem = this.list[i]; + this._pos_map[elem.id] = i; + } + }, + get: function get(elem_id) { + return this.list[this._pos_map[elem_id]]; + }, + index: function index(elem_id) { + return this._pos_map[elem_id]; + } +}); + +function DictStore() { + EventEmitter.call(this); + this.reset(); +} +_.extend(DictStore.prototype, EventEmitter.prototype, { + update: function update(dict) { + _.merge(this.dict, dict); + this.emit("recalculate"); + }, + reset: function reset(dict) { + this.dict = dict || {}; + this.emit("recalculate"); + } +}); + +function LiveStoreMixin(type) { + this.type = type; + + this._updates_before_fetch = undefined; + this._fetchxhr = false; + + this.handle = this.handle.bind(this); + dispatcher.AppDispatcher.register(this.handle); + + // Avoid double-fetch on startup. + if (!(window.ws && window.ws.readyState === WebSocket.CONNECTING)) { + this.fetch(); + } +} +_.extend(LiveStoreMixin.prototype, { + handle: function handle(event) { + if (event.type === actions.ActionTypes.CONNECTION_OPEN) { + return this.fetch(); + } + if (event.type === this.type) { + if (event.cmd === actions.StoreCmds.RESET) { + this.fetch(event.data); + } else if (this._updates_before_fetch) { + console.log("defer update", event); + this._updates_before_fetch.push(event); + } else { + this[event.cmd](event.data); + } + } + }, + close: function close() { + dispatcher.AppDispatcher.unregister(this.handle); + }, + fetch: function fetch(data) { + console.log("fetch " + this.type); + if (this._fetchxhr) { + this._fetchxhr.abort(); + } + this._updates_before_fetch = []; // (JS: empty array is true) + if (data) { + this.handle_fetch(data); + } else { + this._fetchxhr = $.getJSON("/" + this.type).done((function (message) { + this.handle_fetch(message.data); + }).bind(this)).fail((function () { + EventLogActions.add_event("Could not fetch " + this.type); + }).bind(this)); + } + }, + handle_fetch: function handle_fetch(data) { + this._fetchxhr = false; + console.log(this.type + " fetched.", this._updates_before_fetch); + this.reset(data); + var updates = this._updates_before_fetch; + this._updates_before_fetch = false; + for (var i = 0; i < updates.length; i++) { + this.handle(updates[i]); + } + } +}); + +function LiveListStore(type) { + ListStore.call(this); + LiveStoreMixin.call(this, type); +} +_.extend(LiveListStore.prototype, ListStore.prototype, LiveStoreMixin.prototype); + +function LiveDictStore(type) { + DictStore.call(this); + LiveStoreMixin.call(this, type); +} +_.extend(LiveDictStore.prototype, DictStore.prototype, LiveStoreMixin.prototype); + +function FlowStore() { + return new LiveListStore(actions.ActionTypes.FLOW_STORE); +} + +function SettingsStore() { + return new LiveDictStore(actions.ActionTypes.SETTINGS_STORE); +} + +function EventLogStore() { + LiveListStore.call(this, actions.ActionTypes.EVENT_STORE); +} +_.extend(EventLogStore.prototype, LiveListStore.prototype, { + fetch: function fetch() { + LiveListStore.prototype.fetch.apply(this, arguments); + + // Make sure to display updates even if fetching all events failed. + // This way, we can send "fetch failed" log messages to the log. + if (this._fetchxhr) { + this._fetchxhr.fail((function () { + this.handle_fetch(null); + }).bind(this)); + } + } +}); + +module.exports = { + EventLogStore: EventLogStore, + SettingsStore: SettingsStore, + FlowStore: FlowStore +}; + + +},{"../actions.js":2,"../dispatcher.js":21,"../utils.js":26,"events":1,"jquery":"jquery","lodash":"lodash"}],25:[function(require,module,exports){ +"use strict"; + +var EventEmitter = require('events').EventEmitter; +var _ = require("lodash"); + +var utils = require("../utils.js"); + +function SortByStoreOrder(elem) { + return this.store.index(elem.id); +} + +var default_sort = SortByStoreOrder; +var default_filt = function default_filt(elem) { + return true; +}; + +function StoreView(store, filt, sortfun) { + EventEmitter.call(this); + + this.store = store; + + this.add = this.add.bind(this); + this.update = this.update.bind(this); + this.remove = this.remove.bind(this); + this.recalculate = this.recalculate.bind(this); + this.store.addListener("add", this.add); + this.store.addListener("update", this.update); + this.store.addListener("remove", this.remove); + this.store.addListener("recalculate", this.recalculate); + + this.recalculate(filt, sortfun); +} + +_.extend(StoreView.prototype, EventEmitter.prototype, { + close: function close() { + this.store.removeListener("add", this.add); + this.store.removeListener("update", this.update); + this.store.removeListener("remove", this.remove); + this.store.removeListener("recalculate", this.recalculate); + this.removeAllListeners(); + }, + recalculate: function recalculate(filt, sortfun) { + filt = filt || this.filt || default_filt; + sortfun = sortfun || this.sortfun || default_sort; + filt = filt.bind(this); + sortfun = sortfun.bind(this); + this.filt = filt; + this.sortfun = sortfun; + + this.list = this.store.list.filter(filt); + this.list.sort(function (a, b) { + var akey = sortfun(a); + var bkey = sortfun(b); + if (akey < bkey) { + return -1; + } else if (akey > bkey) { + return 1; + } else { + return 0; + } + }); + this.emit("recalculate"); + }, + index: function index(elem) { + return _.sortedIndex(this.list, elem, this.sortfun); + }, + add: function add(elem) { + if (this.filt(elem)) { + var idx = this.index(elem); + if (idx === this.list.length) { + //happens often, .push is way faster. + this.list.push(elem); + } else { + this.list.splice(idx, 0, elem); + } + this.emit("add", elem, idx); + } + }, + update: function update(elem) { + var idx; + var i = this.list.length; + // Search from the back, we usually update the latest entries. + while (i--) { + if (this.list[i].id === elem.id) { + idx = i; + break; + } + } + + if (idx === -1) { + //not contained in list + this.add(elem); + } else if (!this.filt(elem)) { + this.remove(elem.id); + } else { + if (this.sortfun(this.list[idx]) !== this.sortfun(elem)) { + //sortpos has changed + this.remove(this.list[idx]); + this.add(elem); + } else { + this.list[idx] = elem; + this.emit("update", elem, idx); + } + } + }, + remove: function remove(elem_id) { + var idx = this.list.length; + while (idx--) { + if (this.list[idx].id === elem_id) { + this.list.splice(idx, 1); + this.emit("remove", elem_id, idx); + break; + } + } + } +}); + +module.exports = { + StoreView: StoreView +}; + + +},{"../utils.js":26,"events":1,"lodash":"lodash"}],26:[function(require,module,exports){ +"use strict"; + +var $ = require("jquery"); +var _ = require("lodash"); +var actions = require("./actions.js"); + +window.$ = $; +window._ = _; +window.React = require("react"); + +var Key = { + UP: 38, + DOWN: 40, + PAGE_UP: 33, + PAGE_DOWN: 34, + HOME: 36, + END: 35, + LEFT: 37, + RIGHT: 39, + ENTER: 13, + ESC: 27, + TAB: 9, + SPACE: 32, + BACKSPACE: 8, + SHIFT: 16 +}; +// Add A-Z +for (var i = 65; i <= 90; i++) { + Key[String.fromCharCode(i)] = i; +} + +var formatSize = function formatSize(bytes) { + if (bytes === 0) return "0"; + var prefix = ["b", "kb", "mb", "gb", "tb"]; + for (var i = 0; i < prefix.length; i++) { + if (Math.pow(1024, i + 1) > bytes) { + break; + } + } + var precision; + if (bytes % Math.pow(1024, i) === 0) precision = 0;else precision = 1; + return (bytes / Math.pow(1024, i)).toFixed(precision) + prefix[i]; +}; + +var formatTimeDelta = function formatTimeDelta(milliseconds) { + var time = milliseconds; + var prefix = ["ms", "s", "min", "h"]; + var div = [1000, 60, 60]; + var i = 0; + while (Math.abs(time) >= div[i] && i < div.length) { + time = time / div[i]; + i++; + } + return Math.round(time) + prefix[i]; +}; + +var formatTimeStamp = function formatTimeStamp(seconds) { + var ts = new Date(seconds * 1000).toISOString(); + return ts.replace("T", " ").replace("Z", ""); +}; + +// At some places, we need to sort strings alphabetically descending, +// but we can only provide a key function. +// This beauty "reverses" a JS string. +var end = String.fromCharCode(0xffff); +function reverseString(s) { + return String.fromCharCode.apply(String, _.map(s.split(""), function (c) { + return 0xffff - c.charCodeAt(0); + })) + end; +} + +function getCookie(name) { + var r = document.cookie.match(new RegExp("\\b" + name + "=([^;]*)\\b")); + return r ? r[1] : undefined; +} +var xsrf = $.param({ _xsrf: getCookie("_xsrf") }); + +//Tornado XSRF Protection. +$.ajaxPrefilter(function (options) { + if (["post", "put", "delete"].indexOf(options.type.toLowerCase()) >= 0 && options.url[0] === "/") { + if (options.url.indexOf("?") === -1) { + options.url += "?" + xsrf; + } else { + options.url += "&" + xsrf; + } + } +}); +// Log AJAX Errors +$(document).ajaxError(function (event, jqXHR, ajaxSettings, thrownError) { + if (thrownError === "abort") { + return; + } + var message = jqXHR.responseText; + console.error(thrownError, message, arguments); + actions.EventLogActions.add_event(thrownError + ": " + message); + alert(message); +}); + +module.exports = { + formatSize: formatSize, + formatTimeDelta: formatTimeDelta, + formatTimeStamp: formatTimeStamp, + reverseString: reverseString, + Key: Key +}; + + +},{"./actions.js":2,"jquery":"jquery","lodash":"lodash","react":"react"}]},{},[3]) + + +//# sourceMappingURL=app.js.map |