Bug 1370324 - Update Debugger frontend (v0.5) (6/5/2017). r=jdescottes
authorJason Laster <jason.laster.11@gmail.com>
Wed, 07 Jun 2017 17:53:35 +0200
changeset 410846 f58a80807c734285f69680c036bc41f3cb74e6bd
parent 410845 47ce8dbb036e5452c0dd060c274cef056306d258
child 410847 ea32af8eee1459bdcbe85c8a3e7b78b374802d1c
push id7391
push usermtabara@mozilla.com
push dateMon, 12 Jun 2017 13:08:53 +0000
treeherdermozilla-beta@2191d7f87e2e [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersjdescottes
bugs1370324
milestone55.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Bug 1370324 - Update Debugger frontend (v0.5) (6/5/2017). r=jdescottes MozReview-Commit-ID: A6Oz0XnBHuJ
devtools/client/debugger/new/debugger.css
devtools/client/debugger/new/debugger.js
devtools/client/debugger/new/parser-worker.js
devtools/client/debugger/new/test/mochitest/browser_dbg-breakpoints.js
devtools/client/debugger/new/test/mochitest/browser_dbg-scopes.js
devtools/client/preferences/debugger.js
devtools/client/themes/toolbox.css
devtools/client/themes/variables.css
--- a/devtools/client/debugger/new/debugger.css
+++ b/devtools/client/debugger/new/debugger.css
@@ -38,17 +38,25 @@
   color: var(--theme-comment);
   font-size: var(--ui-element-font-size);
   border: 1px solid var(--theme-splitter-color);
   padding: calc(var(--base-spacing) / 2);
   margin: 0 var(--base-spacing);
   transition: var(--base-transition);
 }
 
-.landing-page .panel header input[type=button] {
+.landing-page .panel .hero {
+  height: 100%;
+  display: flex;
+  flex-direction: column;
+  align-items: center;
+  justify-content: center;
+}
+
+.landing-page .panel input[type=button] {
   background-color: var(--theme-tab-toolbar-background);
   color: var(--theme-comment);
   font-size: var(--ui-element-font-size);
   border: 1px solid var(--theme-splitter-color);
   padding: calc(var(--base-spacing) / 2);
   margin: 0 var(--base-spacing);
   transition: var(--base-transition);
 }
@@ -874,21 +882,16 @@ html .arrow.expanded svg {
   border-bottom: 1px solid var(--theme-splitter-color);
   padding-right: 10px;
   display: flex;
   flex-shrink: 0;
 }
 
 .search-field.big {
   height: 40px;
-  font-size: 14px;
-}
-
-.search-field.big input {
-  font-size: 14px;
 }
 
 .search-field i {
   display: block;
   padding: 0;
   width: 16px;
 }
 
@@ -1058,16 +1061,82 @@ html .arrow.expanded svg {
 
 .search-bar .result-list {
   border-bottom: 1px solid var(--theme-splitter-color);
 }
 
 .theme-dark .result-list {
   background-color: var(--theme-body-background);
 }
+.tree {
+  -webkit-user-select: none;
+  -moz-user-select: none;
+  -ms-user-select: none;
+  -o-user-select: none;
+  user-select: none;
+
+  white-space: nowrap;
+  overflow: auto;
+  min-width: 100%;
+}
+
+.tree button {
+  display: block;
+}
+
+.tree .node {
+  padding: 2px 5px;
+  position: relative;
+  cursor: pointer;
+}
+
+.tree .node.focused {
+  color: white;
+  background-color: var(--theme-selection-background);
+}
+
+.theme-dark .tree .node.focused {
+  background-color: var(--theme-selection-background-semitransparent);
+}
+
+html:not([dir="rtl"]) .tree .node > div {
+  margin-left: 10px;
+}
+
+html[dir="rtl"] .tree .node > div {
+  margin-right: 10px;
+}
+
+.tree .node.focused svg {
+  fill: white;
+}
+
+.tree-node button {
+  position: fixed;
+}
+.project-text-search .result {
+  display: flex;
+  margin-left: 20px;
+}
+
+.project-text-search .file-result {
+  font-weight: bold;
+  margin-top: 20px;
+  line-height: 20px;
+}
+
+.project-text-search .line-match {
+  font-family: monospace;
+  display: "flex";
+  grow: 1;
+}
+
+.project-text-search .result .line-number {
+  padding-right: 5px;
+}
 .sources-panel {
   flex: 1;
   display: flex;
   flex-direction: column;
   overflow: hidden;
   position: relative;
 }
 
@@ -1124,19 +1193,69 @@ html .arrow.expanded svg {
 .no-sources-message {
   font-size: 12px;
   color: var(--theme-comment-alt);
   font-weight: lighter;
   padding-top: 5px;
   text-align: center;
 }
 
+.sources-panel .source-footer {
+  position: relative;
+}
+
+.sources-panel .outline {
+  display: flex;
+  flex: 1;
+}
+
+.sources-panel .outline.hidden {
+  display: none;
+}
+
 .hidden {
   display: none;
 }
+
+.source-footer {
+  width: 100%;
+}
+
+.source-footer .tab {
+  flex: 1;
+  justify-content: center;
+  border: 1px solid transparent;
+  border-bottom-left-radius: 2px;
+  border-bottom-right-radius: 2px;
+  display: inline-flex;
+  position: relative;
+  transition: all 0.25s ease;
+  overflow: hidden;
+  padding: 5px;
+  margin-bottom: 4px;
+  margin-top: -1px;
+}
+
+.source-footer .tab:hover {
+  background-color: var(--theme-toolbar-background-alt);
+  border-color: var(--theme-splitter-color);
+  cursor: pointer;
+}
+
+.source-footer .tab.active {
+  color: var(--theme-body-color);
+  background-color: var(--theme-body-background);
+  border-color: var(--theme-splitter-color);
+  border-top-color: transparent;
+}
+
+.source-footer .tab.active path,
+.source-footer .tab:hover path {
+  fill: var(--theme-body-color);
+}
 .outline-list {
   list-style-type: "-";
 }
 
 .outline-list__element {
   color: blue;
   padding-left: 0.5rem;
 }
@@ -1156,86 +1275,44 @@ html .arrow.expanded svg {
 
 .function-signature .paren {
   color: var(--object-color);
 }
 
 .function-signature .comma {
   color: var(--object-color);
 }
-.tree {
-  -webkit-user-select: none;
-  -moz-user-select: none;
-  -ms-user-select: none;
-  -o-user-select: none;
-  user-select: none;
-
-  white-space: nowrap;
-  overflow: auto;
-  min-width: 100%;
-}
-
-.tree button {
-  display: block;
-}
-
-.tree .node {
-  padding: 2px 5px;
-  position: relative;
-  cursor: pointer;
-}
-
-.tree .node.focused {
-  color: white;
-  background-color: var(--theme-selection-background);
-}
-
-.theme-dark .tree .node.focused {
-  background-color: var(--theme-selection-background-semitransparent);
-}
-
-html:not([dir="rtl"]) .tree .node > div {
-  margin-left: 10px;
-}
-
-html[dir="rtl"] .tree .node > div {
-  margin-right: 10px;
-}
-
-.tree .node.focused svg {
-  fill: white;
-}
-
-.tree-node button {
-  position: fixed;
-}
 .conditional-breakpoint-panel {
   cursor: initial;
   margin: 1em 0;
   position: relative;
   display: flex;
   align-items: center;
   background: var(--theme-toolbar-background);
   border-top: 1px solid var(--theme-splitter-color);
   border-bottom: 1px solid var(--theme-splitter-color);
 }
 
 .conditional-breakpoint-panel .prompt {
   font-size: 1.8em;
-  color: var(--theme-comment-alt);
+  color: var(--theme-conditional-breakpoint-color);
   padding-left: 3px;
+  padding-right: 3px;
+  padding-bottom: 3px;
+  text-align: right;
+  width: 30px;
 }
 
 .conditional-breakpoint-panel input {
   margin: 5px 10px;
   width: calc(100% - 4em);
   border: none;
   background: var(--theme-toolbar-background);
   font-size: 14px;
-  color: var(--theme-comment);
+  color: var(--theme-conditional-breakpoint-color);
   line-height: 30px;
 }
 
 .conditional-breakpoint-panel input:focus {
   outline-width: 0;
 }
 .toggle-button-start,
 .toggle-button-end {
@@ -1356,17 +1433,16 @@ html .toggle-button-end.vertical svg {
 
 .source-footer .black-box.blackboxed svg {
   fill: var(--theme-highlight-blue);
 }
 
 .source-footer .blackbox-summary {
   color: var(--theme-body-color);
 }
-
 .search-bar {
   display: flex;
   flex-direction: column;
 }
 
 .search-bar .search-field {
   padding-left: 7px;
   height: var(--editor-searchbar-height);
@@ -1566,17 +1642,17 @@ html .toggle-button-end.vertical svg {
   color: var(--theme-highlight-blue);
   text-decoration: underline;
 }
 .popover .preview .header .link:hover {
   cursor: pointer;
 }
 
 .selected-token {
-  background-color: var(--theme-search-overlays-semitransparent);
+  background-color: var(--theme-highlight-yellow);
   color: var(--theme-selection-color);
 }
 
 .selected-token:hover {
   cursor: pointer;
 }
 
 .popover .preview .function-signature {
@@ -1646,36 +1722,58 @@ html .toggle-button-end.vertical svg {
 .add-to-expression-bar .expression-to-save-button {
   font-size: 14px;
   color: var(--theme-comment);
   cursor: pointer;
 }
 .editor-wrapper {
   --debug-line-background: rgba(226, 236, 247, 0.5);
   --debug-line-border: rgb(145, 188, 219);
+  --editor-searchbar-height: 27px;
+  --editor-second-searchbar-height: 27px;
+
 }
 
 .theme-dark .editor-wrapper {
   --debug-line-background: rgb(73, 82, 103);
   --debug-line-border: rgb(119, 134, 162);
 }
 
+.editor-wrapper .CodeMirror-linewidget {
+  margin-right: -7px;
+}
+
+.theme-dark {
+  --theme-conditional-breakpoint-color: #9fa4a9;
+}
+
+.theme-light {
+  --theme-conditional-breakpoint-color: #ccd1d5;
+}
+
+.theme-firebug {
+  --theme-conditional-breakpoint-color: #ccd1d5;
+}
+
+.out-of-scope .CodeMirror-line,
+.out-of-scope .CodeMirror-linenumber {
+  opacity: 0.8;
+}
+
 /**
  * There's a known codemirror flex issue with chrome that this addresses.
  * BUG https://github.com/devtools-html/debugger.html/issues/63
  */
 .editor-wrapper {
   position: absolute;
   height: calc(100% - 31px);
   width: 100%;
   top: 30px;
   left: 0px;
   --editor-footer-height: 27px;
-  --editor-searchbar-height: 27px;
-  --editor-second-searchbar-height: 27px;
 }
 
 html[dir="rtl"] .editor-mount {
   direction: ltr;
 }
 
 .editor-wrapper .breakpoints {
   position: absolute;
@@ -2280,16 +2378,17 @@ html .breakpoints-list .breakpoint.pause
 .accordion ._header {
   background-color: var(--theme-toolbar-background);
   border-bottom: 1px solid var(--theme-splitter-color);
   cursor: pointer;
   font-size: 12px;
   padding: 5px;
   transition: all 0.25s ease;
   width: 100%;
+  align-items: center;
 
   -webkit-user-select: none;
   -moz-user-select: none;
   -ms-user-select: none;
   -o-user-select: none;
   user-select: none;
 }
 
--- a/devtools/client/debugger/new/debugger.js
+++ b/devtools/client/debugger/new/debugger.js
@@ -1,18 +1,18 @@
 (function webpackUniversalModuleDefinition(root, factory) {
 	if(typeof exports === 'object' && typeof module === 'object')
-		module.exports = factory(require("devtools/client/shared/vendor/react"), require("Services"), require("devtools/shared/flags"), require("devtools/client/sourceeditor/editor"));
+		module.exports = factory(require("devtools/client/shared/vendor/react"), require("Services"), require("devtools/client/shared/vendor/react-dom"), require("devtools/shared/flags"), require("devtools/client/sourceeditor/editor"));
 	else if(typeof define === 'function' && define.amd)
-		define(["devtools/client/shared/vendor/react", "Services", "devtools/shared/flags", "devtools/client/sourceeditor/editor"], factory);
+		define(["devtools/client/shared/vendor/react", "Services", "devtools/client/shared/vendor/react-dom", "devtools/shared/flags", "devtools/client/sourceeditor/editor"], factory);
 	else {
-		var a = typeof exports === 'object' ? factory(require("devtools/client/shared/vendor/react"), require("Services"), require("devtools/shared/flags"), require("devtools/client/sourceeditor/editor")) : factory(root["devtools/client/shared/vendor/react"], root["Services"], root["devtools/shared/flags"], root["devtools/client/sourceeditor/editor"]);
+		var a = typeof exports === 'object' ? factory(require("devtools/client/shared/vendor/react"), require("Services"), require("devtools/client/shared/vendor/react-dom"), require("devtools/shared/flags"), require("devtools/client/sourceeditor/editor")) : factory(root["devtools/client/shared/vendor/react"], root["Services"], root["devtools/client/shared/vendor/react-dom"], root["devtools/shared/flags"], root["devtools/client/sourceeditor/editor"]);
 		for(var i in a) (typeof exports === 'object' ? exports : root)[i] = a[i];
 	}
-})(this, function(__WEBPACK_EXTERNAL_MODULE_2__, __WEBPACK_EXTERNAL_MODULE_29__, __WEBPACK_EXTERNAL_MODULE_121__, __WEBPACK_EXTERNAL_MODULE_995__) {
+})(this, function(__WEBPACK_EXTERNAL_MODULE_2__, __WEBPACK_EXTERNAL_MODULE_29__, __WEBPACK_EXTERNAL_MODULE_31__, __WEBPACK_EXTERNAL_MODULE_121__, __WEBPACK_EXTERNAL_MODULE_995__) {
 return /******/ (function(modules) { // webpackBootstrap
 /******/ 	// The module cache
 /******/ 	var installedModules = {};
 
 /******/ 	// The require function
 /******/ 	function __webpack_require__(moduleId) {
 
 /******/ 		// Check if module is in cache
@@ -59,17 +59,17 @@ return /******/ (function(modules) { // 
 
 /***/ },
 /* 1 */
 /***/ function(module, exports, __webpack_require__) {
 
 	"use strict";
 
 	var React = __webpack_require__(2);
-	var ReactDOM = __webpack_require__(22);
+	var ReactDOM = __webpack_require__(31);
 	//
 	// if (process.env.NODE_ENV !== "production") {
 	//   const Perf = require("react-addons-perf");
 	//   window.Perf = Perf;
 	// }
 
 	var _require = __webpack_require__(131),
 	    bootstrap = _require.bootstrap,
@@ -1091,78 +1091,36 @@ return /******/ (function(modules) { // 
 	      };
 	    }();
 
 	    if (typeof _ret === "object") return _ret.v;
 	  }
 	}
 
 /***/ },
-/* 22 */
-/***/ function(module, exports, __webpack_require__) {
-
-	/**
-	 * ReactDOM v15.3.2
-	 *
-	 * Copyright 2013-present, Facebook, Inc.
-	 * All rights reserved.
-	 *
-	 * This source code is licensed under the BSD-style license found in the
-	 * LICENSE file in the root directory of this source tree. An additional grant
-	 * of patent rights can be found in the PATENTS file in the same directory.
-	 *
-	 */
-	// Based off https://github.com/ForbesLindesay/umd/blob/master/template.js
-	;(function(f) {
-	  // CommonJS
-	  if (true) {
-	    module.exports = f(__webpack_require__(2));
-
-	  // RequireJS
-	  } else if (typeof define === "function" && define.amd) {
-	    define(['react'], f);
-
-	  // <script>
-	  } else {
-	    var g;
-	    if (typeof window !== "undefined") {
-	      g = window;
-	    } else if (typeof global !== "undefined") {
-	      g = global;
-	    } else if (typeof self !== "undefined") {
-	      g = self;
-	    } else {
-	      // works providing we're not in "use strict";
-	      // needed for Java 8 Nashorn
-	      // see https://github.com/facebook/react/issues/3037
-	      g = this;
-	    }
-	    g.ReactDOM = f(g.React);
-	  }
-
-	})(function(React) {
-	  return React.__SECRET_DOM_DO_NOT_USE_OR_YOU_WILL_BE_FIRED;
-	});
-
-
-/***/ },
+/* 22 */,
 /* 23 */,
 /* 24 */,
 /* 25 */,
 /* 26 */,
 /* 27 */,
 /* 28 */,
 /* 29 */
 /***/ function(module, exports) {
 
 	module.exports = __WEBPACK_EXTERNAL_MODULE_29__;
 
 /***/ },
 /* 30 */,
-/* 31 */,
+/* 31 */
+/***/ function(module, exports) {
+
+	module.exports = __WEBPACK_EXTERNAL_MODULE_31__;
+
+/***/ },
 /* 32 */,
 /* 33 */,
 /* 34 */,
 /* 35 */,
 /* 36 */,
 /* 37 */,
 /* 38 */,
 /* 39 */,
@@ -4109,17 +4067,17 @@ return /******/ (function(modules) { // 
 	});
 
 	function update() {
 	  var state = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : initialState;
 	  var action = arguments[1];
 
 	  switch (action.type) {
 	    case constants.CLEAR_TABS:
-	      return state.setIn(["tabs"], Immutable.Map()).setIn("selectedTab", null);
+	      return state.setIn(["tabs"], Immutable.Map()).setIn(["selectedTab"], null);
 
 	    case constants.ADD_TABS:
 	      var tabs = action.value;
 	      if (!tabs) {
 	        return state;
 	      }
 
 	      return state.mergeIn(["tabs"], Immutable.Map(tabs.map(tab => {
@@ -4159,24 +4117,51 @@ return /******/ (function(modules) { // 
 
 	"use strict";
 
 	/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */
 	/* vim: set ft=javascript ts=2 et sw=2 tw=80: */
 	/* This Source Code Form is subject to the terms of the Mozilla Public
 	 * License, v. 2.0. If a copy of the MPL was not distributed with this
 	 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+	var sidePanelItems = {
+	  Firefox: {
+	    name: "Firefox",
+	    clientType: "firefox",
+	    paramName: "firefox-tab",
+	    docsUrlPart: "firefox"
+	  },
+	  Chrome: {
+	    name: "Chrome",
+	    clientType: "chrome",
+	    paramName: "chrome-tab",
+	    docsUrlPart: "chrome"
+	  },
+	  Node: {
+	    name: "Node",
+	    clientType: "node",
+	    paramName: "node-tab",
+	    docsUrlPart: "node"
+	  },
+	  Settings: {
+	    name: "Settings",
+	    clientType: "settings",
+	    paramName: "settings-tab",
+	    docsUrlPart: "settings"
+	  }
+	};
 
 	module.exports = {
 	  CLEAR_TABS: "CLEAR_TABS",
 	  ADD_TABS: "ADD_TABS",
 	  SELECT_TAB: "SELECT_TAB",
 	  FILTER_TABS: "FILTER_TABS",
 	  SET_VALUE: "SET_VALUE",
-	  SET_CONFIG: "SET_CONFIG"
+	  SET_CONFIG: "SET_CONFIG",
+	  sidePanelItems
 	};
 
 /***/ },
 /* 146 */
 /***/ function(module, exports, __webpack_require__) {
 
 	/**
 	 *  Copyright (c) 2014-2015, Facebook, Inc.
@@ -10293,17 +10278,20 @@ return /******/ (function(modules) { // 
 	function getTabs(state) {
 	  var tabs = state.tabs.get("tabs");
 	  var filterString = getFilterString(state);
 
 	  if (filterString === "") {
 	    return tabs;
 	  }
 
-	  return tabs.filter(tab => score(tab.get("title"), filterString) + score(tab.get("url"), filterString) > 0);
+	  return tabs.map(tab => {
+	    var _overallScore = score(tab.get("title"), filterString) + score(tab.get("url"), filterString);
+	    return tab.set("filteredOut", _overallScore === 0);
+	  });
 	}
 
 	function getSelectedTab(state) {
 	  return state.tabs.get("selectedTab");
 	}
 
 	function getFilterString(state) {
 	  return state.tabs.get("filterString");
@@ -11238,17 +11226,17 @@ return /******/ (function(modules) { // 
 	"use strict";
 
 	var React = __webpack_require__(2);
 
 	__webpack_require__(168);
 	var dom = React.DOM;
 
 	var ImPropTypes = __webpack_require__(150);
-
+	var configMap = __webpack_require__(145).sidePanelItems;
 	var Tabs = React.createFactory(__webpack_require__(172));
 	var Sidebar = React.createFactory(__webpack_require__(176));
 	var Settings = React.createFactory(__webpack_require__(179));
 
 	var githubUrl = "https://github.com/devtools-html/debugger.html/blob/master";
 
 	function getTabsByClientType(tabs, clientType) {
 	  return tabs.valueSeq().filter(tab => tab.get("clientType") == clientType);
@@ -11272,17 +11260,17 @@ return /******/ (function(modules) { // 
 	    config: React.PropTypes.object.isRequired,
 	    setValue: React.PropTypes.func.isRequired
 	  },
 
 	  displayName: "LandingPage",
 
 	  getInitialState() {
 	    return {
-	      selectedPane: "Firefox",
+	      selectedPane: configMap.Firefox.name,
 	      firefoxConnected: false,
 	      chromeConnected: false
 	    };
 	  },
 
 	  componentDidUpdate() {
 	    if (this.refs.filterInput) {
 	      this.refs.filterInput.focus();
@@ -11294,117 +11282,145 @@ return /******/ (function(modules) { // 
 	  },
 
 	  onSideBarItemClick(itemTitle) {
 	    if (itemTitle !== this.state.selectedPane) {
 	      this.setState({ selectedPane: itemTitle });
 	    }
 	  },
 
-	  renderPanel() {
+	  renderLaunchButton() {
+	    var selectedPane = this.state.selectedPane;
+	    var name = configMap[selectedPane].name;
+
+
+	    var isConnected = name === configMap.Firefox.name ? this.state.firefoxConnected : this.state.chromeConnected;
+	    var isNodeSelected = name === configMap.Node.name;
+
+	    if (isNodeSelected) {
+	      return dom.h3({}, "Run a node script in the terminal with `--inspect`");
+	    }
+
+	    var connectedStateText = isNodeSelected ? null : `Please open a tab in ${name}`;
+
+	    return isConnected ? connectedStateText : dom.input({
+	      type: "button",
+	      value: `Launch ${configMap[selectedPane].name}`,
+	      onClick: () => this.launchBrowser(configMap[selectedPane].name)
+	    });
+	  },
+
+	  launchBrowser(browser) {
+	    fetch("/launch", {
+	      body: JSON.stringify({ browser }),
+	      headers: {
+	        "Content-Type": "application/json"
+	      },
+	      method: "post"
+	    }).then(resp => {
+	      if (browser === configMap.Firefox.name) {
+	        this.setState({ firefoxConnected: true });
+	      } else {
+	        this.setState({ chromeConnected: true });
+	      }
+	    }).catch(err => {
+	      alert(`Error launching ${browser}. ${err.message}`);
+	    });
+	  },
+
+	  renderEmptyPanel() {
+	    return dom.div({ className: "hero" }, this.renderLaunchButton());
+	  },
+
+	  renderSettings() {
 	    var _props = this.props,
-	        onTabClick = _props.onTabClick,
 	        config = _props.config,
 	        setValue = _props.setValue;
 
-	    var configMap = {
-	      Firefox: {
-	        name: "Firefox",
-	        clientType: "firefox",
-	        paramName: "firefox-tab",
-	        docsUrlPart: "firefox"
-	      },
-	      Chrome: {
-	        name: "Chrome",
-	        clientType: "chrome",
-	        paramName: "chrome-tab",
-	        docsUrlPart: "chrome"
-	      },
-	      Node: {
-	        name: "Node",
-	        clientType: "node",
-	        paramName: "node-tab",
-	        docsUrlPart: "node"
-	      },
-	      Settings: {
-	        name: "Settings",
-	        clientType: "settings",
-	        paramName: "settings-tab",
-	        docsUrlPart: "settings"
-	      }
-	    };
-
-	    var _configMap$state$sele = configMap[this.state.selectedPane],
-	        name = _configMap$state$sele.name,
-	        clientType = _configMap$state$sele.clientType,
-	        paramName = _configMap$state$sele.paramName,
-	        docsUrlPart = _configMap$state$sele.docsUrlPart;
+
+	    return dom.div({}, dom.header({}, dom.h1({}, configMap.Settings.name)), Settings({ config, setValue }));
+	  },
+
+	  renderFilter() {
+	    var selectedPane = this.state.selectedPane;
 	    var _props2 = this.props,
 	        tabs = _props2.tabs,
 	        _props2$filterString = _props2.filterString,
 	        filterString = _props2$filterString === undefined ? "" : _props2$filterString;
-	    var selectedPane = this.state.selectedPane;
+	    var _configMap$selectedPa = configMap[selectedPane],
+	        clientType = _configMap$selectedPa.clientType,
+	        paramName = _configMap$selectedPa.paramName;
 
 
 	    var targets = getTabsByClientType(tabs, clientType);
-	    var isSettingsPaneSelected = name === "Settings";
-
-	    var launchBrowser = browser => {
-	      fetch("/launch", {
-	        body: JSON.stringify({ browser }),
-	        headers: {
-	          "Content-Type": "application/json"
-	        },
-	        method: "post"
-	      }).then(resp => {
-	        if (browser == "firefox") {
-	          this.setState({ firefoxConnected: true });
-	        } else {
-	          this.setState({ chromeConnected: true });
-	        }
-	      }).catch(err => {
-	        alert(`Error launching ${browser}. ${err.message}`);
-	      });
-	    };
-
-	    var isConnected = name == "Firefox" ? this.state.firefoxConnected : this.state.chromeConnected;
-	    var launchButton = isConnected ? null : dom.input({
-	      type: "button",
-	      value: `Launch ${selectedPane}`,
-	      onClick: () => launchBrowser(selectedPane)
-	    });
-	    return dom.main({ className: "panel" }, !isSettingsPaneSelected ? dom.header({}, dom.input({
+
+	    return dom.header({}, dom.input({
 	      ref: "filterInput",
 	      placeholder: "Filter tabs",
 	      value: filterString,
 	      autoFocus: true,
 	      type: "search",
 	      onChange: e => this.onFilterChange(e.target.value),
 	      onKeyDown: e => {
 	        if (targets.size === 1 && e.keyCode === 13) {
 	          this.onTabClick(targets.first(), paramName);
 	        }
 	      }
-	    }), launchButton) : dom.header({}, dom.h1({}, "Settings")), isSettingsPaneSelected ? Settings({ config, setValue }) : Tabs({ targets, paramName, onTabClick }), firstTimeMessage(name, docsUrlPart));
+	    }));
+	  },
+
+	  renderPanel() {
+	    var _props3 = this.props,
+	        onTabClick = _props3.onTabClick,
+	        tabs = _props3.tabs;
+	    var selectedPane = this.state.selectedPane;
+	    var _configMap$selectedPa2 = configMap[selectedPane],
+	        name = _configMap$selectedPa2.name,
+	        clientType = _configMap$selectedPa2.clientType,
+	        paramName = _configMap$selectedPa2.paramName;
+
+
+	    var clientTargets = getTabsByClientType(tabs, clientType);
+	    var tabsDetected = clientTargets && clientTargets.count() > 0;
+	    var targets = clientTargets.filter(t => !t.get("filteredOut"));
+
+	    var isSettingsPaneSelected = name === configMap.Settings.name;
+
+	    if (isSettingsPaneSelected) {
+	      return this.renderSettings();
+	    }
+
+	    if (!tabsDetected) {
+	      return this.renderEmptyPanel();
+	    }
+
+	    return dom.div({}, this.renderFilter(), Tabs({ targets, paramName, onTabClick }));
 	  },
 
 	  render() {
-	    var _props3 = this.props,
-	        supportsFirefox = _props3.supportsFirefox,
-	        supportsChrome = _props3.supportsChrome,
-	        title = _props3.title;
+	    var _props4 = this.props,
+	        supportsFirefox = _props4.supportsFirefox,
+	        supportsChrome = _props4.supportsChrome,
+	        title = _props4.title;
 	    var selectedPane = this.state.selectedPane;
 	    var onSideBarItemClick = this.onSideBarItemClick;
+	    var _configMap$selectedPa3 = configMap[selectedPane],
+	        name = _configMap$selectedPa3.name,
+	        docsUrlPart = _configMap$selectedPa3.docsUrlPart;
+
 
 	    return dom.div({
 	      className: "landing-page"
 	    }, Sidebar({
-	      supportsFirefox, supportsChrome, title,
-	      selectedPane, onSideBarItemClick
-	    }), this.renderPanel());
+	      supportsFirefox,
+	      supportsChrome,
+	      title,
+	      selectedPane,
+	      onSideBarItemClick
+	    }), dom.main({ className: "panel" }, this.renderPanel(), firstTimeMessage(name, docsUrlPart)));
 	  }
 	});
 
 	module.exports = LandingPage;
 
 /***/ },
 /* 168 */
 /***/ function(module, exports) {
@@ -11684,20 +11700,20 @@ return /******/ (function(modules) { // 
 
 	  onInputHandler(e, path) {
 	    var setValue = this.props.setValue;
 
 	    setValue(path, e.target.checked);
 	  },
 
 	  renderConfig(config) {
-	    var configs = [{ name: "dir", label: "direction" }, { name: "theme", label: "theme" }
-	    // Hiding hotReloading option for now. See Issue #242
-	    // { name: "hotReloading", label: "hot reloading", bool: true }
-	    ];
+	    var configs = [{ name: "dir", label: "direction" }, { name: "theme", label: "theme"
+	      // Hiding hotReloading option for now. See Issue #242
+	      // { name: "hotReloading", label: "hot reloading", bool: true }
+	    }];
 
 	    return dom.ul({ className: "tab-list" }, configs.map(c => {
 	      return dom.li({ key: c.name, className: "tab tab-sides" }, dom.div({ className: "tab-title" }, c.label), c.bool ? dom.input({
 	        type: "checkbox",
 	        defaultChecked: config[c.name],
 	        onChange: e => this.onInputHandler(e, c.name)
 	      }, null) : dom.div({
 	        className: "tab-value",
@@ -12127,17 +12143,17 @@ return /******/ (function(modules) { // 
 	 * for actions to install a function to be run once when a specific
 	 * condition is met by an action coming through the system. Think of
 	 * it as a thunk that blocks until the condition is met. Example:
 	 *
 	 * ```js
 	 * const services = { WAIT_UNTIL: require('wait-service').NAME };
 	 *
 	 * { type: services.WAIT_UNTIL,
-	 *   predicate: action => action.type === constants.ADD_ITEM,
+	 *   predicate: action => action.type === "ADD_ITEM",
 	 *   run: (dispatch, getState, action) => {
 	 *     // Do anything here. You only need to accept the arguments
 	 *     // if you need them. `action` is the action that satisfied
 	 *     // the predicate.
 	 *   }
 	 * }
 	 * ```
 	 */
@@ -13285,16 +13301,17 @@ return /******/ (function(modules) { // 
 /* 226 */
 /***/ function(module, exports, __webpack_require__) {
 
 	// @flow
 
 	var { PrefsHelper } = __webpack_require__(830);
 	const { Services: { pref } } = __webpack_require__(830);
 	const { isDevelopment } = __webpack_require__(828);
+	const prefsSchemaVersion = "1.0.0";
 
 	if (isDevelopment()) {
 	  pref("devtools.debugger.client-source-maps-enabled", true);
 	  pref("devtools.debugger.pause-on-exceptions", false);
 	  pref("devtools.debugger.ignore-caught-exceptions", false);
 	  pref("devtools.debugger.call-stack-visible", false);
 	  pref("devtools.debugger.scopes-visible", false);
 	  pref("devtools.debugger.start-panel-collapsed", false);
@@ -13302,16 +13319,17 @@ return /******/ (function(modules) { // 
 	  pref("devtools.debugger.tabs", "[]");
 	  pref("devtools.debugger.ui.framework-grouping-on", true);
 	  pref("devtools.debugger.pending-selected-location", "{}");
 	  pref("devtools.debugger.pending-breakpoints", "[]");
 	  pref("devtools.debugger.expressions", "[]");
 	  pref("devtools.debugger.file-search-case-sensitive", true);
 	  pref("devtools.debugger.file-search-whole-word", false);
 	  pref("devtools.debugger.file-search-regex-match", false);
+	  pref("devtools.debugger.prefs-schema-version", "1.0.0");
 	}
 
 	const prefs = new PrefsHelper("devtools", {
 	  clientSourceMapsEnabled: ["Bool", "debugger.client-source-maps-enabled"],
 	  pauseOnExceptions: ["Bool", "debugger.pause-on-exceptions"],
 	  ignoreCaughtExceptions: ["Bool", "debugger.ignore-caught-exceptions"],
 	  callStackVisible: ["Bool", "debugger.call-stack-visible"],
 	  scopesVisible: ["Bool", "debugger.scopes-visible"],
@@ -13319,18 +13337,25 @@ return /******/ (function(modules) { // 
 	  endPanelCollapsed: ["Bool", "debugger.end-panel-collapsed"],
 	  frameworkGroupingOn: ["Bool", "debugger.ui.framework-grouping-on"],
 	  tabs: ["Json", "debugger.tabs"],
 	  pendingSelectedLocation: ["Json", "debugger.pending-selected-location"],
 	  pendingBreakpoints: ["Json", "debugger.pending-breakpoints"],
 	  expressions: ["Json", "debugger.expressions"],
 	  fileSearchCaseSensitive: ["Bool", "debugger.file-search-case-sensitive"],
 	  fileSearchWholeWord: ["Bool", "debugger.file-search-whole-word"],
-	  fileSearchRegexMatch: ["Bool", "debugger.file-search-regex-match"]
-	});
+	  fileSearchRegexMatch: ["Bool", "debugger.file-search-regex-match"],
+	  debuggerPrefsSchemaVersion: ["Char", "debugger.prefs-schema-version"]
+	});
+
+	if (prefs.debuggerPrefsSchemaVersion !== prefsSchemaVersion) {
+	  // clear pending Breakpoints
+	  prefs.pendingBreakpoints = [];
+	  prefs.debuggerPrefsSchemaVersion = prefsSchemaVersion;
+	}
 
 	module.exports = { prefs };
 
 
 /***/ },
 /* 227 */
 /***/ function(module, exports, __webpack_require__) {
 
@@ -13363,53 +13388,52 @@ return /******/ (function(modules) { // 
 	var _pause = __webpack_require__(239);
 
 	var _pause2 = _interopRequireDefault(_pause);
 
 	var _ui = __webpack_require__(240);
 
 	var _ui2 = _interopRequireDefault(_ui);
 
+	var _ast = __webpack_require__(1058);
+
+	var _ast2 = _interopRequireDefault(_ast);
+
 	var _coverage = __webpack_require__(241);
 
 	var _coverage2 = _interopRequireDefault(_coverage);
 
 	function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
 
-	/* This Source Code Form is subject to the terms of the Mozilla Public
-	 * License, v. 2.0. If a copy of the MPL was not distributed with this
-	 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
-
 	exports.default = {
 	  expressions: _expressions2.default,
 	  eventListeners: _eventListeners2.default,
 	  sources: _sources2.default,
 	  breakpoints: _breakpoints2.default,
 	  asyncRequests: _asyncRequests2.default,
 	  pause: _pause2.default,
 	  ui: _ui2.default,
+	  ast: _ast2.default,
 	  coverage: _coverage2.default
-	};
+	}; /* This Source Code Form is subject to the terms of the Mozilla Public
+	    * License, v. 2.0. If a copy of the MPL was not distributed with this
+	    * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 /***/ },
 /* 228 */
 /***/ function(module, exports, __webpack_require__) {
 
 	"use strict";
 
 	Object.defineProperty(exports, "__esModule", {
 	  value: true
 	});
 	exports.getVisibleExpressions = exports.getExpressions = exports.State = undefined;
 	exports.getExpression = getExpression;
 
-	var _constants = __webpack_require__(229);
-
-	var _constants2 = _interopRequireDefault(_constants);
-
 	var _makeRecord = __webpack_require__(230);
 
 	var _makeRecord2 = _interopRequireDefault(_makeRecord);
 
 	var _immutable = __webpack_require__(146);
 
 	var _reselect = __webpack_require__(993);
 
@@ -13421,42 +13445,42 @@ return /******/ (function(modules) { // 
 	  expressions: (0, _immutable.List)(restoreExpressions())
 	});
 
 	function update() {
 	  var state = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : State();
 	  var action = arguments[1];
 
 	  switch (action.type) {
-	    case _constants2.default.ADD_EXPRESSION:
+	    case "ADD_EXPRESSION":
 	      return appendToList(state, ["expressions"], {
 	        input: action.input,
 	        value: null,
 	        updating: true,
 	        visible: action.visible
 	      });
-	    case _constants2.default.UPDATE_EXPRESSION:
+	    case "UPDATE_EXPRESSION":
 	      var key = action.expression.input;
 	      return updateItemInList(state, ["expressions"], key, {
 	        input: action.input,
 	        value: null,
 	        updating: true,
 	        visible: action.visible
 	      });
-	    case _constants2.default.EVALUATE_EXPRESSION:
+	    case "EVALUATE_EXPRESSION":
 	      if (action.status === "done") {
 	        return updateItemInList(state, ["expressions"], action.input, {
 	          input: action.input,
 	          value: action.value,
 	          updating: false,
 	          visible: action.visible
 	        });
 	      }
 	      break;
-	    case _constants2.default.DELETE_EXPRESSION:
+	    case "DELETE_EXPRESSION":
 	      return deleteExpression(state, action.input);
 	  }
 
 	  return state;
 	}
 
 	function restoreExpressions() {
 	  var exprs = _prefs.prefs.expressions;
@@ -13503,87 +13527,17 @@ return /******/ (function(modules) { // 
 
 	function getExpression(state, input) {
 	  return getExpressions(state).find(exp => exp.input == input);
 	}
 
 	exports.default = update;
 
 /***/ },
-/* 229 */
-/***/ function(module, exports) {
-
-	"use strict";
-
-	Object.defineProperty(exports, "__esModule", {
-	  value: true
-	});
-
-
-	/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */
-	/* vim: set ft=javascript ts=2 et sw=2 tw=80: */
-	/* This Source Code Form is subject to the terms of the Mozilla Public
-	 * License, v. 2.0. If a copy of the MPL was not distributed with this
-	 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
-
-	exports.default = {
-	  UPDATE_EVENT_BREAKPOINTS: "UPDATE_EVENT_BREAKPOINTS",
-	  FETCH_EVENT_LISTENERS: "FETCH_EVENT_LISTENERS",
-
-	  TOGGLE_PRETTY_PRINT: "TOGGLE_PRETTY_PRINT",
-	  BLACKBOX: "BLACKBOX",
-
-	  ADD_BREAKPOINT: "ADD_BREAKPOINT",
-	  REMOVE_BREAKPOINT: "REMOVE_BREAKPOINT",
-	  ENABLE_BREAKPOINT: "ENABLE_BREAKPOINT",
-	  DISABLE_BREAKPOINT: "DISABLE_BREAKPOINT",
-	  SET_BREAKPOINT_CONDITION: "SET_BREAKPOINT_CONDITION",
-	  TOGGLE_BREAKPOINTS: "TOGGLE_BREAKPOINTS",
-
-	  ADD_SOURCE: "ADD_SOURCE",
-	  ADD_SOURCES: "ADD_SOURCES",
-	  LOAD_SOURCE_TEXT: "LOAD_SOURCE_TEXT",
-	  SELECT_SOURCE: "SELECT_SOURCE",
-	  SELECT_SOURCE_URL: "SELECT_SOURCE_URL",
-	  HIGHLIGHT_LINES: "HIGHLIGHT_LINES",
-	  CLEAR_HIGHLIGHT_LINES: "CLEAR_HIGHLIGHT_LINES",
-	  CLOSE_TAB: "CLOSE_TAB",
-	  CLOSE_TABS: "CLOSE_TABS",
-	  NAVIGATE: "NAVIGATE",
-	  RELOAD: "RELOAD",
-
-	  ADD_TABS: "ADD_TABS",
-	  SELECT_TAB: "SELECT_TAB",
-
-	  BREAK_ON_NEXT: "BREAK_ON_NEXT",
-	  RESUME: "RESUME",
-	  PAUSED: "PAUSED",
-	  PAUSE_ON_EXCEPTIONS: "PAUSE_ON_EXCEPTIONS",
-	  COMMAND: "COMMAND",
-	  SELECT_FRAME: "SELECT_FRAME",
-	  LOAD_OBJECT_PROPERTIES: "LOAD_OBJECT_PROPERTIES",
-	  ADD_EXPRESSION: "ADD_EXPRESSION",
-	  EVALUATE_EXPRESSION: "EVALUATE_EXPRESSION",
-	  UPDATE_EXPRESSION: "UPDATE_EXPRESSION",
-	  DELETE_EXPRESSION: "DELETE_EXPRESSION",
-
-	  RECORD_COVERAGE: "RECORD_COVERAGE",
-
-	  TOGGLE_PROJECT_SEARCH: "TOGGLE_PROJECT_SEARCH",
-	  TOGGLE_FILE_SEARCH: "TOGGLE_FILE_SEARCH",
-	  TOGGLE_SYMBOL_SEARCH: "TOGGLE_SYMBOL_SEARCH",
-	  TOGGLE_FRAMEWORK_GROUPING: "TOGGLE_FRAMEWORK_GROUPING",
-	  SET_SYMBOL_SEARCH_TYPE: "SET_SYMBOL_SEARCH_TYPE",
-	  UPDATE_FILE_SEARCH_QUERY: "UPDATE_FILE_SEARCH_QUERY",
-	  TOGGLE_FILE_SEARCH_MODIFIER: "TOGGLE_FILE_SEARCH_MODIFIER",
-	  SHOW_SOURCE: "SHOW_SOURCE",
-	  TOGGLE_PANE: "TOGGLE_PANE"
-	};
-
-/***/ },
+/* 229 */,
 /* 230 */
 /***/ function(module, exports, __webpack_require__) {
 
 	"use strict";
 
 	Object.defineProperty(exports, "__esModule", {
 	  value: true
 	});
@@ -13619,58 +13573,53 @@ return /******/ (function(modules) { // 
 	 * This depends on a performance fix that will go out in 0.29 though;
 	 * @module utils/makeRecord
 	 */
 
 	exports.default = makeRecord;
 
 /***/ },
 /* 231 */
-/***/ function(module, exports, __webpack_require__) {
+/***/ function(module, exports) {
 
 	"use strict";
 
 	Object.defineProperty(exports, "__esModule", {
 	  value: true
 	});
 	exports.getEventListeners = getEventListeners;
-
-	var _constants = __webpack_require__(229);
-
-	var _constants2 = _interopRequireDefault(_constants);
-
-	function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
+	/* This Source Code Form is subject to the terms of the Mozilla Public
+	 * License, v. 2.0. If a copy of the MPL was not distributed with this
+	 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 	var initialState = {
 	  activeEventNames: [],
 	  listeners: [],
 	  fetchingListeners: false
-	}; /* This Source Code Form is subject to the terms of the Mozilla Public
-	    * License, v. 2.0. If a copy of the MPL was not distributed with this
-	    * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+	};
 
 	function update() {
 	  var state = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : initialState;
 	  var action = arguments[1];
 	  var emit = arguments[2];
 
 	  switch (action.type) {
-	    case _constants2.default.UPDATE_EVENT_BREAKPOINTS:
+	    case "UPDATE_EVENT_BREAKPOINTS":
 	      state.activeEventNames = action.eventNames;
 	      // emit("activeEventNames", state.activeEventNames);
 	      break;
-	    case _constants2.default.FETCH_EVENT_LISTENERS:
+	    case "FETCH_EVENT_LISTENERS":
 	      if (action.status === "begin") {
 	        state.fetchingListeners = true;
 	      } else if (action.status === "done") {
 	        state.fetchingListeners = false;
 	        state.listeners = action.listeners;
 	      }
 	      break;
-	    case _constants2.default.NAVIGATE:
+	    case "NAVIGATE":
 	      return initialState;
 	  }
 
 	  return state;
 	}
 
 	function getEventListeners(state) {
 	  return state.eventListeners.listeners;
@@ -13682,17 +13631,20 @@ return /******/ (function(modules) { // 
 /* 232 */
 /***/ function(module, exports, __webpack_require__) {
 
 	"use strict";
 
 	Object.defineProperty(exports, "__esModule", {
 	  value: true
 	});
-	exports.getSelectedSource = exports.getSelectedLocation = exports.getSourcesForTabs = exports.getSourceTabs = exports.getSources = exports.State = undefined;
+	exports.getSelectedSourceText = exports.getSelectedSource = exports.getSelectedLocation = exports.getSourcesForTabs = exports.getSourceTabs = exports.getSources = exports.State = undefined;
+	exports.removeSourceFromTabList = removeSourceFromTabList;
+	exports.removeSourcesFromTabList = removeSourcesFromTabList;
+	exports.getNewSelectedSourceId = getNewSelectedSourceId;
 	exports.getSource = getSource;
 	exports.getSourceByURL = getSourceByURL;
 	exports.getSourceText = getSourceText;
 	exports.getPendingSelectedLocation = getPendingSelectedLocation;
 	exports.getPrettySource = getPrettySource;
 	exports.getSourceInSources = getSourceInSources;
 
 	var _immutable = __webpack_require__(146);
@@ -13728,129 +13680,136 @@ return /******/ (function(modules) { // 
 	 * Sources reducer
 	 * @module reducers/sources
 	 */
 
 	function update() {
 	  var state = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : State();
 	  var action = arguments[1];
 
-	  var availableTabs = null;
 	  var location = null;
 
 	  switch (action.type) {
 	    case "ADD_SOURCE":
 	      {
 	        var _source = action.source;
-	        return state.mergeIn(["sources", action.source.id], _source);
+	        return updateSource(state, _source);
 	      }
 
 	    case "ADD_SOURCES":
 	      {
 	        action.sources.forEach(source => {
 	          state = state.mergeIn(["sources", source.id], source);
 	        });
 
 	        return state;
 	      }
 
 	    case "SELECT_SOURCE":
+	      if (action.status != "start") {
+	        return state;
+	      }
+
 	      location = {
 	        line: action.line,
 	        url: action.source.url
 	      };
+
 	      _prefs.prefs.pendingSelectedLocation = location;
 
-	      var sourceUrl = action.source.url || "";
 	      return state.set("selectedLocation", {
 	        sourceId: action.source.id,
 	        line: action.line
 	      }).set("pendingSelectedLocation", location).merge({
-	        tabs: updateTabList({ sources: state }, sourceUrl, action.tabIndex)
-	      });
+	        tabs: updateTabList({ sources: state }, action.source.url, action.tabIndex)
+	      });
+
+	    case "CLEAR_SELECTED_SOURCE":
+	      location = { url: "" };
+	      _prefs.prefs.pendingSelectedLocation = location;
+
+	      return state.set("selectedLocation", { sourceId: "" }).set("pendingSelectedLocation", location);
 
 	    case "SELECT_SOURCE_URL":
 	      location = {
 	        url: action.url,
 	        line: action.line
 	      };
 
 	      _prefs.prefs.pendingSelectedLocation = location;
 	      return state.set("pendingSelectedLocation", location);
 
 	    case "CLOSE_TAB":
-	      availableTabs = removeSourceFromTabList(state.tabs, action.url);
-
-	      return state.merge({ tabs: availableTabs }).set("selectedLocation", {
-	        sourceId: getNewSelectedSourceId(state, availableTabs)
-	      });
+	      _prefs.prefs.tabs = action.tabs;
+	      return state.merge({ tabs: action.tabs });
 
 	    case "CLOSE_TABS":
-	      availableTabs = removeSourcesFromTabList(state.tabs, action.urls);
-
-	      return state.merge({ tabs: availableTabs }).set("selectedLocation", {
-	        sourceId: getNewSelectedSourceId(state, availableTabs)
-	      });
+	      _prefs.prefs.tabs = action.tabs;
+	      return state.merge({ tabs: action.tabs });
 
 	    case "LOAD_SOURCE_TEXT":
-	      return _updateText(state, action);
+	      return setSourceTextProps(state, action);
 
 	    case "BLACKBOX":
 	      if (action.status === "done") {
 	        return state.setIn(["sources", action.source.id, "isBlackBoxed"], action.value.isBlackBoxed);
 	      }
 	      break;
 
 	    case "TOGGLE_PRETTY_PRINT":
-	      return _updateText(state, action);
+	      return setSourceTextProps(state, action);
 
 	    case "NAVIGATE":
 	      var source = getSelectedSource({ sources: state });
 	      var _url = source && source.get("url");
 	      _prefs.prefs.pendingSelectedLocation = { url: _url };
 	      return State().set("pendingSelectedLocation", { url: _url });
 	  }
 
 	  return state;
 	}
 
-	// TODO: Action is coerced to `any` unfortunately because how we type
-	// asynchronous actions is wrong. The `value` may be null for the
-	// "start" and "error" states but we don't type it like that. We need
-	// to rethink how we type async actions.
-	function _updateText(state, action) {
+	function getTextPropsFromAction(action) {
 	  var source = action.source;
 	  var sourceText = action.value;
 
 	  if (action.status === "start") {
-	    // Merge this in, don't set it. That way the previous value is
-	    // still stored here, and we can retrieve it if whatever we're
-	    // doing fails.
-	    return state.mergeIn(["sourcesText", source.id], {
-	      loading: true
-	    });
-	  }
-
-	  if (action.status === "error") {
-	    return state.setIn(["sourcesText", source.id], I.Map({
-	      error: action.error
-	    }));
-	  }
-
-	  return state.setIn(["sourcesText", source.id], I.Map({
+	    return { loading: true };
+	  } else if (action.status === "error") {
+	    return { error: action.error, loading: false };
+	  }
+	  return {
 	    text: sourceText.text,
 	    id: source.id,
-	    contentType: sourceText.contentType
-	  }));
+	    contentType: sourceText.contentType,
+	    loading: false
+	  };
+	}
+
+	// TODO: Action is coerced to `any` unfortunately because how we type
+	// asynchronous actions is wrong. The `value` may be null for the
+	// "start" and "error" states but we don't type it like that. We need
+	// to rethink how we type async actions.
+	function setSourceTextProps(state, action) {
+	  var source = action.source;
+	  var text = getTextPropsFromAction(action);
+	  var updatedState = state.setIn(["sourcesText", source.id], I.Map(text));
+	  return updateSource(updatedState, text);
+	}
+
+	function updateSource(state, source) {
+	  if (!source.id) {
+	    return state;
+	  }
+
+	  return state.mergeIn(["sources", source.id], source);
 	}
 
 	function removeSourceFromTabList(tabs, url) {
-	  var newTabs = tabs.filter(tab => tab != url);
-	  _prefs.prefs.tabs = newTabs;
-	  return newTabs;
+	  return tabs.filter(tab => tab != url);
 	}
 
 	function removeSourcesFromTabList(tabs, urls) {
 	  return urls.reduce((t, url) => removeSourceFromTabList(t, url), tabs);
 	}
 
 	function restoreTabs() {
 	  var prefsTabs = _prefs.prefs.tabs || [];
@@ -13889,46 +13848,46 @@ return /******/ (function(modules) { // 
 	 * 1. if the selected tab is available, it remains selected
 	 * 2. if it is gone, the next available tab to the left should be active
 	 * 3. if the first tab is active and closed, select the second tab
 	 *
 	 * @memberof reducers/sources
 	 * @static
 	 */
 	function getNewSelectedSourceId(state, availableTabs) {
-	  var selectedLocation = state.selectedLocation;
+	  var selectedLocation = state.sources.selectedLocation;
 	  if (!selectedLocation) {
 	    return "";
 	  }
 
-	  var selectedTab = state.sources.get(selectedLocation.sourceId);
+	  var selectedTab = state.sources.sources.get(selectedLocation.sourceId);
 
 	  var selectedTabUrl = selectedTab ? selectedTab.get("url") : "";
 
 	  if (availableTabs.includes(selectedTabUrl)) {
-	    var _sources = state.sources;
+	    var _sources = state.sources.sources;
 	    if (!_sources) {
 	      return "";
 	    }
 
 	    var selectedSource = _sources.find(source => source.get("url") == selectedTabUrl);
 
 	    if (selectedSource) {
 	      return selectedSource.get("id");
 	    }
 
 	    return "";
 	  }
 
-	  var tabUrls = state.tabs.toJS();
+	  var tabUrls = state.sources.tabs.toJS();
 	  var leftNeighborIndex = Math.max(tabUrls.indexOf(selectedTabUrl) - 1, 0);
 	  var lastAvailbleTabIndex = availableTabs.size - 1;
 	  var newSelectedTabIndex = Math.min(leftNeighborIndex, lastAvailbleTabIndex);
 	  var availableTab = availableTabs.toJS()[newSelectedTabIndex];
-	  var tabSource = getSourceByUrlInSources(state.sources, availableTab);
+	  var tabSource = getSourceByUrlInSources(state.sources.sources, availableTab);
 
 	  if (tabSource) {
 	    return tabSource.get("id");
 	  }
 
 	  return "";
 	}
 
@@ -13995,16 +13954,21 @@ return /******/ (function(modules) { // 
 	var getSelectedSource = exports.getSelectedSource = (0, _reselect.createSelector)(getSelectedLocation, getSources, (selectedLocation, sources) => {
 	  if (!selectedLocation) {
 	    return;
 	  }
 
 	  return sources.get(selectedLocation.sourceId);
 	});
 
+	var getSelectedSourceText = exports.getSelectedSourceText = (0, _reselect.createSelector)(getSelectedSource, getSourcesState, (selectedSource, sources) => {
+	  var id = selectedSource.get("id");
+	  return id ? sources.sourcesText.get(id) : null;
+	});
+
 	exports.default = update;
 
 /***/ },
 /* 233 */
 /***/ function(module, exports, __webpack_require__) {
 
 	"use strict";
 
@@ -14223,20 +14187,16 @@ return /******/ (function(modules) { // 
 	  }
 	  return str;
 	}
 
 	/**
 	 * @memberof utils/utils
 	 * @static
 	 */
-	function updateObj(obj, fields) {
-	  return Object.assign({}, obj, fields);
-	}
-
 	/**
 	 * @memberof utils/utils
 	 * @static
 	 */
 	function throttle(func, ms) {
 	  var timeout = void 0,
 	      _this = void 0;
 	  return function () {
@@ -14256,17 +14216,16 @@ return /******/ (function(modules) { // 
 
 	function waitForMs(ms) {
 	  return new Promise(resolve => setTimeout(resolve, ms));
 	}
 
 	exports.handleError = handleError;
 	exports.promisify = promisify;
 	exports.endTruncateStr = endTruncateStr;
-	exports.updateObj = updateObj;
 	exports.throttle = throttle;
 	exports.waitForMs = waitForMs;
 
 /***/ },
 /* 235 */
 /***/ function(module, exports) {
 
 	"use strict";
@@ -14305,293 +14264,361 @@ return /******/ (function(modules) { // 
 /* 236 */
 /***/ function(module, exports, __webpack_require__) {
 
 	"use strict";
 
 	Object.defineProperty(exports, "__esModule", {
 	  value: true
 	});
-	exports.State = undefined;
-	exports.makeLocationId = makeLocationId;
-	exports.makePendingLocationId = makePendingLocationId;
+
+	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; };
+	/* This Source Code Form is subject to the terms of the Mozilla Public
+	 * License, v. 2.0. If a copy of the MPL was not distributed with this
+	 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+	/**
+	 * Breakpoints reducer
+	 * @module reducers/breakpoints
+	 */
+
+	exports.initialState = initialState;
 	exports.makePendingBreakpoint = makePendingBreakpoint;
 	exports.getBreakpoint = getBreakpoint;
 	exports.getBreakpoints = getBreakpoints;
 	exports.getBreakpointsForSource = getBreakpointsForSource;
 	exports.getBreakpointsDisabled = getBreakpointsDisabled;
 	exports.getBreakpointsLoading = getBreakpointsLoading;
 	exports.getPendingBreakpoints = getPendingBreakpoints;
 
 	var _fromJS = __webpack_require__(237);
 
 	var _fromJS2 = _interopRequireDefault(_fromJS);
 
-	var _utils = __webpack_require__(234);
-
 	var _immutable = __webpack_require__(146);
 
 	var I = _interopRequireWildcard(_immutable);
 
 	var _makeRecord = __webpack_require__(230);
 
 	var _makeRecord2 = _interopRequireDefault(_makeRecord);
 
 	var _prefs = __webpack_require__(226);
 
+	var _breakpoint = __webpack_require__(1057);
+
 	function _interopRequireWildcard(obj) { if (obj && obj.__esModule) { return obj; } else { var newObj = {}; if (obj != null) { for (var key in obj) { if (Object.prototype.hasOwnProperty.call(obj, key)) newObj[key] = obj[key]; } } newObj.default = obj; return newObj; } }
 
 	function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
 
-	var State = exports.State = (0, _makeRecord2.default)({
-	  breakpoints: I.Map(),
-	  pendingBreakpoints: restorePendingBreakpoints(),
-	  breakpointsDisabled: false
-	});
-
-	// Return the first argument that is a string, or null if nothing is a
-	// string.
-
-	/* This Source Code Form is subject to the terms of the Mozilla Public
-	 * License, v. 2.0. If a copy of the MPL was not distributed with this
-	 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
-
-	/**
-	 * Breakpoints reducer
-	 * @module reducers/breakpoints
-	 */
-
-	function firstString() {
-	  for (var _len = arguments.length, args = Array(_len), _key = 0; _key < _len; _key++) {
-	    args[_key] = arguments[_key];
-	  }
-
-	  for (var arg of args) {
-	    if (typeof arg === "string") {
-	      return arg;
-	    }
-	  }
-	  return null;
-	}
-
-	function locationMoved(location, newLocation) {
-	  return location.line !== newLocation.line || location.column != null && location.column !== newLocation.column;
-	}
-
-	function makeLocationId(location) {
-	  var sourceId = location.sourceId,
-	      line = location.line,
-	      column = location.column;
-
-	  var columnString = column || "";
-	  return `${sourceId}:${line}:${columnString}`;
-	}
-
-	function makePendingLocationId(location) {
-	  var sourceUrl = location.sourceUrl,
-	      line = location.line,
-	      column = location.column;
-
-	  var sourceUrlString = sourceUrl || "";
-	  var columnString = column || "";
-	  return `${sourceUrlString}:${line}:${columnString}`;
-	}
-
-	function allBreakpointsDisabled(state) {
-	  return state.breakpoints.every(x => x.disabled);
+	function initialState() {
+	  return (0, _makeRecord2.default)({
+	    breakpoints: I.Map(),
+	    pendingBreakpoints: restorePendingBreakpoints(),
+	    breakpointsDisabled: false
+	  })();
 	}
 
 	function update() {
-	  var state = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : State();
+	  var state = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : initialState();
 	  var action = arguments[1];
 
 	  switch (action.type) {
 	    case "ADD_BREAKPOINT":
 	      {
 	        var newState = addBreakpoint(state, action);
 	        setPendingBreakpoints(newState);
 	        return newState;
 	      }
 
+	    case "SYNC_BREAKPOINT":
+	      {
+	        var _newState = syncBreakpoint(state, action);
+	        setPendingBreakpoints(_newState);
+	        return _newState;
+	      }
+
+	    case "ENABLE_BREAKPOINT":
+	      {
+	        var _newState2 = enableBreakpoint(state, action);
+	        setPendingBreakpoints(_newState2);
+	        return _newState2;
+	      }
+
 	    case "REMOVE_BREAKPOINT":
 	      {
-	        var _newState = removeOrDisableBreakpoint(state, action);
-	        setPendingBreakpoints(_newState);
-	        return _newState;
+	        var _newState3 = removeBreakpoint(state, action);
+	        setPendingBreakpoints(_newState3);
+	        return _newState3;
+	      }
+
+	    case "DISABLE_BREAKPOINT":
+	      {
+	        var _newState4 = disableBreakpoint(state, action);
+	        setPendingBreakpoints(_newState4);
+	        return _newState4;
 	      }
 
 	    case "TOGGLE_BREAKPOINTS":
 	      {
 	        if (action.status === "start") {
 	          return state.set("breakpointsDisabled", action.shouldDisableBreakpoints);
 	        }
 	        break;
 	      }
 
 	    case "SET_BREAKPOINT_CONDITION":
 	      {
-	        var _newState2 = setCondition(state, action);
-	        setPendingBreakpoints(_newState2);
-	        return _newState2;
+	        var _newState5 = setCondition(state, action);
+	        setPendingBreakpoints(_newState5);
+	        return _newState5;
 	      }
 	  }
 
 	  return state;
 	}
 
 	function addBreakpoint(state, action) {
-	  var id = makeLocationId(action.breakpoint.location);
+	  var id = (0, _breakpoint.makeLocationId)(action.breakpoint.location);
 	  if (action.status === "start") {
-	    var bp = state.breakpoints.get(id) || action.breakpoint;
-
-	    var updatedState = state.setIn(["breakpoints", id], (0, _utils.updateObj)(bp, {
-	      disabled: false,
+	    var updatedState = state.setIn(["breakpoints", id], _extends({}, action.breakpoint, {
 	      loading: true,
-	      // We want to do an OR here, but we can't because we need
-	      // empty strings to be truthy, i.e. an empty string is a valid
-	      // condition.
-	      condition: firstString(action.condition, bp.condition)
+	      condition: (0, _breakpoint.firstString)(action.condition, action.breakpoint.condition)
 	    })).set("breakpointsDisabled", false);
 
 	    return updatedState;
 	  }
 
 	  if (action.status === "done") {
 	    var _action$value = action.value,
 	        breakpointId = _action$value.id,
-	        text = _action$value.text;
+	        actualLocation = _action$value.actualLocation,
+	        generatedLocation = _action$value.generatedLocation;
 
 	    var location = action.breakpoint.location;
-	    var actualLocation = action.value.actualLocation;
 
 	    // If the breakpoint moved, update the map
-
-	    if (locationMoved(location, actualLocation)) {
+	    if ((0, _breakpoint.locationMoved)(location, actualLocation)) {
 	      state = slideBreakpoint(state, action);
 	      location = actualLocation;
 	    }
 
-	    var locationId = makeLocationId(location);
-	    var _bp = state.breakpoints.get(locationId);
-	    var _updatedState = state.setIn(["breakpoints", locationId], (0, _utils.updateObj)(_bp, {
+	    var locationId = (0, _breakpoint.makeLocationId)(location);
+	    var bp = state.breakpoints.get(locationId) || action.breakpoint;
+	    var updatedBreakpoint = _extends({}, bp, {
 	      id: breakpointId,
-	      disabled: false,
 	      loading: false,
-	      text: text
-	    }));
-
-	    return updatePendingBreakpoint(_updatedState, _bp);
+	      generatedLocation,
+	      text: ""
+	    });
+	    var _updatedState = state.setIn(["breakpoints", locationId], updatedBreakpoint);
+
+	    return updatePendingBreakpoint(_updatedState, updatedBreakpoint);
 	  }
 
 	  if (action.status === "error") {
 	    // Remove the optimistic update
 	    return state.deleteIn(["breakpoints", id]);
 	  }
 	}
 
-	function disableBreakpoint(state, id) {
+	function syncBreakpoint(state, action) {
+	  if (action.status === "start") {
+	    // add a breakpoint, so we always have something to work with
+	    return optimisticlyAddBreakpoint(state, action.breakpoint);
+	  }
+	  if (action.status === "done") {
+	    // when the action completes, we can commit the breakpoint
+	    var breakpoint = action.breakpoint,
+	        _action$value2 = action.value,
+	        actualLocation = _action$value2.actualLocation,
+	        generatedLocation = _action$value2.generatedLocation;
+
+	    var sameLocation = !(0, _breakpoint.locationMoved)(breakpoint.location, actualLocation);
+
+	    // if the breakpoint is the same as the optimistic breakpoint, we can commit
+	    // to the optimistic value.
+	    if (sameLocation) {
+	      return commitBreakpoint(state, breakpoint, action.value);
+	    }
+
+	    // if the breakpoint is not the same, we will use the actual location sent
+	    // by the server, and correct the breakpoint with that new information.
+	    // Correcting a breakpoint deletes both the pending breakpoint and the
+	    // optimistic breakpoint. Correcting will commit the corrected value
+	    var overrides = { location: actualLocation, generatedLocation };
+	    var updatedState = correctBreakpoint(state, breakpoint, overrides);
+	    var id = (0, _breakpoint.makeLocationId)(actualLocation);
+
+	    // once the corrected breakpoint is added and commited, we can update the
+	    // pending breakpoints with that information.
+	    var correctedBreakpoint = updatedState.breakpoints.get(id);
+	    return updatePendingBreakpoint(updatedState, correctedBreakpoint);
+	  }
+
+	  if (action.status === "error") {
+	    // Remove the optimistic update and pending breakpoint
+	    return deleteBreakpoint(state, action.breakpoint.location);
+	  }
+	}
+
+	function enableBreakpoint(state, action) {
+	  if (action.status != "done") {
+	    return state;
+	  }
+
+	  var id = (0, _breakpoint.makeLocationId)(action.breakpoint.location);
 	  var bp = state.breakpoints.get(id);
-	  var breakpoint = (0, _utils.updateObj)(bp, {
+	  var updatedBreakpoint = _extends({}, bp, {
+	    id: action.value.id,
+	    loading: false,
+	    disabled: false
+	  });
+	  var updatedState = state.setIn(["breakpoints", id], updatedBreakpoint);
+	  return updatePendingBreakpoint(updatedState, updatedBreakpoint);
+	}
+
+	function disableBreakpoint(state, action) {
+	  if (action.status != "done") {
+	    return state;
+	  }
+	  var id = (0, _breakpoint.makeLocationId)(action.breakpoint.location);
+	  var bp = state.breakpoints.get(id);
+	  var breakpoint = _extends({}, bp, {
 	    loading: false,
 	    disabled: true
 	  });
 	  var updatedState = state.setIn(["breakpoints", id], breakpoint);
 	  return updatePendingBreakpoint(updatedState, breakpoint);
 	}
 
-	function deleteBreakpoint(state, id, pendingId) {
+	function deleteBreakpoint(state, location) {
+	  var id = (0, _breakpoint.makeLocationId)(location);
+	  var pendingId = (0, _breakpoint.makePendingLocationId)(location);
+
 	  return state.deleteIn(["breakpoints", id]).deleteIn(["pendingBreakpoints", pendingId]);
 	}
 
-	function removeOrDisableBreakpoint(state, action) {
+	function removeBreakpoint(state, action) {
 	  if (action.status != "done") {
 	    return state;
 	  }
 
-	  var id = makeLocationId(action.breakpoint.location);
-	  var pendingId = makePendingLocationId(action.breakpoint.location);
-
-	  var updatedState = action.disabled ? disableBreakpoint(state, id) : deleteBreakpoint(state, id, pendingId);
-
-	  return updatedState.set("breakpointsDisabled", allBreakpointsDisabled(updatedState));
+	  var updatedState = deleteBreakpoint(state, action.breakpoint.location);
+
+	  return updatedState.set("breakpointsDisabled", (0, _breakpoint.allBreakpointsDisabled)(updatedState));
 	}
 
 	function setCondition(state, action) {
-	  var id = makeLocationId(action.breakpoint.location);
+	  var id = (0, _breakpoint.makeLocationId)(action.breakpoint.location);
 
 	  if (action.status === "start") {
 	    var bp = state.breakpoints.get(id);
-	    return state.setIn(["breakpoints", id], (0, _utils.updateObj)(bp, {
+	    return state.setIn(["breakpoints", id], _extends({}, bp, {
 	      loading: true,
 	      condition: action.condition
 	    }));
 	  }
 
 	  if (action.status === "done") {
-	    var _bp2 = state.breakpoints.get(id);
-	    var updatedBreakpoint = (0, _utils.updateObj)(_bp2, {
+	    var _bp = state.breakpoints.get(id);
+	    var updatedBreakpoint = _extends({}, _bp, {
 	      id: action.value.id,
 	      loading: false
 	    });
-
 	    var updatedState = state.setIn(["breakpoints", id], updatedBreakpoint);
 
 	    return updatePendingBreakpoint(updatedState, updatedBreakpoint);
 	  }
 
 	  if (action.status === "error") {
 	    return state.deleteIn(["breakpoints", id]);
 	  }
 	}
 
+	// Syncing Methods
+	function optimisticlyAddBreakpoint(state, breakpoint) {
+	  var id = (0, _breakpoint.makeLocationId)(breakpoint.location);
+	  var updateOpts = {
+	    loading: true
+	  };
+
+	  return state.setIn(["breakpoints", id], _extends({}, breakpoint, updateOpts));
+	}
+
+	function commitBreakpoint(state, breakpoint) {
+	  var overrides = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : {};
+
+	  // A commited breakpoint is no longer loading, and acts like a normal
+	  // breakpoint
+	  var location = overrides.location || breakpoint.location;
+	  var id = (0, _breakpoint.makeLocationId)(location);
+	  var updatedOpts = _extends({}, overrides, { loading: false });
+	  var updatedBreakpoint = _extends({}, breakpoint, updatedOpts);
+
+	  return state.setIn(["breakpoints", id], updatedBreakpoint);
+	}
+
+	function correctBreakpoint(state, breakpoint, overrides) {
+	  var intermState = deleteBreakpoint(state, breakpoint.location);
+	  var newLocationId = (0, _breakpoint.makeLocationId)(overrides.location);
+	  var newProperties = _extends({ id: newLocationId }, overrides);
+
+	  return commitBreakpoint(intermState, breakpoint, newProperties);
+	}
+
+	// TODO: remove this in favor of the correct/commit breakpoint pattern
 	function slideBreakpoint(state, action) {
-	  var _action$value2 = action.value,
-	      actualLocation = _action$value2.actualLocation,
-	      id = _action$value2.id;
+	  var _action$value3 = action.value,
+	      actualLocation = _action$value3.actualLocation,
+	      id = _action$value3.id;
 	  var breakpoint = action.breakpoint;
 
 	  var currentBp = state.breakpoints.get(id) || (0, _fromJS2.default)(breakpoint);
 
-	  var locationId = makeLocationId(breakpoint.location);
-	  var movedLocationId = makeLocationId(actualLocation);
+	  var locationId = (0, _breakpoint.makeLocationId)(breakpoint.location);
+	  var movedLocationId = (0, _breakpoint.makeLocationId)(actualLocation);
 	  var updatedState = state.deleteIn(["breakpoints", locationId]);
 
-	  return updatedState.setIn(["breakpoints", movedLocationId], (0, _utils.updateObj)(currentBp, { location: actualLocation }));
+	  return updatedState.setIn(["breakpoints", movedLocationId], _extends({}, currentBp, {
+	    location: actualLocation
+	  }));
 	}
 
 	function makePendingBreakpoint(bp) {
 	  var _bp$location = bp.location,
 	      sourceUrl = _bp$location.sourceUrl,
 	      line = _bp$location.line,
 	      column = _bp$location.column,
 	      condition = bp.condition,
-	      disabled = bp.disabled;
+	      disabled = bp.disabled,
+	      generatedLocation = bp.generatedLocation;
 
 
 	  var location = { sourceUrl, line, column };
-	  return { condition, disabled, location };
+	  return { condition, disabled, generatedLocation, location };
 	}
 
 	function setPendingBreakpoints(state) {
 	  _prefs.prefs.pendingBreakpoints = state.pendingBreakpoints;
 	}
 
 	function updatePendingBreakpoint(state, breakpoint) {
-	  var id = makePendingLocationId(breakpoint.location);
+	  var id = (0, _breakpoint.makePendingLocationId)(breakpoint.location);
 	  return state.setIn(["pendingBreakpoints", id], makePendingBreakpoint(breakpoint));
 	}
 
 	function restorePendingBreakpoints() {
 	  return I.Map(_prefs.prefs.pendingBreakpoints);
 	}
 
 	// Selectors
 
 	function getBreakpoint(state, location) {
-	  return state.breakpoints.breakpoints.get(makeLocationId(location));
+	  return state.breakpoints.breakpoints.get((0, _breakpoint.makeLocationId)(location));
 	}
 
 	function getBreakpoints(state) {
 	  return state.breakpoints.breakpoints;
 	}
 
 	function getBreakpointsForSource(state, sourceId) {
 	  return state.breakpoints.breakpoints.filter(bp => {
@@ -14724,43 +14751,39 @@ return /******/ (function(modules) { // 
 
 	  return createMap(value);
 	}
 
 	module.exports = fromJS;
 
 /***/ },
 /* 238 */
-/***/ function(module, exports, __webpack_require__) {
-
-	"use strict";
-
-	Object.defineProperty(exports, "__esModule", {
-	  value: true
-	});
-
-	var _constants = __webpack_require__(229);
-
-	var _constants2 = _interopRequireDefault(_constants);
-
-	function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
-
-	function _toConsumableArray(arr) { if (Array.isArray(arr)) { for (var i = 0, arr2 = Array(arr.length); i < arr.length; i++) { arr2[i] = arr[i]; } return arr2; } else { return Array.from(arr); } } /* This Source Code Form is subject to the terms of the Mozilla Public
-	                                                                                                                                                                                                     * License, v. 2.0. If a copy of the MPL was not distributed with this
-	                                                                                                                                                                                                     * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+/***/ function(module, exports) {
+
+	"use strict";
+
+	Object.defineProperty(exports, "__esModule", {
+	  value: true
+	});
+
+	function _toConsumableArray(arr) { if (Array.isArray(arr)) { for (var i = 0, arr2 = Array(arr.length); i < arr.length; i++) { arr2[i] = arr[i]; } return arr2; } else { return Array.from(arr); } }
+
+	/* This Source Code Form is subject to the terms of the Mozilla Public
+	 * License, v. 2.0. If a copy of the MPL was not distributed with this
+	 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 	var initialState = [];
 
 	function update() {
 	  var state = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : initialState;
 	  var action = arguments[1];
 	  var seqId = action.seqId;
 
 
-	  if (action.type === _constants2.default.NAVIGATE) {
+	  if (action.type === "NAVIGATE") {
 	    return initialState;
 	  } else if (seqId) {
 	    var newState = void 0;
 	    if (action.status === "start") {
 	      newState = [].concat(_toConsumableArray(state), [seqId]);
 	    } else if (action.status === "error" || action.status === "done") {
 	      newState = state.filter(id => id !== seqId);
 	    }
@@ -14797,39 +14820,33 @@ return /******/ (function(modules) { // 
 	exports.getFrames = getFrames;
 	exports.getDebuggeeUrl = getDebuggeeUrl;
 	exports.getChromeScopes = getChromeScopes;
 
 	var _reselect = __webpack_require__(993);
 
 	var _prefs = __webpack_require__(226);
 
-	var _constants = __webpack_require__(229);
-
-	var _constants2 = _interopRequireDefault(_constants);
-
-	function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
-
 	var State = exports.State = () => ({
 	  pause: undefined,
 	  isWaitingOnBreak: false,
 	  frames: undefined,
 	  selectedFrameId: undefined,
 	  loadedObjects: {},
 	  shouldPauseOnExceptions: _prefs.prefs.pauseOnExceptions,
 	  shouldIgnoreCaughtExceptions: _prefs.prefs.ignoreCaughtExceptions,
 	  debuggeeUrl: ""
 	});
 
 	function update() {
 	  var state = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : State();
 	  var action = arguments[1];
 
 	  switch (action.type) {
-	    case _constants2.default.PAUSED:
+	    case "PAUSED":
 	      {
 	        var _selectedFrameId = action.selectedFrameId,
 	            _frames = action.frames,
 	            _loadedObjects = action.loadedObjects,
 	            pauseInfo = action.pauseInfo;
 
 	        pauseInfo.isInterrupted = pauseInfo.why.type === "interrupted";
 
@@ -14843,43 +14860,43 @@ return /******/ (function(modules) { // 
 	          isWaitingOnBreak: false,
 	          pause: pauseInfo,
 	          selectedFrameId: _selectedFrameId,
 	          frames: _frames,
 	          loadedObjects: objectMap
 	        });
 	      }
 
-	    case _constants2.default.RESUME:
+	    case "RESUME":
 	      return Object.assign({}, state, {
 	        pause: null,
 	        frames: null,
 	        selectedFrameId: null,
 	        loadedObjects: {}
 	      });
 
-	    case _constants2.default.TOGGLE_PRETTY_PRINT:
+	    case "TOGGLE_PRETTY_PRINT":
 	      if (action.status == "done") {
 	        var _frames2 = action.value.frames;
 	        var _pause = state.pause;
 	        if (_pause) {
 	          _pause.frame = _frames2[0];
 	        }
 
 	        return Object.assign({}, state, { pause: _pause, frames: _frames2 });
 	      }
 
 	      break;
-	    case _constants2.default.BREAK_ON_NEXT:
+	    case "BREAK_ON_NEXT":
 	      return Object.assign({}, state, { isWaitingOnBreak: true });
 
-	    case _constants2.default.SELECT_FRAME:
+	    case "SELECT_FRAME":
 	      return Object.assign({}, state, { selectedFrameId: action.frame.id });
 
-	    case _constants2.default.LOAD_OBJECT_PROPERTIES:
+	    case "LOAD_OBJECT_PROPERTIES":
 	      if (action.status === "start") {
 	        return _extends({}, state, {
 	          loadedObjects: _extends({}, state.loadedObjects, {
 	            [action.objectId]: {}
 	          })
 	        });
 	      }
 
@@ -14895,20 +14912,20 @@ return /******/ (function(modules) { // 
 	        return _extends({}, state, {
 	          loadedObjects: _extends({}, state.loadedObjects, {
 	            [action.objectId]: { ownProperties, prototype, ownSymbols }
 	          })
 	        });
 	      }
 	      break;
 
-	    case _constants2.default.NAVIGATE:
+	    case "NAVIGATE":
 	      return Object.assign({}, State(), { debuggeeUrl: action.url });
 
-	    case _constants2.default.PAUSE_ON_EXCEPTIONS:
+	    case "PAUSE_ON_EXCEPTIONS":
 	      var _shouldPauseOnExceptions = action.shouldPauseOnExceptions,
 	          _shouldIgnoreCaughtExceptions = action.shouldIgnoreCaughtExceptions;
 
 
 	      _prefs.prefs.pauseOnExceptions = _shouldPauseOnExceptions;
 	      _prefs.prefs.ignoreCaughtExceptions = _shouldIgnoreCaughtExceptions;
 
 	      return Object.assign({}, state, {
@@ -15003,21 +15020,22 @@ return /******/ (function(modules) { // 
 	exports.getHighlightedLineRange = getHighlightedLineRange;
 
 	var _makeRecord = __webpack_require__(230);
 
 	var _makeRecord2 = _interopRequireDefault(_makeRecord);
 
 	var _prefs = __webpack_require__(226);
 
-	var _constants = __webpack_require__(229);
-
-	var _constants2 = _interopRequireDefault(_constants);
-
-	function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
+	function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
+
+	/**
+	 * UI reducer
+	 * @module reducers/ui
+	 */
 
 	var State = exports.State = (0, _makeRecord2.default)({
 	  fileSearchOn: false,
 	  fileSearchQuery: "",
 	  fileSearchModifiers: (0, _makeRecord2.default)({
 	    caseSensitive: _prefs.prefs.fileSearchCaseSensitive,
 	    wholeWord: _prefs.prefs.fileSearchWholeWord,
 	    regexMatch: _prefs.prefs.fileSearchRegexMatch
@@ -15027,53 +15045,48 @@ return /******/ (function(modules) { // 
 	  symbolSearchType: "functions",
 	  shownSource: "",
 	  startPanelCollapsed: _prefs.prefs.startPanelCollapsed,
 	  endPanelCollapsed: _prefs.prefs.endPanelCollapsed,
 	  frameworkGroupingOn: _prefs.prefs.frameworkGroupingOn,
 	  highlightedLineRange: undefined
 	});
 
-	/**
-	 * UI reducer
-	 * @module reducers/ui
-	 */
-
 	function update() {
 	  var state = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : State();
 	  var action = arguments[1];
 
 	  switch (action.type) {
-	    case _constants2.default.TOGGLE_PROJECT_SEARCH:
+	    case "TOGGLE_PROJECT_SEARCH":
 	      {
 	        return state.set("projectSearchOn", action.value);
 	      }
 
-	    case _constants2.default.TOGGLE_FRAMEWORK_GROUPING:
+	    case "TOGGLE_FRAMEWORK_GROUPING":
 	      {
 	        _prefs.prefs.frameworkGroupingOn = action.value;
 	        return state.set("frameworkGroupingOn", action.value);
 	      }
 
-	    case _constants2.default.TOGGLE_FILE_SEARCH:
+	    case "TOGGLE_FILE_SEARCH":
 	      {
 	        return state.set("fileSearchOn", action.value);
 	      }
 
-	    case _constants2.default.TOGGLE_SYMBOL_SEARCH:
+	    case "TOGGLE_SYMBOL_SEARCH":
 	      {
 	        return state.set("symbolSearchOn", action.value);
 	      }
 
-	    case _constants2.default.UPDATE_FILE_SEARCH_QUERY:
+	    case "UPDATE_FILE_SEARCH_QUERY":
 	      {
 	        return state.set("fileSearchQuery", action.query);
 	      }
 
-	    case _constants2.default.TOGGLE_FILE_SEARCH_MODIFIER:
+	    case "TOGGLE_FILE_SEARCH_MODIFIER":
 	      {
 	        var actionVal = !state.getIn(["fileSearchModifiers", action.modifier]);
 
 	        if (action.modifier == "caseSensitive") {
 	          _prefs.prefs.fileSearchCaseSensitive = actionVal;
 	        }
 
 	        if (action.modifier == "wholeWord") {
@@ -15082,27 +15095,27 @@ return /******/ (function(modules) { // 
 
 	        if (action.modifier == "regexMatch") {
 	          _prefs.prefs.fileSearchRegexMatch = actionVal;
 	        }
 
 	        return state.setIn(["fileSearchModifiers", action.modifier], actionVal);
 	      }
 
-	    case _constants2.default.SET_SYMBOL_SEARCH_TYPE:
+	    case "SET_SYMBOL_SEARCH_TYPE":
 	      {
 	        return state.set("symbolSearchType", action.symbolType);
 	      }
 
-	    case _constants2.default.SHOW_SOURCE:
+	    case "SHOW_SOURCE":
 	      {
 	        return state.set("shownSource", action.sourceUrl);
 	      }
 
-	    case _constants2.default.TOGGLE_PANE:
+	    case "TOGGLE_PANE":
 	      {
 	        if (action.position == "start") {
 	          _prefs.prefs.startPanelCollapsed = action.paneCollapsed;
 	          return state.set("startPanelCollapsed", action.paneCollapsed);
 	        }
 
 	        _prefs.prefs.endPanelCollapsed = action.paneCollapsed;
 	        return state.set("endPanelCollapsed", action.paneCollapsed);
@@ -15197,40 +15210,36 @@ return /******/ (function(modules) { // 
 	var _immutable = __webpack_require__(146);
 
 	var I = _interopRequireWildcard(_immutable);
 
 	var _fromJS = __webpack_require__(237);
 
 	var _fromJS2 = _interopRequireDefault(_fromJS);
 
-	var _constants = __webpack_require__(229);
-
-	var _constants2 = _interopRequireDefault(_constants);
-
 	function _interopRequireWildcard(obj) { if (obj && obj.__esModule) { return obj; } else { var newObj = {}; if (obj != null) { for (var key in obj) { if (Object.prototype.hasOwnProperty.call(obj, key)) newObj[key] = obj[key]; } } newObj.default = obj; return newObj; } }
 
 	function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
 
+	var State = exports.State = (0, _makeRecord2.default)({
+	  coverageOn: false,
+	  hitCount: I.Map()
+	});
+
 	/**
 	 * Code Coverage reducer
 	 * @module reducers/coverage
 	 */
 
-	var State = exports.State = (0, _makeRecord2.default)({
-	  coverageOn: false,
-	  hitCount: I.Map()
-	});
-
 	function update() {
 	  var state = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : State();
 	  var action = arguments[1];
 
 	  switch (action.type) {
-	    case _constants2.default.RECORD_COVERAGE:
+	    case "RECORD_COVERAGE":
 	      return state.mergeIn(["hitCount"], (0, _fromJS2.default)(action.value.coverage)).setIn(["coverageOn"], true);
 
 	    default:
 	      {
 	        return state;
 	      }
 	  }
 	}
@@ -15249,37 +15258,67 @@ return /******/ (function(modules) { // 
 	exports.default = update;
 
 /***/ },
 /* 242 */
 /***/ function(module, exports, __webpack_require__) {
 
 	"use strict";
 
-	var expressions = __webpack_require__(228);
-	var sources = __webpack_require__(232);
-	var pause = __webpack_require__(239);
-	var breakpoints = __webpack_require__(236);
-	var eventListeners = __webpack_require__(231);
-	var ui = __webpack_require__(240);
-	var coverage = __webpack_require__(241);
+	var _expressions = __webpack_require__(228);
+
+	var expressions = _interopRequireWildcard(_expressions);
+
+	var _sources = __webpack_require__(232);
+
+	var sources = _interopRequireWildcard(_sources);
+
+	var _pause = __webpack_require__(239);
+
+	var pause = _interopRequireWildcard(_pause);
+
+	var _breakpoints = __webpack_require__(236);
+
+	var breakpoints = _interopRequireWildcard(_breakpoints);
+
+	var _eventListeners = __webpack_require__(231);
+
+	var eventListeners = _interopRequireWildcard(_eventListeners);
+
+	var _ui = __webpack_require__(240);
+
+	var ui = _interopRequireWildcard(_ui);
+
+	var _ast = __webpack_require__(1058);
+
+	var ast = _interopRequireWildcard(_ast);
+
+	var _coverage = __webpack_require__(241);
+
+	var coverage = _interopRequireWildcard(_coverage);
+
+	function _interopRequireWildcard(obj) { if (obj && obj.__esModule) { return obj; } else { var newObj = {}; if (obj != null) { for (var key in obj) { if (Object.prototype.hasOwnProperty.call(obj, key)) newObj[key] = obj[key]; } } newObj.default = obj; return newObj; } }
 
 	/**
 	 * @param object - location
 	 */
 
 	module.exports = {
 	  getSource: sources.getSource,
+	  getNewSelectedSourceId: sources.getNewSelectedSourceId,
+	  removeSourceFromTabList: sources.removeSourceFromTabList,
+	  removeSourcesFromTabList: sources.removeSourcesFromTabList,
 	  getSourceByURL: sources.getSourceByURL,
 	  getSourceInSources: sources.getSourceInSources,
 	  getSources: sources.getSources,
 	  getSourceText: sources.getSourceText,
 	  getSourceTabs: sources.getSourceTabs,
 	  getSourcesForTabs: sources.getSourcesForTabs,
 	  getSelectedSource: sources.getSelectedSource,
+	  getSelectedSourceText: sources.getSelectedSourceText,
 	  getSelectedLocation: sources.getSelectedLocation,
 	  getPendingSelectedLocation: sources.getPendingSelectedLocation,
 	  getPrettySource: sources.getPrettySource,
 
 	  getBreakpoint: breakpoints.getBreakpoint,
 	  getBreakpoints: breakpoints.getBreakpoints,
 	  getPendingBreakpoints: breakpoints.getPendingBreakpoints,
 	  getBreakpointsForSource: breakpoints.getBreakpointsForSource,
@@ -15311,17 +15350,22 @@ return /******/ (function(modules) { // 
 	  getSymbolSearchState: ui.getSymbolSearchState,
 	  getSymbolSearchType: ui.getSymbolSearchType,
 	  getShownSource: ui.getShownSource,
 	  getPaneCollapse: ui.getPaneCollapse,
 
 	  getExpressions: expressions.getExpressions,
 	  getVisibleExpressions: expressions.getVisibleExpressions,
 	  getExpression: expressions.getExpression,
-	  getHighlightedLineRange: ui.getHighlightedLineRange
+	  getHighlightedLineRange: ui.getHighlightedLineRange,
+
+	  getSymbols: ast.getSymbols,
+	  hasSymbols: ast.hasSymbols,
+	  getOutOfScopeLocations: ast.getOutOfScopeLocations,
+	  getSelection: ast.getSelection
 	};
 
 /***/ },
 /* 243 */
 /***/ function(module, exports, __webpack_require__) {
 
 	"use strict";
 
@@ -15350,17 +15394,17 @@ return /******/ (function(modules) { // 
 	__webpack_require__(327);
 
 	__webpack_require__(331);
 
 	var _devtoolsSplitter = __webpack_require__(910);
 
 	var _devtoolsSplitter2 = _interopRequireDefault(_devtoolsSplitter);
 
-	var _ProjectSearch2 = __webpack_require__(915);
+	var _ProjectSearch2 = __webpack_require__(1060);
 
 	var _ProjectSearch3 = _interopRequireDefault(_ProjectSearch2);
 
 	var _Sources2 = __webpack_require__(388);
 
 	var _Sources3 = _interopRequireDefault(_Sources2);
 
 	var _Editor2 = __webpack_require__(426);
@@ -15560,214 +15604,357 @@ return /******/ (function(modules) { // 
 	var _navigation = __webpack_require__(320);
 
 	var navigation = _interopRequireWildcard(_navigation);
 
 	var _ui = __webpack_require__(321);
 
 	var ui = _interopRequireWildcard(_ui);
 
+	var _ast = __webpack_require__(1059);
+
+	var ast = _interopRequireWildcard(_ast);
+
 	var _coverage = __webpack_require__(322);
 
 	var coverage = _interopRequireWildcard(_coverage);
 
 	function _interopRequireWildcard(obj) { if (obj && obj.__esModule) { return obj; } else { var newObj = {}; if (obj != null) { for (var key in obj) { if (Object.prototype.hasOwnProperty.call(obj, key)) newObj[key] = obj[key]; } } newObj.default = obj; return newObj; } }
 
-	exports.default = Object.assign({}, navigation, breakpoints, expressions, eventListeners, sources, pause, ui, coverage);
+	exports.default = Object.assign({}, navigation, breakpoints, expressions, eventListeners, sources, pause, ui, ast, coverage);
 
 /***/ },
 /* 245 */
 /***/ function(module, exports, __webpack_require__) {
 
 	"use strict";
 
 	Object.defineProperty(exports, "__esModule", {
 	  value: true
 	});
 
 	var _slicedToArray = function () { function sliceIterator(arr, i) { var _arr = []; var _n = true; var _d = false; var _e = undefined; try { for (var _i = arr[Symbol.iterator](), _s; !(_n = (_s = _i.next()).done); _n = true) { _arr.push(_s.value); if (i && _arr.length === i) break; } } catch (err) { _d = true; _e = err; } finally { try { if (!_n && _i["return"]) _i["return"](); } finally { if (_d) throw _e; } } return _arr; } return function (arr, i) { if (Array.isArray(arr)) { return arr; } else if (Symbol.iterator in Object(arr)) { return sliceIterator(arr, i); } else { throw new TypeError("Invalid attempt to destructure non-iterable instance"); } }; }();
 
+	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 _getGeneratedLocation = (() => {
+	  var _ref = _asyncToGenerator(function* (source, sourceMaps, location) {
+	    if (!sourceMaps.isOriginalId(location.sourceId)) {
+	      return location;
+	    }
+
+	    return yield sourceMaps.getGeneratedLocation(location, source.toJS());
+	  });
+
+	  return function _getGeneratedLocation(_x2, _x3, _x4) {
+	    return _ref.apply(this, arguments);
+	  };
+	})();
+
+	var syncClientBreakpoint = (() => {
+	  var _ref2 = _asyncToGenerator(function* (sourceId, client, sourceMaps, pendingBreakpoint) {
+	    var generatedSourceId = sourceMaps.isOriginalId(sourceId) ? (0, _devtoolsSourceMap.originalToGeneratedId)(sourceId) : sourceId;
+
+	    // this is the generatedLocation of the pending breakpoint, with
+	    // the source id updated to reflect the new connection
+	    var oldGeneratedLocation = _extends({}, pendingBreakpoint.generatedLocation, {
+	      sourceId: generatedSourceId
+	    });
+
+	    // early return if breakpoint is disabled with overrides to update
+	    // the id as expected, without talking to server
+	    if (pendingBreakpoint.disabled) {
+	      return {
+	        id: generatedSourceId,
+	        actualLocation: _extends({}, pendingBreakpoint.location, { id: sourceId }),
+	        oldGeneratedLocation
+	      };
+	    }
+
+	    // If we are not disabled, set the breakpoint on the server and get
+	    // that info so we can set it on our breakpoints.
+	    var clientBreakpoint = yield client.setBreakpoint(oldGeneratedLocation, pendingBreakpoint.condition, sourceMaps.isOriginalId(sourceId));
+
+	    // Original location is the location in the src file
+	    var clientOriginalLocation = yield sourceMaps.getOriginalLocation(clientBreakpoint.actualLocation);
+
+	    // make sure that we are re-adding the same type of breakpoint. Column
+	    // or line
+	    var actualLocation = (0, _breakpoint.equalizeLocationColumn)(clientOriginalLocation, pendingBreakpoint.location);
+
+	    // the generatedLocation might have slid, so now we can adjust it
+	    var generatedLocation = clientBreakpoint.actualLocation;
+
+	    var id = clientBreakpoint.id,
+	        hitCount = clientBreakpoint.hitCount;
+
+	    return { id, actualLocation, hitCount, generatedLocation };
+	  });
+
+	  return function syncClientBreakpoint(_x5, _x6, _x7, _x8) {
+	    return _ref2.apply(this, arguments);
+	  };
+	})();
+
+	var addClientBreakpoint = (() => {
+	  var _ref3 = _asyncToGenerator(function* (state, client, sourceMaps, breakpoint) {
+	    var location = breakpoint.location;
+	    var source = (0, _selectors.getSource)(state, location.sourceId);
+	    var generatedLocation = yield _getGeneratedLocation(source, sourceMaps, location);
+
+	    var clientBreakpoint = yield client.setBreakpoint(generatedLocation, breakpoint.condition, sourceMaps.isOriginalId(breakpoint.location.sourceId));
+
+	    var actualLocation = yield sourceMaps.getOriginalLocation(clientBreakpoint.actualLocation);
+
+	    var id = clientBreakpoint.id,
+	        hitCount = clientBreakpoint.hitCount;
+
+	    return { id, actualLocation, hitCount, generatedLocation };
+	  });
+
+	  return function addClientBreakpoint(_x9, _x10, _x11, _x12) {
+	    return _ref3.apply(this, arguments);
+	  };
+	})();
+
+	/**
+	 * Enabling a breakpoint
+	 * will reuse the existing breakpoint information that is stored.
+	 *
+	 * @memberof actions/breakpoints
+	 * @static
+	 * @param {Location} $1.location Location  value
+	 */
+
+
 	exports.enableBreakpoint = enableBreakpoint;
+	exports.syncBreakpoint = syncBreakpoint;
 	exports.addBreakpoint = addBreakpoint;
 	exports.disableBreakpoint = disableBreakpoint;
 	exports.removeBreakpoint = removeBreakpoint;
 	exports.toggleAllBreakpoints = toggleAllBreakpoints;
 	exports.setBreakpointCondition = setBreakpointCondition;
 
-	var _constants = __webpack_require__(229);
-
-	var _constants2 = _interopRequireDefault(_constants);
-
 	var _promise = __webpack_require__(193);
 
 	var _selectors = __webpack_require__(242);
 
-	function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
+	var _devtoolsSourceMap = __webpack_require__(898);
+
+	var _breakpoint = __webpack_require__(1057);
 
 	function _asyncToGenerator(fn) { return function () { var gen = fn.apply(this, arguments); return new Promise(function (resolve, reject) { function step(key, arg) { try { var info = gen[key](arg); var value = info.value; } catch (error) { reject(error); return; } if (info.done) { resolve(value); } else { return Promise.resolve(value).then(function (value) { step("next", value); }, function (err) { step("throw", err); }); } } return step("next"); }); }; }
 	/* This Source Code Form is subject to the terms of the Mozilla Public
 	 * License, v. 2.0. If a copy of the MPL was not distributed with this
 	 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 	/**
 	 * Redux actions for breakpoints
 	 * @module actions/breakpoints
 	 */
 
 	function _breakpointExists(state, location) {
 	  var currentBp = (0, _selectors.getBreakpoint)(state, location);
 	  return currentBp && !currentBp.disabled;
 	}
 
-	function _getOrCreateBreakpoint(state, location, condition) {
-	  return (0, _selectors.getBreakpoint)(state, location) || { location, condition, text: "" };
-	}
-
-	/**
-	 * Enabling a breakpoint calls {@link addBreakpoint}
-	 * which will reuse the existing breakpoint information that is stored.
+	function _createBreakpoint(location) {
+	  var overrides = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {};
+	  var condition = overrides.condition,
+	      disabled = overrides.disabled,
+	      generatedLocation = overrides.generatedLocation;
+
+	  var properties = {
+	    condition: condition || null,
+	    disabled: disabled || false,
+	    generatedLocation,
+	    location
+	  };
+
+	  return properties;
+	}
+
+	function enableBreakpoint(location) {
+	  return (_ref4) => {
+	    var dispatch = _ref4.dispatch,
+	        getState = _ref4.getState,
+	        client = _ref4.client,
+	        sourceMaps = _ref4.sourceMaps;
+
+	    var breakpoint = (0, _selectors.getBreakpoint)(getState(), location);
+	    if (!breakpoint) {
+	      throw new Error("attempted to enable a breakpoint that does not exist");
+	    }
+
+	    return dispatch({
+	      type: "ENABLE_BREAKPOINT",
+	      breakpoint,
+	      [_promise.PROMISE]: addClientBreakpoint(getState(), client, sourceMaps, breakpoint)
+	    });
+	  };
+	}
+
+	/**
+	 * Syncing a breakpoint add breakpoint information that is stored, and
+	 * contact the server for more data.
 	 *
 	 * @memberof actions/breakpoints
 	 * @static
-	 */
-	function enableBreakpoint(location) {
-	  return addBreakpoint(location);
-	}
-
-	/**
-	 * Add a new or enable an existing breakpoint
+	 * @param {String} $1.sourceId String  value
+	 * @param {PendingBreakpoint} $1.location PendingBreakpoint  value
+	 */
+	function syncBreakpoint(sourceId, pendingBreakpoint) {
+	  return (_ref5) => {
+	    var dispatch = _ref5.dispatch,
+	        getState = _ref5.getState,
+	        client = _ref5.client,
+	        sourceMaps = _ref5.sourceMaps;
+	    var _pendingBreakpoint$lo = pendingBreakpoint.location,
+	        line = _pendingBreakpoint$lo.line,
+	        sourceUrl = _pendingBreakpoint$lo.sourceUrl,
+	        column = _pendingBreakpoint$lo.column;
+
+	    var location = { sourceId, sourceUrl, line, column };
+	    var breakpoint = _createBreakpoint(location, pendingBreakpoint);
+
+	    var syncPromise = syncClientBreakpoint(sourceId, client, sourceMaps, pendingBreakpoint);
+
+	    return dispatch({
+	      type: "SYNC_BREAKPOINT",
+	      breakpoint,
+	      [_promise.PROMISE]: syncPromise
+	    });
+	  };
+	}
+
+	/**
+	 * Add a new breakpoint
 	 *
 	 * @memberof actions/breakpoints
 	 * @static
 	 * @param {String} $1.condition Conditional breakpoint condition value
-	 * @param {Function} $1.getTextForLine Get the text to represent the line
+	 * @param {Boolean} $1.disabled Disable value for breakpoint value
 	 */
 	function addBreakpoint(location) {
-	  var _ref = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {},
-	      condition = _ref.condition,
-	      getTextForLine = _ref.getTextForLine;
-
-	  return (_ref2) => {
-	    var dispatch = _ref2.dispatch,
-	        getState = _ref2.getState,
-	        client = _ref2.client,
-	        sourceMaps = _ref2.sourceMaps;
+	  var _ref6 = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {},
+	      condition = _ref6.condition;
+
+	  return (_ref7) => {
+	    var dispatch = _ref7.dispatch,
+	        getState = _ref7.getState,
+	        client = _ref7.client,
+	        sourceMaps = _ref7.sourceMaps;
 
 	    if (_breakpointExists(getState(), location)) {
 	      return Promise.resolve();
 	    }
 
-	    var bp = _getOrCreateBreakpoint(getState(), location, condition);
-
+	    var breakpoint = _createBreakpoint(location, { condition });
 	    return dispatch({
-	      type: _constants2.default.ADD_BREAKPOINT,
-	      breakpoint: bp,
+	      type: "ADD_BREAKPOINT",
+	      breakpoint,
 	      condition: condition,
-	      [_promise.PROMISE]: _asyncToGenerator(function* () {
-	        if (sourceMaps.isOriginalId(bp.location.sourceId)) {
-	          var source = (0, _selectors.getSource)(getState(), bp.location.sourceId);
-	          location = yield sourceMaps.getGeneratedLocation(bp.location, source.toJS());
-	        }
-
-	        var _ref4 = yield client.setBreakpoint(location, bp.condition, sourceMaps.isOriginalId(bp.location.sourceId)),
-	            id = _ref4.id,
-	            actualLocation = _ref4.actualLocation,
-	            hitCount = _ref4.hitCount;
-
-	        actualLocation = yield sourceMaps.getOriginalLocation(actualLocation);
-
-	        // If this breakpoint is being re-enabled, it already has a
-	        // text snippet.
-	        var text = bp.text;
-	        if (!text) {
-	          text = getTextForLine ? getTextForLine(actualLocation.line) : "";
-	        }
-	        return { id, actualLocation, text, hitCount };
-	      })()
+	      [_promise.PROMISE]: addClientBreakpoint(getState(), client, sourceMaps, breakpoint)
 	    });
 	  };
 	}
 
 	/**
 	 * Disable a single breakpoint
 	 *
 	 * @memberof actions/breakpoints
 	 * @static
 	 */
 	function disableBreakpoint(location) {
-	  return _removeOrDisableBreakpoint(location, true);
+	  return (_ref8) => {
+	    var dispatch = _ref8.dispatch,
+	        getState = _ref8.getState,
+	        client = _ref8.client;
+
+	    var bp = (0, _selectors.getBreakpoint)(getState(), location);
+	    if (!bp) {
+	      throw new Error("attempt to disable a breakpoint that does not exist");
+	    }
+	    if (bp.loading) {
+	      // TODO(jwl): make this wait until the breakpoint is saved if it
+	      // is still loading
+	      throw new Error("attempt to disable unsaved breakpoint");
+	    }
+
+	    var action = {
+	      type: "DISABLE_BREAKPOINT",
+	      breakpoint: bp,
+	      disabled: true,
+	      [_promise.PROMISE]: client.removeBreakpoint(bp.id)
+	    };
+
+	    return dispatch(action);
+	  };
 	}
 
 	/**
 	 * Remove a single breakpoint
 	 *
 	 * @memberof actions/breakpoints
 	 * @static
 	 */
 	function removeBreakpoint(location) {
-	  return _removeOrDisableBreakpoint(location);
-	}
-
-	function _removeOrDisableBreakpoint(location) {
-	  var isDisabled = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : false;
-
-	  return (_ref5) => {
-	    var dispatch = _ref5.dispatch,
-	        getState = _ref5.getState,
-	        client = _ref5.client;
+	  return (_ref9) => {
+	    var dispatch = _ref9.dispatch,
+	        getState = _ref9.getState,
+	        client = _ref9.client;
 
 	    var bp = (0, _selectors.getBreakpoint)(getState(), location);
 	    if (!bp) {
 	      throw new Error("attempt to remove breakpoint that does not exist");
 	    }
 	    if (bp.loading) {
 	      // TODO(jwl): make this wait until the breakpoint is saved if it
 	      // is still loading
 	      throw new Error("attempt to remove unsaved breakpoint");
 	    }
 
 	    var action = {
-	      type: _constants2.default.REMOVE_BREAKPOINT,
-	      breakpoint: bp,
-	      disabled: isDisabled
-	    };
-
-	    // If the breakpoint is already disabled, we don't need to remove
-	    // it from the server. We just need to dispatch an action
-	    // simulating a successful server request to remove it, and it
-	    // will be removed completely from the state.
-	    if (!bp.disabled) {
-	      return dispatch(Object.assign({}, action, {
-	        [_promise.PROMISE]: client.removeBreakpoint(bp.id)
-	      }));
-	    }
-	    return dispatch(Object.assign({}, action, { status: "done" }));
+	      type: "REMOVE_BREAKPOINT",
+	      breakpoint: bp
+	    };
+
+	    // If the breakpoint is already disabled, we don't need to communicate
+	    // with the server. We just need to dispatch an action
+	    // simulating a successful server request
+	    if (bp.disabled) {
+	      return dispatch(Object.assign({}, action, { status: "done" }));
+	    }
+
+	    return dispatch(Object.assign({}, action, {
+	      [_promise.PROMISE]: client.removeBreakpoint(bp.id)
+	    }));
 	  };
 	}
 
 	/**
 	 * Toggle All Breakpoints
 	 *
 	 * @memberof actions/breakpoints
 	 * @static
 	 */
 	function toggleAllBreakpoints(shouldDisableBreakpoints) {
-	  return (_ref6) => {
-	    var dispatch = _ref6.dispatch,
-	        getState = _ref6.getState;
+	  return (_ref10) => {
+	    var dispatch = _ref10.dispatch,
+	        getState = _ref10.getState;
 
 	    var breakpoints = (0, _selectors.getBreakpoints)(getState());
 	    return dispatch({
-	      type: _constants2.default.TOGGLE_BREAKPOINTS,
+	      type: "TOGGLE_BREAKPOINTS",
 	      shouldDisableBreakpoints,
 	      [_promise.PROMISE]: _asyncToGenerator(function* () {
-	        for (var _ref8 of breakpoints) {
-	          var _ref9 = _slicedToArray(_ref8, 2);
-
-	          var breakpoint = _ref9[1];
+	        for (var _ref12 of breakpoints) {
+	          var _ref13 = _slicedToArray(_ref12, 2);
+
+	          var breakpoint = _ref13[1];
 
 	          if (shouldDisableBreakpoints) {
 	            yield dispatch(disableBreakpoint(breakpoint.location));
 	          } else {
 	            yield dispatch(enableBreakpoint(breakpoint.location));
 	          }
 	        }
 	      })()
@@ -15780,42 +15967,41 @@ return /******/ (function(modules) { // 
 	 *
 	 * @throws {Error} "not implemented"
 	 * @memberof actions/breakpoints
 	 * @static
 	 * @param {Location} location
 	 *        @see DebuggerController.Breakpoints.addBreakpoint
 	 * @param {string} condition
 	 *        The condition to set on the breakpoint
+	 * @param {Boolean} $1.disabled Disable value for breakpoint value
 	 */
 	function setBreakpointCondition(location) {
-	  var _ref10 = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {},
-	      condition = _ref10.condition,
-	      getTextForLine = _ref10.getTextForLine;
-
-	  // location: Location, condition: string, { getTextForLine }) {
-	  return (_ref11) => {
-	    var dispatch = _ref11.dispatch,
-	        getState = _ref11.getState,
-	        client = _ref11.client,
-	        sourceMaps = _ref11.sourceMaps;
+	  var _ref14 = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {},
+	      condition = _ref14.condition;
+
+	  return (_ref15) => {
+	    var dispatch = _ref15.dispatch,
+	        getState = _ref15.getState,
+	        client = _ref15.client,
+	        sourceMaps = _ref15.sourceMaps;
 
 	    var bp = (0, _selectors.getBreakpoint)(getState(), location);
 	    if (!bp) {
-	      return dispatch(addBreakpoint(location, { condition, getTextForLine }));
+	      return dispatch(addBreakpoint(location, { condition }));
 	    }
 
 	    if (bp.loading) {
 	      // TODO(jwl): when this function is called, make sure the action
 	      // creator waits for the breakpoint to exist
 	      throw new Error("breakpoint must be saved");
 	    }
 
 	    return dispatch({
-	      type: _constants2.default.SET_BREAKPOINT_CONDITION,
+	      type: "SET_BREAKPOINT_CONDITION",
 	      breakpoint: bp,
 	      condition: condition,
 	      [_promise.PROMISE]: client.setBreakpointCondition(bp.id, location, condition, sourceMaps.isOriginalId(bp.location.sourceId))
 	    });
 	  };
 	}
 
 /***/ },
@@ -16163,32 +16349,22 @@ return /******/ (function(modules) { // 
 	Object.defineProperty(exports, "__esModule", {
 	  value: true
 	});
 	exports.addExpression = addExpression;
 	exports.updateExpression = updateExpression;
 	exports.deleteExpression = deleteExpression;
 	exports.evaluateExpressions = evaluateExpressions;
 
-	var _constants = __webpack_require__(229);
-
-	var _constants2 = _interopRequireDefault(_constants);
-
 	var _promise = __webpack_require__(193);
 
 	var _selectors = __webpack_require__(242);
 
-	function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
-
 	function _asyncToGenerator(fn) { return function () { var gen = fn.apply(this, arguments); return new Promise(function (resolve, reject) { function step(key, arg) { try { var info = gen[key](arg); var value = info.value; } catch (error) { reject(error); return; } if (info.done) { resolve(value); } else { return Promise.resolve(value).then(function (value) { step("next", value); }, function (err) { step("throw", err); }); } } return step("next"); }); }; }
 
-	function expressionExists(expressions, input) {
-	  return !!expressions.find(e => e.input == input);
-	}
-
 	/**
 	 * Add expression for debugger to watch
 	 *
 	 * @param {object} expression
 	 * @param {number} expression.id
 	 * @memberof actions/pause
 	 * @static
 	 */
@@ -16197,28 +16373,37 @@ return /******/ (function(modules) { // 
 	      _ref$visible = _ref.visible,
 	      visible = _ref$visible === undefined ? true : _ref$visible;
 
 	  return (() => {
 	    var _ref2 = _asyncToGenerator(function* (_ref3) {
 	      var dispatch = _ref3.dispatch,
 	          getState = _ref3.getState;
 
-	      var expressions = (0, _selectors.getExpressions)(getState());
-	      if (!input || expressionExists(expressions, input)) {
-	        var expression = (0, _selectors.getExpression)(getState(), input);
-	        if (!expression.visible && visible) {
-	          yield dispatch(deleteExpression(expression));
-	        } else {
-	          return;
-	        }
+	      if (!input) {
+	        return;
+	      }
+
+	      var expression = (0, _selectors.getExpression)(getState(), input);
+	      if (expression && expression.visible) {
+	        return;
+	      }
+
+	      // Lets make the expression visible
+	      if (expression) {
+	        return dispatch({
+	          type: "UPDATE_EXPRESSION",
+	          expression,
+	          input,
+	          visible: true
+	        });
 	      }
 
 	      dispatch({
-	        type: _constants2.default.ADD_EXPRESSION,
+	        type: "ADD_EXPRESSION",
 	        input,
 	        visible
 	      });
 
 	      var selectedFrame = (0, _selectors.getSelectedFrame)(getState());
 	      var selectedFrameId = selectedFrame ? selectedFrame.id : null;
 	      dispatch(evaluateExpression({ input, visible }, selectedFrameId));
 	    });
@@ -16234,17 +16419,17 @@ return /******/ (function(modules) { // 
 	    var dispatch = _ref4.dispatch,
 	        getState = _ref4.getState;
 
 	    if (!input || input == expression.input) {
 	      return;
 	    }
 
 	    dispatch({
-	      type: _constants2.default.UPDATE_EXPRESSION,
+	      type: "UPDATE_EXPRESSION",
 	      expression,
 	      input: input,
 	      visible: expression.visible
 	    });
 
 	    var selectedFrame = (0, _selectors.getSelectedFrame)(getState());
 	    var selectedFrameId = selectedFrame ? selectedFrame.id : null;
 	    dispatch(evaluateExpressions(selectedFrameId));
@@ -16258,17 +16443,17 @@ return /******/ (function(modules) { // 
 	 * @memberof actions/pause
 	 * @static
 	 */
 	function deleteExpression(expression) {
 	  return (_ref5) => {
 	    var dispatch = _ref5.dispatch;
 
 	    dispatch({
-	      type: _constants2.default.DELETE_EXPRESSION,
+	      type: "DELETE_EXPRESSION",
 	      input: expression.input
 	    });
 	  };
 	}
 
 	/**
 	 *
 	 * @memberof actions/pause
@@ -16305,17 +16490,17 @@ return /******/ (function(modules) { // 
 	        client = _ref8.client;
 
 	    if (!expression.input) {
 	      console.warn("Expressions should not be empty");
 	      return;
 	    }
 
 	    return dispatch({
-	      type: _constants2.default.EVALUATE_EXPRESSION,
+	      type: "EVALUATE_EXPRESSION",
 	      input: expression.input,
 	      visible: expression.visible,
 	      [_promise.PROMISE]: client.evaluate(expression.input, { frameId })
 	    });
 	  };
 	}
 
 /***/ },
@@ -16431,26 +16616,20 @@ return /******/ (function(modules) { // 
 	 * @static
 	 * @param {string} eventNames
 	 */
 
 
 	exports.fetchEventListeners = fetchEventListeners;
 	exports.updateEventBreakpoints = updateEventBreakpoints;
 
-	var _constants = __webpack_require__(229);
-
-	var _constants2 = _interopRequireDefault(_constants);
-
 	var _DevToolsUtils = __webpack_require__(222);
 
 	var _selectors = __webpack_require__(242);
 
-	function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
-
 	function _asyncToGenerator(fn) { return function () { var gen = fn.apply(this, arguments); return new Promise(function (resolve, reject) { function step(key, arg) { try { var info = gen[key](arg); var value = info.value; } catch (error) { reject(error); return; } if (info.done) { resolve(value); } else { return Promise.resolve(value).then(function (value) { step("next", value); }, function (err) { step("throw", err); }); } } return step("next"); }); }; } /* This Source Code Form is subject to the terms of the Mozilla Public
 	                                                                                                                                                                                                                                                                                                                                                                                                                                                                            * License, v. 2.0. If a copy of the MPL was not distributed with this
 	                                                                                                                                                                                                                                                                                                                                                                                                                                                                            * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 	/* global window gThreadClient setNamedTimeout services EVENTS */
 	/* eslint no-shadow: 0  */
 
 	/**
 	 * Redux actions for the event listeners state
@@ -16473,30 +16652,30 @@ return /******/ (function(modules) { // 
 
 	    fetchListenersTimerID = setTimeout(() => {
 	      // In case there is still a request of listeners going on (it
 	      // takes several RDP round trips right now), make sure we wait
 	      // on a currently running request
 	      if (getState().eventListeners.fetchingListeners) {
 	        dispatch({
 	          type: services.WAIT_UNTIL,
-	          predicate: action => action.type === _constants2.default.FETCH_EVENT_LISTENERS && action.status === "done",
+	          predicate: action => action.type === "FETCH_EVENT_LISTENERS" && action.status === "done",
 	          run: dispatch => dispatch(fetchEventListeners())
 	        });
 	        return;
 	      }
 
 	      dispatch({
-	        type: _constants2.default.FETCH_EVENT_LISTENERS,
+	        type: "FETCH_EVENT_LISTENERS",
 	        status: "begin"
 	      });
 
 	      asPaused(getState(), client, _getEventListeners).then(listeners => {
 	        dispatch({
-	          type: _constants2.default.FETCH_EVENT_LISTENERS,
+	          type: "FETCH_EVENT_LISTENERS",
 	          status: "done",
 	          listeners: formatListeners(getState(), listeners)
 	        });
 	      });
 	    }, FETCH_EVENT_LISTENERS_DELAY);
 	  };
 	}
 
@@ -16514,17 +16693,17 @@ return /******/ (function(modules) { // 
 	function updateEventBreakpoints(eventNames) {
 	  return dispatch => {
 	    setNamedTimeout("event-breakpoints-update", 0, () => {
 	      gThreadClient.pauseOnDOMEvents(eventNames, function () {
 	        // Notify that event breakpoints were added/removed on the server.
 	        window.emit(EVENTS.EVENT_BREAKPOINTS_UPDATED);
 
 	        dispatch({
-	          type: _constants2.default.UPDATE_EVENT_BREAKPOINTS,
+	          type: "UPDATE_EVENT_BREAKPOINTS",
 	          eventNames: eventNames
 	        });
 	      });
 	    });
 	  };
 	}
 
 /***/ },
@@ -16539,29 +16718,24 @@ return /******/ (function(modules) { // 
 
 	var _slicedToArray = function () { function sliceIterator(arr, i) { var _arr = []; var _n = true; var _d = false; var _e = undefined; try { for (var _i = arr[Symbol.iterator](), _s; !(_n = (_s = _i.next()).done); _n = true) { _arr.push(_s.value); if (i && _arr.length === i) break; } } catch (err) { _d = true; _e = err; } finally { try { if (!_n && _i["return"]) _i["return"](); } finally { if (_d) throw _e; } } return _arr; } return function (arr, i) { if (Array.isArray(arr)) { return arr; } else if (Symbol.iterator in Object(arr)) { return sliceIterator(arr, i); } else { throw new TypeError("Invalid attempt to destructure non-iterable instance"); } }; }();
 
 	var checkPendingBreakpoint = (() => {
 	  var _ref = _asyncToGenerator(function* (state, dispatch, pendingBreakpoint, source) {
 	    var _pendingBreakpoint$lo = pendingBreakpoint.location,
 	        line = _pendingBreakpoint$lo.line,
 	        sourceUrl = _pendingBreakpoint$lo.sourceUrl,
-	        column = _pendingBreakpoint$lo.column,
-	        condition = pendingBreakpoint.condition;
+	        column = _pendingBreakpoint$lo.column;
 
 	    var sameSource = sourceUrl && sourceUrl === source.url;
 	    var location = { sourceId: source.id, sourceUrl, line, column };
 	    var bp = (0, _selectors.getBreakpoint)(state, location);
 
 	    if (sameSource && !bp) {
-	      if (location.column && (0, _devtoolsConfig.isEnabled)("columnBreakpoints")) {
-	        yield dispatch((0, _breakpoints.addBreakpoint)(location, { condition }));
-	      } else {
-	        yield dispatch((0, _breakpoints.addBreakpoint)(location, { condition }));
-	      }
+	      yield dispatch((0, _breakpoints.syncBreakpoint)(source.id, pendingBreakpoint));
 	    }
 	  });
 
 	  return function checkPendingBreakpoint(_x, _x2, _x3, _x4) {
 	    return _ref.apply(this, arguments);
 	  };
 	})();
 
@@ -16609,28 +16783,24 @@ return /******/ (function(modules) { // 
 	var _promise = __webpack_require__(193);
 
 	var _assert = __webpack_require__(223);
 
 	var _assert2 = _interopRequireDefault(_assert);
 
 	var _pause = __webpack_require__(255);
 
+	var _ast = __webpack_require__(1059);
+
 	var _breakpoints = __webpack_require__(245);
 
-	var _devtoolsConfig = __webpack_require__(828);
-
 	var _prettyPrint = __webpack_require__(903);
 
 	var _source = __webpack_require__(233);
 
-	var _constants = __webpack_require__(229);
-
-	var _constants2 = _interopRequireDefault(_constants);
-
 	var _prefs = __webpack_require__(226);
 
 	var _editor = __webpack_require__(257);
 
 	var _selectors = __webpack_require__(242);
 
 	function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
 
@@ -16656,17 +16826,17 @@ return /******/ (function(modules) { // 
 	}
 
 	function newSource(source) {
 	  return (() => {
 	    var _ref3 = _asyncToGenerator(function* (_ref4) {
 	      var dispatch = _ref4.dispatch,
 	          getState = _ref4.getState;
 
-	      dispatch({ type: _constants2.default.ADD_SOURCE, source });
+	      dispatch({ type: "ADD_SOURCE", source });
 
 	      if (_prefs.prefs.clientSourceMapsEnabled) {
 	        yield dispatch(loadSourceMap(source));
 	      }
 
 	      checkSelectedSource(getState(), dispatch, source);
 	      yield checkPendingBreakpoints(getState(), dispatch, source);
 	    });
@@ -16719,17 +16889,17 @@ return /******/ (function(modules) { // 
 	      var originalSources = urls.map(function (originalUrl) {
 	        return {
 	          url: originalUrl,
 	          id: sourceMaps.generatedToOriginalId(generatedSource.id, originalUrl),
 	          isPrettyPrinted: false
 	        };
 	      });
 
-	      dispatch({ type: _constants2.default.ADD_SOURCES, sources: originalSources });
+	      dispatch({ type: "ADD_SOURCES", sources: originalSources });
 
 	      originalSources.forEach(function (source) {
 	        checkSelectedSource(state, dispatch, source);
 	        checkPendingBreakpoints(state, dispatch, source);
 	      });
 	    });
 
 	    return function (_x10) {
@@ -16754,17 +16924,17 @@ return /******/ (function(modules) { // 
 	    var dispatch = _ref9.dispatch,
 	        getState = _ref9.getState;
 
 	    var source = (0, _selectors.getSourceByURL)(getState(), url);
 	    if (source) {
 	      dispatch(selectSource(source.get("id"), options));
 	    } else {
 	      dispatch({
-	        type: _constants2.default.SELECT_SOURCE_URL,
+	        type: "SELECT_SOURCE_URL",
 	        url: url,
 	        tabIndex: options.tabIndex,
 	        line: options.line
 	      });
 	    }
 	  };
 	}
 
@@ -16784,46 +16954,46 @@ return /******/ (function(modules) { // 
 	      // No connection, do nothing. This happens when the debugger is
 	      // shut down too fast and it tries to display a default source.
 	      return;
 	    }
 
 	    var source = (0, _selectors.getSource)(getState(), id);
 
 	    if (!source) {
-	      return;
-	    }
-
-	    source = source.toJS();
-
-	    // Make sure to start a request to load the source text.
-	    dispatch(loadSourceText(source));
-
-	    dispatch({ type: _constants2.default.TOGGLE_PROJECT_SEARCH, value: false });
-
-	    dispatch({
-	      type: _constants2.default.SELECT_SOURCE,
-	      source: source,
+	      // If there is no source we deselect the current selected source
+	      return dispatch({ type: "CLEAR_SELECTED_SOURCE" });
+	    }
+
+	    dispatch({ type: "TOGGLE_PROJECT_SEARCH", value: false });
+
+	    return dispatch({
+	      type: "SELECT_SOURCE",
+	      source: source.toJS(),
 	      tabIndex: options.tabIndex,
-	      line: options.line
+	      line: options.line,
+	      [_promise.PROMISE]: _asyncToGenerator(function* () {
+	        yield dispatch(loadSourceText(source.toJS()));
+	        yield dispatch((0, _ast.setOutOfScopeLocations)());
+	      })()
 	    });
 	  };
 	}
 
 	/**
 	 * @memberof actions/sources
 	 * @static
 	 */
 	function jumpToMappedLocation(sourceLocation) {
 	  return (() => {
-	    var _ref11 = _asyncToGenerator(function* (_ref12) {
-	      var dispatch = _ref12.dispatch,
-	          getState = _ref12.getState,
-	          client = _ref12.client,
-	          sourceMaps = _ref12.sourceMaps;
+	    var _ref12 = _asyncToGenerator(function* (_ref13) {
+	      var dispatch = _ref13.dispatch,
+	          getState = _ref13.getState,
+	          client = _ref13.client,
+	          sourceMaps = _ref13.sourceMaps;
 
 	      if (!client) {
 	        return;
 	      }
 
 	      var source = (0, _selectors.getSource)(getState(), sourceLocation.sourceId);
 	      var pairedLocation = void 0;
 	      if (sourceMaps.isOriginalId(sourceLocation.sourceId)) {
@@ -16831,98 +17001,112 @@ return /******/ (function(modules) { // 
 	      } else {
 	        pairedLocation = yield sourceMaps.getOriginalLocation(sourceLocation, source.toJS());
 	      }
 
 	      return dispatch(selectSource(pairedLocation.sourceId, { line: pairedLocation.line }));
 	    });
 
 	    return function (_x13) {
-	      return _ref11.apply(this, arguments);
+	      return _ref12.apply(this, arguments);
 	    };
 	  })();
 	}
 
 	/**
 	 * @memberof actions/sources
 	 * @static
 	 */
 	function closeTab(url) {
-	  (0, _editor.removeDocument)(url);
-	  return { type: _constants2.default.CLOSE_TAB, url };
+	  return (_ref14) => {
+	    var dispatch = _ref14.dispatch,
+	        getState = _ref14.getState,
+	        client = _ref14.client;
+
+	    (0, _editor.removeDocument)(url);
+	    var tabs = (0, _selectors.removeSourceFromTabList)((0, _selectors.getSourceTabs)(getState()), url);
+	    var sourceId = (0, _selectors.getNewSelectedSourceId)(getState(), tabs);
+
+	    dispatch({ type: "CLOSE_TAB", url, tabs });
+	    dispatch(selectSource(sourceId));
+	  };
 	}
 
 	/**
 	 * @memberof actions/sources
 	 * @static
 	 */
 	function closeTabs(urls) {
-	  return (_ref13) => {
-	    var dispatch = _ref13.dispatch,
-	        getState = _ref13.getState,
-	        client = _ref13.client;
+	  return (_ref15) => {
+	    var dispatch = _ref15.dispatch,
+	        getState = _ref15.getState,
+	        client = _ref15.client;
 
 	    urls.forEach(url => {
 	      var source = (0, _selectors.getSourceByURL)(getState(), url);
 	      if (source) {
 	        (0, _editor.removeDocument)(source.get("id"));
 	      }
 	    });
 
-	    dispatch({ type: _constants2.default.CLOSE_TABS, urls });
+	    var tabs = (0, _selectors.removeSourcesFromTabList)((0, _selectors.getSourceTabs)(getState()), urls);
+	    var sourceId = (0, _selectors.getNewSelectedSourceId)(getState(), tabs);
+
+	    dispatch({ type: "CLOSE_TABS", urls, tabs });
+	    dispatch(selectSource(sourceId));
 	  };
 	}
 
 	/**
 	 * Toggle the pretty printing of a source's text. All subsequent calls to
 	 * |getText| will return the pretty-toggled text. Nothing will happen for
 	 * non-javascript files.
 	 *
 	 * @memberof actions/sources
 	 * @static
 	 * @param string id The source form from the RDP.
 	 * @returns Promise
 	 *          A promise that resolves to [aSource, prettyText] or rejects to
 	 *          [aSource, error].
 	 */
 	function togglePrettyPrint(sourceId) {
-	  return (_ref14) => {
-	    var dispatch = _ref14.dispatch,
-	        getState = _ref14.getState,
-	        client = _ref14.client,
-	        sourceMaps = _ref14.sourceMaps;
+	  return (_ref16) => {
+	    var dispatch = _ref16.dispatch,
+	        getState = _ref16.getState,
+	        client = _ref16.client,
+	        sourceMaps = _ref16.sourceMaps;
 
 	    var source = (0, _selectors.getSource)(getState(), sourceId).toJS();
 	    var sourceText = (0, _selectors.getSourceText)(getState(), sourceId);
 	    if (sourceText) {
 	      sourceText = sourceText.toJS();
 	    }
 
 	    if (sourceText && sourceText.loading) {
 	      return {};
 	    }
 
 	    (0, _assert2.default)(sourceMaps.isGeneratedId(sourceId), "Pretty-printing only allowed on generated sources");
 
 	    var url = (0, _source.getPrettySourceURL)(source.url);
 	    var id = sourceMaps.generatedToOriginalId(source.id, url);
 	    var originalSource = { url, id, isPrettyPrinted: false };
-	    dispatch({ type: _constants2.default.ADD_SOURCE, source: originalSource });
+	    dispatch({ type: "ADD_SOURCE", source: originalSource });
 
 	    return dispatch({
-	      type: _constants2.default.TOGGLE_PRETTY_PRINT,
+	      type: "TOGGLE_PRETTY_PRINT",
 	      source: originalSource,
 	      [_promise.PROMISE]: _asyncToGenerator(function* () {
-	        var _ref16 = yield (0, _prettyPrint.prettyPrint)({
+	        var _ref18 = yield (0, _prettyPrint.prettyPrint)({
 	          source,
 	          sourceText,
 	          url
 	        }),
-	            code = _ref16.code,
-	            mappings = _ref16.mappings;
+	            code = _ref18.code,
+	            mappings = _ref18.mappings;
 
 	        yield sourceMaps.applySourceMap(source.id, url, code, mappings);
 
 	        var frames = (0, _selectors.getFrames)(getState());
 	        if (frames) {
 	          frames = yield (0, _pause.updateFrameLocations)(frames, sourceMaps);
 	        }
 
@@ -16931,83 +17115,85 @@ return /******/ (function(modules) { // 
 	        return { text: code, contentType: "text/javascript", frames };
 	      })()
 	    });
 	  };
 	}
 
 	function toggleBlackBox(source) {
 	  return (() => {
-	    var _ref17 = _asyncToGenerator(function* (_ref18) {
-	      var dispatch = _ref18.dispatch,
-	          getState = _ref18.getState,
-	          client = _ref18.client,
-	          sourceMaps = _ref18.sourceMaps;
+	    var _ref19 = _asyncToGenerator(function* (_ref20) {
+	      var dispatch = _ref20.dispatch,
+	          getState = _ref20.getState,
+	          client = _ref20.client,
+	          sourceMaps = _ref20.sourceMaps;
 	      var isBlackBoxed = source.isBlackBoxed,
 	          id = source.id;
 
 
 	      return dispatch({
-	        type: _constants2.default.BLACKBOX,
+	        type: "BLACKBOX",
 	        source,
 	        [_promise.PROMISE]: client.blackBox(id, isBlackBoxed)
 	      });
 	    });
 
 	    return function (_x14) {
-	      return _ref17.apply(this, arguments);
+	      return _ref19.apply(this, arguments);
 	    };
 	  })();
 	}
 
 	/**
 	 * @memberof actions/sources
 	 * @static
 	 */
 	function loadSourceText(source) {
-	  return (_ref19) => {
-	    var dispatch = _ref19.dispatch,
-	        getState = _ref19.getState,
-	        client = _ref19.client,
-	        sourceMaps = _ref19.sourceMaps;
-
-	    // Fetch the source text only once.
-	    var textInfo = (0, _selectors.getSourceText)(getState(), source.id);
-	    if (textInfo) {
-	      // It's already loaded or is loading
-	      return Promise.resolve(textInfo);
-	    }
-
-	    return dispatch({
-	      type: _constants2.default.LOAD_SOURCE_TEXT,
-	      source: source,
-	      [_promise.PROMISE]: _asyncToGenerator(function* () {
-	        if (sourceMaps.isOriginalId(source.id)) {
-	          return yield sourceMaps.getOriginalSourceText(source);
-	        }
-
-	        var response = yield client.sourceContents(source.id);
-
-	        var sourceText = {
-	          id: source.id,
-	          text: response.source,
-	          contentType: response.contentType || "text/javascript"
-	        };
-
-	        return sourceText;
-	        // Automatically pretty print if enabled and the test is
-	        // detected to be "minified"
-	        // if (Prefs.autoPrettyPrint &&
-	        //     !source.isPrettyPrinted &&
-	        //     SourceUtils.isMinified(source.id, response.source)) {
-	        //   dispatch(togglePrettyPrint(source));
-	        // }
-	      })()
-	    });
-	  };
+	  return (() => {
+	    var _ref21 = _asyncToGenerator(function* (_ref22) {
+	      var dispatch = _ref22.dispatch,
+	          getState = _ref22.getState,
+	          client = _ref22.client,
+	          sourceMaps = _ref22.sourceMaps;
+
+	      // Fetch the source text only once.
+	      var textInfo = (0, _selectors.getSourceText)(getState(), source.id);
+	      if (textInfo) {
+	        // It's already loaded or is loading
+	        return Promise.resolve(textInfo);
+	      }
+
+	      yield dispatch({
+	        type: "LOAD_SOURCE_TEXT",
+	        source: source,
+	        [_promise.PROMISE]: _asyncToGenerator(function* () {
+	          if (sourceMaps.isOriginalId(source.id)) {
+	            return yield sourceMaps.getOriginalSourceText(source);
+	          }
+
+	          var response = yield client.sourceContents(source.id);
+
+	          var sourceText = {
+	            id: source.id,
+	            text: response.source,
+	            contentType: response.contentType || "text/javascript"
+	          };
+
+	          return sourceText;
+	        })()
+	      });
+
+	      // get the symbols for the source as well
+	      return dispatch((0, _ast.setSymbols)(source));
+	    });
+
+	    return function (_x15) {
+	      return _ref21.apply(this, arguments);
+	    };
+	  })();
 	}
 
 	// delay is in ms
 	var FETCH_SOURCE_RESPONSE_DELAY = 200;
 
 	/**
 	 * Starts fetching all the sources, silently.
 	 *
@@ -17015,36 +17201,36 @@ return /******/ (function(modules) { // 
 	 * @static
 	 * @param array actors
 	 *        The urls for the sources to fetch. If fetching a source's text
 	 *        takes too long, it will be discarded.
 	 * @returns {Promise}
 	 *         A promise that is resolved after source texts have been fetched.
 	 */
 	function getTextForSources(actors) {
-	  return (_ref21) => {
-	    var dispatch = _ref21.dispatch,
-	        getState = _ref21.getState;
+	  return (_ref24) => {
+	    var dispatch = _ref24.dispatch,
+	        getState = _ref24.getState;
 
 	    var deferred = (0, _defer2.default)();
 	    var pending = new Set(actors);
 
 	    var fetched = [];
 
 	    // Can't use promise.all, because if one fetch operation is rejected, then
 	    // everything is considered rejected, thus no other subsequent source will
 	    // be getting fetched. We don't want that. Something like Q's allSettled
 	    // would work like a charm here.
 	    // Try to fetch as many sources as possible.
 
 	    var _loop = function (actor) {
 	      var source = (0, _selectors.getSource)(getState(), actor);
-	      dispatch(loadSourceText(source)).then((_ref30) => {
-	        var text = _ref30.text,
-	            contentType = _ref30.contentType;
+	      dispatch(loadSourceText(source)).then((_ref33) => {
+	        var text = _ref33.text,
+	            contentType = _ref33.contentType;
 
 	        onFetch([source, text, contentType]);
 	      }, err => {
 	        onError(source, err);
 	      });
 	    };
 
 	    for (var actor of actors) {
@@ -17055,54 +17241,54 @@ return /******/ (function(modules) { // 
 
 	    /* Called if fetching a source takes too long. */
 	    function onTimeout() {
 	      pending = new Set();
 	      maybeFinish();
 	    }
 
 	    /* Called if fetching a source finishes successfully. */
-	    function onFetch(_ref22) {
-	      var _ref23 = _slicedToArray(_ref22, 3),
-	          aSource = _ref23[0],
-	          aText = _ref23[1],
-	          aContentType = _ref23[2];
+	    function onFetch(_ref25) {
+	      var _ref26 = _slicedToArray(_ref25, 3),
+	          aSource = _ref26[0],
+	          aText = _ref26[1],
+	          aContentType = _ref26[2];
 
 	      // If fetching the source has previously timed out, discard it this time.
 	      if (!pending.has(aSource.actor)) {
 	        return;
 	      }
 	      pending.delete(aSource.actor);
 	      fetched.push([aSource.actor, aText, aContentType]);
 	      maybeFinish();
 	    }
 
 	    /* Called if fetching a source failed because of an error. */
-	    function onError(_ref24) {
-	      var _ref25 = _slicedToArray(_ref24, 2),
-	          aSource = _ref25[0],
-	          aError = _ref25[1];
+	    function onError(_ref27) {
+	      var _ref28 = _slicedToArray(_ref27, 2),
+	          aSource = _ref28[0],
+	          aError = _ref28[1];
 
 	      pending.delete(aSource.actor);
 	      maybeFinish();
 	    }
 
 	    /* Called every time something interesting
 	     *  happens while fetching sources.
 	     */
 	    function maybeFinish() {
 	      if (pending.size == 0) {
 	        // Sort the fetched sources alphabetically by their url.
 	        if (deferred) {
-	          deferred.resolve(fetched.sort((_ref26, _ref27) => {
-	            var _ref29 = _slicedToArray(_ref26, 1),
-	                aFirst = _ref29[0];
-
-	            var _ref28 = _slicedToArray(_ref27, 1),
-	                aSecond = _ref28[0];
+	          deferred.resolve(fetched.sort((_ref29, _ref30) => {
+	            var _ref32 = _slicedToArray(_ref29, 1),
+	                aFirst = _ref32[0];
+
+	            var _ref31 = _slicedToArray(_ref30, 1),
+	                aSecond = _ref31[0];
 
 	            return aFirst > aSecond ? -1 : 1;
 	          }));
 	        }
 	      }
 	    }
 
 	    return deferred.promise;
@@ -17270,16 +17456,17 @@ return /******/ (function(modules) { // 
 
 	  return new _devtoolsSourceEditor.SourceEditor({
 	    mode: "javascript",
 	    foldGutter: (0, _devtoolsConfig.isEnabled)("codeFolding"),
 	    enableCodeFolding: (0, _devtoolsConfig.isEnabled)("codeFolding"),
 	    readOnly: true,
 	    lineNumbers: true,
 	    theme: "mozilla",
+	    styleActiveLine: false,
 	    lineWrapping: false,
 	    matchBrackets: true,
 	    showAnnotationRuler: true,
 	    gutters,
 	    value: " ",
 	    extraKeys: {
 	      // Override code mirror keymap to avoid conflicts with split console.
 	      Esc: false,
@@ -19442,32 +19629,26 @@ return /******/ (function(modules) { // 
 	exports.stepIn = stepIn;
 	exports.stepOver = stepOver;
 	exports.stepOut = stepOut;
 	exports.resume = resume;
 	exports.breakOnNext = breakOnNext;
 	exports.selectFrame = selectFrame;
 	exports.loadObjectProperties = loadObjectProperties;
 
-	var _constants = __webpack_require__(229);
-
-	var _constants2 = _interopRequireDefault(_constants);
-
 	var _sources = __webpack_require__(254);
 
 	var _promise = __webpack_require__(193);
 
 	var _selectors = __webpack_require__(242);
 
 	var _pause = __webpack_require__(255);
 
 	var _expressions = __webpack_require__(252);
 
-	function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
-
 	function _asyncToGenerator(fn) { return function () { var gen = fn.apply(this, arguments); return new Promise(function (resolve, reject) { function step(key, arg) { try { var info = gen[key](arg); var value = info.value; } catch (error) { reject(error); return; } if (info.done) { resolve(value); } else { return Promise.resolve(value).then(function (value) { step("next", value); }, function (err) { step("throw", err); }); } } return step("next"); }); }; }
 
 	/**
 	 * Redux actions for the pause state
 	 * @module actions/pause
 	 */
 
 	/**
@@ -19479,17 +19660,17 @@ return /******/ (function(modules) { // 
 	function resumed() {
 	  return (_ref) => {
 	    var dispatch = _ref.dispatch,
 	        client = _ref.client;
 
 	    // dispatch(evaluateExpressions(null));
 
 	    return dispatch({
-	      type: _constants2.default.RESUME,
+	      type: "RESUME",
 	      value: undefined
 	    });
 	  };
 	}
 
 	/**
 	 * Debugger has just paused
 	 *
@@ -19507,17 +19688,17 @@ return /******/ (function(modules) { // 
 	      var frames = pauseInfo.frames,
 	          why = pauseInfo.why,
 	          loadedObjects = pauseInfo.loadedObjects;
 
 	      frames = yield (0, _pause.updateFrameLocations)(frames, sourceMaps);
 	      var frame = frames[0];
 
 	      dispatch({
-	        type: _constants2.default.PAUSED,
+	        type: "PAUSED",
 	        pauseInfo: { why, frame, frames },
 	        frames: frames,
 	        selectedFrameId: frame.id,
 	        loadedObjects: loadedObjects || []
 	      });
 
 	      dispatch((0, _expressions.evaluateExpressions)(frame.id));
 
@@ -19536,17 +19717,17 @@ return /******/ (function(modules) { // 
 	 * @static
 	 */
 	function pauseOnExceptions(shouldPauseOnExceptions, shouldIgnoreCaughtExceptions) {
 	  return (_ref4) => {
 	    var dispatch = _ref4.dispatch,
 	        client = _ref4.client;
 
 	    dispatch({
-	      type: _constants2.default.PAUSE_ON_EXCEPTIONS,
+	      type: "PAUSE_ON_EXCEPTIONS",
 	      shouldPauseOnExceptions,
 	      shouldIgnoreCaughtExceptions,
 	      [_promise.PROMISE]: client.pauseOnExceptions(shouldPauseOnExceptions, shouldIgnoreCaughtExceptions)
 	    });
 	  };
 	}
 
 	/**
@@ -19562,17 +19743,17 @@ return /******/ (function(modules) { // 
 	  return (_ref6) => {
 	    var dispatch = _ref6.dispatch,
 	        client = _ref6.client;
 
 	    // execute debugger thread command e.g. stepIn, stepOver
 	    client[type]();
 
 	    return dispatch({
-	      type: _constants2.default.COMMAND,
+	      type: "COMMAND",
 	      value: undefined
 	    });
 	  };
 	}
 
 	/**
 	 * StepIn
 	 * @memberof actions/pause
@@ -19652,34 +19833,34 @@ return /******/ (function(modules) { // 
 	function breakOnNext() {
 	  return (_ref11) => {
 	    var dispatch = _ref11.dispatch,
 	        client = _ref11.client;
 
 	    client.breakOnNext();
 
 	    return dispatch({
-	      type: _constants2.default.BREAK_ON_NEXT,
+	      type: "BREAK_ON_NEXT",
 	      value: true
 	    });
 	  };
 	}
 
 	/**
 	 * @memberof actions/pause
 	 * @static
 	 */
 	function selectFrame(frame) {
 	  return (_ref12) => {
 	    var dispatch = _ref12.dispatch;
 
 	    dispatch((0, _expressions.evaluateExpressions)(frame.id));
 	    dispatch((0, _sources.selectSource)(frame.location.sourceId, { line: frame.location.line }));
 	    dispatch({
-	      type: _constants2.default.SELECT_FRAME,
+	      type: "SELECT_FRAME",
 	      frame
 	    });
 	  };
 	}
 
 	/**
 	 * @memberof actions/pause
 	 * @static
@@ -19692,17 +19873,17 @@ return /******/ (function(modules) { // 
 
 	    var objectId = object.actor || object.objectId;
 
 	    if ((0, _selectors.getLoadedObject)(getState(), objectId)) {
 	      return;
 	    }
 
 	    dispatch({
-	      type: _constants2.default.LOAD_OBJECT_PROPERTIES,
+	      type: "LOAD_OBJECT_PROPERTIES",
 	      objectId,
 	      [_promise.PROMISE]: client.getProperties(object)
 	    });
 	  };
 	}
 
 /***/ },
 /* 320 */
@@ -19711,30 +19892,24 @@ return /******/ (function(modules) { // 
 	"use strict";
 
 	Object.defineProperty(exports, "__esModule", {
 	  value: true
 	});
 	exports.willNavigate = willNavigate;
 	exports.navigated = navigated;
 
-	var _constants = __webpack_require__(229);
-
-	var _constants2 = _interopRequireDefault(_constants);
-
 	var _editor = __webpack_require__(257);
 
 	var _sources = __webpack_require__(232);
 
 	var _utils = __webpack_require__(234);
 
 	var _sources2 = __webpack_require__(254);
 
-	function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
-
 	function _asyncToGenerator(fn) { return function () { var gen = fn.apply(this, arguments); return new Promise(function (resolve, reject) { function step(key, arg) { try { var info = gen[key](arg); var value = info.value; } catch (error) { reject(error); return; } if (info.done) { resolve(value); } else { return Promise.resolve(value).then(function (value) { step("next", value); }, function (err) { step("throw", err); }); } } return step("next"); }); }; }
 
 	/**
 	 * Redux actions for the navigation state
 	 * @module actions/navigation
 	 */
 
 	/**
@@ -19748,17 +19923,17 @@ return /******/ (function(modules) { // 
 	          getState = _ref2.getState,
 	          client = _ref2.client,
 	          sourceMaps = _ref2.sourceMaps;
 
 	      yield sourceMaps.clearSourceMaps();
 	      (0, _editor.clearDocuments)();
 
 	      dispatch({
-	        type: _constants2.default.NAVIGATE,
+	        type: "NAVIGATE",
 	        url: event.url
 	      });
 	    });
 
 	    return function (_x) {
 	      return _ref.apply(this, arguments);
 	    };
 	  })();
@@ -19804,187 +19979,175 @@ return /******/ (function(modules) { // 
 	exports.setSelectedSymbolType = setSelectedSymbolType;
 	exports.setFileSearchQuery = setFileSearchQuery;
 	exports.toggleFileSearchModifier = toggleFileSearchModifier;
 	exports.showSource = showSource;
 	exports.togglePaneCollapse = togglePaneCollapse;
 	exports.highlightLineRange = highlightLineRange;
 	exports.clearHighlightLineRange = clearHighlightLineRange;
 
-	var _constants = __webpack_require__(229);
-
-	var _constants2 = _interopRequireDefault(_constants);
-
 	var _selectors = __webpack_require__(242);
 
-	function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
-
 	function toggleProjectSearch(toggleValue) {
 	  return (_ref) => {
 	    var dispatch = _ref.dispatch,
 	        getState = _ref.getState;
 
 	    var projectSearchState = (0, _selectors.getProjectSearchState)(getState());
 	    if (toggleValue === undefined) {
 	      return dispatch({
-	        type: _constants2.default.TOGGLE_PROJECT_SEARCH,
+	        type: "TOGGLE_PROJECT_SEARCH",
 	        value: !projectSearchState
 	      });
 	    }
 
 	    if (projectSearchState == toggleValue) {
 	      return;
 	    }
 
 	    dispatch({
-	      type: _constants2.default.TOGGLE_PROJECT_SEARCH,
+	      type: "TOGGLE_PROJECT_SEARCH",
 	      value: toggleValue
 	    });
 	  };
 	}
 
 	function toggleFileSearch(toggleValue) {
 	  return (_ref2) => {
 	    var dispatch = _ref2.dispatch,
 	        getState = _ref2.getState;
 
 	    if (toggleValue != null) {
 	      dispatch({
-	        type: _constants2.default.TOGGLE_FILE_SEARCH,
+	        type: "TOGGLE_FILE_SEARCH",
 	        value: toggleValue
 	      });
 	    } else {
 	      dispatch({
-	        type: _constants2.default.TOGGLE_FILE_SEARCH,
+	        type: "TOGGLE_FILE_SEARCH",
 	        value: !(0, _selectors.getFileSearchState)(getState())
 	      });
 	    }
 	  };
 	}
 
 	function toggleSymbolSearch(toggleValue) {
 	  return (_ref3) => {
 	    var dispatch = _ref3.dispatch,
 	        getState = _ref3.getState;
 
 	    dispatch({
-	      type: _constants2.default.TOGGLE_SYMBOL_SEARCH,
+	      type: "TOGGLE_SYMBOL_SEARCH",
 	      value: toggleValue
 	    });
 	  };
 	}
 
 	function toggleFrameworkGrouping(toggleValue) {
 	  return (_ref4) => {
 	    var dispatch = _ref4.dispatch,
 	        getState = _ref4.getState;
 
 	    dispatch({
-	      type: _constants2.default.TOGGLE_FRAMEWORK_GROUPING,
+	      type: "TOGGLE_FRAMEWORK_GROUPING",
 	      value: toggleValue
 	    });
 	  };
 	}
 
 	function setSelectedSymbolType(symbolType) {
 	  return (_ref5) => {
 	    var dispatch = _ref5.dispatch,
 	        getState = _ref5.getState;
 
 	    dispatch({
-	      type: _constants2.default.SET_SYMBOL_SEARCH_TYPE,
+	      type: "SET_SYMBOL_SEARCH_TYPE",
 	      symbolType
 	    });
 	  };
 	}
 
 	function setFileSearchQuery(query) {
 	  return {
-	    type: _constants2.default.UPDATE_FILE_SEARCH_QUERY,
+	    type: "UPDATE_FILE_SEARCH_QUERY",
 	    query
 	  };
 	}
 
 	function toggleFileSearchModifier(modifier) {
-	  return { type: _constants2.default.TOGGLE_FILE_SEARCH_MODIFIER, modifier };
+	  return { type: "TOGGLE_FILE_SEARCH_MODIFIER", modifier };
 	}
 
 	function showSource(sourceId) {
 	  return (_ref6) => {
 	    var dispatch = _ref6.dispatch,
 	        getState = _ref6.getState;
 
 	    var source = (0, _selectors.getSource)(getState(), sourceId);
 	    dispatch({
-	      type: _constants2.default.SHOW_SOURCE,
+	      type: "SHOW_SOURCE",
 	      sourceUrl: source.get("url")
 	    });
 	  };
 	}
 
 	function togglePaneCollapse(position, paneCollapsed) {
 	  return {
-	    type: _constants2.default.TOGGLE_PANE,
+	    type: "TOGGLE_PANE",
 	    position,
 	    paneCollapsed
 	  };
 	}
 
 	/**
 	 * @memberof actions/sources
 	 * @static
 	 */
 	function highlightLineRange(location) {
 	  return {
-	    type: _constants2.default.HIGHLIGHT_LINES,
+	    type: "HIGHLIGHT_LINES",
 	    location
 	  };
 	}
 
 	/**
 	 * @memberof actions/sources
 	 * @static
 	 */
 	function clearHighlightLineRange() {
 	  return {
-	    type: _constants2.default.CLEAR_HIGHLIGHT_LINES
+	    type: "CLEAR_HIGHLIGHT_LINES"
 	  };
 	}
 
 /***/ },
 /* 322 */
-/***/ function(module, exports, __webpack_require__) {
+/***/ function(module, exports) {
 
 	"use strict";
 
 	Object.defineProperty(exports, "__esModule", {
 	  value: true
 	});
 	exports.recordCoverage = recordCoverage;
 
-	var _constants = __webpack_require__(229);
-
-	var _constants2 = _interopRequireDefault(_constants);
-
-	function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
-
 	function _asyncToGenerator(fn) { return function () { var gen = fn.apply(this, arguments); return new Promise(function (resolve, reject) { function step(key, arg) { try { var info = gen[key](arg); var value = info.value; } catch (error) { reject(error); return; } if (info.done) { resolve(value); } else { return Promise.resolve(value).then(function (value) { step("next", value); }, function (err) { step("throw", err); }); } } return step("next"); }); }; }
 
 	function recordCoverage() {
 	  return (() => {
 	    var _ref = _asyncToGenerator(function* (_ref2) {
 	      var dispatch = _ref2.dispatch,
 	          getState = _ref2.getState,
 	          client = _ref2.client;
 
 	      var _ref3 = yield client.recordCoverage(),
 	          coverage = _ref3.coverage;
 
 	      return dispatch({
-	        type: _constants2.default.RECORD_COVERAGE,
+	        type: "RECORD_COVERAGE",
 	        value: { coverage }
 	      });
 	    });
 
 	    return function (_x) {
 	      return _ref.apply(this, arguments);
 	    };
 	  })();
@@ -20043,17 +20206,17 @@ return /******/ (function(modules) { // 
 	// 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.
 
 	'use strict';
 
-	var punycode = __webpack_require__(916);
+	var punycode = __webpack_require__(335);
 	var util = __webpack_require__(336);
 
 	exports.parse = urlParse;
 	exports.resolve = urlResolve;
 	exports.resolveObject = urlResolveObject;
 	exports.format = urlFormat;
 
 	exports.Url = Url;
@@ -20755,17 +20918,551 @@ return /******/ (function(modules) { // 
 	    }
 	    host = host.substr(0, host.length - port.length);
 	  }
 	  if (host) this.hostname = host;
 	};
 
 
 /***/ },
-/* 335 */,
+/* 335 */
+/***/ function(module, exports, __webpack_require__) {
+
+	var __WEBPACK_AMD_DEFINE_RESULT__;/* WEBPACK VAR INJECTION */(function(module, global) {/*! https://mths.be/punycode v1.3.2 by @mathias */
+	;(function(root) {
+
+		/** Detect free variables */
+		var freeExports = typeof exports == 'object' && exports &&
+			!exports.nodeType && exports;
+		var freeModule = typeof module == 'object' && module &&
+			!module.nodeType && module;
+		var freeGlobal = typeof global == 'object' && global;
+		if (
+			freeGlobal.global === freeGlobal ||
+			freeGlobal.window === freeGlobal ||
+			freeGlobal.self === freeGlobal
+		) {
+			root = freeGlobal;
+		}
+
+		/**
+		 * The `punycode` object.
+		 * @name punycode
+		 * @type Object
+		 */
+		var punycode,
+
+		/** Highest positive signed 32-bit float value */
+		maxInt = 2147483647, // aka. 0x7FFFFFFF or 2^31-1
+
+		/** Bootstring parameters */
+		base = 36,
+		tMin = 1,
+		tMax = 26,
+		skew = 38,
+		damp = 700,
+		initialBias = 72,
+		initialN = 128, // 0x80
+		delimiter = '-', // '\x2D'
+
+		/** Regular expressions */
+		regexPunycode = /^xn--/,
+		regexNonASCII = /[^\x20-\x7E]/, // unprintable ASCII chars + non-ASCII chars
+		regexSeparators = /[\x2E\u3002\uFF0E\uFF61]/g, // RFC 3490 separators
+
+		/** Error messages */
+		errors = {
+			'overflow': 'Overflow: input needs wider integers to process',
+			'not-basic': 'Illegal input >= 0x80 (not a basic code point)',
+			'invalid-input': 'Invalid input'
+		},
+
+		/** Convenience shortcuts */
+		baseMinusTMin = base - tMin,
+		floor = Math.floor,
+		stringFromCharCode = String.fromCharCode,
+
+		/** Temporary variable */
+		key;
+
+		/*--------------------------------------------------------------------------*/
+
+		/**
+		 * A generic error utility function.
+		 * @private
+		 * @param {String} type The error type.
+		 * @returns {Error} Throws a `RangeError` with the applicable error message.
+		 */
+		function error(type) {
+			throw RangeError(errors[type]);
+		}
+
+		/**
+		 * A generic `Array#map` utility function.
+		 * @private
+		 * @param {Array} array The array to iterate over.
+		 * @param {Function} callback The function that gets called for every array
+		 * item.
+		 * @returns {Array} A new array of values returned by the callback function.
+		 */
+		function map(array, fn) {
+			var length = array.length;
+			var result = [];
+			while (length--) {
+				result[length] = fn(array[length]);
+			}
+			return result;
+		}
+
+		/**
+		 * A simple `Array#map`-like wrapper to work with domain name strings or email
+		 * addresses.
+		 * @private
+		 * @param {String} domain The domain name or email address.
+		 * @param {Function} callback The function that gets called for every
+		 * character.
+		 * @returns {Array} A new string of characters returned by the callback
+		 * function.
+		 */
+		function mapDomain(string, fn) {
+			var parts = string.split('@');
+			var result = '';
+			if (parts.length > 1) {
+				// In email addresses, only the domain name should be punycoded. Leave
+				// the local part (i.e. everything up to `@`) intact.
+				result = parts[0] + '@';
+				string = parts[1];
+			}
+			// Avoid `split(regex)` for IE8 compatibility. See #17.
+			string = string.replace(regexSeparators, '\x2E');
+			var labels = string.split('.');
+			var encoded = map(labels, fn).join('.');
+			return result + encoded;
+		}
+
+		/**
+		 * Creates an array containing the numeric code points of each Unicode
+		 * character in the string. While JavaScript uses UCS-2 internally,
+		 * this function will convert a pair of surrogate halves (each of which
+		 * UCS-2 exposes as separate characters) into a single code point,
+		 * matching UTF-16.
+		 * @see `punycode.ucs2.encode`
+		 * @see <https://mathiasbynens.be/notes/javascript-encoding>
+		 * @memberOf punycode.ucs2
+		 * @name decode
+		 * @param {String} string The Unicode input string (UCS-2).
+		 * @returns {Array} The new array of code points.
+		 */
+		function ucs2decode(string) {
+			var output = [],
+			    counter = 0,
+			    length = string.length,
+			    value,
+			    extra;
+			while (counter < length) {
+				value = string.charCodeAt(counter++);
+				if (value >= 0xD800 && value <= 0xDBFF && counter < length) {
+					// high surrogate, and there is a next character
+					extra = string.charCodeAt(counter++);
+					if ((extra & 0xFC00) == 0xDC00) { // low surrogate
+						output.push(((value & 0x3FF) << 10) + (extra & 0x3FF) + 0x10000);
+					} else {
+						// unmatched surrogate; only append this code unit, in case the next
+						// code unit is the high surrogate of a surrogate pair
+						output.push(value);
+						counter--;
+					}
+				} else {
+					output.push(value);
+				}
+			}
+			return output;
+		}
+
+		/**
+		 * Creates a string based on an array of numeric code points.
+		 * @see `punycode.ucs2.decode`
+		 * @memberOf punycode.ucs2
+		 * @name encode
+		 * @param {Array} codePoints The array of numeric code points.
+		 * @returns {String} The new Unicode string (UCS-2).
+		 */
+		function ucs2encode(array) {
+			return map(array, function(value) {
+				var output = '';
+				if (value > 0xFFFF) {
+					value -= 0x10000;
+					output += stringFromCharCode(value >>> 10 & 0x3FF | 0xD800);
+					value = 0xDC00 | value & 0x3FF;
+				}
+				output += stringFromCharCode(value);
+				return output;
+			}).join('');
+		}
+
+		/**
+		 * Converts a basic code point into a digit/integer.
+		 * @see `digitToBasic()`
+		 * @private
+		 * @param {Number} codePoint The basic numeric code point value.
+		 * @returns {Number} The numeric value of a basic code point (for use in
+		 * representing integers) in the range `0` to `base - 1`, or `base` if
+		 * the code point does not represent a value.
+		 */
+		function basicToDigit(codePoint) {
+			if (codePoint - 48 < 10) {
+				return codePoint - 22;
+			}
+			if (codePoint - 65 < 26) {
+				return codePoint - 65;
+			}
+			if (codePoint - 97 < 26) {
+				return codePoint - 97;
+			}
+			return base;
+		}
+
+		/**
+		 * Converts a digit/integer into a basic code point.
+		 * @see `basicToDigit()`
+		 * @private
+		 * @param {Number} digit The numeric value of a basic code point.
+		 * @returns {Number} The basic code point whose value (when used for
+		 * representing integers) is `digit`, which needs to be in the range
+		 * `0` to `base - 1`. If `flag` is non-zero, the uppercase form is
+		 * used; else, the lowercase form is used. The behavior is undefined
+		 * if `flag` is non-zero and `digit` has no uppercase form.
+		 */
+		function digitToBasic(digit, flag) {
+			//  0..25 map to ASCII a..z or A..Z
+			// 26..35 map to ASCII 0..9
+			return digit + 22 + 75 * (digit < 26) - ((flag != 0) << 5);
+		}
+
+		/**
+		 * Bias adaptation function as per section 3.4 of RFC 3492.
+		 * http://tools.ietf.org/html/rfc3492#section-3.4
+		 * @private
+		 */
+		function adapt(delta, numPoints, firstTime) {
+			var k = 0;
+			delta = firstTime ? floor(delta / damp) : delta >> 1;
+			delta += floor(delta / numPoints);
+			for (/* no initialization */; delta > baseMinusTMin * tMax >> 1; k += base) {
+				delta = floor(delta / baseMinusTMin);
+			}
+			return floor(k + (baseMinusTMin + 1) * delta / (delta + skew));
+		}
+
+		/**
+		 * Converts a Punycode string of ASCII-only symbols to a string of Unicode
+		 * symbols.
+		 * @memberOf punycode
+		 * @param {String} input The Punycode string of ASCII-only symbols.
+		 * @returns {String} The resulting string of Unicode symbols.
+		 */
+		function decode(input) {
+			// Don't use UCS-2
+			var output = [],
+			    inputLength = input.length,
+			    out,
+			    i = 0,
+			    n = initialN,
+			    bias = initialBias,
+			    basic,
+			    j,
+			    index,
+			    oldi,
+			    w,
+			    k,
+			    digit,
+			    t,
+			    /** Cached calculation results */
+			    baseMinusT;
+
+			// Handle the basic code points: let `basic` be the number of input code
+			// points before the last delimiter, or `0` if there is none, then copy
+			// the first basic code points to the output.
+
+			basic = input.lastIndexOf(delimiter);
+			if (basic < 0) {
+				basic = 0;
+			}
+
+			for (j = 0; j < basic; ++j) {
+				// if it's not a basic code point
+				if (input.charCodeAt(j) >= 0x80) {
+					error('not-basic');
+				}
+				output.push(input.charCodeAt(j));
+			}
+
+			// Main decoding loop: start just after the last delimiter if any basic code
+			// points were copied; start at the beginning otherwise.
+
+			for (index = basic > 0 ? basic + 1 : 0; index < inputLength; /* no final expression */) {
+
+				// `index` is the index of the next character to be consumed.
+				// Decode a generalized variable-length integer into `delta`,
+				// which gets added to `i`. The overflow checking is easier
+				// if we increase `i` as we go, then subtract off its starting
+				// value at the end to obtain `delta`.
+				for (oldi = i, w = 1, k = base; /* no condition */; k += base) {
+
+					if (index >= inputLength) {
+						error('invalid-input');
+					}
+
+					digit = basicToDigit(input.charCodeAt(index++));
+
+					if (digit >= base || digit > floor((maxInt - i) / w)) {
+						error('overflow');
+					}
+
+					i += digit * w;
+					t = k <= bias ? tMin : (k >= bias + tMax ? tMax : k - bias);
+
+					if (digit < t) {
+						break;
+					}
+
+					baseMinusT = base - t;
+					if (w > floor(maxInt / baseMinusT)) {
+						error('overflow');
+					}
+
+					w *= baseMinusT;
+
+				}
+
+				out = output.length + 1;
+				bias = adapt(i - oldi, out, oldi == 0);
+
+				// `i` was supposed to wrap around from `out` to `0`,
+				// incrementing `n` each time, so we'll fix that now:
+				if (floor(i / out) > maxInt - n) {
+					error('overflow');
+				}
+
+				n += floor(i / out);
+				i %= out;
+
+				// Insert `n` at position `i` of the output
+				output.splice(i++, 0, n);
+
+			}
+
+			return ucs2encode(output);
+		}
+
+		/**
+		 * Converts a string of Unicode symbols (e.g. a domain name label) to a
+		 * Punycode string of ASCII-only symbols.
+		 * @memberOf punycode
+		 * @param {String} input The string of Unicode symbols.
+		 * @returns {String} The resulting Punycode string of ASCII-only symbols.
+		 */
+		function encode(input) {
+			var n,
+			    delta,
+			    handledCPCount,
+			    basicLength,
+			    bias,
+			    j,
+			    m,
+			    q,
+			    k,
+			    t,
+			    currentValue,
+			    output = [],
+			    /** `inputLength` will hold the number of code points in `input`. */
+			    inputLength,
+			    /** Cached calculation results */
+			    handledCPCountPlusOne,
+			    baseMinusT,
+			    qMinusT;
+
+			// Convert the input in UCS-2 to Unicode
+			input = ucs2decode(input);
+
+			// Cache the length
+			inputLength = input.length;
+
+			// Initialize the state
+			n = initialN;
+			delta = 0;
+			bias = initialBias;
+
+			// Handle the basic code points
+			for (j = 0; j < inputLength; ++j) {
+				currentValue = input[j];
+				if (currentValue < 0x80) {
+					output.push(stringFromCharCode(currentValue));
+				}
+			}
+
+			handledCPCount = basicLength = output.length;
+
+			// `handledCPCount` is the number of code points that have been handled;
+			// `basicLength` is the number of basic code points.
+
+			// Finish the basic string - if it is not empty - with a delimiter
+			if (basicLength) {
+				output.push(delimiter);
+			}
+
+			// Main encoding loop:
+			while (handledCPCount < inputLength) {
+
+				// All non-basic code points < n have been handled already. Find the next
+				// larger one:
+				for (m = maxInt, j = 0; j < inputLength; ++j) {
+					currentValue = input[j];
+					if (currentValue >= n && currentValue < m) {
+						m = currentValue;
+					}
+				}
+
+				// Increase `delta` enough to advance the decoder's <n,i> state to <m,0>,
+				// but guard against overflow
+				handledCPCountPlusOne = handledCPCount + 1;
+				if (m - n > floor((maxInt - delta) / handledCPCountPlusOne)) {
+					error('overflow');
+				}
+
+				delta += (m - n) * handledCPCountPlusOne;
+				n = m;
+
+				for (j = 0; j < inputLength; ++j) {
+					currentValue = input[j];
+
+					if (currentValue < n && ++delta > maxInt) {
+						error('overflow');
+					}
+
+					if (currentValue == n) {
+						// Represent delta as a generalized variable-length integer
+						for (q = delta, k = base; /* no condition */; k += base) {
+							t = k <= bias ? tMin : (k >= bias + tMax ? tMax : k - bias);
+							if (q < t) {
+								break;
+							}
+							qMinusT = q - t;
+							baseMinusT = base - t;
+							output.push(
+								stringFromCharCode(digitToBasic(t + qMinusT % baseMinusT, 0))
+							);
+							q = floor(qMinusT / baseMinusT);
+						}
+
+						output.push(stringFromCharCode(digitToBasic(q, 0)));
+						bias = adapt(delta, handledCPCountPlusOne, handledCPCount == basicLength);
+						delta = 0;
+						++handledCPCount;
+					}
+				}
+
+				++delta;
+				++n;
+
+			}
+			return output.join('');
+		}
+
+		/**
+		 * Converts a Punycode string representing a domain name or an email address
+		 * to Unicode. Only the Punycoded parts of the input will be converted, i.e.
+		 * it doesn't matter if you call it on a string that has already been
+		 * converted to Unicode.
+		 * @memberOf punycode
+		 * @param {String} input The Punycoded domain name or email address to
+		 * convert to Unicode.
+		 * @returns {String} The Unicode representation of the given Punycode
+		 * string.
+		 */
+		function toUnicode(input) {
+			return mapDomain(input, function(string) {
+				return regexPunycode.test(string)
+					? decode(string.slice(4).toLowerCase())
+					: string;
+			});
+		}
+
+		/**
+		 * Converts a Unicode string representing a domain name or an email address to
+		 * Punycode. Only the non-ASCII parts of the domain name will be converted,
+		 * i.e. it doesn't matter if you call it with a domain that's already in
+		 * ASCII.
+		 * @memberOf punycode
+		 * @param {String} input The domain name or email address to convert, as a
+		 * Unicode string.
+		 * @returns {String} The Punycode representation of the given domain name or
+		 * email address.
+		 */
+		function toASCII(input) {
+			return mapDomain(input, function(string) {
+				return regexNonASCII.test(string)
+					? 'xn--' + encode(string)
+					: string;
+			});
+		}
+
+		/*--------------------------------------------------------------------------*/
+
+		/** Define the public API */
+		punycode = {
+			/**
+			 * A string representing the current Punycode.js version number.
+			 * @memberOf punycode
+			 * @type String
+			 */
+			'version': '1.3.2',
+			/**
+			 * An object of methods to convert from JavaScript's internal character
+			 * representation (UCS-2) to Unicode code points, and back.
+			 * @see <https://mathiasbynens.be/notes/javascript-encoding>
+			 * @memberOf punycode
+			 * @type Object
+			 */
+			'ucs2': {
+				'decode': ucs2decode,
+				'encode': ucs2encode
+			},
+			'decode': decode,
+			'encode': encode,
+			'toASCII': toASCII,
+			'toUnicode': toUnicode
+		};
+
+		/** Expose `punycode` */
+		// Some AMD build optimizers, like r.js, check for specific condition patterns
+		// like the following:
+		if (
+			true
+		) {
+			!(__WEBPACK_AMD_DEFINE_RESULT__ = function() {
+				return punycode;
+			}.call(exports, __webpack_require__, exports, module), __WEBPACK_AMD_DEFINE_RESULT__ !== undefined && (module.exports = __WEBPACK_AMD_DEFINE_RESULT__));
+		} else if (freeExports && freeModule) {
+			if (module.exports == freeExports) { // in Node.js or RingoJS v0.8.0+
+				freeModule.exports = punycode;
+			} else { // in Narwhal or RingoJS v0.7.0-
+				for (key in punycode) {
+					punycode.hasOwnProperty(key) && (freeExports[key] = punycode[key]);
+				}
+			}
+		} else { // in Rhino or a web browser
+			root.punycode = punycode;
+		}
+
+	}(this));
+
+	/* WEBPACK VAR INJECTION */}.call(exports, __webpack_require__(51)(module), (function() { return this; }())))
+
+/***/ },
 /* 336 */
 /***/ function(module, exports) {
 
 	'use strict';
 
 	module.exports = {
 	  isString: function(arg) {
 	    return typeof(arg) === 'string';
@@ -20957,17 +21654,17 @@ return /******/ (function(modules) { // 
 	"use strict";
 
 	Object.defineProperty(exports, "__esModule", {
 	  value: true
 	});
 
 	var _react = __webpack_require__(2);
 
-	var _reactDom = __webpack_require__(22);
+	var _reactDom = __webpack_require__(31);
 
 	var _reactDom2 = _interopRequireDefault(_reactDom);
 
 	var _fuzzaldrinPlus = __webpack_require__(161);
 
 	var _classnames = __webpack_require__(175);
 
 	var _classnames2 = _interopRequireDefault(_classnames);
@@ -21026,16 +21723,17 @@ return /******/ (function(modules) { // 
 	  }
 
 	  getSearchResults() {
 	    var inputValue = this.state.inputValue;
 
 	    if (inputValue == "") {
 	      return [];
 	    }
+
 	    return (0, _fuzzaldrinPlus.filter)(this.props.items, this.state.inputValue, {
 	      key: "value"
 	    });
 	  }
 
 	  onKeyDown(e) {
 	    var searchResults = this.getSearchResults(),
 	        resultCount = searchResults.length;
@@ -21105,31 +21803,20 @@ return /******/ (function(modules) { // 
 	      onBlur: () => this.setState({ focused: false }),
 	      onKeyDown: this.onKeyDown,
 	      handleClose: this.props.close
 	    }), this.renderResults(searchResults));
 	  }
 	}
 
 	exports.default = Autocomplete;
-	Autocomplete.propTypes = {
-	  selectItem: _react.PropTypes.func.isRequired,
-	  onSelectedItem: _react.PropTypes.func,
-	  items: _react.PropTypes.array,
-	  close: _react.PropTypes.func.isRequired,
-	  inputValue: _react.PropTypes.string.isRequired,
-	  placeholder: _react.PropTypes.string,
-	  size: _react.PropTypes.string
-	};
-
-	Autocomplete.displayName = "Autocomplete";
-
 	Autocomplete.defaultProps = {
 	  size: ""
 	};
+	Autocomplete.displayName = "Autocomplete";
 
 /***/ },
 /* 343 */
 /***/ function(module, exports, __webpack_require__) {
 
 	"use strict";
 
 	Object.defineProperty(exports, "__esModule", {
@@ -21660,22 +22347,16 @@ return /******/ (function(modules) { // 
 	    className,
 	    title: tooltip,
 	    key: type
 	  }, (0, _Svg2.default)(type));
 	};
 
 	class SearchInput extends _react.Component {
 
-	  static get defaultProps() {
-	    return {
-	      size: ""
-	    };
-	  }
-
 	  renderSvg() {
 	    var _props = this.props,
 	        count = _props.count,
 	        query = _props.query;
 
 
 	    if (count == 0 && query.trim() != "") {
 	      return (0, _Svg2.default)("sad-face");
@@ -21741,30 +22422,18 @@ return /******/ (function(modules) { // 
 	      spellCheck: false
 	    }), _react.DOM.div({ className: "summary" }, query != "" ? summaryMsg : ""), this.renderNav(), (0, _Close2.default)({
 	      handleClick: handleClose,
 	      buttonClass: size
 	    }));
 	  }
 	}
 
-	SearchInput.propTypes = {
-	  query: _react.PropTypes.string.isRequired,
-	  count: _react.PropTypes.number.isRequired,
-	  placeholder: _react.PropTypes.string.isRequired,
-	  summaryMsg: _react.PropTypes.string.isRequired,
-	  onChange: _react.PropTypes.func.isRequired,
-	  handleClose: _react.PropTypes.func.isRequired,
-	  onKeyUp: _react.PropTypes.func,
-	  onKeyDown: _react.PropTypes.func,
-	  onFocus: _react.PropTypes.func,
-	  onBlur: _react.PropTypes.func,
-	  size: _react.PropTypes.string,
-	  handleNext: _react.PropTypes.func,
-	  handlePrev: _react.PropTypes.func
+	SearchInput.defaultProps = {
+	  size: ""
 	};
 
 	exports.default = SearchInput;
 
 /***/ },
 /* 378 */
 /***/ function(module, exports, __webpack_require__) {
 
@@ -21840,45 +22509,44 @@ return /******/ (function(modules) { // 
 	class ResultList extends _react.Component {
 
 	  constructor(props) {
 	    super(props);
 	    this.renderListItem = this.renderListItem.bind(this);
 	  }
 
 	  renderListItem(item, index) {
+	    var _props = this.props,
+	        selectItem = _props.selectItem,
+	        selected = _props.selected;
+
 	    return _react.DOM.li({
-	      onClick: event => this.props.selectItem(event, item, index),
+	      onClick: event => selectItem(event, item, index),
 	      key: `${item.id}${item.value}${index}`,
 	      ref: index,
 	      title: item.value,
 	      className: (0, _classnames2.default)({
-	        selected: index === this.props.selected
+	        selected: index === selected
 	      })
 	    }, _react.DOM.div({ className: "title" }, item.title), _react.DOM.div({ className: "subtitle" }, item.subtitle));
 	  }
 
 	  render() {
-	    var size = this.props.size;
+	    var _props2 = this.props,
+	        size = _props2.size,
+	        items = _props2.items;
 
 	    size = size || "";
 	    return _react.DOM.ul({
 	      className: `result-list ${size}`
-	    }, this.props.items.map(this.renderListItem));
+	    }, items.map(this.renderListItem));
 	  }
 	}
 
 	exports.default = ResultList;
-	ResultList.propTypes = {
-	  items: _react.PropTypes.array.isRequired,
-	  selected: _react.PropTypes.number.isRequired,
-	  selectItem: _react.PropTypes.func.isRequired,
-	  size: _react.PropTypes.string
-	};
-
 	ResultList.defaultProps = {
 	  size: ""
 	};
 
 /***/ },
 /* 384 */
 /***/ function(module, exports) {
 
@@ -21913,26 +22581,26 @@ return /******/ (function(modules) { // 
 	var _reactRedux = __webpack_require__(151);
 
 	var _text = __webpack_require__(389);
 
 	var _actions = __webpack_require__(244);
 
 	var _actions2 = _interopRequireDefault(_actions);
 
-	var _Svg = __webpack_require__(344);
-
-	var _Svg2 = _interopRequireDefault(_Svg);
-
 	var _selectors = __webpack_require__(242);
 
 	var _devtoolsConfig = __webpack_require__(828);
 
 	__webpack_require__(424);
 
+	var _classnames = __webpack_require__(175);
+
+	var _classnames2 = _interopRequireDefault(_classnames);
+
 	var _Outline2 = __webpack_require__(921);
 
 	var _Outline3 = _interopRequireDefault(_Outline2);
 
 	var _SourcesTree2 = __webpack_require__(390);
 
 	var _SourcesTree3 = _interopRequireDefault(_SourcesTree2);
 
@@ -21944,52 +22612,48 @@ return /******/ (function(modules) { // 
 
 	class Sources extends _react.Component {
 
 	  constructor(props) {
 	    super(props);
 	    this.state = { selectedPane: "sources" };
 
 	    this.renderShortcut = this.renderShortcut.bind(this);
-	    this.togglePane = this.togglePane.bind(this);
+	    this.showPane = this.showPane.bind(this);
 	    this.renderFooter = this.renderFooter.bind(this);
 	  }
 
-	  togglePane() {
-	    var selectedPane = this.state.selectedPane === "sources" ? "outline" : "sources";
-
+	  showPane(selectedPane) {
 	    this.setState({ selectedPane });
 	  }
 
-	  renderOutlineToggleButton() {
+	  renderOutlineTabs() {
 	    if (!(0, _devtoolsConfig.isEnabled)("outline")) {
 	      return;
 	    }
 
-	    var selectedPane = this.state.selectedPane;
-
-	    var showSourcesTooltip = L10N.getStr("sourcesPane.showSourcesTooltip");
-	    var showOutlineTooltip = L10N.getStr("sourcesPane.showOutlineTooltip");
-
-	    var isSourcesPaneSelected = selectedPane === "sources";
-	    var tooltip = isSourcesPaneSelected ? showOutlineTooltip : showSourcesTooltip;
-	    var type = isSourcesPaneSelected ? "showSources" : "showOutline";
-
-	    return _react.DOM.button({
-	      className: "action",
-	      onClick: this.togglePane,
-	      key: type,
-	      title: tooltip
-	    }, (0, _Svg2.default)(type));
+	    return [_react.DOM.div({
+	      className: (0, _classnames2.default)("tab", {
+	        active: this.state.selectedPane === "sources"
+	      }),
+	      onClick: () => this.showPane("sources"),
+	      key: "sources-tab"
+	    }, "Sources View"), _react.DOM.div({
+	      className: (0, _classnames2.default)("tab", {
+	        active: this.state.selectedPane === "outline"
+	      }),
+	      onClick: () => this.showPane("outline"),
+	      key: "outline-tab"
+	    }, "Outline View")];
 	  }
 
 	  renderFooter() {
 	    return _react.DOM.div({
 	      className: "source-footer"
-	    }, _react.DOM.div({ className: "commands" }, this.renderOutlineToggleButton()));
+	    }, this.renderOutlineTabs());
 	  }
 
 	  renderShortcut() {
 	    if (this.props.horizontal) {
 	      return _react.DOM.span({
 	        className: "sources-header-info",
 	        dir: "ltr",
 	        onClick: () => this.props.toggleProjectSearch()
@@ -22308,17 +22972,17 @@ return /******/ (function(modules) { // 
 	      getChildren: item => {
 	        if ((0, _sourcesTree.nodeHasChildren)(item)) {
 	          return item.contents;
 	        }
 	        return [];
 	      },
 	      getRoots: () => sourceTree.contents,
 	      getKey: (item, i) => item.path,
-	      itemHeight: 18,
+	      itemHeight: 19,
 	      autoExpandDepth: 1,
 	      autoExpandAll: false,
 	      onFocus: this.focusItem,
 	      listItems,
 	      highlightItems,
 	      renderItem: this.renderItem
 	    });
 
@@ -23903,17 +24567,17 @@ return /******/ (function(modules) { // 
 	"use strict";
 
 	Object.defineProperty(exports, "__esModule", {
 	  value: true
 	});
 
 	var _react = __webpack_require__(2);
 
-	var _reactDom = __webpack_require__(22);
+	var _reactDom = __webpack_require__(31);
 
 	var _reactDom2 = _interopRequireDefault(_reactDom);
 
 	var _reactImmutableProptypes = __webpack_require__(150);
 
 	var _reactImmutableProptypes2 = _interopRequireDefault(_reactImmutableProptypes);
 
 	var _redux = __webpack_require__(3);
@@ -23941,19 +24605,27 @@ return /******/ (function(modules) { // 
 	var _EditorMenu = __webpack_require__(656);
 
 	var _EditorMenu2 = _interopRequireDefault(_EditorMenu);
 
 	var _ConditionalPanel = __webpack_require__(711);
 
 	var _devtoolsLaunchpad = __webpack_require__(131);
 
+	var _range = __webpack_require__(1026);
+
+	var _range2 = _interopRequireDefault(_range);
+
+	var _flatMap = __webpack_require__(1067);
+
+	var _flatMap2 = _interopRequireDefault(_flatMap);
+
 	var _selectors = __webpack_require__(242);
 
-	var _breakpoints = __webpack_require__(236);
+	var _breakpoint = __webpack_require__(1057);
 
 	var _actions = __webpack_require__(244);
 
 	var _actions2 = _interopRequireDefault(_actions);
 
 	var _Footer2 = __webpack_require__(427);
 
 	var _Footer3 = _interopRequireDefault(_Footer2);
@@ -23979,18 +24651,16 @@ return /******/ (function(modules) { // 
 	var _ColumnBreakpoint3 = _interopRequireDefault(_ColumnBreakpoint2);
 
 	var _HitMarker2 = __webpack_require__(715);
 
 	var _HitMarker3 = _interopRequireDefault(_HitMarker2);
 
 	var _editor = __webpack_require__(257);
 
-	var _scopes = __webpack_require__(732);
-
 	__webpack_require__(716);
 
 	__webpack_require__(1046);
 
 	var _devtoolsSourceEditor = __webpack_require__(994);
 
 	function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
 
@@ -24027,18 +24697,17 @@ return /******/ (function(modules) { // 
 	    this.lastJumpLine = null;
 
 	    this.state = {
 	      searchResults: {
 	        index: -1,
 	        count: 0
 	      },
 	      highlightedLineRange: null,
-	      selectedToken: null,
-	      selectedExpression: null
+	      selectedToken: null
 	    };
 
 	    var self = this;
 	    self.closeConditionalPanel = this.closeConditionalPanel.bind(this);
 	    self.onEscape = this.onEscape.bind(this);
 	    self.onGutterClick = this.onGutterClick.bind(this);
 	    self.onGutterContextMenu = this.onGutterContextMenu.bind(this);
 	    self.onScroll = this.onScroll.bind(this);
@@ -24069,16 +24738,20 @@ return /******/ (function(modules) { // 
 	        this.showMessage("");
 	      }
 	    } else if (!(0, _editor.isTextForSource)(sourceText)) {
 	      this.showMessage(sourceText.get("error") || L10N.getStr("loadingText"));
 	    } else if (this.props.sourceText !== sourceText) {
 	      this.showSourceText(sourceText, selectedLocation);
 	    }
 
+	    if (this.props.outOfScopeLocations !== nextProps.outOfScopeLocations) {
+	      (0, _editor.clearLineClass)(this.editor.codeMirror, "out-of-scope");
+	    }
+
 	    this.setDebugLine(nextProps.selectedFrame, selectedLocation);
 	    (0, _editor.resizeBreakpointGutter)(this.editor.codeMirror);
 	  }
 
 	  setupEditor() {
 	    var editor = (0, _editor.createEditor)();
 
 	    // disables the default search shortcuts
@@ -24225,32 +24898,36 @@ return /******/ (function(modules) { // 
 
 	  /*
 	   * The default Esc command is overridden in the CodeMirror keymap to allow
 	   * the Esc keypress event to be catched by the toolbox and trigger the
 	   * split console. Restore it here, but preventDefault if and only if there
 	   * is a multiselection.
 	   */
 	  onEscape(key, e) {
+	    if (!this.editor) {
+	      return;
+	    }
+
 	    var codeMirror = this.editor.codeMirror;
 
 	    if (codeMirror.listSelections().length > 1) {
 	      codeMirror.execCommand("singleSelection");
 	      e.preventDefault();
 	    }
 	  }
 
-	  onScroll(e) {
-	    return this.setState({ selectedToken: null, selectedExpression: null });
+	  onScroll() {
+	    this.clearPreviewSelection();
 	  }
 
 	  onMouseOver(e) {
 	    var target = e.target;
 
-	    if (!target.parentElement.closest(".CodeMirror-line")) {
+	    if (!this.inSelectedFrameSource() || !target.parentElement.closest(".CodeMirror-line") || target.parentElement.closest(".out-of-scope")) {
 	      return;
 	    }
 	    this.previewSelectedToken(e);
 	  }
 
 	  onTokenClick(e) {
 	    var target = e.target;
 
@@ -24272,66 +24949,42 @@ return /******/ (function(modules) { // 
 	    var codeMirror = this.editor.editor.codeMirror;
 
 	    var ctx = { ed: this.editor, cm: codeMirror };
 
 	    var direction = e.shiftKey ? "prev" : "next";
 	    (0, _editor.traverseResults)(e, ctx, query, direction, searchModifiers.toJS());
 	  }
 
+	  clearPreviewSelection() {
+	    this.props.clearSelection();
+	    return this.setState({ selectedToken: null });
+	  }
+
 	  previewSelectedToken(e) {
 	    var _this = this;
 
 	    return _asyncToGenerator(function* () {
 	      var _props3 = _this.props,
 	          selectedFrame = _props3.selectedFrame,
 	          selectedSource = _props3.selectedSource,
-	          pauseData = _props3.pauseData,
 	          sourceText = _props3.sourceText,
-	          addExpression = _props3.addExpression;
-	      var selectedToken = _this.state.selectedToken;
+	          setSelection = _props3.setSelection,
+	          selection = _props3.selection;
 
 	      var token = e.target;
-
-	      if (!selectedFrame || !sourceText || !selectedSource || selectedFrame.location.sourceId !== selectedSource.get("id")) {
-	        return;
-	      }
-
-	      if (selectedToken) {
-	        selectedToken.classList.remove("selected-token");
-	        _this.setState({ selectedToken: null, selectedExpression: null });
-	      }
-
-	      var _ref = yield (0, _editor.resolveToken)(_this.editor.codeMirror, token, sourceText, selectedFrame),
-	          expression = _ref.expression,
-	          inScope = _ref.inScope;
-
-	      if (!inScope) {
+	      var tokenText = token.innerText.trim();
+
+	      if (selection && selection.updating || !selectedFrame || !sourceText || !selectedSource || tokenText === "" || tokenText.match(/\s/) || selectedFrame.location.sourceId !== selectedSource.get("id")) {
 	        return;
 	      }
 
-	      var variables = (0, _scopes.getVisibleVariablesFromScope)(pauseData, selectedFrame);
-
-	      if (expression) {
-	        addExpression(expression.value, { visible: false });
-	      }
-
-	      var displayedExpression = (0, _editor.previewExpression)({
-	        expression: expression,
-	        variables,
-	        selectedFrame,
-	        tokenText: token.textContent
-	      });
-
-	      if (displayedExpression) {
-	        _this.setState({
-	          selectedToken: token,
-	          selectedExpression: displayedExpression
-	        });
-	      }
+	      var location = (0, _editor.getTokenLocation)(_this.editor.codeMirror, token);
+	      setSelection(tokenText, location);
+	      _this.setState({ selectedToken: token });
 	    })();
 	  }
 
 	  openMenu(event, codeMirror) {
 	    var _props4 = this.props,
 	        selectedSource = _props4.selectedSource,
 	        selectedLocation = _props4.selectedLocation,
 	        showSource = _props4.showSource,
@@ -24348,20 +25001,20 @@ return /******/ (function(modules) { // 
 	      showSource,
 	      jumpToMappedLocation,
 	      addExpression,
 	      toggleBlackBox,
 	      onGutterContextMenu: this.onGutterContextMenu
 	    });
 	  }
 
-	  updateSearchResults(_ref2) {
-	    var count = _ref2.count,
-	        _ref2$index = _ref2.index,
-	        index = _ref2$index === undefined ? -1 : _ref2$index;
+	  updateSearchResults(_ref) {
+	    var count = _ref.count,
+	        _ref$index = _ref.index,
+	        index = _ref$index === undefined ? -1 : _ref$index;
 
 	    this.setState({ searchResults: { count, index } });
 	  }
 
 	  onGutterClick(cm, line, gutter, ev) {
 	    var selectedSource = this.props.selectedSource;
 
 	    // ignore right clicks in the gutter
@@ -24426,17 +25079,17 @@ return /******/ (function(modules) { // 
 	    var panel = (0, _ConditionalPanel.renderConditionalPanel)({
 	      condition,
 	      setBreakpoint,
 	      closePanel: this.closeConditionalPanel
 	    });
 
 	    this.cbPanel = this.editor.codeMirror.addLineWidget(line, panel, {
 	      coverGutter: true,
-	      noHScroll: true
+	      noHScroll: false
 	    });
 	    this.cbPanel.node.querySelector("input").focus();
 	  }
 
 	  closeConditionalPanel() {
 	    this.cbPanel.clear();
 	    this.cbPanel = null;
 	  }
@@ -24615,46 +25268,46 @@ return /******/ (function(modules) { // 
 	        selectedSource = _props7.selectedSource;
 
 	    var isLoading = sourceText && sourceText.get("loading");
 
 	    if (isLoading || !breakpoints || selectedSource && selectedSource.get("isBlackBoxed")) {
 	      return;
 	    }
 
-	    var breakpointMarkers = breakpoints.valueSeq().filter(b => !b.location.column).map(bp => Breakpoint({
-	      key: (0, _breakpoints.makeLocationId)(bp.location),
+	    var breakpointMarkers = breakpoints.valueSeq().filter(b => (0, _devtoolsConfig.isEnabled)("columnBreakpoints") ? !b.location.column : true).map(bp => Breakpoint({
+	      key: (0, _breakpoint.makeLocationId)(bp.location),
 	      breakpoint: bp,
 	      editor: this.editor && this.editor.codeMirror
 	    }));
 
-	    var columnBreakpointBookmarks = breakpoints.valueSeq().filter(b => b.location.column).map(bp => ColumnBreakpoint({
-	      key: (0, _breakpoints.makeLocationId)(bp.location),
+	    var columnBreakpointBookmarks = breakpoints.valueSeq().filter(b => (0, _devtoolsConfig.isEnabled)("columnBreakpoints") ? b.location.column : false).map(bp => ColumnBreakpoint({
+	      key: (0, _breakpoint.makeLocationId)(bp.location),
 	      breakpoint: bp,
 	      editor: this.editor && this.editor.codeMirror
 	    }));
 
 	    return breakpointMarkers.concat(columnBreakpointBookmarks);
 	  }
 
 	  renderHitCounts() {
 	    var _props8 = this.props,
 	        hitCount = _props8.hitCount,
 	        sourceText = _props8.sourceText;
 
 	    var isLoading = sourceText && sourceText.get("loading");
 
-	    if (isLoading || !hitCount) {
+	    if (isLoading || !hitCount || !this.editor) {
 	      return;
 	    }
 
 	    return hitCount.filter(marker => marker.get("count") > 0).map(marker => HitMarker({
 	      key: marker.get("line"),
 	      hitData: marker.toJS(),
-	      editor: this.editor && this.editor.codeMirror
+	      editor: this.editor.codeMirror
 	    }));
 	  }
 
 	  getInlineEditorStyles() {
 	    var _props9 = this.props,
 	        selectedSource = _props9.selectedSource,
 	        horizontal = _props9.horizontal,
 	        searchOn = _props9.searchOn;
@@ -24672,64 +25325,76 @@ return /******/ (function(modules) { // 
 	    }
 
 	    return {
 	      height: subtractions.length === 0 ? "100%" : `calc(100% - ${subtractions.join(" - ")})`
 	    };
 	  }
 
 	  renderPreview() {
-	    var _state = this.state,
-	        selectedToken = _state.selectedToken,
-	        selectedExpression = _state.selectedExpression;
+	    var selectedToken = this.state.selectedToken;
 	    var _props10 = this.props,
-	        selectedFrame = _props10.selectedFrame,
-	        sourceText = _props10.sourceText;
+	        sourceText = _props10.sourceText,
+	        selection = _props10.selection;
 
 
 	    if (!this.editor || !sourceText) {
 	      return null;
 	    }
 
-	    if (!selectedToken || !selectedFrame || !selectedExpression) {
-	      return;
-	    }
-
-	    var token = selectedToken.textContent;
-
-	    var value = (0, _editor.getExpressionValue)(selectedExpression, {
-	      getExpression: this.props.getExpression
-	    });
-
-	    if (typeof value == "undefined" || value.type == "undefined") {
+	    if (!selection || !selectedToken) {
+	      return;
+	    }
+
+	    var result = selection.result,
+	        expression = selection.expression;
+
+	    var value = result;
+	    if (typeof value == "undefined" || value.type == "undefined" || value.optimizedOut) {
 	      return;
 	    }
 
 	    return Preview({
 	      value,
-	      expression: token,
+	      expression: expression,
 	      popoverTarget: selectedToken,
-	      onClose: () => {
-	        this.setState({
-	          selectedToken: null,
-	          selectedExpression: null
-	        });
-	      }
+	      onClose: () => this.clearPreviewSelection()
+	    });
+	  }
+
+	  inSelectedFrameSource() {
+	    var _props11 = this.props,
+	        selectedLocation = _props11.selectedLocation,
+	        selectedFrame = _props11.selectedFrame;
+
+	    return selectedFrame && selectedLocation && selectedFrame.location.sourceId == selectedLocation.sourceId;
+	  }
+
+	  renderOutOfScopedLocations() {
+	    var outOfScopeLocations = this.props.outOfScopeLocations;
+
+
+	    if (!this.inSelectedFrameSource() || !outOfScopeLocations || !this.editor) {
+	      return;
+	    }
+
+	    (0, _flatMap2.default)(outOfScopeLocations, location => (0, _range2.default)(location.start.line, location.end.line)).forEach(line => {
+	      this.editor.codeMirror.addLineClass(line - 1, "line", "out-of-scope");
 	    });
 	  }
 
 	  render() {
-	    var _props11 = this.props,
-	        sourceText = _props11.sourceText,
-	        selectSource = _props11.selectSource,
-	        selectedSource = _props11.selectedSource,
-	        highlightLineRange = _props11.highlightLineRange,
-	        clearHighlightLineRange = _props11.clearHighlightLineRange,
-	        coverageOn = _props11.coverageOn,
-	        horizontal = _props11.horizontal;
+	    var _props12 = this.props,
+	        sourceText = _props12.sourceText,
+	        selectSource = _props12.selectSource,
+	        selectedSource = _props12.selectedSource,
+	        highlightLineRange = _props12.highlightLineRange,
+	        clearHighlightLineRange = _props12.clearHighlightLineRange,
+	        coverageOn = _props12.coverageOn,
+	        horizontal = _props12.horizontal;
 	    var searchResults = this.state.searchResults;
 
 
 	    return _react.DOM.div({
 	      className: (0, _classnames2.default)("editor-wrapper", { "coverage-on": coverageOn })
 	    }, SearchBar({
 	      editor: this.editor,
 	      selectSource,
@@ -24737,17 +25402,17 @@ return /******/ (function(modules) { // 
 	      highlightLineRange,
 	      clearHighlightLineRange,
 	      sourceText,
 	      searchResults,
 	      updateSearchResults: this.updateSearchResults
 	    }), _react.DOM.div({
 	      className: "editor-mount devtools-monospace",
 	      style: this.getInlineEditorStyles()
-	    }), this.renderHighlightLines(), this.renderBreakpoints(), this.renderHitCounts(), Footer({ editor: this.editor, horizontal }), this.renderPreview());
+	    }), this.renderOutOfScopedLocations(), this.renderHighlightLines(), this.renderBreakpoints(), this.renderHitCounts(), Footer({ editor: this.editor, horizontal }), this.renderPreview());
 	  }
 	}
 
 	Editor.displayName = "Editor";
 
 	Editor.propTypes = {
 	  breakpoints: _reactImmutableProptypes2.default.map.isRequired,
 	  hitCount: _react.PropTypes.object,
@@ -24774,18 +25439,21 @@ return /******/ (function(modules) { // 
 	  addExpression: _react.PropTypes.func.isRequired,
 	  horizontal: _react.PropTypes.bool,
 	  query: _react.PropTypes.string.isRequired,
 	  searchModifiers: _reactImmutableProptypes2.default.recordOf({
 	    caseSensitive: _react.PropTypes.bool.isRequired,
 	    regexMatch: _react.PropTypes.bool.isRequired,
 	    wholeWord: _react.PropTypes.bool.isRequired
 	  }).isRequired,
+	  selection: _react.PropTypes.object,
 	  startPanelSize: _react.PropTypes.number,
-	  endPanelSize: _react.PropTypes.number
+	  endPanelSize: _react.PropTypes.number,
+	  clearSelection: _react.PropTypes.func.isRequired,
+	  outOfScopeLocations: _react.PropTypes.array
 	};
 
 	Editor.contextTypes = {
 	  shortcuts: _react.PropTypes.object
 	};
 
 	var expressionsSel = state => state.expressions.expressions;
 	var getExpressionSel = (0, _reselect.createSelector)(expressionsSel, expressions => input => expressions.find(exp => exp.input == input));
@@ -24804,17 +25472,19 @@ return /******/ (function(modules) { // 
 	    loadedObjects: (0, _selectors.getLoadedObjects)(state),
 	    breakpoints: (0, _selectors.getBreakpointsForSource)(state, sourceId || ""),
 	    hitCount: (0, _selectors.getHitCountForSource)(state, sourceId),
 	    selectedFrame: (0, _selectors.getSelectedFrame)(state),
 	    getExpression: getExpressionSel(state),
 	    pauseData: (0, _selectors.getPause)(state),
 	    coverageOn: (0, _selectors.getCoverageEnabled)(state),
 	    query: (0, _selectors.getFileSearchQueryState)(state),
-	    searchModifiers: (0, _selectors.getFileSearchModifierState)(state)
+	    searchModifiers: (0, _selectors.getFileSearchModifierState)(state),
+	    outOfScopeLocations: (0, _selectors.getOutOfScopeLocations)(state),
+	    selection: (0, _selectors.getSelection)(state)
 	  };
 	}, dispatch => (0, _redux.bindActionCreators)(_actions2.default, dispatch))(Editor);
 
 /***/ },
 /* 427 */
 /***/ function(module, exports, __webpack_require__) {
 
 	"use strict";
@@ -24859,20 +25529,19 @@ return /******/ (function(modules) { // 
 
 	var PaneToggleButton = (0, _react.createFactory)(_PaneToggle2.default);
 
 	class SourceFooter extends _react.PureComponent {
 
 	  prettyPrintButton() {
 	    var _props = this.props,
 	        selectedSource = _props.selectedSource,
-	        sourceText = _props.sourceText,
 	        togglePrettyPrint = _props.togglePrettyPrint;
 
-	    var sourceLoaded = selectedSource && sourceText && !sourceText.get("loading");
+	    var sourceLoaded = selectedSource && !selectedSource.get("loading");
 
 	    if (!(0, _editor.shouldShowPrettyPrint)(selectedSource)) {
 	      return;
 	    }
 
 	    var tooltip = L10N.getStr("sourceTabs.prettyPrint");
 	    var type = "prettyPrint";
 
@@ -24886,20 +25555,19 @@ return /******/ (function(modules) { // 
 	      title: tooltip,
 	      "aria-label": tooltip
 	    }, (0, _Svg2.default)(type));
 	  }
 
 	  blackBoxButton() {
 	    var _props2 = this.props,
 	        selectedSource = _props2.selectedSource,
-	        sourceText = _props2.sourceText,
 	        toggleBlackBox = _props2.toggleBlackBox;
 
-	    var sourceLoaded = selectedSource && sourceText && !sourceText.get("loading");
+	    var sourceLoaded = selectedSource && !selectedSource.get("loading");
 
 	    var blackboxed = selectedSource.get("isBlackBoxed");
 
 	    if (!(0, _devtoolsConfig.isEnabled)("blackbox")) {
 	      return;
 	    }
 
 	    var tooltip = L10N.getStr("sourceFooter.blackbox");
@@ -24984,17 +25652,16 @@ return /******/ (function(modules) { // 
 
 	SourceFooter.displayName = "SourceFooter";
 
 	exports.default = (0, _reactRedux.connect)(state => {
 	  var selectedSource = (0, _selectors.getSelectedSource)(state);
 	  var selectedId = selectedSource && selectedSource.get("id");
 	  return {
 	    selectedSource,
-	    sourceText: (0, _selectors.getSourceText)(state, selectedId),
 	    prettySource: (0, _selectors.getPrettySource)(state, selectedId),
 	    endPanelCollapsed: (0, _selectors.getPaneCollapse)(state, "end")
 	  };
 	}, dispatch => (0, _redux.bindActionCreators)(_actions2.default, dispatch))(SourceFooter);
 
 /***/ },
 /* 428 */
 /***/ function(module, exports, __webpack_require__) {
@@ -25015,16 +25682,17 @@ return /******/ (function(modules) { // 
 
 	var _Svg2 = _interopRequireDefault(_Svg);
 
 	__webpack_require__(429);
 
 	function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
 
 	class PaneToggleButton extends _react.Component {
+
 	  shouldComponentUpdate(nextProps) {
 	    var _props = this.props,
 	        collapsed = _props.collapsed,
 	        horizontal = _props.horizontal;
 
 
 	    return horizontal !== nextProps.horizontal || collapsed !== nextProps.collapsed;
 	  }
@@ -25044,23 +25712,16 @@ return /******/ (function(modules) { // 
 	        vertical: horizontal != null ? !horizontal : false
 	      }),
 	      onClick: () => handleClick(position, collapsed),
 	      title
 	    }, (0, _Svg2.default)("togglePanes"));
 	  }
 	}
 
-	PaneToggleButton.propTypes = {
-	  position: _react.PropTypes.string.isRequired,
-	  collapsed: _react.PropTypes.bool.isRequired,
-	  horizontal: _react.PropTypes.bool,
-	  handleClick: _react.PropTypes.func.isRequired
-	};
-
 	PaneToggleButton.displayName = "PaneToggleButton";
 
 	exports.default = PaneToggleButton;
 
 /***/ },
 /* 429 */
 /***/ function(module, exports) {
 
@@ -25081,17 +25742,17 @@ return /******/ (function(modules) { // 
 	"use strict";
 
 	Object.defineProperty(exports, "__esModule", {
 	  value: true
 	});
 
 	var _react = __webpack_require__(2);
 
-	var _reactDom = __webpack_require__(22);
+	var _reactDom = __webpack_require__(31);
 
 	var _reactRedux = __webpack_require__(151);
 
 	var _redux = __webpack_require__(3);
 
 	var _fuzzaldrinPlus = __webpack_require__(161);
 
 	var _Svg = __webpack_require__(344);
@@ -25101,18 +25762,16 @@ return /******/ (function(modules) { // 
 	var _actions = __webpack_require__(244);
 
 	var _actions2 = _interopRequireDefault(_actions);
 
 	var _selectors = __webpack_require__(242);
 
 	var _editor = __webpack_require__(257);
 
-	var _parser = __webpack_require__(827);
-
 	var _resultList = __webpack_require__(343);
 
 	var _classnames = __webpack_require__(175);
 
 	var _classnames2 = _interopRequireDefault(_classnames);
 
 	var _debounce = __webpack_require__(651);
 
@@ -25127,18 +25786,16 @@ return /******/ (function(modules) { // 
 	var _ResultList2 = __webpack_require__(383);
 
 	var _ResultList3 = _interopRequireDefault(_ResultList2);
 
 	__webpack_require__(653);
 
 	function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
 
-	function _asyncToGenerator(fn) { return function () { var gen = fn.apply(this, arguments); return new Promise(function (resolve, reject) { function step(key, arg) { try { var info = gen[key](arg); var value = info.value; } catch (error) { reject(error); return; } if (info.done) { resolve(value); } else { return Promise.resolve(value).then(function (value) { step("next", value); }, function (err) { step("throw", err); }); } } return step("next"); }); }; }
-
 	var SearchInput = (0, _react.createFactory)(_SearchInput3.default);
 
 	var ResultList = (0, _react.createFactory)(_ResultList3.default);
 
 	function formatSymbol(symbol) {
 	  return {
 	    id: `${symbol.name}:${symbol.location.start.line}`,
 	    title: symbol.name,
@@ -25241,17 +25898,16 @@ return /******/ (function(modules) { // 
 	    shortcuts.on(symbolSearchShortcut, (_, e) => this.toggleSymbolSearch(e, {
 	      toggle: false,
 	      searchType: "functions"
 	    }));
 	  }
 
 	  componentDidUpdate(prevProps, prevState) {
 	    var _props = this.props,
-	        sourceText = _props.sourceText,
 	        selectedSource = _props.selectedSource,
 	        query = _props.query,
 	        modifiers = _props.modifiers,
 	        searchOn = _props.searchOn,
 	        symbolSearchOn = _props.symbolSearchOn,
 	        selectedSymbolType = _props.selectedSymbolType;
 
 	    var searchInput = this.searchInput();
@@ -25259,18 +25915,18 @@ return /******/ (function(modules) { // 
 	    if (searchInput) {
 	      searchInput.focus();
 	    }
 
 	    if (this.refs.resultList && this.refs.resultList.refs) {
 	      (0, _resultList.scrollList)(this.refs.resultList.refs, this.state.selectedResultIndex);
 	    }
 
-	    var hasLoaded = sourceText && !sourceText.get("loading");
-	    var wasLoading = prevProps.sourceText && prevProps.sourceText.get("loading");
+	    var hasLoaded = selectedSource && !selectedSource.get("loading");
+	    var wasLoading = prevProps.selectedSource && prevProps.selectedSource.get("loading");
 
 	    var doneLoading = wasLoading && hasLoaded;
 	    var changedFiles = selectedSource != prevProps.selectedSource && hasLoaded;
 	    var modifiersUpdated = modifiers && !modifiers.equals(prevProps.modifiers);
 
 	    var isOpen = searchOn || symbolSearchOn;
 	    var changedSearchType = selectedSymbolType != prevProps.selectedSymbolType || symbolSearchOn != prevProps.symbolSearchOn;
 
@@ -25291,20 +25947,20 @@ return /******/ (function(modules) { // 
 
 	    if (ed && modifiers) {
 	      var ctx = { ed, cm: ed.codeMirror };
 	      (0, _editor.removeOverlay)(ctx, query, modifiers.toJS());
 	    }
 	  }
 
 	  closeSearch(e) {
-	    var ed = this.props.editor;
-
-
-	    if (this.props.searchOn && ed) {
+	    var editor = this.props.editor;
+
+
+	    if (this.props.searchOn && editor) {
 	      this.clearSearch();
 	      this.props.toggleFileSearch(false);
 	      this.props.toggleSymbolSearch(false);
 	      this.props.setSelectedSymbolType("functions");
 	      this.props.clearHighlightLineRange();
 	      e.stopPropagation();
 	      e.preventDefault();
 	    }
@@ -25336,25 +25992,25 @@ return /******/ (function(modules) { // 
 	    }
 	  }
 
 	  toggleSymbolSearch(e) {
 	    var _ref = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {},
 	        toggle = _ref.toggle,
 	        searchType = _ref.searchType;
 
-	    var sourceText = this.props.sourceText;
+	    var selectedSource = this.props.selectedSource;
 
 
 	    if (e) {
 	      e.preventDefault();
 	      e.stopPropagation();
 	    }
 
-	    if (!sourceText) {
+	    if (!selectedSource) {
 	      return;
 	    }
 
 	    if (!this.props.searchOn) {
 	      this.props.toggleFileSearch();
 	    }
 
 	    if (this.props.symbolSearchOn) {
@@ -25396,83 +26052,69 @@ return /******/ (function(modules) { // 
 	      if (input instanceof HTMLInputElement) {
 	        return input;
 	      }
 	    }
 	    return null;
 	  }
 
 	  updateSymbolSearchResults(query) {
-	    var _this = this;
-
-	    return _asyncToGenerator(function* () {
-	      var _props3 = _this.props,
-	          sourceText = _props3.sourceText,
-	          updateSearchResults = _props3.updateSearchResults,
-	          selectedSymbolType = _props3.selectedSymbolType;
-
-
-	      if (query == "" || !sourceText) {
-	        return;
-	      }
-
-	      var _ref2 = yield (0, _parser.getSymbols)(sourceText.toJS()),
-	          functions = _ref2.functions,
-	          variables = _ref2.variables;
-
-	      var formattedSymbolDeclaration = {
-	        variables: variables.map(formatSymbol),
-	        functions: functions.map(formatSymbol)
-	      };
-
-	      var symbolSearchResults = (0, _fuzzaldrinPlus.filter)(formattedSymbolDeclaration[selectedSymbolType], query, { key: "value" });
-
-	      updateSearchResults({ count: symbolSearchResults.length });
-	      return _this.setState({ symbolSearchResults });
-	    })();
+	    var _props3 = this.props,
+	        selectedSource = _props3.selectedSource,
+	        updateSearchResults = _props3.updateSearchResults,
+	        selectedSymbolType = _props3.selectedSymbolType,
+	        symbols = _props3.symbols;
+
+
+	    if (query == "" || !selectedSource) {
+	      return;
+	    }
+
+	    var symbolSearchResults = (0, _fuzzaldrinPlus.filter)(symbols[selectedSymbolType], query, {
+	      key: "value"
+	    });
+
+	    updateSearchResults({ count: symbolSearchResults.length });
+	    return this.setState({ symbolSearchResults });
 	  }
 
 	  doSearch(query) {
-	    var _this2 = this;
-
-	    return _asyncToGenerator(function* () {
-	      var _props4 = _this2.props,
-	          sourceText = _props4.sourceText,
-	          setFileSearchQuery = _props4.setFileSearchQuery,
-	          ed = _props4.editor;
-
-	      if (!sourceText || !sourceText.get("text")) {
-	        return;
-	      }
-
-	      setFileSearchQuery(query);
-
-	      if (_this2.props.symbolSearchOn) {
-	        return yield _this2.updateSymbolSearchResults(query);
-	      } else if (ed) {
-	        _this2.searchContents(query);
-	      }
-	    })();
+	    var _props4 = this.props,
+	        selectedSource = _props4.selectedSource,
+	        setFileSearchQuery = _props4.setFileSearchQuery,
+	        ed = _props4.editor;
+
+	    if (!selectedSource || !selectedSource.get("text")) {
+	      return;
+	    }
+
+	    setFileSearchQuery(query);
+
+	    if (this.props.symbolSearchOn) {
+	      return this.updateSymbolSearchResults(query);
+	    } else if (ed) {
+	      this.searchContents(query);
+	    }
 	  }
 
 	  searchContents(query) {
 	    var _props5 = this.props,
-	        sourceText = _props5.sourceText,
+	        selectedSource = _props5.selectedSource,
 	        modifiers = _props5.modifiers,
 	        ed = _props5.editor,
 	        index = _props5.searchResults.index;
 
 
-	    if (!ed || !sourceText || !sourceText.get("text") || !modifiers) {
+	    if (!ed || !selectedSource || !selectedSource.get("text") || !modifiers) {
 	      return;
 	    }
 
 	    var ctx = { ed, cm: ed.codeMirror };
 
-	    var newCount = (0, _editor.countMatches)(query, sourceText.get("text"), modifiers.toJS());
+	    var newCount = (0, _editor.countMatches)(query, selectedSource.get("text"), modifiers.toJS());
 
 	    if (index == -1) {
 	      (0, _editor.clearIndex)(ctx, query, modifiers.toJS());
 	    }
 
 	    var newIndex = (0, _editor.find)(ctx, query, true, modifiers.toJS());
 	    this.props.updateSearchResults({
 	      count: newCount,
@@ -25592,21 +26234,17 @@ return /******/ (function(modules) { // 
 	        start: item.location.start.line,
 	        end: item.location.end.line,
 	        sourceId: selectedSource.get("id")
 	      });
 	    }
 	  }
 
 	  onChange(e) {
-	    var _this3 = this;
-
-	    return _asyncToGenerator(function* () {
-	      return _this3.doSearch(e.target.value);
-	    })();
+	    return this.doSearch(e.target.value);
 	  }
 
 	  onKeyUp(e) {
 	    if (e.key !== "Enter" && e.key !== "F3") {
 	      return;
 	    }
 
 	    this.traverseResults(e, e.shiftKey);
@@ -25785,22 +26423,39 @@ return /******/ (function(modules) { // 
 	  }
 	}
 
 	SearchBar.displayName = "SearchBar";
 	SearchBar.contextTypes = {
 	  shortcuts: _react.PropTypes.object
 	};
 
+	function _getFormattedSymbols(state) {
+	  var source = (0, _selectors.getSelectedSource)(state);
+	  if (!source) {
+	    return { variables: [], functions: [] };
+	  }
+
+	  var _getSymbols = (0, _selectors.getSymbols)(state, source.toJS()),
+	      variables = _getSymbols.variables,
+	      functions = _getSymbols.functions;
+
+	  return {
+	    variables: variables.map(formatSymbol),
+	    functions: functions.map(formatSymbol)
+	  };
+	}
+
 	exports.default = (0, _reactRedux.connect)(state => {
 	  return {
 	    searchOn: (0, _selectors.getFileSearchState)(state),
 	    query: (0, _selectors.getFileSearchQueryState)(state),
 	    modifiers: (0, _selectors.getFileSearchModifierState)(state),
 	    symbolSearchOn: (0, _selectors.getSymbolSearchState)(state),
+	    symbols: _getFormattedSymbols(state),
 	    selectedSymbolType: (0, _selectors.getSymbolSearchType)(state)
 	  };
 	}, dispatch => (0, _redux.bindActionCreators)(_actions2.default, dispatch))(SearchBar);
 
 /***/ },
 /* 434 */,
 /* 435 */,
 /* 436 */,
@@ -26708,19 +27363,21 @@ return /******/ (function(modules) { // 
 	var _get = __webpack_require__(67);
 
 	var _get2 = _interopRequireDefault(_get);
 
 	var _devtoolsReps = __webpack_require__(924);
 
 	function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
 
+	function _toConsumableArray(arr) { if (Array.isArray(arr)) { for (var i = 0, arr2 = Array(arr.length); i < arr.length; i++) { arr2[i] = arr[i]; } return arr2; } else { return Array.from(arr); } }
+
 	var WINDOW_PROPERTIES = {};
 
-	if (typeof window == "object") {
+	if (typeof window === "object") {
 	  WINDOW_PROPERTIES = Object.getOwnPropertyNames(window);
 	}
 
 	function getValue(item) {
 	  return (0, _get2.default)(item, "contents.value", undefined);
 	}
 
 	function isBucket(item) {
@@ -26767,24 +27424,41 @@ return /******/ (function(modules) { // 
 	  var value = getValue(item);
 	  return value.class == "Promise";
 	}
 
 	function getPromiseProperties(item) {
 	  var _getValue = getValue(item),
 	      _getValue$promiseStat = _getValue.promiseState,
 	      reason = _getValue$promiseStat.reason,
-	      value = _getValue$promiseStat.value;
-
-	  return createNode("reason", `${item.path}/reason`, {
-	    value: !reason ? value : reason
-	  });
-	}
-
-	function isDefault(item) {
+	      value = _getValue$promiseStat.value,
+	      state = _getValue$promiseStat.state;
+
+	  var properties = [];
+
+	  if (state) {
+	    properties.push(createNode("<state>", `${item.path}/state`, { value: state }));
+	  }
+
+	  if (reason) {
+	    properties.push(createNode("<reason>", `${item.path}/reason`, { value: reason }));
+	  }
+
+	  if (value) {
+	    properties.push(createNode("<value>", `${item.path}/value`, { value: value }));
+	  }
+
+	  return properties;
+	}
+
+	function isDefault(item, roots) {
+	  if (roots && roots.length === 1) {
+	    var value = getValue(roots[0]);
+	    return value.class === "Window";
+	  }
 	  return WINDOW_PROPERTIES.includes(item.name);
 	}
 
 	function sortProperties(properties) {
 	  return properties.sort((a, b) => {
 	    // Sort numbers in ascending order and sort strings lexicographically
 	    var aInt = parseInt(a, 10);
 	    var bInt = parseInt(b, 10);
@@ -26864,25 +27538,34 @@ return /******/ (function(modules) { // 
 	  } else {
 	    nodes = makeNodesForOwnProps(properties, parentPath, ownProperties);
 	  }
 
 	  for (var index in ownSymbols) {
 	    nodes.push(createNode(ownSymbols[index].name, `${parentPath}/##symbol-${index}`, ownSymbols[index].descriptor));
 	  }
 
+	  if (isPromise(parent)) {
+	    var _nodes;
+
+	    (_nodes = nodes).push.apply(_nodes, _toConsumableArray(getPromiseProperties(parent)));
+	  }
+
 	  // Add the prototype if it exists and is not null
 	  if (prototype && prototype.type !== "null") {
 	    nodes.push(createNode("__proto__", `${parentPath}/__proto__`, { value: prototype }));
 	  }
 
 	  return nodes;
 	}
 
 	function createNode(name, path, contents) {
+	  if (contents === undefined) {
+	    return null;
+	  }
 	  // The path is important to uniquely identify the item in the entire
 	  // tree. This helps debugging & optimizes React's rendering of large
 	  // lists. The path will be separated by property name,
 	  // i.e. `{ foo: { bar: { baz: 5 }}}` will have a path of `foo/bar/baz`
 	  // for the inner object.
 	  return { name, path, contents };
 	}
 
@@ -26926,19 +27609,16 @@ return /******/ (function(modules) { // 
 	      ownProperties = _ref3.ownProperties,
 	      prototype = _ref3.prototype;
 
 	  if (!ownProperties && !prototype) {
 	    return [];
 	  }
 
 	  var children = makeNodesForProperties(loadedProps, item);
-	  if (isPromise(item)) {
-	    children.unshift(getPromiseProperties(item));
-	  }
 	  actors[key] = children;
 	  return children;
 	}
 
 	exports.nodeHasChildren = nodeHasChildren;
 	exports.nodeIsOptimizedOut = nodeIsOptimizedOut;
 	exports.nodeIsMissingArguments = nodeIsMissingArguments;
 	exports.nodeHasProperties = nodeHasProperties;
@@ -27071,16 +27751,21 @@ return /******/ (function(modules) { // 
 
 	    this.actors = {};
 
 	    var self = this;
 	    self.getChildren = this.getChildren.bind(this);
 	    self.renderItem = this.renderItem.bind(this);
 	  }
 
+	  isDefaultProperty(item) {
+	    var roots = this.props.roots;
+	    return (0, _objectInspector.isDefault)(item, roots);
+	  }
+
 	  getChildren(item) {
 	    var getObjectProperties = this.props.getObjectProperties;
 	    var actors = this.actors;
 
 
 	    return (0, _objectInspector.getChildren)({
 	      getObjectProperties,
 	      actors,
@@ -27104,19 +27789,21 @@ return /******/ (function(modules) { // 
 	    } else if ((0, _objectInspector.nodeHasProperties)(item) || (0, _objectInspector.nodeIsPrimitive)(item)) {
 	      var object = item.contents.value;
 	      objectValue = (0, _Rep2.default)({ object, mode: _devtoolsReps.MODE.TINY });
 	    }
 
 	    return _react.DOM.div({
 	      className: (0, _classnames2.default)("node object-node", {
 	        focused,
-	        "default-property": (0, _objectInspector.isDefault)(item)
+	        "default-property": this.isDefaultProperty(item)
 	      }),
-	      style: { marginLeft: depth * 15 },
+	      style: {
+	        marginLeft: depth * 15 + ((0, _objectInspector.nodeIsPrimitive)(item) ? 15 : 0)
+	      },
 	      onClick: e => {
 	        e.stopPropagation();
 	        setExpanded(item, !expanded);
 	      },
 	      onDoubleClick: event => {
 	        event.stopPropagation();
 	        this.props.onDoubleClick(item, {
 	          depth,
@@ -27154,17 +27841,17 @@ return /******/ (function(modules) { // 
 	      getChildren: this.getChildren,
 	      getRoots: () => roots,
 	      getKey: item => item.path,
 	      autoExpand: 0,
 	      autoExpandDepth,
 	      autoExpandAll: false,
 	      disabledFocus: true,
 	      onExpand: item => {
-	        if ((0, _objectInspector.nodeHasProperties)(item)) {
+	        if (item && item.contents && (0, _objectInspector.nodeHasProperties)(item)) {
 	          loadObjectProperties(item.contents.value);
 	        }
 	      },
 	      renderItem: this.renderItem
 	    });
 	  }
 	}
 
@@ -27221,17 +27908,17 @@ return /******/ (function(modules) { // 
 	"use strict";
 
 	Object.defineProperty(exports, "__esModule", {
 	  value: true
 	});
 
 	var _react = __webpack_require__(2);
 
-	var _reactDom = __webpack_require__(22);
+	var _reactDom = __webpack_require__(31);
 
 	var _reactDom2 = _interopRequireDefault(_reactDom);
 
 	var _classnames = __webpack_require__(175);
 
 	var _classnames2 = _interopRequireDefault(_classnames);
 
 	var _BracketArrow2 = __webpack_require__(1029);
@@ -27738,17 +28425,17 @@ return /******/ (function(modules) { // 
 
 	Object.defineProperty(exports, "__esModule", {
 	  value: true
 	});
 	exports.renderConditionalPanel = undefined;
 
 	var _react = __webpack_require__(2);
 
-	var _reactDom = __webpack_require__(22);
+	var _reactDom = __webpack_require__(31);
 
 	var _reactDom2 = _interopRequireDefault(_reactDom);
 
 	var _Close = __webpack_require__(378);
 
 	var _Close2 = _interopRequireDefault(_Close);
 
 	__webpack_require__(712);
@@ -27815,17 +28502,17 @@ return /******/ (function(modules) { // 
 	Object.defineProperty(exports, "__esModule", {
 	  value: true
 	});
 
 	var _react = __webpack_require__(2);
 
 	var _devtoolsConfig = __webpack_require__(828);
 
-	var _reactDom = __webpack_require__(22);
+	var _reactDom = __webpack_require__(31);
 
 	var _reactDom2 = _interopRequireDefault(_reactDom);
 
 	var _classnames = __webpack_require__(175);
 
 	var _classnames2 = _interopRequireDefault(_classnames);
 
 	var _Svg = __webpack_require__(344);
@@ -27918,16 +28605,17 @@ return /******/ (function(modules) { // 
 
 	function makeMarker() {
 	  var marker = markerEl.cloneNode(true);
 	  marker.className = "editor hit-marker";
 	  return marker;
 	}
 
 	class HitMarker extends _react.Component {
+
 	  addMarker() {
 	    var hitData = this.props.hitData;
 	    var line = hitData.line - 1;
 
 	    this.props.editor.setGutterMarker(line, "hit-markers", makeMarker());
 
 	    this.props.editor.addLineClass(line, "line", "hit-marker");
 	  }
@@ -27962,21 +28650,16 @@ return /******/ (function(modules) { // 
 
 	  render() {
 	    return null;
 	  }
 	}
 
 	HitMarker.displayName = "HitMarker";
 
-	HitMarker.propTypes = {
-	  hitData: _react.PropTypes.object.isRequired,
-	  editor: _react.PropTypes.object.isRequired
-	};
-
 	exports.default = HitMarker;
 
 /***/ },
 /* 716 */
 /***/ function(module, exports) {
 
 	// removed by extract-text-webpack-plugin
 
@@ -28294,16 +28977,23 @@ return /******/ (function(modules) { // 
 
 	  if (value.exception) {
 	    return {
 	      path: value.from,
 	      value: value.exception
 	    };
 	  }
 
+	  if (value.error) {
+	    return {
+	      path: value.from,
+	      value: value.error
+	    };
+	  }
+
 	  if (typeof value.result == "object") {
 	    return {
 	      path: value.result.actor,
 	      value: value.result
 	    };
 	  }
 
 	  return {
@@ -28418,17 +29108,17 @@ return /******/ (function(modules) { // 
 	    var root = {
 	      name: expression.input,
 	      path,
 	      contents: { value }
 	    };
 
 	    return _react.DOM.div({
 	      className: "expression-container",
-	      key: path || input
+	      key: `${path}/${input}`
 	    }, ObjectInspector({
 	      roots: [root],
 	      getObjectProperties: id => loadedObjects[id],
 	      autoExpandDepth: 0,
 	      onDoubleClick: (item, options) => this.editExpression(expression, options),
 	      loadObjectProperties
 	    }), CloseButton({ handleClick: e => this.deleteExpression(e, expression) }));
 	  }
@@ -28608,17 +29298,17 @@ return /******/ (function(modules) { // 
 	var _classnames2 = _interopRequireDefault(_classnames);
 
 	var _actions = __webpack_require__(244);
 
 	var _actions2 = _interopRequireDefault(_actions);
 
 	var _selectors = __webpack_require__(242);
 
-	var _breakpoints = __webpack_require__(236);
+	var _breakpoint = __webpack_require__(1057);
 
 	var _utils = __webpack_require__(234);
 
 	var _path = __webpack_require__(235);
 
 	var _Close = __webpack_require__(378);
 
 	var _Close2 = _interopRequireDefault(_Close);
@@ -28630,18 +29320,18 @@ return /******/ (function(modules) { // 
 	var get = __webpack_require__(67);
 
 
 	function isCurrentlyPausedAtBreakpoint(pause, breakpoint) {
 	  if (!pause || pause.isInterrupted) {
 	    return false;
 	  }
 
-	  var bpId = (0, _breakpoints.makeLocationId)(breakpoint.location);
-	  var pausedId = (0, _breakpoints.makeLocationId)(get(pause, "frame.location"));
+	  var bpId = (0, _breakpoint.makeLocationId)(breakpoint.location);
+	  var pausedId = (0, _breakpoint.makeLocationId)(get(pause, "frame.location"));
 	  return bpId === pausedId;
 	}
 
 	function renderSourceLocation(source, line, column) {
 	  var url = source.get("url") ? (0, _path.basename)(source.get("url")) : null;
 	  var bpLocation = line + (column ? `:${column}` : "");
 	  // const line = url !== "" ? `: ${line}` : "";
 	  return url ? _react.DOM.div({ className: "location" }, `${(0, _utils.endTruncateStr)(url, 30)}: ${bpLocation}`) : null;
@@ -28726,17 +29416,17 @@ return /******/ (function(modules) { // 
 	  disableBreakpoint: _react.PropTypes.func.isRequired,
 	  selectSource: _react.PropTypes.func.isRequired,
 	  removeBreakpoint: _react.PropTypes.func.isRequired
 	};
 
 	function updateLocation(sources, pause, bp) {
 	  var source = (0, _selectors.getSourceInSources)(sources, bp.location.sourceId);
 	  var isCurrentlyPaused = isCurrentlyPausedAtBreakpoint(pause, bp);
-	  var locationId = (0, _breakpoints.makeLocationId)(bp.location);
+	  var locationId = (0, _breakpoint.makeLocationId)(bp.location);
 
 	  var location = Object.assign({}, bp.location, { source });
 	  var localBP = Object.assign({}, bp, {
 	    location,
 	    locationId,
 	    isCurrentlyPaused
 	  });
 
@@ -29477,20 +30167,16 @@ return /******/ (function(modules) { // 
 
 	  render() {
 	    return _react.DOM.div({ className: "accordion" }, this.props.items.map(this.renderContainer));
 	  }
 	}
 
 	Accordion.displayName = "Accordion";
 
-	Accordion.propTypes = {
-	  items: _react.PropTypes.array.isRequired
-	};
-
 	exports.default = Accordion;
 
 /***/ },
 /* 740 */
 /***/ function(module, exports) {
 
 	// removed by extract-text-webpack-plugin
 
@@ -29502,17 +30188,17 @@ return /******/ (function(modules) { // 
 	"use strict";
 
 	Object.defineProperty(exports, "__esModule", {
 	  value: true
 	});
 
 	var _react = __webpack_require__(2);
 
-	var _reactDom = __webpack_require__(22);
+	var _reactDom = __webpack_require__(31);
 
 	var _reactRedux = __webpack_require__(151);
 
 	var _redux = __webpack_require__(3);
 
 	var _selectors = __webpack_require__(242);
 
 	var _Svg = __webpack_require__(344);
@@ -29875,17 +30561,18 @@ return /******/ (function(modules) { // 
 	    var topOffsets = sourceTabEls.map(t => t.getBoundingClientRect().top);
 	    return Math.min.apply(Math, _toConsumableArray(topOffsets));
 	  }
 
 	  var tabTopOffset = getTopOffset();
 	  return sourceTabs.filter((tab, index) => {
 	    // adding 10px helps account for cases where the tab might be offset by
 	    // styling such as selected tabs which don't have a border.
-	    return sourceTabEls[index].getBoundingClientRect().top > tabTopOffset + 10;
+	    var el = sourceTabEls[index];
+	    return el && el.getBoundingClientRect().top > tabTopOffset + 10;
 	  });
 	}
 
 	/**
 	 * Clipboard function taken from
 	 * https://dxr.mozilla.org/mozilla-central/source/devtools/shared/platform/content/clipboard.js
 	 */
 	function copyToTheClipboard(string) {
@@ -30262,20 +30949,16 @@ return /******/ (function(modules) { // 
 	    });
 	  }
 
 	  render() {
 	    return _react.DOM.div({ className: "dropdown-block" }, this.renderPanel(), this.renderButton(), this.renderMask());
 	  }
 	}
 
-	Dropdown.propTypes = {
-	  panel: _react.PropTypes.object
-	};
-
 	Dropdown.displayName = "Dropdown";
 
 	exports.default = Dropdown;
 
 /***/ },
 /* 752 */
 /***/ function(module, exports) {
 
@@ -30293,17 +30976,38 @@ return /******/ (function(modules) { // 
 /* 756 */,
 /* 757 */,
 /* 758 */,
 /* 759 */,
 /* 760 */,
 /* 761 */,
 /* 762 */,
 /* 763 */,
-/* 764 */,
+/* 764 */
+/***/ function(module, exports, __webpack_require__) {
+
+	var baseFor = __webpack_require__(395),
+	    keys = __webpack_require__(205);
+
+	/**
+	 * The base implementation of `_.forOwn` without support for iteratee shorthands.
+	 *
+	 * @private
+	 * @param {Object} object The object to iterate over.
+	 * @param {Function} iteratee The function invoked per iteration.
+	 * @returns {Object} Returns `object`.
+	 */
+	function baseForOwn(object, iteratee) {
+	  return object && baseFor(object, iteratee, keys);
+	}
+
+	module.exports = baseForOwn;
+
+
+/***/ },
 /* 765 */,
 /* 766 */,
 /* 767 */,
 /* 768 */,
 /* 769 */,
 /* 770 */,
 /* 771 */,
 /* 772 */,
@@ -30368,24 +31072,26 @@ return /******/ (function(modules) { // 
 
 	var _devtoolsUtils = __webpack_require__(900);
 
 	var WorkerDispatcher = _devtoolsUtils.workerUtils.WorkerDispatcher;
 
 
 	var dispatcher = new WorkerDispatcher();
 
+	var getClosestExpression = dispatcher.task("getClosestExpression");
 	var getSymbols = dispatcher.task("getSymbols");
 	var getVariablesInScope = dispatcher.task("getVariablesInScope");
-	var resolveToken = dispatcher.task("resolveToken");
+	var getOutOfScopeLocations = dispatcher.task("getOutOfScopeLocations");
 
 	module.exports = {
 	  getSymbols,
 	  getVariablesInScope,
-	  resolveToken,
+	  getOutOfScopeLocations,
+	  getClosestExpression,
 	  startParserWorker: dispatcher.start.bind(dispatcher),
 	  stopParserWorker: dispatcher.stop.bind(dispatcher)
 	};
 
 /***/ },
 /* 828 */
 /***/ function(module, exports, __webpack_require__) {
 
@@ -31677,19 +32383,23 @@ return /******/ (function(modules) { // 
 
 	  return {
 	    id: bpClient.actor,
 	    actualLocation
 	  };
 	}
 
 	function removeBreakpoint(breakpointId) {
-	  var bpClient = bpClients[breakpointId];
-	  delete bpClients[breakpointId];
-	  return bpClient.remove();
+	  try {
+	    var bpClient = bpClients[breakpointId];
+	    delete bpClients[breakpointId];
+	    return bpClient.remove();
+	  } catch (_error) {
+	    console.warn("No breakpoint to delete on server");
+	  }
 	}
 
 	function setBreakpointCondition(breakpointId, location, condition, noSliding) {
 	  var bpClient = bpClients[breakpointId];
 	  delete bpClients[breakpointId];
 
 	  return bpClient.setCondition(threadClient, condition, noSliding).then(_bpClient => onNewBreakpoint(location, [{}, _bpClient]));
 	}
@@ -31729,25 +32439,23 @@ return /******/ (function(modules) { // 
 	function getProperties(grip) {
 	  var objClient = threadClient.pauseGrip(grip);
 
 	  return objClient.getPrototypeAndProperties().then(resp => {
 	    var ownProperties = resp.ownProperties,
 	        safeGetterValues = resp.safeGetterValues;
 
 	    for (var name in safeGetterValues) {
-	      if (name in ownProperties) {
-	        var getterValue = safeGetterValues[name].getterValue;
-
-	        ownProperties[name].value = getterValue;
-	      } else {
-	        ownProperties[name] = safeGetterValues[name];
-	      }
-	    }
-
+	      var _safeGetterValues$nam = safeGetterValues[name],
+	          enumerable = _safeGetterValues$nam.enumerable,
+	          writable = _safeGetterValues$nam.writable,
+	          getterValue = _safeGetterValues$nam.getterValue;
+
+	      ownProperties[name] = { enumerable, writable, value: getterValue };
+	    }
 	    return resp;
 	  });
 	}
 
 	function pauseOnExceptions(shouldPauseOnExceptions, shouldIgnoreCaughtExceptions) {
 	  return threadClient.pauseOnExceptions(shouldPauseOnExceptions, shouldIgnoreCaughtExceptions);
 	}
 
@@ -32347,17 +33055,17 @@ return /******/ (function(modules) { // 
 	exports.teardownWorkers = teardownWorkers;
 
 	var _react = __webpack_require__(2);
 
 	var _react2 = _interopRequireDefault(_react);
 
 	var _redux = __webpack_require__(3);
 
-	var _reactDom = __webpack_require__(22);
+	var _reactDom = __webpack_require__(31);
 
 	var _reactDom2 = _interopRequireDefault(_reactDom);
 
 	var _devtoolsConfig = __webpack_require__(828);
 
 	var _devtoolsLaunchpad = __webpack_require__(131);
 
 	var _devtoolsSourceMap = __webpack_require__(898);
@@ -32714,51 +33422,24 @@ return /******/ (function(modules) { // 
 	module.exports = {
 	  prettyPrint,
 	  startPrettyPrintWorker: dispatcher.start.bind(dispatcher),
 	  stopPrettyPrintWorker: dispatcher.stop.bind(dispatcher)
 	};
 
 /***/ },
 /* 904 */
-/***/ function(module, exports, __webpack_require__) {
-
-	"use strict";
-
-	Object.defineProperty(exports, "__esModule", {
-	  value: true
-	});
-	exports.resolveToken = undefined;
-
-	var resolveToken = exports.resolveToken = (() => {
-	  var _ref = _asyncToGenerator(function* (cm, token, sourceText, frame) {
-	    var loc = getTokenLocation(cm, token);
-	    return yield (0, _parser.resolveToken)(sourceText.toJS(), token.textContent || "", loc, frame);
-	  });
-
-	  return function resolveToken(_x, _x2, _x3, _x4) {
-	    return _ref.apply(this, arguments);
-	  };
-	})();
-
+/***/ function(module, exports) {
+
+	"use strict";
+
+	Object.defineProperty(exports, "__esModule", {
+	  value: true
+	});
 	exports.getTokenLocation = getTokenLocation;
-	exports.getThisFromFrame = getThisFromFrame;
-	exports.previewExpression = previewExpression;
-	exports.getExpressionValue = getExpressionValue;
-
-	var _parser = __webpack_require__(827);
-
-	var _get = __webpack_require__(67);
-
-	var _get2 = _interopRequireDefault(_get);
-
-	function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
-
-	function _asyncToGenerator(fn) { return function () { var gen = fn.apply(this, arguments); return new Promise(function (resolve, reject) { function step(key, arg) { try { var info = gen[key](arg); var value = info.value; } catch (error) { reject(error); return; } if (info.done) { resolve(value); } else { return Promise.resolve(value).then(function (value) { step("next", value); }, function (err) { step("throw", err); }); } } return step("next"); }); }; }
-
 	function getTokenLocation(codeMirror, tokenEl) {
 	  var lineOffset = 1;
 
 	  var _tokenEl$getBoundingC = tokenEl.getBoundingClientRect(),
 	      left = _tokenEl$getBoundingC.left,
 	      top = _tokenEl$getBoundingC.top;
 
 	  var _codeMirror$coordsCha = codeMirror.coordsChar({ left, top }),
@@ -32766,73 +33447,16 @@ return /******/ (function(modules) { // 
 	      ch = _codeMirror$coordsCha.ch;
 
 	  return {
 	    line: line + lineOffset,
 	    column: ch
 	  };
 	}
 
-	function getThisFromFrame(selectedFrame) {
-	  if ("this" in selectedFrame) {
-	    return { value: selectedFrame.this };
-	  }
-
-	  return null;
-	}
-
-	// TODO Better define the value for `variables` map once we do it in
-	// debugger-html
-	function previewExpression(_ref2) {
-	  var expression = _ref2.expression,
-	      selectedFrame = _ref2.selectedFrame,
-	      variables = _ref2.variables,
-	      tokenText = _ref2.tokenText;
-
-	  if (!tokenText) {
-	    return null;
-	  }
-
-	  if (tokenText === "this") {
-	    return getThisFromFrame(selectedFrame);
-	  }
-
-	  if (variables.has(tokenText)) {
-	    var variableKey = variables.get(tokenText);
-	    if ((0, _get2.default)(variableKey, "contents.value.type") == "undefined") {
-	      return null;
-	    }
-
-	    return variables.get(tokenText);
-	  }
-
-	  return expression || null;
-	}
-
-	// `getExpressionValue` and `previewExpression` are utility functions
-	// for resolving which expression to show in the preview.
-	// Get ExpressionValue, knows how to get the appropriate value for each type:
-	// variable, expression, raw value.
-	function getExpressionValue(selectedExpression, _ref3) {
-	  var getExpression = _ref3.getExpression;
-
-	  var variableValue = (0, _get2.default)(selectedExpression, "contents.value");
-	  if (typeof variableValue === "boolean" || variableValue) {
-	    return variableValue;
-	  }
-
-	  var expressionValue = getExpression(selectedExpression.value);
-	  if (expressionValue) {
-	    return (0, _get2.default)(expressionValue, "value.result");
-	  }
-
-	  var rawValue = selectedExpression.value;
-	  return rawValue;
-	}
-
 /***/ },
 /* 905 */,
 /* 906 */,
 /* 907 */,
 /* 908 */,
 /* 909 */,
 /* 910 */
 /***/ function(module, exports, __webpack_require__) {
@@ -32845,17 +33469,17 @@ return /******/ (function(modules) { // 
 
 /***/ },
 /* 911 */
 /***/ function(module, exports, __webpack_require__) {
 
 	"use strict";
 
 	var React = __webpack_require__(2);
-	var ReactDOM = __webpack_require__(22);
+	var ReactDOM = __webpack_require__(31);
 	var Draggable = React.createFactory(__webpack_require__(912));
 	var dom = React.DOM,
 	    PropTypes = React.PropTypes;
 
 
 	__webpack_require__(913);
 
 	/**
@@ -33102,17 +33726,17 @@ return /******/ (function(modules) { // 
 
 	"use strict";
 
 	/* This Source Code Form is subject to the terms of the Mozilla Public
 	 * License, v. 2.0. If a copy of the MPL was not distributed with this file,
 	 * You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 	var React = __webpack_require__(2);
-	var ReactDOM = __webpack_require__(22);
+	var ReactDOM = __webpack_require__(31);
 	var dom = React.DOM,
 	    PropTypes = React.PropTypes;
 
 
 	var Draggable = React.createClass({
 	  displayName: "Draggable",
 
 	  propTypes: {
@@ -33160,696 +33784,19 @@ return /******/ (function(modules) { // 
 /***/ },
 /* 913 */
 /***/ function(module, exports) {
 
 	// removed by extract-text-webpack-plugin
 
 /***/ },
 /* 914 */,
-/* 915 */
-/***/ function(module, exports, __webpack_require__) {
-
-	"use strict";
-
-	Object.defineProperty(exports, "__esModule", {
-	  value: true
-	});
-
-	var _react = __webpack_require__(2);
-
-	var _reactRedux = __webpack_require__(151);
-
-	var _redux = __webpack_require__(3);
-
-	var _actions = __webpack_require__(244);
-
-	var _actions2 = _interopRequireDefault(_actions);
-
-	var _selectors = __webpack_require__(242);
-
-	var _utils = __webpack_require__(234);
-
-	var _url = __webpack_require__(334);
-
-	var _source = __webpack_require__(233);
-
-	__webpack_require__(917);
-
-	var _Autocomplete2 = __webpack_require__(342);
-
-	var _Autocomplete3 = _interopRequireDefault(_Autocomplete2);
-
-	function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
-
-	var Autocomplete = (0, _react.createFactory)(_Autocomplete3.default);
-
-	function searchResults(sources) {
-	  function getSourcePath(source) {
-	    var _parseURL = (0, _url.parse)(source.get("url")),
-	        path = _parseURL.path,
-	        href = _parseURL.href;
-	    // for URLs like "about:home" the path is null so we pass the full href
-
-
-	    return path || href;
-	  }
-
-	  return sources.valueSeq().filter(source => !(0, _source.isPretty)(source.toJS()) && source.get("url")).map(source => ({
-	    value: getSourcePath(source),
-	    title: getSourcePath(source).split("/").pop(),
-	    subtitle: (0, _utils.endTruncateStr)(getSourcePath(source), 100),
-	    id: source.get("id")
-	  })).toJS();
-	}
-
-	class ProjectSearch extends _react.Component {
-
-	  constructor(props) {
-	    super(props);
-
-	    this.state = {
-	      inputValue: ""
-	    };
-
-	    this.toggle = this.toggle.bind(this);
-	    this.onEscape = this.onEscape.bind(this);
-	    this.close = this.close.bind(this);
-	  }
-
-	  componentWillUnmount() {
-	    var shortcuts = this.context.shortcuts;
-	    var searchKeys = [L10N.getStr("sources.search.key2"), L10N.getStr("sources.search.key2")];
-	    searchKeys.forEach(key => shortcuts.off(key, this.toggle));
-	    shortcuts.off("Escape", this.onEscape);
-	  }
-
-	  componentDidMount() {
-	    var shortcuts = this.context.shortcuts;
-	    var searchKeys = [L10N.getStr("sources.search.key2"), L10N.getStr("sources.search.alt.key")];
-	    searchKeys.forEach(key => shortcuts.on(key, this.toggle));
-	    shortcuts.on("Escape", this.onEscape);
-	  }
-
-	  toggle(key, e) {
-	    e.preventDefault();
-	    this.props.toggleProjectSearch();
-	  }
-
-	  onEscape(shortcut, e) {
-	    if (this.props.searchOn) {
-	      e.preventDefault();
-	      this.close();
-	    }
-	  }
-
-	  close() {
-	    this.setState({ inputValue: "" });
-	    this.props.toggleProjectSearch(false);
-	  }
-
-	  render() {
-	    if (!this.props.searchOn) {
-	      return null;
-	    }
-
-	    return _react.DOM.div({ className: "search-container" }, Autocomplete({
-	      selectItem: (e, result) => {
-	        this.props.selectSource(result.id);
-	        this.close();
-	      },
-	      close: this.close,
-	      items: searchResults(this.props.sources),
-	      inputValue: this.state.inputValue,
-	      placeholder: L10N.getStr("sourceSearch.search"),
-	      size: "big"
-	    }));
-	  }
-	}
-
-	ProjectSearch.propTypes = {
-	  sources: _react.PropTypes.object.isRequired,
-	  selectSource: _react.PropTypes.func.isRequired,
-	  toggleProjectSearch: _react.PropTypes.func.isRequired,
-	  searchOn: _react.PropTypes.bool
-	};
-
-	ProjectSearch.contextTypes = {
-	  shortcuts: _react.PropTypes.object
-	};
-
-	ProjectSearch.displayName = "ProjectSearch";
-
-	exports.default = (0, _reactRedux.connect)(state => ({
-	  sources: (0, _selectors.getSources)(state),
-	  searchOn: (0, _selectors.getProjectSearchState)(state)
-	}), dispatch => (0, _redux.bindActionCreators)(_actions2.default, dispatch))(ProjectSearch);
-
-/***/ },
-/* 916 */
-/***/ function(module, exports, __webpack_require__) {
-
-	var __WEBPACK_AMD_DEFINE_RESULT__;/* WEBPACK VAR INJECTION */(function(module, global) {/*! https://mths.be/punycode v1.3.2 by @mathias */
-	;(function(root) {
-
-		/** Detect free variables */
-		var freeExports = typeof exports == 'object' && exports &&
-			!exports.nodeType && exports;
-		var freeModule = typeof module == 'object' && module &&
-			!module.nodeType && module;
-		var freeGlobal = typeof global == 'object' && global;
-		if (
-			freeGlobal.global === freeGlobal ||
-			freeGlobal.window === freeGlobal ||
-			freeGlobal.self === freeGlobal
-		) {
-			root = freeGlobal;
-		}
-
-		/**
-		 * The `punycode` object.
-		 * @name punycode
-		 * @type Object
-		 */
-		var punycode,
-
-		/** Highest positive signed 32-bit float value */
-		maxInt = 2147483647, // aka. 0x7FFFFFFF or 2^31-1
-
-		/** Bootstring parameters */
-		base = 36,
-		tMin = 1,
-		tMax = 26,
-		skew = 38,
-		damp = 700,
-		initialBias = 72,
-		initialN = 128, // 0x80
-		delimiter = '-', // '\x2D'
-
-		/** Regular expressions */
-		regexPunycode = /^xn--/,
-		regexNonASCII = /[^\x20-\x7E]/, // unprintable ASCII chars + non-ASCII chars
-		regexSeparators = /[\x2E\u3002\uFF0E\uFF61]/g, // RFC 3490 separators
-
-		/** Error messages */
-		errors = {
-			'overflow': 'Overflow: input needs wider integers to process',
-			'not-basic': 'Illegal input >= 0x80 (not a basic code point)',
-			'invalid-input': 'Invalid input'
-		},
-
-		/** Convenience shortcuts */
-		baseMinusTMin = base - tMin,
-		floor = Math.floor,
-		stringFromCharCode = String.fromCharCode,
-
-		/** Temporary variable */
-		key;
-
-		/*--------------------------------------------------------------------------*/
-
-		/**
-		 * A generic error utility function.
-		 * @private
-		 * @param {String} type The error type.
-		 * @returns {Error} Throws a `RangeError` with the applicable error message.
-		 */
-		function error(type) {
-			throw RangeError(errors[type]);
-		}
-
-		/**
-		 * A generic `Array#map` utility function.
-		 * @private
-		 * @param {Array} array The array to iterate over.
-		 * @param {Function} callback The function that gets called for every array
-		 * item.
-		 * @returns {Array} A new array of values returned by the callback function.
-		 */
-		function map(array, fn) {
-			var length = array.length;
-			var result = [];
-			while (length--) {
-				result[length] = fn(array[length]);
-			}
-			return result;
-		}
-
-		/**
-		 * A simple `Array#map`-like wrapper to work with domain name strings or email
-		 * addresses.
-		 * @private
-		 * @param {String} domain The domain name or email address.
-		 * @param {Function} callback The function that gets called for every
-		 * character.
-		 * @returns {Array} A new string of characters returned by the callback
-		 * function.
-		 */
-		function mapDomain(string, fn) {
-			var parts = string.split('@');
-			var result = '';
-			if (parts.length > 1) {
-				// In email addresses, only the domain name should be punycoded. Leave
-				// the local part (i.e. everything up to `@`) intact.
-				result = parts[0] + '@';
-				string = parts[1];
-			}
-			// Avoid `split(regex)` for IE8 compatibility. See #17.
-			string = string.replace(regexSeparators, '\x2E');
-			var labels = string.split('.');
-			var encoded = map(labels, fn).join('.');
-			return result + encoded;
-		}
-
-		/**
-		 * Creates an array containing the numeric code points of each Unicode
-		 * character in the string. While JavaScript uses UCS-2 internally,
-		 * this function will convert a pair of surrogate halves (each of which
-		 * UCS-2 exposes as separate characters) into a single code point,
-		 * matching UTF-16.
-		 * @see `punycode.ucs2.encode`
-		 * @see <https://mathiasbynens.be/notes/javascript-encoding>
-		 * @memberOf punycode.ucs2
-		 * @name decode
-		 * @param {String} string The Unicode input string (UCS-2).
-		 * @returns {Array} The new array of code points.
-		 */
-		function ucs2decode(string) {
-			var output = [],
-			    counter = 0,
-			    length = string.length,
-			    value,
-			    extra;
-			while (counter < length) {
-				value = string.charCodeAt(counter++);
-				if (value >= 0xD800 && value <= 0xDBFF && counter < length) {
-					// high surrogate, and there is a next character
-					extra = string.charCodeAt(counter++);
-					if ((extra & 0xFC00) == 0xDC00) { // low surrogate
-						output.push(((value & 0x3FF) << 10) + (extra & 0x3FF) + 0x10000);
-					} else {
-						// unmatched surrogate; only append this code unit, in case the next
-						// code unit is the high surrogate of a surrogate pair
-						output.push(value);
-						counter--;
-					}
-				} else {
-					output.push(value);
-				}
-			}
-			return output;
-		}
-
-		/**
-		 * Creates a string based on an array of numeric code points.
-		 * @see `punycode.ucs2.decode`
-		 * @memberOf punycode.ucs2
-		 * @name encode
-		 * @param {Array} codePoints The array of numeric code points.
-		 * @returns {String} The new Unicode string (UCS-2).
-		 */
-		function ucs2encode(array) {
-			return map(array, function(value) {
-				var output = '';
-				if (value > 0xFFFF) {
-					value -= 0x10000;
-					output += stringFromCharCode(value >>> 10 & 0x3FF | 0xD800);
-					value = 0xDC00 | value & 0x3FF;
-				}
-				output += stringFromCharCode(value);
-				return output;
-			}).join('');
-		}
-
-		/**
-		 * Converts a basic code point into a digit/integer.
-		 * @see `digitToBasic()`
-		 * @private
-		 * @param {Number} codePoint The basic numeric code point value.
-		 * @returns {Number} The numeric value of a basic code point (for use in
-		 * representing integers) in the range `0` to `base - 1`, or `base` if
-		 * the code point does not represent a value.
-		 */
-		function basicToDigit(codePoint) {
-			if (codePoint - 48 < 10) {
-				return codePoint - 22;
-			}
-			if (codePoint - 65 < 26) {
-				return codePoint - 65;
-			}
-			if (codePoint - 97 < 26) {
-				return codePoint - 97;
-			}
-			return base;
-		}
-
-		/**
-		 * Converts a digit/integer into a basic code point.
-		 * @see `basicToDigit()`
-		 * @private
-		 * @param {Number} digit The numeric value of a basic code point.
-		 * @returns {Number} The basic code point whose value (when used for
-		 * representing integers) is `digit`, which needs to be in the range
-		 * `0` to `base - 1`. If `flag` is non-zero, the uppercase form is
-		 * used; else, the lowercase form is used. The behavior is undefined
-		 * if `flag` is non-zero and `digit` has no uppercase form.
-		 */
-		function digitToBasic(digit, flag) {
-			//  0..25 map to ASCII a..z or A..Z
-			// 26..35 map to ASCII 0..9
-			return digit + 22 + 75 * (digit < 26) - ((flag != 0) << 5);
-		}
-
-		/**
-		 * Bias adaptation function as per section 3.4 of RFC 3492.
-		 * http://tools.ietf.org/html/rfc3492#section-3.4
-		 * @private
-		 */
-		function adapt(delta, numPoints, firstTime) {
-			var k = 0;
-			delta = firstTime ? floor(delta / damp) : delta >> 1;
-			delta += floor(delta / numPoints);
-			for (/* no initialization */; delta > baseMinusTMin * tMax >> 1; k += base) {
-				delta = floor(delta / baseMinusTMin);
-			}
-			return floor(k + (baseMinusTMin + 1) * delta / (delta + skew));
-		}
-
-		/**
-		 * Converts a Punycode string of ASCII-only symbols to a string of Unicode
-		 * symbols.
-		 * @memberOf punycode
-		 * @param {String} input The Punycode string of ASCII-only symbols.
-		 * @returns {String} The resulting string of Unicode symbols.
-		 */
-		function decode(input) {
-			// Don't use UCS-2
-			var output = [],
-			    inputLength = input.length,
-			    out,
-			    i = 0,
-			    n = initialN,
-			    bias = initialBias,
-			    basic,
-			    j,
-			    index,
-			    oldi,
-			    w,
-			    k,
-			    digit,
-			    t,
-			    /** Cached calculation results */
-			    baseMinusT;
-
-			// Handle the basic code points: let `basic` be the number of input code
-			// points before the last delimiter, or `0` if there is none, then copy
-			// the first basic code points to the output.
-
-			basic = input.lastIndexOf(delimiter);
-			if (basic < 0) {
-				basic = 0;
-			}
-
-			for (j = 0; j < basic; ++j) {
-				// if it's not a basic code point
-				if (input.charCodeAt(j) >= 0x80) {
-					error('not-basic');
-				}
-				output.push(input.charCodeAt(j));
-			}
-
-			// Main decoding loop: start just after the last delimiter if any basic code
-			// points were copied; start at the beginning otherwise.
-
-			for (index = basic > 0 ? basic + 1 : 0; index < inputLength; /* no final expression */) {
-
-				// `index` is the index of the next character to be consumed.
-				// Decode a generalized variable-length integer into `delta`,
-				// which gets added to `i`. The overflow checking is easier
-				// if we increase `i` as we go, then subtract off its starting
-				// value at the end to obtain `delta`.
-				for (oldi = i, w = 1, k = base; /* no condition */; k += base) {
-
-					if (index >= inputLength) {
-						error('invalid-input');
-					}
-
-					digit = basicToDigit(input.charCodeAt(index++));
-
-					if (digit >= base || digit > floor((maxInt - i) / w)) {
-						error('overflow');
-					}
-
-					i += digit * w;
-					t = k <= bias ? tMin : (k >= bias + tMax ? tMax : k - bias);
-
-					if (digit < t) {
-						break;
-					}
-
-					baseMinusT = base - t;
-					if (w > floor(maxInt / baseMinusT)) {
-						error('overflow');
-					}
-
-					w *= baseMinusT;
-
-				}
-
-				out = output.length + 1;
-				bias = adapt(i - oldi, out, oldi == 0);
-
-				// `i` was supposed to wrap around from `out` to `0`,
-				// incrementing `n` each time, so we'll fix that now:
-				if (floor(i / out) > maxInt - n) {
-					error('overflow');
-				}
-
-				n += floor(i / out);
-				i %= out;
-
-				// Insert `n` at position `i` of the output
-				output.splice(i++, 0, n);
-
-			}
-
-			return ucs2encode(output);
-		}
-
-		/**
-		 * Converts a string of Unicode symbols (e.g. a domain name label) to a
-		 * Punycode string of ASCII-only symbols.
-		 * @memberOf punycode
-		 * @param {String} input The string of Unicode symbols.
-		 * @returns {String} The resulting Punycode string of ASCII-only symbols.
-		 */
-		function encode(input) {
-			var n,
-			    delta,
-			    handledCPCount,
-			    basicLength,
-			    bias,
-			    j,
-			    m,
-			    q,
-			    k,
-			    t,
-			    currentValue,
-			    output = [],
-			    /** `inputLength` will hold the number of code points in `input`. */
-			    inputLength,
-			    /** Cached calculation results */
-			    handledCPCountPlusOne,
-			    baseMinusT,
-			    qMinusT;
-
-			// Convert the input in UCS-2 to Unicode
-			input = ucs2decode(input);
-
-			// Cache the length
-			inputLength = input.length;
-
-			// Initialize the state
-			n = initialN;
-			delta = 0;
-			bias = initialBias;
-
-			// Handle the basic code points
-			for (j = 0; j < inputLength; ++j) {
-				currentValue = input[j];
-				if (currentValue < 0x80) {
-					output.push(stringFromCharCode(currentValue));
-				}
-			}
-
-			handledCPCount = basicLength = output.length;
-
-			// `handledCPCount` is the number of code points that have been handled;
-			// `basicLength` is the number of basic code points.
-
-			// Finish the basic string - if it is not empty - with a delimiter
-			if (basicLength) {
-				output.push(delimiter);
-			}
-
-			// Main encoding loop:
-			while (handledCPCount < inputLength) {
-
-				// All non-basic code points < n have been handled already. Find the next
-				// larger one:
-				for (m = maxInt, j = 0; j < inputLength; ++j) {
-					currentValue = input[j];
-					if (currentValue >= n && currentValue < m) {
-						m = currentValue;
-					}
-				}
-
-				// Increase `delta` enough to advance the decoder's <n,i> state to <m,0>,
-				// but guard against overflow
-				handledCPCountPlusOne = handledCPCount + 1;
-				if (m - n > floor((maxInt - delta) / handledCPCountPlusOne)) {
-					error('overflow');
-				}
-
-				delta += (m - n) * handledCPCountPlusOne;
-				n = m;
-
-				for (j = 0; j < inputLength; ++j) {
-					currentValue = input[j];
-
-					if (currentValue < n && ++delta > maxInt) {
-						error('overflow');
-					}
-
-					if (currentValue == n) {
-						// Represent delta as a generalized variable-length integer
-						for (q = delta, k = base; /* no condition */; k += base) {
-							t = k <= bias ? tMin : (k >= bias + tMax ? tMax : k - bias);
-							if (q < t) {
-								break;
-							}
-							qMinusT = q - t;
-							baseMinusT = base - t;
-							output.push(
-								stringFromCharCode(digitToBasic(t + qMinusT % baseMinusT, 0))
-							);
-							q = floor(qMinusT / baseMinusT);
-						}
-
-						output.push(stringFromCharCode(digitToBasic(q, 0)));
-						bias = adapt(delta, handledCPCountPlusOne, handledCPCount == basicLength);
-						delta = 0;
-						++handledCPCount;
-					}
-				}
-
-				++delta;
-				++n;
-
-			}
-			return output.join('');
-		}
-
-		/**
-		 * Converts a Punycode string representing a domain name or an email address
-		 * to Unicode. Only the Punycoded parts of the input will be converted, i.e.
-		 * it doesn't matter if you call it on a string that has already been
-		 * converted to Unicode.
-		 * @memberOf punycode
-		 * @param {String} input The Punycoded domain name or email address to
-		 * convert to Unicode.
-		 * @returns {String} The Unicode representation of the given Punycode
-		 * string.
-		 */
-		function toUnicode(input) {
-			return mapDomain(input, function(string) {
-				return regexPunycode.test(string)
-					? decode(string.slice(4).toLowerCase())
-					: string;
-			});
-		}
-
-		/**
-		 * Converts a Unicode string representing a domain name or an email address to
-		 * Punycode. Only the non-ASCII parts of the domain name will be converted,
-		 * i.e. it doesn't matter if you call it with a domain that's already in
-		 * ASCII.
-		 * @memberOf punycode
-		 * @param {String} input The domain name or email address to convert, as a
-		 * Unicode string.
-		 * @returns {String} The Punycode representation of the given domain name or
-		 * email address.
-		 */
-		function toASCII(input) {
-			return mapDomain(input, function(string) {
-				return regexNonASCII.test(string)
-					? 'xn--' + encode(string)
-					: string;
-			});
-		}
-
-		/*--------------------------------------------------------------------------*/
-
-		/** Define the public API */
-		punycode = {
-			/**
-			 * A string representing the current Punycode.js version number.
-			 * @memberOf punycode
-			 * @type String
-			 */
-			'version': '1.3.2',
-			/**
-			 * An object of methods to convert from JavaScript's internal character
-			 * representation (UCS-2) to Unicode code points, and back.
-			 * @see <https://mathiasbynens.be/notes/javascript-encoding>
-			 * @memberOf punycode
-			 * @type Object
-			 */
-			'ucs2': {
-				'decode': ucs2decode,
-				'encode': ucs2encode
-			},
-			'decode': decode,
-			'encode': encode,
-			'toASCII': toASCII,
-			'toUnicode': toUnicode
-		};
-
-		/** Expose `punycode` */
-		// Some AMD build optimizers, like r.js, check for specific condition patterns
-		// like the following:
-		if (
-			true
-		) {
-			!(__WEBPACK_AMD_DEFINE_RESULT__ = function() {
-				return punycode;
-			}.call(exports, __webpack_require__, exports, module), __WEBPACK_AMD_DEFINE_RESULT__ !== undefined && (module.exports = __WEBPACK_AMD_DEFINE_RESULT__));
-		} else if (freeExports && freeModule) {
-			if (module.exports == freeExports) { // in Node.js or RingoJS v0.8.0+
-				freeModule.exports = punycode;
-			} else { // in Narwhal or RingoJS v0.7.0-
-				for (key in punycode) {
-					punycode.hasOwnProperty(key) && (freeExports[key] = punycode[key]);
-				}
-			}
-		} else { // in Rhino or a web browser
-			root.punycode = punycode;
-		}
-
-	}(this));
-
-	/* WEBPACK VAR INJECTION */}.call(exports, __webpack_require__(51)(module), (function() { return this; }())))
-
-/***/ },
-/* 917 */
-/***/ function(module, exports) {
-
-	// removed by extract-text-webpack-plugin
-
-/***/ },
+/* 915 */,
+/* 916 */,
+/* 917 */,
 /* 918 */,
 /* 919 */
 /***/ function(module, exports) {
 
 	module.exports = "<svg xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 1792 1792\"><path d=\"M1395 1184q0 13-10 23l-50 50q-10 10-23 10t-23-10l-393-393-393 393q-10 10-23 10t-23-10l-50-50q-10-10-10-23t10-23l466-466q10-10 23-10t23 10l466 466q10 10 10 23z\" fill=\"#696969\"></path></svg>"
 
 /***/ },
 /* 920 */
@@ -33880,120 +33827,80 @@ return /******/ (function(modules) { // 
 	var _actions = __webpack_require__(244);
 
 	var _actions2 = _interopRequireDefault(_actions);
 
 	var _selectors = __webpack_require__(242);
 
 	var _devtoolsConfig = __webpack_require__(828);
 
-	var _parser = __webpack_require__(827);
-
 	__webpack_require__(922);
 
 	var _previewFunction = __webpack_require__(701);
 
 	var _previewFunction2 = _interopRequireDefault(_previewFunction);
 
 	function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
 
-	function _asyncToGenerator(fn) { return function () { var gen = fn.apply(this, arguments); return new Promise(function (resolve, reject) { function step(key, arg) { try { var info = gen[key](arg); var value = info.value; } catch (error) { reject(error); return; } if (info.done) { resolve(value); } else { return Promise.resolve(value).then(function (value) { step("next", value); }, function (err) { step("throw", err); }); } } return step("next"); }); }; }
-
 	class Outline extends _react.Component {
 
 	  constructor(props) {
 	    super(props);
-	    var sourceText = props.sourceText,
-	        isHidden = props.isHidden;
-
 	    this.state = {};
-	    if (!isHidden) {
-	      this.setSymbolDeclarations(sourceText);
-	    }
-	  }
-
-	  componentWillReceiveProps(_ref) {
-	    var sourceText = _ref.sourceText;
-
-	    if (sourceText) {
-	      this.setSymbolDeclarations(sourceText);
-	    }
-	  }
-
-	  // TODO: move this logic out of the component and into a reducer
-	  setSymbolDeclarations(sourceText) {
-	    var _this = this;
-
-	    return _asyncToGenerator(function* () {
-	      var symbolDeclarations = yield (0, _parser.getSymbols)(sourceText.toJS());
-
-	      if (symbolDeclarations !== _this.state.symbolDeclarations) {
-	        _this.setState({
-	          symbolDeclarations
-	        });
-	      }
-	    })();
 	  }
 
 	  selectItem(location) {
 	    var _props = this.props,
 	        selectedSource = _props.selectedSource,
 	        selectSource = _props.selectSource;
 
+	    if (!selectedSource) {
+	      return;
+	    }
 	    var selectedSourceId = selectedSource.get("id");
 	    var startLine = location.start.line;
 	    selectSource(selectedSourceId, { line: startLine });
 	  }
 
 	  renderFunction(func) {
+	    var name = func.name,
+	        location = func.location;
+
+
 	    return _react.DOM.li({
-	      key: func.id,
+	      key: `${name}:${location.start.line}:${location.start.column}`,
 	      className: "outline-list__element",
-	      onClick: () => this.selectItem(func.location)
-	    }, (0, _previewFunction2.default)(func));
+	      onClick: () => this.selectItem(location)
+	    }, (0, _previewFunction2.default)({ name }));
 	  }
 
 	  renderFunctions() {
-	    var symbolDeclarations = this.state.symbolDeclarations;
-
-	    if (!symbolDeclarations) {
-	      return;
-	    }
-
-	    var functions = symbolDeclarations.functions;
-
-
-	    return functions.filter(func => func.name != "anonymous").map(func => this.renderFunction(func));
+	    var symbols = this.props.symbols;
+
+
+	    return symbols.functions.filter(func => func.name != "anonymous").map(func => this.renderFunction(func));
 	  }
 
 	  render() {
 	    var isHidden = this.props.isHidden;
 
 	    if (!(0, _devtoolsConfig.isEnabled)("outline")) {
 	      return null;
 	    }
 
 	    return _react.DOM.div({ className: (0, _classnames2.default)("outline", { hidden: isHidden }) }, _react.DOM.ul({ className: "outline-list" }, this.renderFunctions()));
 	  }
 	}
 
-	Outline.propTypes = {
-	  isHidden: _react.PropTypes.bool.isRequired,
-	  sourceText: _react.PropTypes.object,
-	  selectSource: _react.PropTypes.func.isRequired,
-	  selectedSource: _react.PropTypes.object
-	};
-
 	Outline.displayName = "Outline";
 
 	exports.default = (0, _reactRedux.connect)(state => {
 	  var selectedSource = (0, _selectors.getSelectedSource)(state);
-	  var sourceId = selectedSource ? selectedSource.get("id") : null;
 	  return {
-	    sourceText: (0, _selectors.getSourceText)(state, sourceId),
+	    symbols: (0, _selectors.getSymbols)(state, selectedSource && selectedSource.toJS()),
 	    selectedSource
 	  };
 	}, dispatch => (0, _redux.bindActionCreators)(_actions2.default, dispatch))(Outline);
 
 /***/ },
 /* 922 */
 /***/ function(module, exports) {
 
@@ -37267,17 +37174,17 @@ return /******/ (function(modules) { // 
 	  rep: wrapRender(GripMap),
 	  supportsObject
 	};
 
 /***/ },
 /* 960 */
 /***/ function(module, exports) {
 
-	module.exports = "# This Source Code Form is subject to the terms of the Mozilla Public\n# License, v. 2.0. If a copy of the MPL was not distributed with this\n# file, You can obtain one at http://mozilla.org/MPL/2.0/.\n\n# LOCALIZATION NOTE These strings are used inside the Debugger\n# which is available from the Web Developer sub-menu -> 'Debugger'.\n# The correct localization of this file might be to keep it in\n# English, or another language commonly spoken among web developers.\n# You want to make that choice consistent across the developer tools.\n# A good criteria is the language in which you'd find the best\n# documentation on web development on the web.\n\n# LOCALIZATION NOTE (collapsePanes): This is the tooltip for the button\n# that collapses the left and right panes in the debugger UI.\ncollapsePanes=Collapse panes\n\n# LOCALIZATION NOTE (copySourceUrl): This is the text that appears in the\n# context menu to copy the source URL of file open.\ncopySourceUrl=Copy Source Url\n\n# LOCALIZATION NOTE (copySourceUrl.accesskey): Access key to copy the source URL of a file from\n# the context menu.\ncopySourceUrl.accesskey=u\n\n# LOCALIZATION NOTE (copyStackTrace): This is the text that appears in the\n# context menu to copy the stack trace methods, file names and row number.\ncopyStackTrace=Copy Stack Trace\n\n# LOCALIZATION NOTE (copyStackTrace.accesskey): Access key to copy the stack trace data from\n# the context menu.\ncopyStackTrace.accesskey=c\n\n# LOCALIZATION NOTE (expandPanes): This is the tooltip for the button\n# that expands the left and right panes in the debugger UI.\nexpandPanes=Expand panes\n\n# LOCALIZATION NOTE (pauseButtonTooltip): The tooltip that is displayed for the pause\n# button when the debugger is in a running state.\npauseButtonTooltip=Pause %S\n\n# LOCALIZATION NOTE (pausePendingButtonTooltip): The tooltip that is displayed for\n# the pause button after it's been clicked but before the next JavaScript to run.\npausePendingButtonTooltip=Waiting for next execution\n\n# LOCALIZATION NOTE (resumeButtonTooltip): The label that is displayed on the pause\n# button when the debugger is in a paused state.\nresumeButtonTooltip=Resume %S\n\n# LOCALIZATION NOTE (stepOverTooltip): The label that is displayed on the\n# button that steps over a function call.\nstepOverTooltip=Step Over %S\n\n# LOCALIZATION NOTE (stepInTooltip): The label that is displayed on the\n# button that steps into a function call.\nstepInTooltip=Step In %S\n\n# LOCALIZATION NOTE (stepOutTooltip): The label that is displayed on the\n# button that steps out of a function call.\nstepOutTooltip=Step Out %S\n\n# LOCALIZATION NOTE (noWorkersText): The text to display in the workers list\n# when there are no workers.\nnoWorkersText=This page has no workers.\n\n# LOCALIZATION NOTE (noSourcesText): The text to display in the sources list\n# when there are no sources.\nnoSourcesText=This page has no sources.\n\n# LOCALIZATION NOTE (noEventListenersText): The text to display in the events tab\n# when there are no events.\nnoEventListenersText=No event listeners to display\n\n# LOCALIZATION NOTE (eventListenersHeader): The text to display in the events\n# header.\neventListenersHeader=Event Listeners\n\n# LOCALIZATION NOTE (noStackFramesText): The text to display in the call stack tab\n# when there are no stack frames.\nnoStackFramesText=No stack frames to display\n\n# LOCALIZATION NOTE (eventCheckboxTooltip): The tooltip text to display when\n# the user hovers over the checkbox used to toggle an event breakpoint.\neventCheckboxTooltip=Toggle breaking on this event\n\n# LOCALIZATION NOTE (eventOnSelector): The text to display in the events tab\n# for every event item, between the event type and event selector.\neventOnSelector=on\n\n# LOCALIZATION NOTE (eventInSource): The text to display in the events tab\n# for every event item, between the event selector and listener's owner source.\neventInSource=in\n\n# LOCALIZATION NOTE (eventNodes): The text to display in the events tab when\n# an event is listened on more than one target node.\neventNodes=%S nodes\n\n# LOCALIZATION NOTE (eventNative): The text to display in the events tab when\n# a listener is added from plugins, thus getting translated to native code.\neventNative=[native code]\n\n# LOCALIZATION NOTE (*Events): The text to display in the events tab for\n# each group of sub-level event entries.\nanimationEvents=Animation\naudioEvents=Audio\nbatteryEvents=Battery\nclipboardEvents=Clipboard\ncompositionEvents=Composition\ndeviceEvents=Device\ndisplayEvents=Display\ndragAndDropEvents=Drag and Drop\ngamepadEvents=Gamepad\nindexedDBEvents=IndexedDB\ninteractionEvents=Interaction\nkeyboardEvents=Keyboard\nmediaEvents=HTML5 Media\nmouseEvents=Mouse\nmutationEvents=Mutation\nnavigationEvents=Navigation\npointerLockEvents=Pointer Lock\nsensorEvents=Sensor\nstorageEvents=Storage\ntimeEvents=Time\ntouchEvents=Touch\notherEvents=Other\n\n# LOCALIZATION NOTE (blackboxCheckboxTooltip2): The tooltip text to display when\n# the user hovers over the checkbox used to toggle blackboxing its associated\n# source.\nblackboxCheckboxTooltip2=Toggle blackboxing\n\n# LOCALIZATION NOTE (sources.search.key2): Key shortcut to open the search for\n# searching all the source files the debugger has seen.\nsources.search.key2=CmdOrCtrl+P\n\n# LOCALIZATION NOTE (sources.search.alt.key): A second key shortcut to open the\n# search for searching all the source files the debugger has seen.\nsources.search.alt.key=CmdOrCtrl+O\n\n# LOCALIZATION NOTE (sources.noSourcesAvailable): Text shown when the debugger\n# does not have any sources.\nsources.noSourcesAvailable=This page has no sources\n\n# LOCALIZATION NOTE (sourcesPane.showOutlineTooltip): The text that may appear\n# as a tooltip when hovering over the 'toggle sources' button in\n# left sidebar\nsourcesPane.showSourcesTooltip=Show sources\nsourcesPane.showOutlineTooltip=Show outline\n\n# LOCALIZATION NOTE (sourceSearch.search.key2): Key shortcut to open the search\n# for searching within a the currently opened files in the editor\nsourceSearch.search.key2=CmdOrCtrl+F\n\n# LOCALIZATION NOTE (sourceSearch.search.placeholder): placeholder text in\n# the source search input bar\nsourceSearch.search.placeholder=Search in file…\n\n# LOCALIZATION NOTE (sourceSearch.search.again.key2): Key shortcut to highlight\n# the next occurrence of the last search triggered from a source search\nsourceSearch.search.again.key2=CmdOrCtrl+G\n\n# LOCALIZATION NOTE (sourceSearch.search.againPrev.key2): Key shortcut to highlight\n# the previous occurrence of the last search triggered from a source search\nsourceSearch.search.againPrev.key2=CmdOrCtrl+Shift+G\n\n# LOCALIZATION NOTE (sourceSearch.resultsSummary1): Shows a summary of\n# the number of matches for autocomplete\nsourceSearch.resultsSummary1=%d results\n\n# LOCALIZATION NOTE (noMatchingStringsText): The text to display in the\n# global search results when there are no matching strings after filtering.\nnoMatchingStringsText=No matches found\n\n# LOCALIZATION NOTE (emptySearchText): This is the text that appears in the\n# filter text box when it is empty and the scripts container is selected.\nemptySearchText=Search scripts (%S)\n\n# LOCALIZATION NOTE (emptyVariablesFilterText): This is the text that\n# appears in the filter text box for the variables view container.\nemptyVariablesFilterText=Filter variables\n\n# LOCALIZATION NOTE (emptyPropertiesFilterText): This is the text that\n# appears in the filter text box for the editor's variables view bubble.\nemptyPropertiesFilterText=Filter properties\n\n# LOCALIZATION NOTE (searchPanelFilter): This is the text that appears in the\n# filter panel popup for the filter scripts operation.\nsearchPanelFilter=Filter scripts (%S)\n\n# LOCALIZATION NOTE (searchPanelGlobal): This is the text that appears in the\n# filter panel popup for the global search operation.\nsearchPanelGlobal=Search in all files (%S)\n\n# LOCALIZATION NOTE (searchPanelFunction): This is the text that appears in the\n# filter panel popup for the function search operation.\nsearchPanelFunction=Search for function definition (%S)\n\n# LOCALIZATION NOTE (searchPanelToken): This is the text that appears in the\n# filter panel popup for the token search operation.\nsearchPanelToken=Find in this file (%S)\n\n# LOCALIZATION NOTE (searchPanelGoToLine): This is the text that appears in the\n# filter panel popup for the line search operation.\nsearchPanelGoToLine=Go to line (%S)\n\n# LOCALIZATION NOTE (searchPanelVariable): This is the text that appears in the\n# filter panel popup for the variables search operation.\nsearchPanelVariable=Filter variables (%S)\n\n# LOCALIZATION NOTE (breakpointMenuItem): The text for all the elements that\n# are displayed in the breakpoints menu item popup.\nbreakpointMenuItem.setConditional=Configure conditional breakpoint\nbreakpointMenuItem.enableSelf=Enable breakpoint\nbreakpointMenuItem.disableSelf=Disable breakpoint\nbreakpointMenuItem.deleteSelf=Remove breakpoint\nbreakpointMenuItem.enableOthers=Enable others\nbreakpointMenuItem.disableOthers=Disable others\nbreakpointMenuItem.deleteOthers=Remove others\nbreakpointMenuItem.enableAll=Enable all breakpoints\nbreakpointMenuItem.disableAll=Disable all breakpoints\nbreakpointMenuItem.deleteAll=Remove all breakpoints\n\n# LOCALIZATION NOTE (breakpoints.header): Breakpoints right sidebar pane header.\nbreakpoints.header=Breakpoints\n\n# LOCALIZATION NOTE (breakpoints.none): The text that appears when there are\n# no breakpoints present\nbreakpoints.none=No Breakpoints\n\n# LOCALIZATION NOTE (breakpoints.enable): The text that may appear as a tooltip\n# when hovering over the 'disable breakpoints' switch button in right sidebar\nbreakpoints.enable=Enable Breakpoints\n\n# LOCALIZATION NOTE (breakpoints.disable): The text that may appear as a tooltip\n# when hovering over the 'disable breakpoints' switch button in right sidebar\nbreakpoints.disable=Disable Breakpoints\n\n# LOCALIZATION NOTE (breakpoints.removeBreakpointTooltip): The tooltip that is displayed\n# for remove breakpoint button in right sidebar\nbreakpoints.removeBreakpointTooltip=Remove Breakpoint\n\n# LOCALIZATION NOTE (callStack.header): Call Stack right sidebar pane header.\ncallStack.header=Call Stack\n\n# LOCALIZATION NOTE (callStack.notPaused): Call Stack right sidebar pane\n# message when not paused.\ncallStack.notPaused=Not Paused\n\n# LOCALIZATION NOTE (callStack.collapse): Call Stack right sidebar pane\n# message to hide some of the frames that are shown.\ncallStack.collapse=Collapse Rows\n\n# LOCALIZATION NOTE (callStack.expand): Call Stack right sidebar pane\n# message to show more of the frames.\ncallStack.expand=Expand Rows\n\n# LOCALIZATION NOTE (editor.searchResults): Editor Search bar message\n# for the summarizing the selected search result. e.g. 5 of 10 results.\neditor.searchResults=%d of %d results\n\n# LOCALIZATION NOTE (sourceSearch.singleResult): Copy shown when there is one result.\neditor.singleResult=1 result\n\n# LOCALIZATION NOTE (editor.noResults): Editor Search bar message\n# for when no results found.\neditor.noResults=no results\n\n# LOCALIZATION NOTE (editor.searchResults.nextResult): Editor Search bar\n# tooltip for traversing to the Next Result\neditor.searchResults.nextResult=Next Result\n\n# LOCALIZATION NOTE (editor.searchResults.prevResult): Editor Search bar\n# tooltip for traversing to the Previous Result\neditor.searchResults.prevResult=Previous Result\n\n# LOCALIZATION NOTE (editor.searchTypeToggleTitle): Search bar title for\n# toggling search type buttons(function search, variable search)\neditor.searchTypeToggleTitle=Search for:\n\n# LOCALIZATION NOTE (editor.addBreakpoint): Editor gutter context menu item\n# for adding a breakpoint on a line.\neditor.addBreakpoint=Add Breakpoint\n\n# LOCALIZATION NOTE (editor.disableBreakpoint): Editor gutter context menu item\n# for disabling a breakpoint on a line.\neditor.disableBreakpoint=Disable Breakpoint\n\n# LOCALIZATION NOTE (editor.enableBreakpoint): Editor gutter context menu item\n# for enabling a breakpoint on a line.\neditor.enableBreakpoint=Enable Breakpoint\n\n# LOCALIZATION NOTE (editor.removeBreakpoint): Editor gutter context menu item\n# for removing a breakpoint on a line.\neditor.removeBreakpoint=Remove Breakpoint\n\n# LOCALIZATION NOTE (editor.editBreakpoint): Editor gutter context menu item\n# for setting a breakpoint condition on a line.\neditor.editBreakpoint=Edit Breakpoint\n\n# LOCALIZATION NOTE (editor.addConditionalBreakpoint): Editor gutter context\n# menu item for adding a breakpoint condition on a line.\neditor.addConditionalBreakpoint=Add Conditional Breakpoint\n\n# LOCALIZATION NOTE (editor.conditionalPanel.placeholder): Placeholder text for\n# input element inside ConditionalPanel component\neditor.conditionalPanel.placeholder=This breakpoint will pause when the expression is true\n\n# LOCALIZATION NOTE (editor.conditionalPanel.placeholder): Tooltip text for\n# close button inside ConditionalPanel component\neditor.conditionalPanel.close=Cancel edit breakpoint and close\n\n# LOCALIZATION NOTE (editor.jumpToMappedLocation1): Context menu item\n# for navigating to a source mapped location\neditor.jumpToMappedLocation1=Jump to %S location\n\n# LOCALIZATION NOTE (framework.disableGrouping): This is the text that appears in the\n# context menu to disable framework grouping.\nframework.disableGrouping=Disable Framework Grouping\n\n# LOCALIZATION NOTE (framework.disableGrouping.accesskey): Access key to toggle\n# framework grouping from the context menu.\nframework.disableGrouping.accesskey=u\n\n# LOCALIZATION NOTE (framework.enableGrouping): This is the text that appears in the\n# context menu to enable framework grouping.\nframework.enableGrouping=Enable Framework Grouping\n\n# LOCALIZATION NOTE (framework.enableGrouping.accesskey): Access key to toggle\n# framework grouping from the context menu.\nframework.enableGrouping.accesskey=u\n\n# LOCALIZATION NOTE (generated): Source Map term for a server source location\ngenerated=generated\n\n# LOCALIZATION NOTE (original): Source Map term for a debugger UI source location\noriginal=original\n\n# LOCALIZATION NOTE (expressions.placeholder): Placeholder text for expression\n# input element\nexpressions.placeholder=Add Watch Expression\n\n# LOCALIZATION NOTE (sourceTabs.closeTab): Editor source tab context menu item\n# for closing the selected tab below the mouse.\nsourceTabs.closeTab=Close tab\n\n# LOCALIZATION NOTE (sourceTabs.closeTab.accesskey): Access key to close the currently select\n# source tab from the editor context menu item.\nsourceTabs.closeTab.accesskey=c\n\n# LOCALIZATION NOTE (sourceTabs.closeOtherTabs): Editor source tab context menu item\n# for closing the other tabs.\nsourceTabs.closeOtherTabs=Close others\n\n# LOCALIZATION NOTE (sourceTabs.closeOtherTabs.accesskey): Access key to close other source tabs\n# from the editor context menu.\nsourceTabs.closeOtherTabs.accesskey=o\n\n# LOCALIZATION NOTE (sourceTabs.closeTabsToEnd): Editor source tab context menu item\n# for closing the tabs to the end (the right for LTR languages) of the selected tab.\nsourceTabs.closeTabsToEnd=Close tabs to the right\n\n# LOCALIZATION NOTE (sourceTabs.closeTabsToEnd.accesskey): Access key to close source tabs\n# after the selected tab from the editor context menu.\nsourceTabs.closeTabsToEnd.accesskey=e\n\n# LOCALIZATION NOTE (sourceTabs.closeAllTabs): Editor source tab context menu item\n# for closing all tabs.\nsourceTabs.closeAllTabs=Close all tabs\n\n# LOCALIZATION NOTE (sourceTabs.closeAllTabs.accesskey): Access key to close all tabs from the\n# editor context menu.\nsourceTabs.closeAllTabs.accesskey=a\n\n# LOCALIZATION NOTE (sourceTabs.revealInTree): Editor source tab context menu item\n# for revealing source in tree.\nsourceTabs.revealInTree=Reveal in Tree\n\n# LOCALIZATION NOTE (sourceTabs.revealInTree.accesskey): Access key to reveal a source in the\n# tree from the context menu.\nsourceTabs.revealInTree.accesskey=r\n\n# LOCALIZATION NOTE (sourceTabs.copyLink): Editor source tab context menu item\n# for copying a link address.\nsourceTabs.copyLink=Copy Link Address\n\n# LOCALIZATION NOTE (sourceTabs.copyLink.accesskey): Access key to copy a link addresss from the\n# editor context menu.\nsourceTabs.copyLink.accesskey=l\n\n# LOCALIZATION NOTE (sourceTabs.prettyPrint): Editor source tab context menu item\n# for pretty printing the source.\nsourceTabs.prettyPrint=Pretty Print Source\n\n# LOCALIZATION NOTE (sourceTabs.prettyPrint.accesskey): Access key to pretty print a source from\n# the editor context menu.\nsourceTabs.prettyPrint.accesskey=p\n\n# LOCALIZATION NOTE (sourceFooter.blackbox): Tooltip text associated\n# with the blackbox button\nsourceFooter.blackbox=Blackbox Source\n\n# LOCALIZATION NOTE (sourceFooter.unblackbox): Tooltip text associated\n# with the blackbox button\nsourceFooter.unblackbox=Unblackbox Source\n\n# LOCALIZATION NOTE (sourceFooter.unblackbox.accesskey): Access key to blackbox\n# an associated source\nsourceFooter.unblackbox.accesskey=b\n\n# LOCALIZATION NOTE (sourceFooter.blackbox.accesskey): Access key to blackbox\n# an associated source\nsourceFooter.blackbox.accesskey=b\n\n# LOCALIZATION NOTE (sourceFooter.blackboxed): Text associated\n# with a blackboxed source\nsourceFooter.blackboxed=Blackboxed Source\n\n# LOCALIZATION NOTE (sourceTabs.closeTabButtonTooltip): The tooltip that is displayed\n# for close tab button in source tabs.\nsourceTabs.closeTabButtonTooltip=Close tab\n\n# LOCALIZATION NOTE (sourceTabs.newTabButtonTooltip): The tooltip that is displayed for\n# new tab button in source tabs.\nsourceTabs.newTabButtonTooltip=Search for sources (%S)\n\n# LOCALIZATION NOTE (scopes.header): Scopes right sidebar pane header.\nscopes.header=Scopes\n\n# LOCALIZATION NOTE (scopes.notAvailable): Scopes right sidebar pane message\n# for when the debugger is paused, but there isn't pause data.\nscopes.notAvailable=Scopes Unavailable\n\n# LOCALIZATION NOTE (scopes.notPaused): Scopes right sidebar pane message\n# for when the debugger is not paused.\nscopes.notPaused=Not Paused\n\n# LOCALIZATION NOTE (scopes.block): Refers to a block of code in\n# the scopes pane when the debugger is paused.\nscopes.block=Block\n\n# LOCALIZATION NOTE (sources.header): Sources left sidebar header\nsources.header=Sources\n\n# LOCALIZATION NOTE (sources.search): Sources left sidebar prompt\n# e.g. Cmd+P to search. On a mac, we use the command unicode character.\n# On windows, it's ctrl.\nsources.search=%S to search\n\n# LOCALIZATION NOTE (watchExpressions.header): Watch Expressions right sidebar\n# pane header.\nwatchExpressions.header=Watch Expressions\n\n# LOCALIZATION NOTE (watchExpressions.refreshButton): Watch Expressions header\n# button for refreshing the expressions.\nwatchExpressions.refreshButton=Refresh\n\n# LOCALIZATION NOTE (welcome.search): The center pane welcome panel's\n# search prompt. e.g. cmd+p to search for files. On windows, it's ctrl, on\n# a mac we use the unicode character.\nwelcome.search=%S to search for sources\n\n# LOCALIZATION NOTE (sourceSearch.search): The center pane Source Search\n# prompt for searching for files.\nsourceSearch.search=Search Sources…\n\n# LOCALIZATION NOTE (sourceSearch.noResults): The center pane Source Search\n# message when the query did not match any of the sources.\nsourceSearch.noResults=No files matching %S found\n\n# LOCALIZATION NOTE (ignoreExceptions): The pause on exceptions button tooltip\n# when the debugger will not pause on exceptions.\nignoreExceptions=Ignore exceptions. Click to pause on uncaught exceptions\n\n# LOCALIZATION NOTE (pauseOnUncaughtExceptions): The pause on exceptions button\n# tooltip when the debugger will pause on uncaught exceptions.\npauseOnUncaughtExceptions=Pause on uncaught exceptions. Click to pause on all exceptions\n\n# LOCALIZATION NOTE (pauseOnExceptions): The pause on exceptions button tooltip\n# when the debugger will pause on all exceptions.\npauseOnExceptions=Pause on all exceptions. Click to ignore exceptions\n\n# LOCALIZATION NOTE (loadingText): The text that is displayed in the script\n# editor when the loading process has started but there is no file to display\n# yet.\nloadingText=Loading\\u2026\n\n# LOCALIZATION NOTE (errorLoadingText2): The text that is displayed in the debugger\n# viewer when there is an error loading a file\nerrorLoadingText2=Error loading this URL: %S\n\n# LOCALIZATION NOTE (addWatchExpressionText): The text that is displayed in the\n# watch expressions list to add a new item.\naddWatchExpressionText=Add watch expression\n\n# LOCALIZATION NOTE (addWatchExpressionButton): The button that is displayed in the\n# variables view popup.\naddWatchExpressionButton=Watch\n\n# LOCALIZATION NOTE (emptyVariablesText): The text that is displayed in the\n# variables pane when there are no variables to display.\nemptyVariablesText=No variables to display\n\n# LOCALIZATION NOTE (scopeLabel): The text that is displayed in the variables\n# pane as a header for each variable scope (e.g. \"Global scope, \"With scope\",\n# etc.).\nscopeLabel=%S scope\n\n# LOCALIZATION NOTE (watchExpressionsScopeLabel): The name of the watch\n# expressions scope. This text is displayed in the variables pane as a header for\n# the watch expressions scope.\nwatchExpressionsScopeLabel=Watch expressions\n\n# LOCALIZATION NOTE (globalScopeLabel): The name of the global scope. This text\n# is added to scopeLabel and displayed in the variables pane as a header for\n# the global scope.\nglobalScopeLabel=Global\n\n# LOCALIZATION NOTE (variablesViewErrorStacktrace): This is the text that is\n# shown before the stack trace in an error.\nvariablesViewErrorStacktrace=Stack trace:\n\n# LOCALIZATION NOTE (variablesViewMoreObjects): the text that is displayed\n# when you have an object preview that does not show all of the elements. At the end of the list\n# you see \"N more...\" in the web console output.\n# This is a semi-colon list of plural forms.\n# See: http://developer.mozilla.org/en/docs/Localization_and_Plurals\n# #1 number of remaining items in the object\n# example: 3 more…\nvariablesViewMoreObjects=#1 more…;#1 more…\n\n# LOCALIZATION NOTE (variablesEditableNameTooltip): The text that is displayed\n# in the variables list on an item with an editable name.\nvariablesEditableNameTooltip=Double click to edit\n\n# LOCALIZATION NOTE (variablesEditableValueTooltip): The text that is displayed\n# in the variables list on an item with an editable value.\nvariablesEditableValueTooltip=Click to change value\n\n# LOCALIZATION NOTE (variablesCloseButtonTooltip): The text that is displayed\n# in the variables list on an item which can be removed.\nvariablesCloseButtonTooltip=Click to remove\n\n# LOCALIZATION NOTE (variablesEditButtonTooltip): The text that is displayed\n# in the variables list on a getter or setter which can be edited.\nvariablesEditButtonTooltip=Click to set value\n\n# LOCALIZATION NOTE (variablesEditableValueTooltip): The text that is displayed\n# in a tooltip on the \"open in inspector\" button in the the variables list for a\n# DOMNode item.\nvariablesDomNodeValueTooltip=Click to select the node in the inspector\n\n# LOCALIZATION NOTE (configurable|...|Tooltip): The text that is displayed\n# in the variables list on certain variables or properties as tooltips.\n# Expanations of what these represent can be found at the following links:\n# https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Object/defineProperty\n# https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Object/isExtensible\n# https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Object/isFrozen\n# https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Object/isSealed\n# It's probably best to keep these in English.\nconfigurableTooltip=configurable\nenumerableTooltip=enumerable\nwritableTooltip=writable\nfrozenTooltip=frozen\nsealedTooltip=sealed\nextensibleTooltip=extensible\noverriddenTooltip=overridden\nWebIDLTooltip=WebIDL\n\n# LOCALIZATION NOTE (variablesSeparatorLabel): The text that is displayed\n# in the variables list as a separator between the name and value.\nvariablesSeparatorLabel=:\n\n# LOCALIZATION NOTE (watchExpressionsSeparatorLabel2): The text that is displayed\n# in the watch expressions list as a separator between the code and evaluation.\nwatchExpressionsSeparatorLabel2=\\u0020→\n\n# LOCALIZATION NOTE (functionSearchSeparatorLabel): The text that is displayed\n# in the functions search panel as a separator between function's inferred name\n# and its real name (if available).\nfunctionSearchSeparatorLabel=←\n\n# LOCALIZATION NOTE(symbolSearch.search.functionsPlaceholder): The placeholder\n# text displayed when the user searches for functions in a file\nsymbolSearch.search.functionsPlaceholder=Search functions…\n\n# LOCALIZATION NOTE(symbolSearch.search.variablesPlaceholder): The placeholder\n# text displayed when the user searches for variables in a file\nsymbolSearch.search.variablesPlaceholder=Search variables…\n\n# LOCALIZATION NOTE(symbolSearch.search.key2): The Key Shortcut for\n# searching for a function or variable\nsymbolSearch.search.key2=CmdOrCtrl+Shift+O\n\n# LOCALIZATION NOTE(symbolSearch.searchModifier.regex): A search option\n# when searching text in a file\nsymbolSearch.searchModifier.regex=Regex\n\n# LOCALIZATION NOTE(symbolSearch.searchModifier.caseSensitive): A search option\n# when searching text in a file\nsymbolSearch.searchModifier.caseSensitive=Case sensitive\n\n# LOCALIZATION NOTE(symbolSearch.searchModifier.wholeWord): A search option\n# when searching text in a file\nsymbolSearch.searchModifier.wholeWord=Whole word\n\n# LOCALIZATION NOTE (resumptionOrderPanelTitle): This is the text that appears\n# as a description in the notification panel popup, when multiple debuggers are\n# open in separate tabs and the user tries to resume them in the wrong order.\n# The substitution parameter is the URL of the last paused window that must be\n# resumed first.\nresumptionOrderPanelTitle=There are one or more paused debuggers. Please resume the most-recently paused debugger first at: %S\n\nvariablesViewOptimizedOut=(optimized away)\nvariablesViewUninitialized=(uninitialized)\nvariablesViewMissingArgs=(unavailable)\n\nanonymousSourcesLabel=Anonymous Sources\n\nexperimental=This is an experimental feature\n\n# LOCALIZATION NOTE (whyPaused.debuggerStatement): The text that is displayed\n# in a info block explaining how the debugger is currently paused due to a `debugger`\n# statement in the code\nwhyPaused.debuggerStatement=Paused on debugger statement\n\n# LOCALIZATION NOTE (whyPaused.breakpoint): The text that is displayed\n# in a info block explaining how the debugger is currently paused on a breakpoint\nwhyPaused.breakpoint=Paused on breakpoint\n\n# LOCALIZATION NOTE (whyPaused.exception): The text that is displayed\n# in a info block explaining how the debugger is currently paused on an exception\nwhyPaused.exception=Paused on exception\n\n# LOCALIZATION NOTE (whyPaused.resumeLimit): The text that is displayed\n# in a info block explaining how the debugger is currently paused while stepping\n# in or out of the stack\nwhyPaused.resumeLimit=Paused while stepping\n\n# LOCALIZATION NOTE (whyPaused.pauseOnDOMEvents): The text that is displayed\n# in a info block explaining how the debugger is currently paused on a\n# dom event\nwhyPaused.pauseOnDOMEvents=Paused on event listener\n\n# LOCALIZATION NOTE (whyPaused.breakpointConditionThrown): The text that is displayed\n# in an info block when evaluating a conditional breakpoint throws an error\nwhyPaused.breakpointConditionThrown=Error with conditional breakpoint\n\n# LOCALIZATION NOTE (whyPaused.xhr): The text that is displayed\n# in a info block explaining how the debugger is currently paused on an\n# xml http request\nwhyPaused.xhr=Paused on XMLHttpRequest\n\n# LOCALIZATION NOTE (whyPaused.promiseRejection): The text that is displayed\n# in a info block explaining how the debugger is currently paused on a\n# promise rejection\nwhyPaused.promiseRejection=Paused on promise rejection\n\n# LOCALIZATION NOTE (whyPaused.assert): The text that is displayed\n# in a info block explaining how the debugger is currently paused on an\n# assert\nwhyPaused.assert=Paused on assertion\n\n# LOCALIZATION NOTE (whyPaused.debugCommand): The text that is displayed\n# in a info block explaining how the debugger is currently paused on a\n# debugger statement\nwhyPaused.debugCommand=Paused on debugged function\n\n# LOCALIZATION NOTE (whyPaused.other): The text that is displayed\n# in a info block explaining how the debugger is currently paused on an event\n# listener breakpoint set\nwhyPaused.other=Debugger paused\n\n# LOCALIZATION NOTE (ctrl): The text that is used for documenting\n# keyboard shortcuts that use the control key\nctrl=Ctrl\n"
+	module.exports = "# This Source Code Form is subject to the terms of the Mozilla Public\n# License, v. 2.0. If a copy of the MPL was not distributed with this\n# file, You can obtain one at http://mozilla.org/MPL/2.0/.\n\n# LOCALIZATION NOTE These strings are used inside the Debugger\n# which is available from the Web Developer sub-menu -> 'Debugger'.\n# The correct localization of this file might be to keep it in\n# English, or another language commonly spoken among web developers.\n# You want to make that choice consistent across the developer tools.\n# A good criteria is the language in which you'd find the best\n# documentation on web development on the web.\n\n# LOCALIZATION NOTE (collapsePanes): This is the tooltip for the button\n# that collapses the left and right panes in the debugger UI.\ncollapsePanes=Collapse panes\n\n# LOCALIZATION NOTE (copySourceUrl): This is the text that appears in the\n# context menu to copy the source URL of file open.\ncopySourceUrl=Copy Source Url\n\n# LOCALIZATION NOTE (copySourceUrl.accesskey): Access key to copy the source URL of a file from\n# the context menu.\ncopySourceUrl.accesskey=u\n\n# LOCALIZATION NOTE (copyStackTrace): This is the text that appears in the\n# context menu to copy the stack trace methods, file names and row number.\ncopyStackTrace=Copy Stack Trace\n\n# LOCALIZATION NOTE (copyStackTrace.accesskey): Access key to copy the stack trace data from\n# the context menu.\ncopyStackTrace.accesskey=c\n\n# LOCALIZATION NOTE (expandPanes): This is the tooltip for the button\n# that expands the left and right panes in the debugger UI.\nexpandPanes=Expand panes\n\n# LOCALIZATION NOTE (pauseButtonTooltip): The tooltip that is displayed for the pause\n# button when the debugger is in a running state.\npauseButtonTooltip=Pause %S\n\n# LOCALIZATION NOTE (pausePendingButtonTooltip): The tooltip that is displayed for\n# the pause button after it's been clicked but before the next JavaScript to run.\npausePendingButtonTooltip=Waiting for next execution\n\n# LOCALIZATION NOTE (resumeButtonTooltip): The label that is displayed on the pause\n# button when the debugger is in a paused state.\nresumeButtonTooltip=Resume %S\n\n# LOCALIZATION NOTE (stepOverTooltip): The label that is displayed on the\n# button that steps over a function call.\nstepOverTooltip=Step Over %S\n\n# LOCALIZATION NOTE (stepInTooltip): The label that is displayed on the\n# button that steps into a function call.\nstepInTooltip=Step In %S\n\n# LOCALIZATION NOTE (stepOutTooltip): The label that is displayed on the\n# button that steps out of a function call.\nstepOutTooltip=Step Out %S\n\n# LOCALIZATION NOTE (noWorkersText): The text to display in the workers list\n# when there are no workers.\nnoWorkersText=This page has no workers.\n\n# LOCALIZATION NOTE (noSourcesText): The text to display in the sources list\n# when there are no sources.\nnoSourcesText=This page has no sources.\n\n# LOCALIZATION NOTE (noEventListenersText): The text to display in the events tab\n# when there are no events.\nnoEventListenersText=No event listeners to display\n\n# LOCALIZATION NOTE (eventListenersHeader): The text to display in the events\n# header.\neventListenersHeader=Event Listeners\n\n# LOCALIZATION NOTE (noStackFramesText): The text to display in the call stack tab\n# when there are no stack frames.\nnoStackFramesText=No stack frames to display\n\n# LOCALIZATION NOTE (eventCheckboxTooltip): The tooltip text to display when\n# the user hovers over the checkbox used to toggle an event breakpoint.\neventCheckboxTooltip=Toggle breaking on this event\n\n# LOCALIZATION NOTE (eventOnSelector): The text to display in the events tab\n# for every event item, between the event type and event selector.\neventOnSelector=on\n\n# LOCALIZATION NOTE (eventInSource): The text to display in the events tab\n# for every event item, between the event selector and listener's owner source.\neventInSource=in\n\n# LOCALIZATION NOTE (eventNodes): The text to display in the events tab when\n# an event is listened on more than one target node.\neventNodes=%S nodes\n\n# LOCALIZATION NOTE (eventNative): The text to display in the events tab when\n# a listener is added from plugins, thus getting translated to native code.\neventNative=[native code]\n\n# LOCALIZATION NOTE (*Events): The text to display in the events tab for\n# each group of sub-level event entries.\nanimationEvents=Animation\naudioEvents=Audio\nbatteryEvents=Battery\nclipboardEvents=Clipboard\ncompositionEvents=Composition\ndeviceEvents=Device\ndisplayEvents=Display\ndragAndDropEvents=Drag and Drop\ngamepadEvents=Gamepad\nindexedDBEvents=IndexedDB\ninteractionEvents=Interaction\nkeyboardEvents=Keyboard\nmediaEvents=HTML5 Media\nmouseEvents=Mouse\nmutationEvents=Mutation\nnavigationEvents=Navigation\npointerLockEvents=Pointer Lock\nsensorEvents=Sensor\nstorageEvents=Storage\ntimeEvents=Time\ntouchEvents=Touch\notherEvents=Other\n\n# LOCALIZATION NOTE (blackboxCheckboxTooltip2): The tooltip text to display when\n# the user hovers over the checkbox used to toggle blackboxing its associated\n# source.\nblackboxCheckboxTooltip2=Toggle blackboxing\n\n# LOCALIZATION NOTE (sources.search.key2): Key shortcut to open the search for\n# searching all the source files the debugger has seen.\nsources.search.key2=CmdOrCtrl+P\n\n# LOCALIZATION NOTE (sources.search.alt.key): A second key shortcut to open the\n# search for searching all the source files the debugger has seen.\nsources.search.alt.key=CmdOrCtrl+O\n\n# LOCALIZATION NOTE (sources.noSourcesAvailable): Text shown when the debugger\n# does not have any sources.\nsources.noSourcesAvailable=This page has no sources\n\n# LOCALIZATION NOTE (sourcesPane.showSourcesTooltip): The tooltip shown when\n# the user will navigate to the source tree view.\nsourcesPane.showSourcesTooltip=Show sources\n\n# LOCALIZATION NOTE (sourcesPane.showOutlineTooltip): The tooltip shown when\n# the user will navigate to the source outline view.\nsourcesPane.showOutlineTooltip=Show outline\n\n# LOCALIZATION NOTE (sourceSearch.search.key2): Key shortcut to open the search\n# for searching within a the currently opened files in the editor\nsourceSearch.search.key2=CmdOrCtrl+F\n\n# LOCALIZATION NOTE (sourceSearch.search.placeholder): placeholder text in\n# the source search input bar\nsourceSearch.search.placeholder=Search in file…\n\n# LOCALIZATION NOTE (sourceSearch.search.again.key2): Key shortcut to highlight\n# the next occurrence of the last search triggered from a source search\nsourceSearch.search.again.key2=CmdOrCtrl+G\n\n# LOCALIZATION NOTE (sourceSearch.search.againPrev.key2): Key shortcut to highlight\n# the previous occurrence of the last search triggered from a source search\nsourceSearch.search.againPrev.key2=CmdOrCtrl+Shift+G\n\n# LOCALIZATION NOTE (sourceSearch.resultsSummary1): Shows a summary of\n# the number of matches for autocomplete\nsourceSearch.resultsSummary1=%d results\n\n# LOCALIZATION NOTE (noMatchingStringsText): The text to display in the\n# global search results when there are no matching strings after filtering.\nnoMatchingStringsText=No matches found\n\n# LOCALIZATION NOTE (emptySearchText): This is the text that appears in the\n# filter text box when it is empty and the scripts container is selected.\nemptySearchText=Search scripts (%S)\n\n# LOCALIZATION NOTE (emptyVariablesFilterText): This is the text that\n# appears in the filter text box for the variables view container.\nemptyVariablesFilterText=Filter variables\n\n# LOCALIZATION NOTE (emptyPropertiesFilterText): This is the text that\n# appears in the filter text box for the editor's variables view bubble.\nemptyPropertiesFilterText=Filter properties\n\n# LOCALIZATION NOTE (searchPanelFilter): This is the text that appears in the\n# filter panel popup for the filter scripts operation.\nsearchPanelFilter=Filter scripts (%S)\n\n# LOCALIZATION NOTE (searchPanelGlobal): This is the text that appears in the\n# filter panel popup for the global search operation.\nsearchPanelGlobal=Search in all files (%S)\n\n# LOCALIZATION NOTE (searchPanelFunction): This is the text that appears in the\n# filter panel popup for the function search operation.\nsearchPanelFunction=Search for function definition (%S)\n\n# LOCALIZATION NOTE (searchPanelToken): This is the text that appears in the\n# filter panel popup for the token search operation.\nsearchPanelToken=Find in this file (%S)\n\n# LOCALIZATION NOTE (searchPanelGoToLine): This is the text that appears in the\n# filter panel popup for the line search operation.\nsearchPanelGoToLine=Go to line (%S)\n\n# LOCALIZATION NOTE (searchPanelVariable): This is the text that appears in the\n# filter panel popup for the variables search operation.\nsearchPanelVariable=Filter variables (%S)\n\n# LOCALIZATION NOTE (breakpointMenuItem): The text for all the elements that\n# are displayed in the breakpoints menu item popup.\nbreakpointMenuItem.setConditional=Configure conditional breakpoint\nbreakpointMenuItem.enableSelf=Enable breakpoint\nbreakpointMenuItem.disableSelf=Disable breakpoint\nbreakpointMenuItem.deleteSelf=Remove breakpoint\nbreakpointMenuItem.enableOthers=Enable others\nbreakpointMenuItem.disableOthers=Disable others\nbreakpointMenuItem.deleteOthers=Remove others\nbreakpointMenuItem.enableAll=Enable all breakpoints\nbreakpointMenuItem.disableAll=Disable all breakpoints\nbreakpointMenuItem.deleteAll=Remove all breakpoints\n\n# LOCALIZATION NOTE (breakpoints.header): Breakpoints right sidebar pane header.\nbreakpoints.header=Breakpoints\n\n# LOCALIZATION NOTE (breakpoints.none): The text that appears when there are\n# no breakpoints present\nbreakpoints.none=No Breakpoints\n\n# LOCALIZATION NOTE (breakpoints.enable): The text that may appear as a tooltip\n# when hovering over the 'disable breakpoints' switch button in right sidebar\nbreakpoints.enable=Enable Breakpoints\n\n# LOCALIZATION NOTE (breakpoints.disable): The text that may appear as a tooltip\n# when hovering over the 'disable breakpoints' switch button in right sidebar\nbreakpoints.disable=Disable Breakpoints\n\n# LOCALIZATION NOTE (breakpoints.removeBreakpointTooltip): The tooltip that is displayed\n# for remove breakpoint button in right sidebar\nbreakpoints.removeBreakpointTooltip=Remove Breakpoint\n\n# LOCALIZATION NOTE (callStack.header): Call Stack right sidebar pane header.\ncallStack.header=Call Stack\n\n# LOCALIZATION NOTE (callStack.notPaused): Call Stack right sidebar pane\n# message when not paused.\ncallStack.notPaused=Not Paused\n\n# LOCALIZATION NOTE (callStack.collapse): Call Stack right sidebar pane\n# message to hide some of the frames that are shown.\ncallStack.collapse=Collapse Rows\n\n# LOCALIZATION NOTE (callStack.expand): Call Stack right sidebar pane\n# message to show more of the frames.\ncallStack.expand=Expand Rows\n\n# LOCALIZATION NOTE (editor.searchResults): Editor Search bar message\n# for the summarizing the selected search result. e.g. 5 of 10 results.\neditor.searchResults=%d of %d results\n\n# LOCALIZATION NOTE (sourceSearch.singleResult): Copy shown when there is one result.\neditor.singleResult=1 result\n\n# LOCALIZATION NOTE (editor.noResults): Editor Search bar message\n# for when no results found.\neditor.noResults=no results\n\n# LOCALIZATION NOTE (editor.searchResults.nextResult): Editor Search bar\n# tooltip for traversing to the Next Result\neditor.searchResults.nextResult=Next Result\n\n# LOCALIZATION NOTE (editor.searchResults.prevResult): Editor Search bar\n# tooltip for traversing to the Previous Result\neditor.searchResults.prevResult=Previous Result\n\n# LOCALIZATION NOTE (editor.searchTypeToggleTitle): Search bar title for\n# toggling search type buttons(function search, variable search)\neditor.searchTypeToggleTitle=Search for:\n\n# LOCALIZATION NOTE (editor.addBreakpoint): Editor gutter context menu item\n# for adding a breakpoint on a line.\neditor.addBreakpoint=Add Breakpoint\n\n# LOCALIZATION NOTE (editor.disableBreakpoint): Editor gutter context menu item\n# for disabling a breakpoint on a line.\neditor.disableBreakpoint=Disable Breakpoint\n\n# LOCALIZATION NOTE (editor.enableBreakpoint): Editor gutter context menu item\n# for enabling a breakpoint on a line.\neditor.enableBreakpoint=Enable Breakpoint\n\n# LOCALIZATION NOTE (editor.removeBreakpoint): Editor gutter context menu item\n# for removing a breakpoint on a line.\neditor.removeBreakpoint=Remove Breakpoint\n\n# LOCALIZATION NOTE (editor.editBreakpoint): Editor gutter context menu item\n# for setting a breakpoint condition on a line.\neditor.editBreakpoint=Edit Breakpoint\n\n# LOCALIZATION NOTE (editor.addConditionalBreakpoint): Editor gutter context\n# menu item for adding a breakpoint condition on a line.\neditor.addConditionalBreakpoint=Add Conditional Breakpoint\n\n# LOCALIZATION NOTE (editor.conditionalPanel.placeholder): Placeholder text for\n# input element inside ConditionalPanel component\neditor.conditionalPanel.placeholder=This breakpoint will pause when the expression is true\n\n# LOCALIZATION NOTE (editor.conditionalPanel.placeholder): Tooltip text for\n# close button inside ConditionalPanel component\neditor.conditionalPanel.close=Cancel edit breakpoint and close\n\n# LOCALIZATION NOTE (editor.jumpToMappedLocation1): Context menu item\n# for navigating to a source mapped location\neditor.jumpToMappedLocation1=Jump to %S location\n\n# LOCALIZATION NOTE (framework.disableGrouping): This is the text that appears in the\n# context menu to disable framework grouping.\nframework.disableGrouping=Disable Framework Grouping\n\n# LOCALIZATION NOTE (framework.disableGrouping.accesskey): Access key to toggle\n# framework grouping from the context menu.\nframework.disableGrouping.accesskey=u\n\n# LOCALIZATION NOTE (framework.enableGrouping): This is the text that appears in the\n# context menu to enable framework grouping.\nframework.enableGrouping=Enable Framework Grouping\n\n# LOCALIZATION NOTE (framework.enableGrouping.accesskey): Access key to toggle\n# framework grouping from the context menu.\nframework.enableGrouping.accesskey=u\n\n# LOCALIZATION NOTE (generated): Source Map term for a server source location\ngenerated=generated\n\n# LOCALIZATION NOTE (original): Source Map term for a debugger UI source location\noriginal=original\n\n# LOCALIZATION NOTE (expressions.placeholder): Placeholder text for expression\n# input element\nexpressions.placeholder=Add Watch Expression\n\n# LOCALIZATION NOTE (sourceTabs.closeTab): Editor source tab context menu item\n# for closing the selected tab below the mouse.\nsourceTabs.closeTab=Close tab\n\n# LOCALIZATION NOTE (sourceTabs.closeTab.accesskey): Access key to close the currently select\n# source tab from the editor context menu item.\nsourceTabs.closeTab.accesskey=c\n\n# LOCALIZATION NOTE (sourceTabs.closeOtherTabs): Editor source tab context menu item\n# for closing the other tabs.\nsourceTabs.closeOtherTabs=Close others\n\n# LOCALIZATION NOTE (sourceTabs.closeOtherTabs.accesskey): Access key to close other source tabs\n# from the editor context menu.\nsourceTabs.closeOtherTabs.accesskey=o\n\n# LOCALIZATION NOTE (sourceTabs.closeTabsToEnd): Editor source tab context menu item\n# for closing the tabs to the end (the right for LTR languages) of the selected tab.\nsourceTabs.closeTabsToEnd=Close tabs to the right\n\n# LOCALIZATION NOTE (sourceTabs.closeTabsToEnd.accesskey): Access key to close source tabs\n# after the selected tab from the editor context menu.\nsourceTabs.closeTabsToEnd.accesskey=e\n\n# LOCALIZATION NOTE (sourceTabs.closeAllTabs): Editor source tab context menu item\n# for closing all tabs.\nsourceTabs.closeAllTabs=Close all tabs\n\n# LOCALIZATION NOTE (sourceTabs.closeAllTabs.accesskey): Access key to close all tabs from the\n# editor context menu.\nsourceTabs.closeAllTabs.accesskey=a\n\n# LOCALIZATION NOTE (sourceTabs.revealInTree): Editor source tab context menu item\n# for revealing source in tree.\nsourceTabs.revealInTree=Reveal in Tree\n\n# LOCALIZATION NOTE (sourceTabs.revealInTree.accesskey): Access key to reveal a source in the\n# tree from the context menu.\nsourceTabs.revealInTree.accesskey=r\n\n# LOCALIZATION NOTE (sourceTabs.copyLink): Editor source tab context menu item\n# for copying a link address.\nsourceTabs.copyLink=Copy Link Address\n\n# LOCALIZATION NOTE (sourceTabs.copyLink.accesskey): Access key to copy a link addresss from the\n# editor context menu.\nsourceTabs.copyLink.accesskey=l\n\n# LOCALIZATION NOTE (sourceTabs.prettyPrint): Editor source tab context menu item\n# for pretty printing the source.\nsourceTabs.prettyPrint=Pretty Print Source\n\n# LOCALIZATION NOTE (sourceTabs.prettyPrint.accesskey): Access key to pretty print a source from\n# the editor context menu.\nsourceTabs.prettyPrint.accesskey=p\n\n# LOCALIZATION NOTE (sourceFooter.blackbox): Tooltip text associated\n# with the blackbox button\nsourceFooter.blackbox=Blackbox Source\n\n# LOCALIZATION NOTE (sourceFooter.unblackbox): Tooltip text associated\n# with the blackbox button\nsourceFooter.unblackbox=Unblackbox Source\n\n# LOCALIZATION NOTE (sourceFooter.unblackbox.accesskey): Access key to blackbox\n# an associated source\nsourceFooter.unblackbox.accesskey=b\n\n# LOCALIZATION NOTE (sourceFooter.blackbox.accesskey): Access key to blackbox\n# an associated source\nsourceFooter.blackbox.accesskey=b\n\n# LOCALIZATION NOTE (sourceFooter.blackboxed): Text associated\n# with a blackboxed source\nsourceFooter.blackboxed=Blackboxed Source\n\n# LOCALIZATION NOTE (sourceTabs.closeTabButtonTooltip): The tooltip that is displayed\n# for close tab button in source tabs.\nsourceTabs.closeTabButtonTooltip=Close tab\n\n# LOCALIZATION NOTE (sourceTabs.newTabButtonTooltip): The tooltip that is displayed for\n# new tab button in source tabs.\nsourceTabs.newTabButtonTooltip=Search for sources (%S)\n\n# LOCALIZATION NOTE (scopes.header): Scopes right sidebar pane header.\nscopes.header=Scopes\n\n# LOCALIZATION NOTE (scopes.notAvailable): Scopes right sidebar pane message\n# for when the debugger is paused, but there isn't pause data.\nscopes.notAvailable=Scopes Unavailable\n\n# LOCALIZATION NOTE (scopes.notPaused): Scopes right sidebar pane message\n# for when the debugger is not paused.\nscopes.notPaused=Not Paused\n\n# LOCALIZATION NOTE (scopes.block): Refers to a block of code in\n# the scopes pane when the debugger is paused.\nscopes.block=Block\n\n# LOCALIZATION NOTE (sources.header): Sources left sidebar header\nsources.header=Sources\n\n# LOCALIZATION NOTE (sources.search): Sources left sidebar prompt\n# e.g. Cmd+P to search. On a mac, we use the command unicode character.\n# On windows, it's ctrl.\nsources.search=%S to search\n\n# LOCALIZATION NOTE (watchExpressions.header): Watch Expressions right sidebar\n# pane header.\nwatchExpressions.header=Watch Expressions\n\n# LOCALIZATION NOTE (watchExpressions.refreshButton): Watch Expressions header\n# button for refreshing the expressions.\nwatchExpressions.refreshButton=Refresh\n\n# LOCALIZATION NOTE (welcome.search): The center pane welcome panel's\n# search prompt. e.g. cmd+p to search for files. On windows, it's ctrl, on\n# a mac we use the unicode character.\nwelcome.search=%S to search for sources\n\n# LOCALIZATION NOTE (sourceSearch.search): The center pane Source Search\n# prompt for searching for files.\nsourceSearch.search=Search Sources…\n\n# LOCALIZATION NOTE (sourceSearch.noResults): The center pane Source Search\n# message when the query did not match any of the sources.\nsourceSearch.noResults=No files matching %S found\n\n# LOCALIZATION NOTE (ignoreExceptions): The pause on exceptions button tooltip\n# when the debugger will not pause on exceptions.\nignoreExceptions=Ignore exceptions. Click to pause on uncaught exceptions\n\n# LOCALIZATION NOTE (pauseOnUncaughtExceptions): The pause on exceptions button\n# tooltip when the debugger will pause on uncaught exceptions.\npauseOnUncaughtExceptions=Pause on uncaught exceptions. Click to pause on all exceptions\n\n# LOCALIZATION NOTE (pauseOnExceptions): The pause on exceptions button tooltip\n# when the debugger will pause on all exceptions.\npauseOnExceptions=Pause on all exceptions. Click to ignore exceptions\n\n# LOCALIZATION NOTE (loadingText): The text that is displayed in the script\n# editor when the loading process has started but there is no file to display\n# yet.\nloadingText=Loading\\u2026\n\n# LOCALIZATION NOTE (errorLoadingText2): The text that is displayed in the debugger\n# viewer when there is an error loading a file\nerrorLoadingText2=Error loading this URL: %S\n\n# LOCALIZATION NOTE (addWatchExpressionText): The text that is displayed in the\n# watch expressions list to add a new item.\naddWatchExpressionText=Add watch expression\n\n# LOCALIZATION NOTE (addWatchExpressionButton): The button that is displayed in the\n# variables view popup.\naddWatchExpressionButton=Watch\n\n# LOCALIZATION NOTE (emptyVariablesText): The text that is displayed in the\n# variables pane when there are no variables to display.\nemptyVariablesText=No variables to display\n\n# LOCALIZATION NOTE (scopeLabel): The text that is displayed in the variables\n# pane as a header for each variable scope (e.g. \"Global scope, \"With scope\",\n# etc.).\nscopeLabel=%S scope\n\n# LOCALIZATION NOTE (watchExpressionsScopeLabel): The name of the watch\n# expressions scope. This text is displayed in the variables pane as a header for\n# the watch expressions scope.\nwatchExpressionsScopeLabel=Watch expressions\n\n# LOCALIZATION NOTE (globalScopeLabel): The name of the global scope. This text\n# is added to scopeLabel and displayed in the variables pane as a header for\n# the global scope.\nglobalScopeLabel=Global\n\n# LOCALIZATION NOTE (variablesViewErrorStacktrace): This is the text that is\n# shown before the stack trace in an error.\nvariablesViewErrorStacktrace=Stack trace:\n\n# LOCALIZATION NOTE (variablesViewMoreObjects): the text that is displayed\n# when you have an object preview that does not show all of the elements. At the end of the list\n# you see \"N more...\" in the web console output.\n# This is a semi-colon list of plural forms.\n# See: http://developer.mozilla.org/en/docs/Localization_and_Plurals\n# #1 number of remaining items in the object\n# example: 3 more…\nvariablesViewMoreObjects=#1 more…;#1 more…\n\n# LOCALIZATION NOTE (variablesEditableNameTooltip): The text that is displayed\n# in the variables list on an item with an editable name.\nvariablesEditableNameTooltip=Double click to edit\n\n# LOCALIZATION NOTE (variablesEditableValueTooltip): The text that is displayed\n# in the variables list on an item with an editable value.\nvariablesEditableValueTooltip=Click to change value\n\n# LOCALIZATION NOTE (variablesCloseButtonTooltip): The text that is displayed\n# in the variables list on an item which can be removed.\nvariablesCloseButtonTooltip=Click to remove\n\n# LOCALIZATION NOTE (variablesEditButtonTooltip): The text that is displayed\n# in the variables list on a getter or setter which can be edited.\nvariablesEditButtonTooltip=Click to set value\n\n# LOCALIZATION NOTE (variablesEditableValueTooltip): The text that is displayed\n# in a tooltip on the \"open in inspector\" button in the the variables list for a\n# DOMNode item.\nvariablesDomNodeValueTooltip=Click to select the node in the inspector\n\n# LOCALIZATION NOTE (configurable|...|Tooltip): The text that is displayed\n# in the variables list on certain variables or properties as tooltips.\n# Expanations of what these represent can be found at the following links:\n# https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Object/defineProperty\n# https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Object/isExtensible\n# https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Object/isFrozen\n# https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Object/isSealed\n# It's probably best to keep these in English.\nconfigurableTooltip=configurable\nenumerableTooltip=enumerable\nwritableTooltip=writable\nfrozenTooltip=frozen\nsealedTooltip=sealed\nextensibleTooltip=extensible\noverriddenTooltip=overridden\nWebIDLTooltip=WebIDL\n\n# LOCALIZATION NOTE (variablesSeparatorLabel): The text that is displayed\n# in the variables list as a separator between the name and value.\nvariablesSeparatorLabel=:\n\n# LOCALIZATION NOTE (watchExpressionsSeparatorLabel2): The text that is displayed\n# in the watch expressions list as a separator between the code and evaluation.\nwatchExpressionsSeparatorLabel2=\\u0020→\n\n# LOCALIZATION NOTE (functionSearchSeparatorLabel): The text that is displayed\n# in the functions search panel as a separator between function's inferred name\n# and its real name (if available).\nfunctionSearchSeparatorLabel=←\n\n# LOCALIZATION NOTE(symbolSearch.search.functionsPlaceholder): The placeholder\n# text displayed when the user searches for functions in a file\nsymbolSearch.search.functionsPlaceholder=Search functions…\n\n# LOCALIZATION NOTE(symbolSearch.search.variablesPlaceholder): The placeholder\n# text displayed when the user searches for variables in a file\nsymbolSearch.search.variablesPlaceholder=Search variables…\n\n# LOCALIZATION NOTE(symbolSearch.search.key2): The Key Shortcut for\n# searching for a function or variable\nsymbolSearch.search.key2=CmdOrCtrl+Shift+O\n\n# LOCALIZATION NOTE(symbolSearch.searchModifier.regex): A search option\n# when searching text in a file\nsymbolSearch.searchModifier.regex=Regex\n\n# LOCALIZATION NOTE(symbolSearch.searchModifier.caseSensitive): A search option\n# when searching text in a file\nsymbolSearch.searchModifier.caseSensitive=Case sensitive\n\n# LOCALIZATION NOTE(symbolSearch.searchModifier.wholeWord): A search option\n# when searching text in a file\nsymbolSearch.searchModifier.wholeWord=Whole word\n\n# LOCALIZATION NOTE (resumptionOrderPanelTitle): This is the text that appears\n# as a description in the notification panel popup, when multiple debuggers are\n# open in separate tabs and the user tries to resume them in the wrong order.\n# The substitution parameter is the URL of the last paused window that must be\n# resumed first.\nresumptionOrderPanelTitle=There are one or more paused debuggers. Please resume the most-recently paused debugger first at: %S\n\nvariablesViewOptimizedOut=(optimized away)\nvariablesViewUninitialized=(uninitialized)\nvariablesViewMissingArgs=(unavailable)\n\nanonymousSourcesLabel=Anonymous Sources\n\nexperimental=This is an experimental feature\n\n# LOCALIZATION NOTE (whyPaused.debuggerStatement): The text that is displayed\n# in a info block explaining how the debugger is currently paused due to a `debugger`\n# statement in the code\nwhyPaused.debuggerStatement=Paused on debugger statement\n\n# LOCALIZATION NOTE (whyPaused.breakpoint): The text that is displayed\n# in a info block explaining how the debugger is currently paused on a breakpoint\nwhyPaused.breakpoint=Paused on breakpoint\n\n# LOCALIZATION NOTE (whyPaused.exception): The text that is displayed\n# in a info block explaining how the debugger is currently paused on an exception\nwhyPaused.exception=Paused on exception\n\n# LOCALIZATION NOTE (whyPaused.resumeLimit): The text that is displayed\n# in a info block explaining how the debugger is currently paused while stepping\n# in or out of the stack\nwhyPaused.resumeLimit=Paused while stepping\n\n# LOCALIZATION NOTE (whyPaused.pauseOnDOMEvents): The text that is displayed\n# in a info block explaining how the debugger is currently paused on a\n# dom event\nwhyPaused.pauseOnDOMEvents=Paused on event listener\n\n# LOCALIZATION NOTE (whyPaused.breakpointConditionThrown): The text that is displayed\n# in an info block when evaluating a conditional breakpoint throws an error\nwhyPaused.breakpointConditionThrown=Error with conditional breakpoint\n\n# LOCALIZATION NOTE (whyPaused.xhr): The text that is displayed\n# in a info block explaining how the debugger is currently paused on an\n# xml http request\nwhyPaused.xhr=Paused on XMLHttpRequest\n\n# LOCALIZATION NOTE (whyPaused.promiseRejection): The text that is displayed\n# in a info block explaining how the debugger is currently paused on a\n# promise rejection\nwhyPaused.promiseRejection=Paused on promise rejection\n\n# LOCALIZATION NOTE (whyPaused.assert): The text that is displayed\n# in a info block explaining how the debugger is currently paused on an\n# assert\nwhyPaused.assert=Paused on assertion\n\n# LOCALIZATION NOTE (whyPaused.debugCommand): The text that is displayed\n# in a info block explaining how the debugger is currently paused on a\n# debugger statement\nwhyPaused.debugCommand=Paused on debugged function\n\n# LOCALIZATION NOTE (whyPaused.other): The text that is displayed\n# in a info block explaining how the debugger is currently paused on an event\n# listener breakpoint set\nwhyPaused.other=Debugger paused\n\n# LOCALIZATION NOTE (ctrl): The text that is used for documenting\n# keyboard shortcuts that use the control key\nctrl=Ctrl\n"
 
 /***/ },
 /* 961 */,
 /* 962 */,
 /* 963 */
 /***/ function(module, exports, __webpack_require__) {
 
 	var baseKeys = __webpack_require__(217),
@@ -46468,16 +46375,20 @@ return /******/ (function(modules) { // 
 	}
 
 /***/ },
 /* 994 */
 /***/ function(module, exports, __webpack_require__) {
 
 	"use strict";
 
+	/* This Source Code Form is subject to the terms of the Mozilla Public
+	 * License, v. 2.0. If a copy of the MPL was not distributed with this
+	 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
 	var SourceEditor = __webpack_require__(995);
 	var SourceEditorUtils = __webpack_require__(996);
 
 	module.exports = { SourceEditor, SourceEditorUtils };
 
 /***/ },
 /* 995 */
 /***/ function(module, exports) {
@@ -46485,16 +46396,20 @@ return /******/ (function(modules) { // 
 	module.exports = __WEBPACK_EXTERNAL_MODULE_995__;
 
 /***/ },
 /* 996 */
 /***/ function(module, exports) {
 
 	"use strict";
 
+	/* This Source Code Form is subject to the terms of the Mozilla Public
+	 * License, v. 2.0. If a copy of the MPL was not distributed with this
+	 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
 	function forEachLine(codeMirror, iter) {
 	  codeMirror.doc.iter(0, codeMirror.lineCount(), iter);
 	}
 
 	function removeLineClass(codeMirror, line, className) {
 	  codeMirror.removeLineClass(line, "line", className);
 	}
 
@@ -47182,31 +47097,33 @@ return /******/ (function(modules) { // 
 	Object.defineProperty(exports, "__esModule", {
 	  value: true
 	});
 
 	var _react = __webpack_require__(2);
 
 	var _devtoolsConfig = __webpack_require__(828);
 
+	var _reactDom = __webpack_require__(31);
+
+	var _reactDom2 = _interopRequireDefault(_reactDom);
+
 	var _Svg = __webpack_require__(344);
 
 	var _Svg2 = _interopRequireDefault(_Svg);
 
 	var _classnames = __webpack_require__(175);
 
 	var _classnames2 = _interopRequireDefault(_classnames);
 
 	function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
 
-	var ReactDOM = __webpack_require__(22);
-
-
 	var breakpointSvg = document.createElement("span");
-	ReactDOM.render((0, _Svg2.default)("column-breakpoint"), breakpointSvg);
+
+	_reactDom2.default.render((0, _Svg2.default)("column-breakpoint"), breakpointSvg);
 
 	function makeBookmark(isDisabled) {
 	  var bp = breakpointSvg.cloneNode(true);
 	  bp.className = (0, _classnames2.default)("editor column-breakpoint", {
 	    "breakpoint-disabled": isDisabled
 	  });
 
 	  return bp;
@@ -47411,17 +47328,17 @@ return /******/ (function(modules) { // 
 	      key: frameOrGroup[0].id
 	    })));
 	  }
 
 	  renderToggleButton(frames) {
 	    var buttonMessage = this.state.showAllFrames ? L10N.getStr("callStack.collapse") : L10N.getStr("callStack.expand");
 
 	    frames = (0, _frame.collapseFrames)(frames);
-	    if (frames.length < NUM_FRAMES_SHOWN) {
+	    if (frames.length <= NUM_FRAMES_SHOWN) {
 	      return null;
 	    }
 
 	    return _react.DOM.div({ className: "show-more", onClick: this.toggleFramesDisplay }, buttonMessage);
 	  }
 
 	  render() {
 	    var frames = this.props.frames;
@@ -48235,35 +48152,23 @@ return /******/ (function(modules) { // 
 	var _classnames = __webpack_require__(175);
 
 	var _classnames2 = _interopRequireDefault(_classnames);
 
 	__webpack_require__(1030);
 
 	function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
 
-	var BracketArrow = (_ref) => {
-	  var orientation = _ref.orientation,
-	      left = _ref.left,
-	      top = _ref.top,
-	      bottom = _ref.bottom;
-
+	var BracketArrow = (orientation, left, top, bottom) => {
 	  return _react.DOM.div({
 	    className: (0, _classnames2.default)("bracket-arrow", orientation || "up"),
 	    style: { left, top, bottom }
 	  }, "");
 	};
 
-	BracketArrow.propTypes = {
-	  orientation: _react.PropTypes.string,
-	  left: _react.PropTypes.number,
-	  top: _react.PropTypes.number,
-	  bottom: _react.PropTypes.number
-	};
-
 	BracketArrow.displayName = "BracketArrow";
 
 	exports.default = BracketArrow;
 
 /***/ },
 /* 1030 */
 /***/ function(module, exports) {
 
@@ -48826,12 +48731,872 @@ return /******/ (function(modules) { // 
 	  }
 	  return string.match(pattern) || [];
 	}
 
 	module.exports = kebabCase;
 
 	/* WEBPACK VAR INJECTION */}.call(exports, (function() { return this; }())))
 
+/***/ },
+/* 1049 */,
+/* 1050 */,
+/* 1051 */,
+/* 1052 */,
+/* 1053 */,
+/* 1054 */,
+/* 1055 */,
+/* 1056 */,
+/* 1057 */
+/***/ function(module, exports) {
+
+	"use strict";
+
+	Object.defineProperty(exports, "__esModule", {
+	  value: true
+	});
+
+	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; };
+
+	exports.firstString = firstString;
+	exports.locationMoved = locationMoved;
+	exports.makeLocationId = makeLocationId;
+	exports.makePendingLocationId = makePendingLocationId;
+	exports.allBreakpointsDisabled = allBreakpointsDisabled;
+	exports.equalizeLocationColumn = equalizeLocationColumn;
+	// Return the first argument that is a string, or null if nothing is a
+	// string.
+	function firstString() {
+	  for (var _len = arguments.length, args = Array(_len), _key = 0; _key < _len; _key++) {
+	    args[_key] = arguments[_key];
+	  }
+
+	  for (var arg of args) {
+	    if (typeof arg === "string") {
+	      return arg;
+	    }
+	  }
+	  return null;
+	}
+
+	function locationMoved(location, newLocation) {
+	  return location.line !== newLocation.line || location.column != null && location.column !== newLocation.column;
+	}
+
+	function makeLocationId(location) {
+	  var sourceId = location.sourceId,
+	      line = location.line,
+	      column = location.column;
+
+	  var columnString = column || "";
+	  return `${sourceId}:${line}:${columnString}`;
+	}
+
+	function makePendingLocationId(location) {
+	  var sourceUrl = location.sourceUrl,
+	      line = location.line,
+	      column = location.column;
+
+	  var sourceUrlString = sourceUrl || "";
+	  var columnString = column || "";
+	  return `${sourceUrlString}:${line}:${columnString}`;
+	}
+
+	function allBreakpointsDisabled(state) {
+	  return state.breakpoints.every(x => x.disabled);
+	}
+
+	// syncing
+	function equalizeLocationColumn(location, hasColumn) {
+	  if (hasColumn) {
+	    return location;
+	  }
+	  return _extends({}, location, { column: undefined });
+	}
+
+/***/ },
+/* 1058 */
+/***/ function(module, exports, __webpack_require__) {
+
+	"use strict";
+
+	Object.defineProperty(exports, "__esModule", {
+	  value: true
+	});
+	exports.initialState = initialState;
+	exports.getSymbols = getSymbols;
+	exports.hasSymbols = hasSymbols;
+	exports.getOutOfScopeLocations = getOutOfScopeLocations;
+	exports.getSelection = getSelection;
+
+	var _immutable = __webpack_require__(146);
+
+	var I = _interopRequireWildcard(_immutable);
+
+	var _makeRecord = __webpack_require__(230);
+
+	var _makeRecord2 = _interopRequireDefault(_makeRecord);
+
+	function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
+
+	function _interopRequireWildcard(obj) { if (obj && obj.__esModule) { return obj; } else { var newObj = {}; if (obj != null) { for (var key in obj) { if (Object.prototype.hasOwnProperty.call(obj, key)) newObj[key] = obj[key]; } } newObj.default = obj; return newObj; } }
+
+	/**
+	 * UI reducer
+	 * @module reducers/ui
+	 */
+
+	function initialState() {
+	  return (0, _makeRecord2.default)({
+	    symbols: I.Map(),
+	    outOfScopeLocations: null,
+	    selection: null
+	  })();
+	}
+
+	function update() {
+	  var state = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : initialState();
+	  var action = arguments[1];
+
+	  switch (action.type) {
+	    case "SET_SYMBOLS":
+	      {
+	        var source = action.source,
+	            _symbols = action.symbols;
+
+	        return state.setIn(["symbols", source.id], _symbols);
+	      }
+
+	    case "OUT_OF_SCOPE_LOCATIONS":
+	      {
+	        return state.set("outOfScopeLocations", action.locations);
+	      }
+
+	    case "CLEAR_SELECTION":
+	      {
+	        return state.set("selection", null);
+	      }
+
+	    case "SET_SELECTION":
+	      {
+	        if (action.status == "start") {
+	          return state.set("selection", { updating: true });
+	        }
+
+	        if (!action.value) {
+	          return state.set("selection", null);
+	        }
+
+	        var _action$value = action.value,
+	            expression = _action$value.expression,
+	            location = _action$value.location,
+	            result = _action$value.result;
+
+	        return state.set("selection", {
+	          updating: false,
+	          expression,
+	          location,
+	          result
+	        });
+	      }
+
+	    default:
+	      {
+	        return state;
+	      }
+	  }
+	}
+
+	// NOTE: we'd like to have the app state fully typed
+	// https://github.com/devtools-html/debugger.html/blob/master/src/reducers/sources.js#L179-L185
+	function getSymbols(state, source) {
+	  var emptySet = { variables: [], functions: [] };
+	  if (!source) {
+	    return emptySet;
+	  }
+
+	  var symbols = state.ast.getIn(["symbols", source.id]);
+	  return symbols || emptySet;
+	}
+
+	function hasSymbols(state, source) {
+	  if (!source) {
+	    return false;
+	  }
+
+	  return !!state.ast.getIn(["symbols", source.id]);
+	}
+
+	function getOutOfScopeLocations(state) {
+	  return state.ast.get("outOfScopeLocations");
+	}
+
+	function getSelection(state) {
+	  return state.ast.get("selection");
+	}
+
+	exports.default = update;
+
+/***/ },
+/* 1059 */
+/***/ function(module, exports, __webpack_require__) {
+
+	"use strict";
+
+	Object.defineProperty(exports, "__esModule", {
+	  value: true
+	});
+	exports.setSymbols = setSymbols;
+	exports.setOutOfScopeLocations = setOutOfScopeLocations;
+	exports.clearSelection = clearSelection;
+	exports.setSelection = setSelection;
+
+	var _selectors = __webpack_require__(242);
+
+	var _promise = __webpack_require__(193);
+
+	var _parser = __webpack_require__(827);
+
+	var _parser2 = _interopRequireDefault(_parser);
+
+	function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
+
+	function _asyncToGenerator(fn) { return function () { var gen = fn.apply(this, arguments); return new Promise(function (resolve, reject) { function step(key, arg) { try { var info = gen[key](arg); var value = info.value; } catch (error) { reject(error); return; } if (info.done) { resolve(value); } else { return Promise.resolve(value).then(function (value) { step("next", value); }, function (err) { step("throw", err); }); } } return step("next"); }); }; }
+
+	function setSymbols(source) {
+	  return (() => {
+	    var _ref = _asyncToGenerator(function* (_ref2) {
+	      var dispatch = _ref2.dispatch,
+	          getState = _ref2.getState;
+
+	      if ((0, _selectors.hasSymbols)(getState(), source)) {
+	        return;
+	      }
+
+	      var sourceText = (0, _selectors.getSourceText)(getState(), source.id);
+	      if (!sourceText) {
+	        return;
+	      }
+
+	      var symbols = yield _parser2.default.getSymbols(sourceText.toJS());
+
+	      dispatch({
+	        type: "SET_SYMBOLS",
+	        source,
+	        symbols
+	      });
+	    });
+
+	    return function (_x) {
+	      return _ref.apply(this, arguments);
+	    };
+	  })();
+	}
+
+	function setOutOfScopeLocations() {
+	  return (() => {
+	    var _ref3 = _asyncToGenerator(function* (_ref4) {
+	      var dispatch = _ref4.dispatch,
+	          getState = _ref4.getState;
+
+	      var location = (0, _selectors.getSelectedLocation)(getState());
+	      var sourceText = (0, _selectors.getSourceText)(getState(), location.sourceId);
+
+	      if (!location.line || !sourceText) {
+	        return dispatch({
+	          type: "OUT_OF_SCOPE_LOCATIONS",
+	          locations: null
+	        });
+	      }
+
+	      var locations = yield _parser2.default.getOutOfScopeLocations(sourceText.toJS(), location);
+
+	      return dispatch({
+	        type: "OUT_OF_SCOPE_LOCATIONS",
+	        locations
+	      });
+	    });
+
+	    return function (_x2) {
+	      return _ref3.apply(this, arguments);
+	    };
+	  })();
+	}
+
+	function clearSelection() {
+	  return (_ref5) => {
+	    var dispatch = _ref5.dispatch,
+	        getState = _ref5.getState,
+	        client = _ref5.client;
+
+	    var currentSelection = (0, _selectors.getSelection)(getState());
+	    if (!currentSelection) {
+	      return;
+	    }
+
+	    return dispatch({
+	      type: "CLEAR_SELECTION"
+	    });
+	  };
+	}
+
+	function setSelection(token, position) {
+	  return (() => {
+	    var _ref6 = _asyncToGenerator(function* (_ref7) {
+	      var dispatch = _ref7.dispatch,
+	          getState = _ref7.getState,
+	          client = _ref7.client;
+
+	      var currentSelection = (0, _selectors.getSelection)(getState());
+	      if (currentSelection && currentSelection.updating) {
+	        return;
+	      }
+
+	      var sourceText = (0, _selectors.getSelectedSourceText)(getState());
+	      var selectedFrame = (0, _selectors.getSelectedFrame)(getState());
+
+	      yield dispatch({
+	        type: "SET_SELECTION",
+	        [_promise.PROMISE]: _asyncToGenerator(function* () {
+	          var closestExpression = yield _parser2.default.getClosestExpression(sourceText.toJS(), token, position);
+
+	          if (!closestExpression) {
+	            return;
+	          }
+
+	          var expression = closestExpression.expression,
+	              location = closestExpression.location;
+
+
+	          if (!expression) {
+	            return;
+	          }
+
+	          var _ref9 = yield client.evaluate(expression, {
+	            frameId: selectedFrame.id
+	          }),
+	              result = _ref9.result;
+
+	          return {
+	            expression,
+	            result,
+	            location
+	          };
+	        })()
+	      });
+	    });
+
+	    return function (_x3) {
+	      return _ref6.apply(this, arguments);
+	    };
+	  })();
+	}
+
+/***/ },
+/* 1060 */
+/***/ function(module, exports, __webpack_require__) {
+
+	"use strict";
+
+	Object.defineProperty(exports, "__esModule", {
+	  value: true
+	});
+
+	var _react = __webpack_require__(2);
+
+	var _reactRedux = __webpack_require__(151);
+
+	var _redux = __webpack_require__(3);
+
+	var _actions = __webpack_require__(244);
+
+	var _actions2 = _interopRequireDefault(_actions);
+
+	var _selectors = __webpack_require__(242);
+
+	var _utils = __webpack_require__(234);
+
+	var _url = __webpack_require__(334);
+
+	var _source = __webpack_require__(233);
+
+	var _devtoolsConfig = __webpack_require__(828);
+
+	var _projectSearch = __webpack_require__(1061);
+
+	__webpack_require__(1062);
+
+	var _Autocomplete2 = __webpack_require__(342);
+
+	var _Autocomplete3 = _interopRequireDefault(_Autocomplete2);
+
+	var _TextSearch2 = __webpack_require__(1064);
+
+	var _TextSearch3 = _interopRequireDefault(_TextSearch2);
+
+	function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
+
+	var Autocomplete = (0, _react.createFactory)(_Autocomplete3.default);
+
+	var TextSearch = (0, _react.createFactory)(_TextSearch3.default);
+
+	function getSourcePath(source) {
+	  if (!source.url) {
+	    return "";
+	  }
+
+	  var _parseURL = (0, _url.parse)(source.url),
+	      path = _parseURL.path,
+	      href = _parseURL.href;
+	  // for URLs like "about:home" the path is null so we pass the full href
+
+
+	  return path || href;
+	}
+
+	function searchResults(sources, query) {
+	  if ((0, _devtoolsConfig.isEnabled)("projectTextSearch")) {
+	    return projectSearchResults(sources, query);
+	  }
+
+	  return fileSearchResults(sources);
+	}
+
+	function fileSearchResults(sourceMap) {
+	  return sourceMap.valueSeq().toJS().filter(source => !(0, _source.isPretty)(source)).map(source => ({
+	    value: getSourcePath(source),
+	    title: getSourcePath(source).split("/").pop(),
+	    subtitle: (0, _utils.endTruncateStr)(getSourcePath(source), 100),
+	    id: source.id
+	  }));
+	}
+
+	function projectSearchResults(sources, query) {
+	  return sources.valueSeq().toJS().map(source => (0, _projectSearch.searchSource)(source, query).map(result => ({
+	    value: result.match,
+	    title: result.text,
+	    subtitle: (0, _utils.endTruncateStr)(getSourcePath(source), 100),
+	    id: `${result.text}/${result.line}/${result.column}`
+	  })));
+	}
+
+	class ProjectSearch extends _react.Component {
+
+	  constructor(props) {
+	    super(props);
+
+	    this.state = {
+	      inputValue: ""
+	    };
+
+	    this.toggle = this.toggle.bind(this);
+	    this.onEscape = this.onEscape.bind(this);
+	    this.close = this.close.bind(this);
+	  }
+
+	  componentWillUnmount() {
+	    var shortcuts = this.context.shortcuts;
+	    var searchKeys = [L10N.getStr("sources.search.key2"), L10N.getStr("sources.search.key2")];
+	    searchKeys.forEach(key => shortcuts.off(key, this.toggle));
+	    shortcuts.off("Escape", this.onEscape);
+	  }
+
+	  componentDidMount() {
+	    var shortcuts = this.context.shortcuts;
+	    var searchKeys = [L10N.getStr("sources.search.key2"), L10N.getStr("sources.search.alt.key")];
+	    searchKeys.forEach(key => shortcuts.on(key, this.toggle));
+	    shortcuts.on("Escape", this.onEscape);
+	  }
+
+	  toggle(key, e) {
+	    e.preventDefault();
+	    this.props.toggleProjectSearch();
+	  }
+
+	  onEscape(shortcut, e) {
+	    if (this.props.searchOn) {
+	      e.preventDefault();
+	      this.close();
+	    }
+	  }
+
+	  close() {
+	    this.setState({ inputValue: "" });
+	    this.props.toggleProjectSearch(false);
+	  }
+
+	  renderFileSearch() {
+	    return Autocomplete({
+	      selectItem: (e, result) => {
+	        this.props.selectSource(result.id);
+	        this.close();
+	      },
+	      close: this.close,
+	      items: searchResults(this.props.sources, this.state.inputValue),
+	      inputValue: this.state.inputValue,
+	      placeholder: L10N.getStr("sourceSearch.search"),
+	      size: "big"
+	    });
+	  }
+
+	  renderTextSearch() {
+	    var sources = this.props.sources;
+
+
+	    return TextSearch({
+	      sources
+	    });
+	  }
+
+	  render() {
+	    var searchOn = this.props.searchOn;
+
+	    if (!searchOn) {
+	      return null;
+	    }
+
+	    return _react.DOM.div({ className: "search-container" }, (0, _devtoolsConfig.isEnabled)("projectTextSearch") ? this.renderTextSearch() : this.renderFileSearch());
+	  }
+	}
+
+	ProjectSearch.propTypes = {
+	  sources: _react.PropTypes.object.isRequired,
+	  selectSource: _react.PropTypes.func.isRequired,
+	  toggleProjectSearch: _react.PropTypes.func.isRequired,
+	  searchOn: _react.PropTypes.bool
+	};
+
+	ProjectSearch.contextTypes = {
+	  shortcuts: _react.PropTypes.object
+	};
+
+	ProjectSearch.displayName = "ProjectSearch";
+
+	exports.default = (0, _reactRedux.connect)(state => ({
+	  sources: (0, _selectors.getSources)(state),
+	  searchOn: (0, _selectors.getProjectSearchState)(state)
+	}), dispatch => (0, _redux.bindActionCreators)(_actions2.default, dispatch))(ProjectSearch);
+
+/***/ },
+/* 1061 */
+/***/ function(module, exports) {
+
+	"use strict";
+
+	Object.defineProperty(exports, "__esModule", {
+	  value: true
+	});
+	exports.searchSource = searchSource;
+
+	function _toConsumableArray(arr) { if (Array.isArray(arr)) { for (var i = 0, arr2 = Array(arr.length); i < arr.length; i++) { arr2[i] = arr[i]; } return arr2; } else { return Array.from(arr); } }
+
+	function searchSource(source, queryText) {
+	  var _ref;
+
+	  var text = source.text,
+	      id = source.id,
+	      url = source.url,
+	      loading = source.loading;
+
+	  if (loading || !text || queryText == "") {
+	    return [];
+	  }
+
+	  var lines = text.split("\n");
+	  var result = undefined;
+	  var query = new RegExp(queryText, "g");
+
+	  var matches = lines.map((_text, line) => {
+	    var indices = [];
+
+	    while (result = query.exec(_text)) {
+	      indices.push({
+	        line: line + 1,
+	        column: result.index,
+	        match: result[0],
+	        text: result.input,
+	        id,
+	        url
+	      });
+	    }
+	    return indices;
+	  }).filter(_matches => _matches.length > 0);
+
+	  matches = (_ref = []).concat.apply(_ref, _toConsumableArray(matches));
+	  return matches;
+	}
+
+/***/ },
+/* 1062 */
+/***/ function(module, exports) {
+
+	// removed by extract-text-webpack-plugin
+
+/***/ },
+/* 1063 */,
+/* 1064 */
+/***/ function(module, exports, __webpack_require__) {
+
+	"use strict";
+
+	Object.defineProperty(exports, "__esModule", {
+	  value: true
+	});
+
+	var _react = __webpack_require__(2);
+
+	var _classnames = __webpack_require__(175);
+
+	var _classnames2 = _interopRequireDefault(_classnames);
+
+	var _Svg = __webpack_require__(344);
+
+	var _Svg2 = _interopRequireDefault(_Svg);
+
+	var _ManagedTree2 = __webpack_require__(419);
+
+	var _ManagedTree3 = _interopRequireDefault(_ManagedTree2);
+
+	__webpack_require__(1065);
+
+	function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
+
+	var ManagedTree = (0, _react.createFactory)(_ManagedTree3.default);
+
+	class TextSearch extends _react.Component {
+	  constructor(props) {
+	    super(props);
+	  }
+
+	  renderFile(file, expanded) {
+	    return _react.DOM.div({
+	      className: "file-result"
+	    }, (0, _Svg2.default)("arrow", {
+	      className: (0, _classnames2.default)({
+	        expanded
+	      })
+	    }), file.filepath);
+	  }
+
+	  renderLine(match) {
+	    return _react.DOM.div({ className: "result" }, _react.DOM.span({
+	      className: "line-number"
+	    }, match.line), _react.DOM.span({ className: "line-match" }, match.value));
+	  }
+
+	  renderResults() {
+	    var results = this.props.results;
+
+	    return ManagedTree({
+	      getRoots: () => results,
+	      getChildren: file => {
+	        return file.matches || [];
+	      },
+	      itemHeight: 20,
+	      autoExpand: 1,
+	      autoExpandDepth: 1,
+	      getParent: item => null,
+	      getKey: item => item.filepath || `${item.value}/${item.line}`,
+	      renderItem: (item, depth, focused, _, expanded) => item.filepath ? this.renderFile(item, expanded) : this.renderLine(item)
+	    });
+	  }
+
+	  render() {
+	    return _react.DOM.div({
+	      className: "project-text-search"
+	    }, this.renderResults());
+	  }
+	}
+
+	exports.default = TextSearch;
+	TextSearch.displayName = "TextSearch";
+
+/***/ },
+/* 1065 */
+/***/ function(module, exports) {
+
+	// removed by extract-text-webpack-plugin
+
+/***/ },
+/* 1066 */,
+/* 1067 */
+/***/ function(module, exports, __webpack_require__) {
+
+	var baseFlatten = __webpack_require__(707),
+	    map = __webpack_require__(1068);
+
+	/**
+	 * Creates a flattened array of values by running each element in `collection`
+	 * thru `iteratee` and flattening the mapped results. The iteratee is invoked
+	 * with three arguments: (value, index|key, collection).
+	 *
+	 * @static
+	 * @memberOf _
+	 * @since 4.0.0
+	 * @category Collection
+	 * @param {Array|Object} collection The collection to iterate over.
+	 * @param {Function} [iteratee=_.identity] The function invoked per iteration.
+	 * @returns {Array} Returns the new flattened array.
+	 * @example
+	 *
+	 * function duplicate(n) {
+	 *   return [n, n];
+	 * }
+	 *
+	 * _.flatMap([1, 2], duplicate);
+	 * // => [1, 1, 2, 2]
+	 */
+	function flatMap(collection, iteratee) {
+	  return baseFlatten(map(collection, iteratee), 1);
+	}
+
+	module.exports = flatMap;
+
+
+/***/ },
+/* 1068 */
+/***/ function(module, exports, __webpack_require__) {
+
+	var arrayMap = __webpack_require__(110),
+	    baseIteratee = __webpack_require__(264),
+	    baseMap = __webpack_require__(1069),
+	    isArray = __webpack_require__(70);
+
+	/**
+	 * Creates an array of values by running each element in `collection` thru
+	 * `iteratee`. The iteratee is invoked with three arguments:
+	 * (value, index|key, collection).
+	 *
+	 * Many lodash methods are guarded to work as iteratees for methods like
+	 * `_.every`, `_.filter`, `_.map`, `_.mapValues`, `_.reject`, and `_.some`.
+	 *
+	 * The guarded methods are:
+	 * `ary`, `chunk`, `curry`, `curryRight`, `drop`, `dropRight`, `every`,
+	 * `fill`, `invert`, `parseInt`, `random`, `range`, `rangeRight`, `repeat`,
+	 * `sampleSize`, `slice`, `some`, `sortBy`, `split`, `take`, `takeRight`,
+	 * `template`, `trim`, `trimEnd`, `trimStart`, and `words`
+	 *
+	 * @static
+	 * @memberOf _
+	 * @since 0.1.0
+	 * @category Collection
+	 * @param {Array|Object} collection The collection to iterate over.
+	 * @param {Function} [iteratee=_.identity] The function invoked per iteration.
+	 * @returns {Array} Returns the new mapped array.
+	 * @example
+	 *
+	 * function square(n) {
+	 *   return n * n;
+	 * }
+	 *
+	 * _.map([4, 8], square);
+	 * // => [16, 64]
+	 *
+	 * _.map({ 'a': 4, 'b': 8 }, square);
+	 * // => [16, 64] (iteration order is not guaranteed)
+	 *
+	 * var users = [
+	 *   { 'user': 'barney' },
+	 *   { 'user': 'fred' }
+	 * ];
+	 *
+	 * // The `_.property` iteratee shorthand.
+	 * _.map(users, 'user');
+	 * // => ['barney', 'fred']
+	 */
+	function map(collection, iteratee) {
+	  var func = isArray(collection) ? arrayMap : baseMap;
+	  return func(collection, baseIteratee(iteratee, 3));
+	}
+
+	module.exports = map;
+
+
+/***/ },
+/* 1069 */
+/***/ function(module, exports, __webpack_require__) {
+
+	var baseEach = __webpack_require__(1070),
+	    isArrayLike = __webpack_require__(220);
+
+	/**
+	 * The base implementation of `_.map` without support for iteratee shorthands.
+	 *
+	 * @private
+	 * @param {Array|Object} collection The collection to iterate over.
+	 * @param {Function} iteratee The function invoked per iteration.
+	 * @returns {Array} Returns the new mapped array.
+	 */
+	function baseMap(collection, iteratee) {
+	  var index = -1,
+	      result = isArrayLike(collection) ? Array(collection.length) : [];
+
+	  baseEach(collection, function(value, key, collection) {
+	    result[++index] = iteratee(value, key, collection);
+	  });
+	  return result;
+	}
+
+	module.exports = baseMap;
+
+
+/***/ },
+/* 1070 */
+/***/ function(module, exports, __webpack_require__) {
+
+	var baseForOwn = __webpack_require__(764),
+	    createBaseEach = __webpack_require__(1071);
+
+	/**
+	 * The base implementation of `_.forEach` without support for iteratee shorthands.
+	 *
+	 * @private
+	 * @param {Array|Object} collection The collection to iterate over.
+	 * @param {Function} iteratee The function invoked per iteration.
+	 * @returns {Array|Object} Returns `collection`.
+	 */
+	var baseEach = createBaseEach(baseForOwn);
+
+	module.exports = baseEach;
+
+
+/***/ },
+/* 1071 */
+/***/ function(module, exports, __webpack_require__) {
+
+	var isArrayLike = __webpack_require__(220);
+
+	/**
+	 * Creates a `baseEach` or `baseEachRight` function.
+	 *
+	 * @private
+	 * @param {Function} eachFunc The function to iterate over a collection.
+	 * @param {boolean} [fromRight] Specify iterating from right to left.
+	 * @returns {Function} Returns the new base function.
+	 */
+	function createBaseEach(eachFunc, fromRight) {
+	  return function(collection, iteratee) {
+	    if (collection == null) {
+	      return collection;
+	    }
+	    if (!isArrayLike(collection)) {
+	      return eachFunc(collection, iteratee);
+	    }
+	    var length = collection.length,
+	        index = fromRight ? length : -1,
+	        iterable = Object(collection);
+
+	    while ((fromRight ? index-- : ++index < length)) {
+	      if (iteratee(iterable[index], index, iterable) === false) {
+	        break;
+	      }
+	    }
+	    return collection;
+	  };
+	}
+
+	module.exports = createBaseEach;
+
+
 /***/ }
 /******/ ])
 });
 ;
\ No newline at end of file
--- a/devtools/client/debugger/new/parser-worker.js
+++ b/devtools/client/debugger/new/parser-worker.js
@@ -3442,19 +3442,149 @@ return /******/ (function(modules) { // 
 	  }
 	  return -1;
 	}
 
 	module.exports = baseFindIndex;
 
 
 /***/ },
-/* 264 */,
-/* 265 */,
-/* 266 */,
+/* 264 */
+/***/ function(module, exports, __webpack_require__) {
+
+	var baseMatches = __webpack_require__(265),
+	    baseMatchesProperty = __webpack_require__(294),
+	    identity = __webpack_require__(298),
+	    isArray = __webpack_require__(70),
+	    property = __webpack_require__(299);
+
+	/**
+	 * The base implementation of `_.iteratee`.
+	 *
+	 * @private
+	 * @param {*} [value=_.identity] The value to convert to an iteratee.
+	 * @returns {Function} Returns the iteratee.
+	 */
+	function baseIteratee(value) {
+	  // Don't store the `typeof` result in a variable to avoid a JIT bug in Safari 9.
+	  // See https://bugs.webkit.org/show_bug.cgi?id=156034 for more details.
+	  if (typeof value == 'function') {
+	    return value;
+	  }
+	  if (value == null) {
+	    return identity;
+	  }
+	  if (typeof value == 'object') {
+	    return isArray(value)
+	      ? baseMatchesProperty(value[0], value[1])
+	      : baseMatches(value);
+	  }
+	  return property(value);
+	}
+
+	module.exports = baseIteratee;
+
+
+/***/ },
+/* 265 */
+/***/ function(module, exports, __webpack_require__) {
+
+	var baseIsMatch = __webpack_require__(266),
+	    getMatchData = __webpack_require__(291),
+	    matchesStrictComparable = __webpack_require__(293);
+
+	/**
+	 * The base implementation of `_.matches` which doesn't clone `source`.
+	 *
+	 * @private
+	 * @param {Object} source The object of property values to match.
+	 * @returns {Function} Returns the new spec function.
+	 */
+	function baseMatches(source) {
+	  var matchData = getMatchData(source);
+	  if (matchData.length == 1 && matchData[0][2]) {
+	    return matchesStrictComparable(matchData[0][0], matchData[0][1]);
+	  }
+	  return function(object) {
+	    return object === source || baseIsMatch(object, source, matchData);
+	  };
+	}
+
+	module.exports = baseMatches;
+
+
+/***/ },
+/* 266 */
+/***/ function(module, exports, __webpack_require__) {
+
+	var Stack = __webpack_require__(267),
+	    baseIsEqual = __webpack_require__(273);
+
+	/** Used to compose bitmasks for value comparisons. */
+	var COMPARE_PARTIAL_FLAG = 1,
+	    COMPARE_UNORDERED_FLAG = 2;
+
+	/**
+	 * The base implementation of `_.isMatch` without support for iteratee shorthands.
+	 *
+	 * @private
+	 * @param {Object} object The object to inspect.
+	 * @param {Object} source The object of property values to match.
+	 * @param {Array} matchData The property names, values, and compare flags to match.
+	 * @param {Function} [customizer] The function to customize comparisons.
+	 * @returns {boolean} Returns `true` if `object` is a match, else `false`.
+	 */
+	function baseIsMatch(object, source, matchData, customizer) {
+	  var index = matchData.length,
+	      length = index,
+	      noCustomizer = !customizer;
+
+	  if (object == null) {
+	    return !length;
+	  }
+	  object = Object(object);
+	  while (index--) {
+	    var data = matchData[index];
+	    if ((noCustomizer && data[2])
+	          ? data[1] !== object[data[0]]
+	          : !(data[0] in object)
+	        ) {
+	      return false;
+	    }
+	  }
+	  while (++index < length) {
+	    data = matchData[index];
+	    var key = data[0],
+	        objValue = object[key],
+	        srcValue = data[1];
+
+	    if (noCustomizer && data[2]) {
+	      if (objValue === undefined && !(key in object)) {
+	        return false;
+	      }
+	    } else {
+	      var stack = new Stack;
+	      if (customizer) {
+	        var result = customizer(objValue, srcValue, key, object, source, stack);
+	      }
+	      if (!(result === undefined
+	            ? baseIsEqual(srcValue, objValue, COMPARE_PARTIAL_FLAG | COMPARE_UNORDERED_FLAG, customizer, stack)
+	            : result
+	          )) {
+	        return false;
+	      }
+	    }
+	  }
+	  return true;
+	}
+
+	module.exports = baseIsMatch;
+
+
+/***/ },
 /* 267 */
 /***/ function(module, exports, __webpack_require__) {
 
 	var ListCache = __webpack_require__(93),
 	    stackClear = __webpack_require__(268),
 	    stackDelete = __webpack_require__(269),
 	    stackGet = __webpack_require__(270),
 	    stackHas = __webpack_require__(271),
@@ -3603,19 +3733,228 @@ return /******/ (function(modules) { // 
 	  this.size = data.size;
 	  return this;
 	}
 
 	module.exports = stackSet;
 
 
 /***/ },
-/* 273 */,
-/* 274 */,
-/* 275 */,
+/* 273 */
+/***/ function(module, exports, __webpack_require__) {
+
+	var baseIsEqualDeep = __webpack_require__(274),
+	    isObjectLike = __webpack_require__(14);
+
+	/**
+	 * The base implementation of `_.isEqual` which supports partial comparisons
+	 * and tracks traversed objects.
+	 *
+	 * @private
+	 * @param {*} value The value to compare.
+	 * @param {*} other The other value to compare.
+	 * @param {boolean} bitmask The bitmask flags.
+	 *  1 - Unordered comparison
+	 *  2 - Partial comparison
+	 * @param {Function} [customizer] The function to customize comparisons.
+	 * @param {Object} [stack] Tracks traversed `value` and `other` objects.
+	 * @returns {boolean} Returns `true` if the values are equivalent, else `false`.
+	 */
+	function baseIsEqual(value, other, bitmask, customizer, stack) {
+	  if (value === other) {
+	    return true;
+	  }
+	  if (value == null || other == null || (!isObjectLike(value) && !isObjectLike(other))) {
+	    return value !== value && other !== other;
+	  }
+	  return baseIsEqualDeep(value, other, bitmask, customizer, baseIsEqual, stack);
+	}
+
+	module.exports = baseIsEqual;
+
+
+/***/ },
+/* 274 */
+/***/ function(module, exports, __webpack_require__) {
+
+	var Stack = __webpack_require__(267),
+	    equalArrays = __webpack_require__(275),
+	    equalByTag = __webpack_require__(281),
+	    equalObjects = __webpack_require__(284),
+	    getTag = __webpack_require__(198),
+	    isArray = __webpack_require__(70),
+	    isBuffer = __webpack_require__(210),
+	    isTypedArray = __webpack_require__(212);
+
+	/** Used to compose bitmasks for value comparisons. */
+	var COMPARE_PARTIAL_FLAG = 1;
+
+	/** `Object#toString` result references. */
+	var argsTag = '[object Arguments]',
+	    arrayTag = '[object Array]',
+	    objectTag = '[object Object]';
+
+	/** Used for built-in method references. */
+	var objectProto = Object.prototype;
+
+	/** Used to check objects for own properties. */
+	var hasOwnProperty = objectProto.hasOwnProperty;
+
+	/**
+	 * A specialized version of `baseIsEqual` for arrays and objects which performs
+	 * deep comparisons and tracks traversed objects enabling objects with circular
+	 * references to be compared.
+	 *
+	 * @private
+	 * @param {Object} object The object to compare.
+	 * @param {Object} other The other object to compare.
+	 * @param {number} bitmask The bitmask flags. See `baseIsEqual` for more details.
+	 * @param {Function} customizer The function to customize comparisons.
+	 * @param {Function} equalFunc The function to determine equivalents of values.
+	 * @param {Object} [stack] Tracks traversed `object` and `other` objects.
+	 * @returns {boolean} Returns `true` if the objects are equivalent, else `false`.
+	 */
+	function baseIsEqualDeep(object, other, bitmask, customizer, equalFunc, stack) {
+	  var objIsArr = isArray(object),
+	      othIsArr = isArray(other),
+	      objTag = objIsArr ? arrayTag : getTag(object),
+	      othTag = othIsArr ? arrayTag : getTag(other);
+
+	  objTag = objTag == argsTag ? objectTag : objTag;
+	  othTag = othTag == argsTag ? objectTag : othTag;
+
+	  var objIsObj = objTag == objectTag,
+	      othIsObj = othTag == objectTag,
+	      isSameTag = objTag == othTag;
+
+	  if (isSameTag && isBuffer(object)) {
+	    if (!isBuffer(other)) {
+	      return false;
+	    }
+	    objIsArr = true;
+	    objIsObj = false;
+	  }
+	  if (isSameTag && !objIsObj) {
+	    stack || (stack = new Stack);
+	    return (objIsArr || isTypedArray(object))
+	      ? equalArrays(object, other, bitmask, customizer, equalFunc, stack)
+	      : equalByTag(object, other, objTag, bitmask, customizer, equalFunc, stack);
+	  }
+	  if (!(bitmask & COMPARE_PARTIAL_FLAG)) {
+	    var objIsWrapped = objIsObj && hasOwnProperty.call(object, '__wrapped__'),
+	        othIsWrapped = othIsObj && hasOwnProperty.call(other, '__wrapped__');
+
+	    if (objIsWrapped || othIsWrapped) {
+	      var objUnwrapped = objIsWrapped ? object.value() : object,
+	          othUnwrapped = othIsWrapped ? other.value() : other;
+
+	      stack || (stack = new Stack);
+	      return equalFunc(objUnwrapped, othUnwrapped, bitmask, customizer, stack);
+	    }
+	  }
+	  if (!isSameTag) {
+	    return false;
+	  }
+	  stack || (stack = new Stack);
+	  return equalObjects(object, other, bitmask, customizer, equalFunc, stack);
+	}
+
+	module.exports = baseIsEqualDeep;
+
+
+/***/ },
+/* 275 */
+/***/ function(module, exports, __webpack_require__) {
+
+	var SetCache = __webpack_require__(276),
+	    arraySome = __webpack_require__(279),
+	    cacheHas = __webpack_require__(280);
+
+	/** Used to compose bitmasks for value comparisons. */
+	var COMPARE_PARTIAL_FLAG = 1,
+	    COMPARE_UNORDERED_FLAG = 2;
+
+	/**
+	 * A specialized version of `baseIsEqualDeep` for arrays with support for
+	 * partial deep comparisons.
+	 *
+	 * @private
+	 * @param {Array} array The array to compare.
+	 * @param {Array} other The other array to compare.
+	 * @param {number} bitmask The bitmask flags. See `baseIsEqual` for more details.
+	 * @param {Function} customizer The function to customize comparisons.
+	 * @param {Function} equalFunc The function to determine equivalents of values.
+	 * @param {Object} stack Tracks traversed `array` and `other` objects.
+	 * @returns {boolean} Returns `true` if the arrays are equivalent, else `false`.
+	 */
+	function equalArrays(array, other, bitmask, customizer, equalFunc, stack) {
+	  var isPartial = bitmask & COMPARE_PARTIAL_FLAG,
+	      arrLength = array.length,
+	      othLength = other.length;
+
+	  if (arrLength != othLength && !(isPartial && othLength > arrLength)) {
+	    return false;
+	  }
+	  // Assume cyclic values are equal.
+	  var stacked = stack.get(array);
+	  if (stacked && stack.get(other)) {
+	    return stacked == other;
+	  }
+	  var index = -1,
+	      result = true,
+	      seen = (bitmask & COMPARE_UNORDERED_FLAG) ? new SetCache : undefined;
+
+	  stack.set(array, other);
+	  stack.set(other, array);
+
+	  // Ignore non-index properties.
+	  while (++index < arrLength) {
+	    var arrValue = array[index],
+	        othValue = other[index];
+
+	    if (customizer) {
+	      var compared = isPartial
+	        ? customizer(othValue, arrValue, index, other, array, stack)
+	        : customizer(arrValue, othValue, index, array, other, stack);
+	    }
+	    if (compared !== undefined) {
+	      if (compared) {
+	        continue;
+	      }
+	      result = false;
+	      break;
+	    }
+	    // Recursively compare arrays (susceptible to call stack limits).
+	    if (seen) {
+	      if (!arraySome(other, function(othValue, othIndex) {
+	            if (!cacheHas(seen, othIndex) &&
+	                (arrValue === othValue || equalFunc(arrValue, othValue, bitmask, customizer, stack))) {
+	              return seen.push(othIndex);
+	            }
+	          })) {
+	        result = false;
+	        break;
+	      }
+	    } else if (!(
+	          arrValue === othValue ||
+	            equalFunc(arrValue, othValue, bitmask, customizer, stack)
+	        )) {
+	      result = false;
+	      break;
+	    }
+	  }
+	  stack['delete'](array);
+	  stack['delete'](other);
+	  return result;
+	}
+
+	module.exports = equalArrays;
+
+
+/***/ },
 /* 276 */
 /***/ function(module, exports, __webpack_require__) {
 
 	var MapCache = __webpack_require__(76),
 	    setCacheAdd = __webpack_require__(277),
 	    setCacheHas = __webpack_require__(278);
 
 	/**
@@ -3684,17 +4023,45 @@ return /******/ (function(modules) { // 
 	function setCacheHas(value) {
 	  return this.__data__.has(value);
 	}
 
 	module.exports = setCacheHas;
 
 
 /***/ },
-/* 279 */,
+/* 279 */
+/***/ function(module, exports) {
+
+	/**
+	 * A specialized version of `_.some` for arrays without support for iteratee
+	 * shorthands.
+	 *
+	 * @private
+	 * @param {Array} [array] The array to iterate over.
+	 * @param {Function} predicate The function invoked per iteration.
+	 * @returns {boolean} Returns `true` if any element passes the predicate check,
+	 *  else `false`.
+	 */
+	function arraySome(array, predicate) {
+	  var index = -1,
+	      length = array == null ? 0 : array.length;
+
+	  while (++index < length) {
+	    if (predicate(array[index], index, array)) {
+	      return true;
+	    }
+	  }
+	  return false;
+	}
+
+	module.exports = arraySome;
+
+
+/***/ },
 /* 280 */
 /***/ function(module, exports) {
 
 	/**
 	 * Checks if a `cache` value for `key` exists.
 	 *
 	 * @private
 	 * @param {Object} cache The cache to query.
@@ -3704,17 +4071,134 @@ return /******/ (function(modules) { // 
 	function cacheHas(cache, key) {
 	  return cache.has(key);
 	}
 
 	module.exports = cacheHas;
 
 
 /***/ },
-/* 281 */,
+/* 281 */
+/***/ function(module, exports, __webpack_require__) {
+
+	var Symbol = __webpack_require__(7),
+	    Uint8Array = __webpack_require__(282),
+	    eq = __webpack_require__(97),
+	    equalArrays = __webpack_require__(275),
+	    mapToArray = __webpack_require__(203),
+	    setToArray = __webpack_require__(283);
+
+	/** Used to compose bitmasks for value comparisons. */
+	var COMPARE_PARTIAL_FLAG = 1,
+	    COMPARE_UNORDERED_FLAG = 2;
+
+	/** `Object#toString` result references. */
+	var boolTag = '[object Boolean]',
+	    dateTag = '[object Date]',
+	    errorTag = '[object Error]',
+	    mapTag = '[object Map]',
+	    numberTag = '[object Number]',
+	    regexpTag = '[object RegExp]',
+	    setTag = '[object Set]',
+	    stringTag = '[object String]',
+	    symbolTag = '[object Symbol]';
+
+	var arrayBufferTag = '[object ArrayBuffer]',
+	    dataViewTag = '[object DataView]';
+
+	/** Used to convert symbols to primitives and strings. */
+	var symbolProto = Symbol ? Symbol.prototype : undefined,
+	    symbolValueOf = symbolProto ? symbolProto.valueOf : undefined;
+
+	/**
+	 * A specialized version of `baseIsEqualDeep` for comparing objects of
+	 * the same `toStringTag`.
+	 *
+	 * **Note:** This function only supports comparing values with tags of
+	 * `Boolean`, `Date`, `Error`, `Number`, `RegExp`, or `String`.
+	 *
+	 * @private
+	 * @param {Object} object The object to compare.
+	 * @param {Object} other The other object to compare.
+	 * @param {string} tag The `toStringTag` of the objects to compare.
+	 * @param {number} bitmask The bitmask flags. See `baseIsEqual` for more details.
+	 * @param {Function} customizer The function to customize comparisons.
+	 * @param {Function} equalFunc The function to determine equivalents of values.
+	 * @param {Object} stack Tracks traversed `object` and `other` objects.
+	 * @returns {boolean} Returns `true` if the objects are equivalent, else `false`.
+	 */
+	function equalByTag(object, other, tag, bitmask, customizer, equalFunc, stack) {
+	  switch (tag) {
+	    case dataViewTag:
+	      if ((object.byteLength != other.byteLength) ||
+	          (object.byteOffset != other.byteOffset)) {
+	        return false;
+	      }
+	      object = object.buffer;
+	      other = other.buffer;
+
+	    case arrayBufferTag:
+	      if ((object.byteLength != other.byteLength) ||
+	          !equalFunc(new Uint8Array(object), new Uint8Array(other))) {
+	        return false;
+	      }
+	      return true;
+
+	    case boolTag:
+	    case dateTag:
+	    case numberTag:
+	      // Coerce booleans to `1` or `0` and dates to milliseconds.
+	      // Invalid dates are coerced to `NaN`.
+	      return eq(+object, +other);
+
+	    case errorTag:
+	      return object.name == other.name && object.message == other.message;
+
+	    case regexpTag:
+	    case stringTag:
+	      // Coerce regexes to strings and treat strings, primitives and objects,
+	      // as equal. See http://www.ecma-international.org/ecma-262/7.0/#sec-regexp.prototype.tostring
+	      // for more details.
+	      return object == (other + '');
+
+	    case mapTag:
+	      var convert = mapToArray;
+
+	    case setTag:
+	      var isPartial = bitmask & COMPARE_PARTIAL_FLAG;
+	      convert || (convert = setToArray);
+
+	      if (object.size != other.size && !isPartial) {
+	        return false;
+	      }
+	      // Assume cyclic values are equal.
+	      var stacked = stack.get(object);
+	      if (stacked) {
+	        return stacked == other;
+	      }
+	      bitmask |= COMPARE_UNORDERED_FLAG;
+
+	      // Recursively compare objects (susceptible to call stack limits).
+	      stack.set(object, other);
+	      var result = equalArrays(convert(object), convert(other), bitmask, customizer, equalFunc, stack);
+	      stack['delete'](object);
+	      return result;
+
+	    case symbolTag:
+	      if (symbolValueOf) {
+	        return symbolValueOf.call(object) == symbolValueOf.call(other);
+	      }
+	  }
+	  return false;
+	}
+
+	module.exports = equalByTag;
+
+
+/***/ },
 /* 282 */
 /***/ function(module, exports, __webpack_require__) {
 
 	var root = __webpack_require__(8);
 
 	/** Built-in value references. */
 	var Uint8Array = root.Uint8Array;
 
@@ -3741,17 +4225,111 @@ return /******/ (function(modules) { // 
 	  });
 	  return result;
 	}
 
 	module.exports = setToArray;
 
 
 /***/ },
-/* 284 */,
+/* 284 */
+/***/ function(module, exports, __webpack_require__) {
+
+	var getAllKeys = __webpack_require__(285);
+
+	/** Used to compose bitmasks for value comparisons. */
+	var COMPARE_PARTIAL_FLAG = 1;
+
+	/** Used for built-in method references. */
+	var objectProto = Object.prototype;
+
+	/** Used to check objects for own properties. */
+	var hasOwnProperty = objectProto.hasOwnProperty;
+
+	/**
+	 * A specialized version of `baseIsEqualDeep` for objects with support for
+	 * partial deep comparisons.
+	 *
+	 * @private
+	 * @param {Object} object The object to compare.
+	 * @param {Object} other The other object to compare.
+	 * @param {number} bitmask The bitmask flags. See `baseIsEqual` for more details.
+	 * @param {Function} customizer The function to customize comparisons.
+	 * @param {Function} equalFunc The function to determine equivalents of values.
+	 * @param {Object} stack Tracks traversed `object` and `other` objects.
+	 * @returns {boolean} Returns `true` if the objects are equivalent, else `false`.
+	 */
+	function equalObjects(object, other, bitmask, customizer, equalFunc, stack) {
+	  var isPartial = bitmask & COMPARE_PARTIAL_FLAG,
+	      objProps = getAllKeys(object),
+	      objLength = objProps.length,
+	      othProps = getAllKeys(other),
+	      othLength = othProps.length;
+
+	  if (objLength != othLength && !isPartial) {
+	    return false;
+	  }
+	  var index = objLength;
+	  while (index--) {
+	    var key = objProps[index];
+	    if (!(isPartial ? key in other : hasOwnProperty.call(other, key))) {
+	      return false;
+	    }
+	  }
+	  // Assume cyclic values are equal.
+	  var stacked = stack.get(object);
+	  if (stacked && stack.get(other)) {
+	    return stacked == other;
+	  }
+	  var result = true;
+	  stack.set(object, other);
+	  stack.set(other, object);
+
+	  var skipCtor = isPartial;
+	  while (++index < objLength) {
+	    key = objProps[index];
+	    var objValue = object[key],
+	        othValue = other[key];
+
+	    if (customizer) {
+	      var compared = isPartial
+	        ? customizer(othValue, objValue, key, other, object, stack)
+	        : customizer(objValue, othValue, key, object, other, stack);
+	    }
+	    // Recursively compare objects (susceptible to call stack limits).
+	    if (!(compared === undefined
+	          ? (objValue === othValue || equalFunc(objValue, othValue, bitmask, customizer, stack))
+	          : compared
+	        )) {
+	      result = false;
+	      break;
+	    }
+	    skipCtor || (skipCtor = key == 'constructor');
+	  }
+	  if (result && !skipCtor) {
+	    var objCtor = object.constructor,
+	        othCtor = other.constructor;
+
+	    // Non `Object` object instances with different constructors are not equal.
+	    if (objCtor != othCtor &&
+	        ('constructor' in object && 'constructor' in other) &&
+	        !(typeof objCtor == 'function' && objCtor instanceof objCtor &&
+	          typeof othCtor == 'function' && othCtor instanceof othCtor)) {
+	      result = false;
+	    }
+	  }
+	  stack['delete'](object);
+	  stack['delete'](other);
+	  return result;
+	}
+
+	module.exports = equalObjects;
+
+
+/***/ },
 /* 285 */
 /***/ function(module, exports, __webpack_require__) {
 
 	var baseGetAllKeys = __webpack_require__(286),
 	    getSymbols = __webpack_require__(288),
 	    keys = __webpack_require__(205);
 
 	/**
@@ -3912,23 +4490,236 @@ return /******/ (function(modules) { // 
 	function stubArray() {
 	  return [];
 	}
 
 	module.exports = stubArray;
 
 
 /***/ },
-/* 291 */,
-/* 292 */,
-/* 293 */,
-/* 294 */,
-/* 295 */,
-/* 296 */,
-/* 297 */,
+/* 291 */
+/***/ function(module, exports, __webpack_require__) {
+
+	var isStrictComparable = __webpack_require__(292),
+	    keys = __webpack_require__(205);
+
+	/**
+	 * Gets the property names, values, and compare flags of `object`.
+	 *
+	 * @private
+	 * @param {Object} object The object to query.
+	 * @returns {Array} Returns the match data of `object`.
+	 */
+	function getMatchData(object) {
+	  var result = keys(object),
+	      length = result.length;
+
+	  while (length--) {
+	    var key = result[length],
+	        value = object[key];
+
+	    result[length] = [key, value, isStrictComparable(value)];
+	  }
+	  return result;
+	}
+
+	module.exports = getMatchData;
+
+
+/***/ },
+/* 292 */
+/***/ function(module, exports, __webpack_require__) {
+
+	var isObject = __webpack_require__(84);
+
+	/**
+	 * Checks if `value` is suitable for strict equality comparisons, i.e. `===`.
+	 *
+	 * @private
+	 * @param {*} value The value to check.
+	 * @returns {boolean} Returns `true` if `value` if suitable for strict
+	 *  equality comparisons, else `false`.
+	 */
+	function isStrictComparable(value) {
+	  return value === value && !isObject(value);
+	}
+
+	module.exports = isStrictComparable;
+
+
+/***/ },
+/* 293 */
+/***/ function(module, exports) {
+
+	/**
+	 * A specialized version of `matchesProperty` for source values suitable
+	 * for strict equality comparisons, i.e. `===`.
+	 *
+	 * @private
+	 * @param {string} key The key of the property to get.
+	 * @param {*} srcValue The value to match.
+	 * @returns {Function} Returns the new spec function.
+	 */
+	function matchesStrictComparable(key, srcValue) {
+	  return function(object) {
+	    if (object == null) {
+	      return false;
+	    }
+	    return object[key] === srcValue &&
+	      (srcValue !== undefined || (key in Object(object)));
+	  };
+	}
+
+	module.exports = matchesStrictComparable;
+
+
+/***/ },
+/* 294 */
+/***/ function(module, exports, __webpack_require__) {
+
+	var baseIsEqual = __webpack_require__(273),
+	    get = __webpack_require__(67),
+	    hasIn = __webpack_require__(295),
+	    isKey = __webpack_require__(71),
+	    isStrictComparable = __webpack_require__(292),
+	    matchesStrictComparable = __webpack_require__(293),
+	    toKey = __webpack_require__(111);
+
+	/** Used to compose bitmasks for value comparisons. */
+	var COMPARE_PARTIAL_FLAG = 1,
+	    COMPARE_UNORDERED_FLAG = 2;
+
+	/**
+	 * The base implementation of `_.matchesProperty` which doesn't clone `srcValue`.
+	 *
+	 * @private
+	 * @param {string} path The path of the property to get.
+	 * @param {*} srcValue The value to match.
+	 * @returns {Function} Returns the new spec function.
+	 */
+	function baseMatchesProperty(path, srcValue) {
+	  if (isKey(path) && isStrictComparable(srcValue)) {
+	    return matchesStrictComparable(toKey(path), srcValue);
+	  }
+	  return function(object) {
+	    var objValue = get(object, path);
+	    return (objValue === undefined && objValue === srcValue)
+	      ? hasIn(object, path)
+	      : baseIsEqual(srcValue, objValue, COMPARE_PARTIAL_FLAG | COMPARE_UNORDERED_FLAG);
+	  };
+	}
+
+	module.exports = baseMatchesProperty;
+
+
+/***/ },
+/* 295 */
+/***/ function(module, exports, __webpack_require__) {
+
+	var baseHasIn = __webpack_require__(296),
+	    hasPath = __webpack_require__(297);
+
+	/**
+	 * Checks if `path` is a direct or inherited property of `object`.
+	 *
+	 * @static
+	 * @memberOf _
+	 * @since 4.0.0
+	 * @category Object
+	 * @param {Object} object The object to query.
+	 * @param {Array|string} path The path to check.
+	 * @returns {boolean} Returns `true` if `path` exists, else `false`.
+	 * @example
+	 *
+	 * var object = _.create({ 'a': _.create({ 'b': 2 }) });
+	 *
+	 * _.hasIn(object, 'a');
+	 * // => true
+	 *
+	 * _.hasIn(object, 'a.b');
+	 * // => true
+	 *
+	 * _.hasIn(object, ['a', 'b']);
+	 * // => true
+	 *
+	 * _.hasIn(object, 'b');
+	 * // => false
+	 */
+	function hasIn(object, path) {
+	  return object != null && hasPath(object, path, baseHasIn);
+	}
+
+	module.exports = hasIn;
+
+
+/***/ },
+/* 296 */
+/***/ function(module, exports) {
+
+	/**
+	 * The base implementation of `_.hasIn` without support for deep paths.
+	 *
+	 * @private
+	 * @param {Object} [object] The object to query.
+	 * @param {Array|string} key The key to check.
+	 * @returns {boolean} Returns `true` if `key` exists, else `false`.
+	 */
+	function baseHasIn(object, key) {
+	  return object != null && key in Object(object);
+	}
+
+	module.exports = baseHasIn;
+
+
+/***/ },
+/* 297 */
+/***/ function(module, exports, __webpack_require__) {
+
+	var castPath = __webpack_require__(69),
+	    isArguments = __webpack_require__(208),
+	    isArray = __webpack_require__(70),
+	    isIndex = __webpack_require__(117),
+	    isLength = __webpack_require__(214),
+	    toKey = __webpack_require__(111);
+
+	/**
+	 * Checks if `path` exists on `object`.
+	 *
+	 * @private
+	 * @param {Object} object The object to query.
+	 * @param {Array|string} path The path to check.
+	 * @param {Function} hasFunc The function to check properties.
+	 * @returns {boolean} Returns `true` if `path` exists, else `false`.
+	 */
+	function hasPath(object, path, hasFunc) {
+	  path = castPath(path, object);
+
+	  var index = -1,
+	      length = path.length,
+	      result = false;
+
+	  while (++index < length) {
+	    var key = toKey(path[index]);
+	    if (!(result = object != null && hasFunc(object, key))) {
+	      break;
+	    }
+	    object = object[key];
+	  }
+	  if (result || ++index != length) {
+	    return result;
+	  }
+	  length = object == null ? 0 : object.length;
+	  return !!length && isLength(length) && isIndex(key, length) &&
+	    (isArray(object) || isArguments(object));
+	}
+
+	module.exports = hasPath;
+
+
+/***/ },
 /* 298 */
 /***/ function(module, exports) {
 
 	/**
 	 * This method returns the first argument it receives.
 	 *
 	 * @static
 	 * @since 0.1.0
@@ -3946,19 +4737,96 @@ return /******/ (function(modules) { // 
 	function identity(value) {
 	  return value;
 	}
 
 	module.exports = identity;
 
 
 /***/ },
-/* 299 */,
-/* 300 */,
-/* 301 */,
+/* 299 */
+/***/ function(module, exports, __webpack_require__) {
+
+	var baseProperty = __webpack_require__(300),
+	    basePropertyDeep = __webpack_require__(301),
+	    isKey = __webpack_require__(71),
+	    toKey = __webpack_require__(111);
+
+	/**
+	 * Creates a function that returns the value at `path` of a given object.
+	 *
+	 * @static
+	 * @memberOf _
+	 * @since 2.4.0
+	 * @category Util
+	 * @param {Array|string} path The path of the property to get.
+	 * @returns {Function} Returns the new accessor function.
+	 * @example
+	 *
+	 * var objects = [
+	 *   { 'a': { 'b': 2 } },
+	 *   { 'a': { 'b': 1 } }
+	 * ];
+	 *
+	 * _.map(objects, _.property('a.b'));
+	 * // => [2, 1]
+	 *
+	 * _.map(_.sortBy(objects, _.property(['a', 'b'])), 'a.b');
+	 * // => [1, 2]
+	 */
+	function property(path) {
+	  return isKey(path) ? baseProperty(toKey(path)) : basePropertyDeep(path);
+	}
+
+	module.exports = property;
+
+
+/***/ },
+/* 300 */
+/***/ function(module, exports) {
+
+	/**
+	 * The base implementation of `_.property` without support for deep paths.
+	 *
+	 * @private
+	 * @param {string} key The key of the property to get.
+	 * @returns {Function} Returns the new accessor function.
+	 */
+	function baseProperty(key) {
+	  return function(object) {
+	    return object == null ? undefined : object[key];
+	  };
+	}
+
+	module.exports = baseProperty;
+
+
+/***/ },
+/* 301 */
+/***/ function(module, exports, __webpack_require__) {
+
+	var baseGet = __webpack_require__(68);
+
+	/**
+	 * A specialized version of `baseProperty` which supports deep paths.
+	 *
+	 * @private
+	 * @param {Array|string} path The path of the property to get.
+	 * @returns {Function} Returns the new accessor function.
+	 */
+	function basePropertyDeep(path) {
+	  return function(object) {
+	    return baseGet(object, path);
+	  };
+	}
+
+	module.exports = basePropertyDeep;
+
+
+/***/ },
 /* 302 */
 /***/ function(module, exports, __webpack_require__) {
 
 	var toFinite = __webpack_require__(303);
 
 	/**
 	 * Converts `value` to an integer.
 	 *
@@ -7510,17 +8378,17 @@ return /******/ (function(modules) { // 
 	};
 
 	pp$1.parseExportDeclaration = function () {
 	  return this.parseStatement(true);
 	};
 
 	pp$1.isExportDefaultSpecifier = function () {
 	  if (this.match(types.name)) {
-	    return this.state.value !== "type" && this.state.value !== "async" && this.state.value !== "interface";
+	    return this.state.value !== "async";
 	  }
 
 	  if (!this.match(types._default)) {
 	    return false;
 	  }
 
 	  var lookahead = this.lookahead();
 	  return lookahead.type === types.comma || lookahead.type === types.name && lookahead.value === "from";
@@ -7823,16 +8691,18 @@ return /******/ (function(modules) { // 
 	        break;
 
 	      case "ObjectProperty":
 	        this.toAssignable(node.value, isBinding, contextDescription);
 	        break;
 
 	      case "SpreadProperty":
 	        node.type = "RestProperty";
+	        var arg = node.argument;
+	        this.toAssignable(arg, isBinding, contextDescription);
 	        break;
 
 	      case "ArrayExpression":
 	        node.type = "ArrayPattern";
 	        this.toAssignableList(node.elements, isBinding, contextDescription);
 	        break;
 
 	      case "AssignmentExpression":
@@ -9340,17 +10210,18 @@ return /******/ (function(modules) { // 
 	  this.state.leadingComments.push(comment);
 	};
 
 	pp$6.processComment = function (node) {
 	  if (node.type === "Program" && node.body.length > 0) return;
 
 	  var stack = this.state.commentStack;
 
-	  var lastChild = void 0,
+	  var firstChild = void 0,
+	      lastChild = void 0,
 	      trailingComments = void 0,
 	      i = void 0,
 	      j = void 0;
 
 	  if (this.state.trailingComments.length > 0) {
 	    // If the first comment in trailingComments comes after the
 	    // current node, then we're good - all comments in the array will
 	    // come after the node and so it's safe to add them as official
@@ -9371,20 +10242,48 @@ return /******/ (function(modules) { // 
 	    var lastInStack = last(stack);
 	    if (stack.length > 0 && lastInStack.trailingComments && lastInStack.trailingComments[0].start >= node.end) {
 	      trailingComments = lastInStack.trailingComments;
 	      lastInStack.trailingComments = null;
 	    }
 	  }
 
 	  // Eating the stack.
+	  if (stack.length > 0 && last(stack).start >= node.start) {
+	    firstChild = stack.pop();
+	  }
+
 	  while (stack.length > 0 && last(stack).start >= node.start) {
 	    lastChild = stack.pop();
 	  }
 
+	  if (!lastChild && firstChild) lastChild = firstChild;
+
+	  // Attach comments that follow a trailing comma on the last
+	  // property in an object literal or a trailing comma in function arguments
+	  // as trailing comments
+	  if (firstChild && (firstChild.type === "ObjectProperty" || node.type === "CallExpression") && this.state.leadingComments.length > 0) {
+	    var lastComment = last(this.state.leadingComments);
+	    if (lastComment.start >= node.start) {
+	      if (this.state.commentPreviousNode) {
+	        for (j = 0; j < this.state.leadingComments.length; j++) {
+	          if (this.state.leadingComments[j].end < this.state.commentPreviousNode.end) {
+	            this.state.leadingComments.splice(j, 1);
+	            j--;
+	          }
+	        }
+
+	        if (this.state.leadingComments.length > 0) {
+	          firstChild.trailingComments = this.state.leadingComments;
+	          this.state.leadingComments = [];
+	        }
+	      }
+	    }
+	  }
+
 	  if (lastChild) {
 	    if (lastChild.leadingComments) {
 	      if (lastChild !== node && last(lastChild.leadingComments).end <= node.start) {
 	        node.leadingComments = lastChild.leadingComments;
 	        lastChild.leadingComments = null;
 	      } else {
 	        // A leading comment for an anonymous class had been stolen by its first ClassMethod,
 	        // so this takes back the leading comment.
@@ -10119,17 +11018,17 @@ return /******/ (function(modules) { // 
 	  node.rest = null;
 	  node.typeParameters = null;
 
 	  if (this.isRelational("<")) {
 	    node.typeParameters = this.flowParseTypeParameterDeclaration();
 	  }
 
 	  this.expect(types.parenL);
-	  while (this.match(types.name)) {
+	  while (!this.match(types.parenR) && !this.match(types.ellipsis)) {
 	    node.params.push(this.flowParseFunctionTypeParam());
 	    if (!this.match(types.parenR)) {
 	      this.expect(types.comma);
 	    }
 	  }
 
 	  if (this.eat(types.ellipsis)) {
 	    node.rest = this.flowParseFunctionTypeParam();
@@ -10676,16 +11575,26 @@ return /******/ (function(modules) { // 
 
 	  // export type
 	  instance.extend("shouldParseExportDeclaration", function (inner) {
 	    return function () {
 	      return this.isContextual("type") || this.isContextual("interface") || inner.call(this);
 	    };
 	  });
 
+	  instance.extend("isExportDefaultSpecifier", function (inner) {
+	    return function () {
+	      if (this.match(types.name) && (this.state.value === "type" || this.state.value === "interface")) {
+	        return false;
+	      }
+
+	      return inner.call(this);
+	    };
+	  });
+
 	  instance.extend("parseConditional", function (inner) {
 	    return function (expr, noIn, startPos, startLoc, refNeedsArrowPos) {
 	      // only do the expensive clone if there is a question mark
 	      // and if we come from inside parens
 	      if (refNeedsArrowPos && this.match(types.question)) {
 	        var state = this.state.clone();
 	        try {
 	          return inner.call(this, expr, noIn, startPos, startLoc);
@@ -19395,24 +20304,24 @@ return /******/ (function(modules) { // 
 	  // initialized. Since we know we're in Chrome, we'll just detect this case
 	  // explicitly
 	  if (typeof window !== 'undefined' && window.process && window.process.type === 'renderer') {
 	    return true;
 	  }
 
 	  // is webkit? http://stackoverflow.com/a/16459606/376773
 	  // document is undefined in react-native: https://github.com/facebook/react-native/pull/1632
-	  return (typeof document !== 'undefined' && document && document.documentElement && document.documentElement.style && document.documentElement.style.WebkitAppearance) ||
+	  return (typeof document !== 'undefined' && document.documentElement && document.documentElement.style && document.documentElement.style.WebkitAppearance) ||
 	    // is firebug? http://stackoverflow.com/a/398120/376773
-	    (typeof window !== 'undefined' && window && window.console && (window.console.firebug || (window.console.exception && window.console.table))) ||
+	    (typeof window !== 'undefined' && window.console && (window.console.firebug || (window.console.exception && window.console.table))) ||
 	    // is firefox >= v31?
 	    // https://developer.mozilla.org/en-US/docs/Tools/Web_Console#Styling_messages
-	    (typeof navigator !== 'undefined' && navigator && navigator.userAgent && navigator.userAgent.toLowerCase().match(/firefox\/(\d+)/) && parseInt(RegExp.$1, 10) >= 31) ||
+	    (typeof navigator !== 'undefined' && navigator.userAgent && navigator.userAgent.toLowerCase().match(/firefox\/(\d+)/) && parseInt(RegExp.$1, 10) >= 31) ||
 	    // double check webkit in userAgent just in case we are in a worker
-	    (typeof navigator !== 'undefined' && navigator && navigator.userAgent && navigator.userAgent.toLowerCase().match(/applewebkit\/(\d+)/));
+	    (typeof navigator !== 'undefined' && navigator.userAgent && navigator.userAgent.toLowerCase().match(/applewebkit\/(\d+)/));
 	}
 
 	/**
 	 * Map %j to `JSON.stringify()`, since no Web Inspectors do that by default.
 	 */
 
 	exports.formatters.j = function(v) {
 	  try {
@@ -19552,17 +20461,17 @@ return /******/ (function(modules) { // 
 	 * Expose `debug()` as the module.
 	 */
 
 	exports = module.exports = createDebug.debug = createDebug['default'] = createDebug;
 	exports.coerce = coerce;
 	exports.disable = disable;
 	exports.enable = enable;
 	exports.enabled = enabled;
-	exports.humanize = __webpack_require__(1024);
+	exports.humanize = __webpack_require__(581);
 
 	/**
 	 * The currently active debug mode names, and names to skip.
 	 */
 
 	exports.names = [];
 	exports.skips = [];
 
@@ -19744,17 +20653,174 @@ return /******/ (function(modules) { // 
 
 	function coerce(val) {
 	  if (val instanceof Error) return val.stack || val.message;
 	  return val;
 	}
 
 
 /***/ },
-/* 581 */,
+/* 581 */
+/***/ function(module, exports) {
+
+	/**
+	 * Helpers.
+	 */
+
+	var s = 1000;
+	var m = s * 60;
+	var h = m * 60;
+	var d = h * 24;
+	var y = d * 365.25;
+
+	/**
+	 * Parse or format the given `val`.
+	 *
+	 * Options:
+	 *
+	 *  - `long` verbose formatting [false]
+	 *
+	 * @param {String|Number} val
+	 * @param {Object} [options]
+	 * @throws {Error} throw an error if val is not a non-empty string or a number
+	 * @return {String|Number}
+	 * @api public
+	 */
+
+	module.exports = function(val, options) {
+	  options = options || {};
+	  var type = typeof val;
+	  if (type === 'string' && val.length > 0) {
+	    return parse(val);
+	  } else if (type === 'number' && isNaN(val) === false) {
+	    return options.long ? fmtLong(val) : fmtShort(val);
+	  }
+	  throw new Error(
+	    'val is not a non-empty string or a valid number. val=' +
+	      JSON.stringify(val)
+	  );
+	};
+
+	/**
+	 * Parse the given `str` and return milliseconds.
+	 *
+	 * @param {String} str
+	 * @return {Number}
+	 * @api private
+	 */
+
+	function parse(str) {
+	  str = String(str);
+	  if (str.length > 100) {
+	    return;
+	  }
+	  var match = /^((?:\d+)?\.?\d+) *(milliseconds?|msecs?|ms|seconds?|secs?|s|minutes?|mins?|m|hours?|hrs?|h|days?|d|years?|yrs?|y)?$/i.exec(
+	    str
+	  );
+	  if (!match) {
+	    return;
+	  }
+	  var n = parseFloat(match[1]);
+	  var type = (match[2] || 'ms').toLowerCase();
+	  switch (type) {
+	    case 'years':
+	    case 'year':
+	    case 'yrs':
+	    case 'yr':
+	    case 'y':
+	      return n * y;
+	    case 'days':
+	    case 'day':
+	    case 'd':
+	      return n * d;
+	    case 'hours':
+	    case 'hour':
+	    case 'hrs':
+	    case 'hr':
+	    case 'h':
+	      return n * h;
+	    case 'minutes':
+	    case 'minute':
+	    case 'mins':
+	    case 'min':
+	    case 'm':
+	      return n * m;
+	    case 'seconds':
+	    case 'second':
+	    case 'secs':
+	    case 'sec':
+	    case 's':
+	      return n * s;
+	    case 'milliseconds':
+	    case 'millisecond':
+	    case 'msecs':
+	    case 'msec':
+	    case 'ms':
+	      return n;
+	    default:
+	      return undefined;
+	  }
+	}
+
+	/**
+	 * Short format for `ms`.
+	 *
+	 * @param {Number} ms
+	 * @return {String}
+	 * @api private
+	 */
+
+	function fmtShort(ms) {
+	  if (ms >= d) {
+	    return Math.round(ms / d) + 'd';
+	  }
+	  if (ms >= h) {
+	    return Math.round(ms / h) + 'h';
+	  }
+	  if (ms >= m) {
+	    return Math.round(ms / m) + 'm';
+	  }
+	  if (ms >= s) {
+	    return Math.round(ms / s) + 's';
+	  }
+	  return ms + 'ms';
+	}
+
+	/**
+	 * Long format for `ms`.
+	 *
+	 * @param {Number} ms
+	 * @return {String}
+	 * @api private
+	 */
+
+	function fmtLong(ms) {
+	  return plural(ms, d, 'day') ||
+	    plural(ms, h, 'hour') ||
+	    plural(ms, m, 'minute') ||
+	    plural(ms, s, 'second') ||
+	    ms + ' ms';
+	}
+
+	/**
+	 * Pluralization helper.
+	 */
+
+	function plural(ms, n, name) {
+	  if (ms < n) {
+	    return;
+	  }
+	  if (ms < n * 1.5) {
+	    return Math.floor(ms / n) + ' ' + name;
+	  }
+	  return Math.ceil(ms / n) + ' ' + name + 's';
+	}
+
+
+/***/ },
 /* 582 */
 /***/ function(module, exports, __webpack_require__) {
 
 	var assignValue = __webpack_require__(114),
 	    copyObject = __webpack_require__(406),
 	    createAssigner = __webpack_require__(410),
 	    isArrayLike = __webpack_require__(220),
 	    isPrototype = __webpack_require__(218),
@@ -25469,17 +26535,17 @@ return /******/ (function(modules) { // 
 
 
 /***/ },
 /* 631 */
 /***/ function(module, exports, __webpack_require__) {
 
 	/* WEBPACK VAR INJECTION */(function(process) {'use strict';
 	var escapeStringRegexp = __webpack_require__(632);
-	var ansiStyles = __webpack_require__(633);
+	var ansiStyles = __webpack_require__(1056);
 	var stripAnsi = __webpack_require__(634);
 	var hasAnsi = __webpack_require__(636);
 	var supportsColor = __webpack_require__(637);
 	var defineProps = Object.defineProperties;
 	var isSimpleWindowsTerm = process.platform === 'win32' && !/^xterm/i.test(({"NODE_ENV":"production","TARGET":"firefox-panel"}).TERM);
 
 	function Chalk(options) {
 		// detect mode if not set manually
@@ -25604,88 +26670,17 @@ return /******/ (function(modules) { // 
 			throw new TypeError('Expected a string');
 		}
 
 		return str.replace(matchOperatorsRe, '\\$&');
 	};
 
 
 /***/ },
-/* 633 */
-/***/ function(module, exports, __webpack_require__) {
-
-	/* WEBPACK VAR INJECTION */(function(module) {'use strict';
-
-	function assembleStyles () {
-		var styles = {
-			modifiers: {
-				reset: [0, 0],
-				bold: [1, 22], // 21 isn't widely supported and 22 does the same thing
-				dim: [2, 22],
-				italic: [3, 23],
-				underline: [4, 24],
-				inverse: [7, 27],
-				hidden: [8, 28],
-				strikethrough: [9, 29]
-			},
-			colors: {
-				black: [30, 39],
-				red: [31, 39],
-				green: [32, 39],
-				yellow: [33, 39],
-				blue: [34, 39],
-				magenta: [35, 39],
-				cyan: [36, 39],
-				white: [37, 39],
-				gray: [90, 39]
-			},
-			bgColors: {
-				bgBlack: [40, 49],
-				bgRed: [41, 49],
-				bgGreen: [42, 49],
-				bgYellow: [43, 49],
-				bgBlue: [44, 49],
-				bgMagenta: [45, 49],
-				bgCyan: [46, 49],
-				bgWhite: [47, 49]
-			}
-		};
-
-		// fix humans
-		styles.colors.grey = styles.colors.gray;
-
-		Object.keys(styles).forEach(function (groupName) {
-			var group = styles[groupName];
-
-			Object.keys(group).forEach(function (styleName) {
-				var style = group[styleName];
-
-				styles[styleName] = group[styleName] = {
-					open: '\u001b[' + style[0] + 'm',
-					close: '\u001b[' + style[1] + 'm'
-				};
-			});
-
-			Object.defineProperty(styles, groupName, {
-				value: group,
-				enumerable: false
-			});
-		});
-
-		return styles;
-	}
-
-	Object.defineProperty(module, 'exports', {
-		enumerable: true,
-		get: assembleStyles
-	});
-
-	/* WEBPACK VAR INJECTION */}.call(exports, __webpack_require__(51)(module)))
-
-/***/ },
+/* 633 */,
 /* 634 */
 /***/ function(module, exports, __webpack_require__) {
 
 	'use strict';
 	var ansiRegex = __webpack_require__(635)();
 
 	module.exports = function (str) {
 		return typeof str === 'string' ? str.replace(ansiRegex, '') : str;
@@ -28447,19 +29442,114 @@ return /******/ (function(modules) { // 
 /* 698 */,
 /* 699 */,
 /* 700 */,
 /* 701 */,
 /* 702 */,
 /* 703 */,
 /* 704 */,
 /* 705 */,
-/* 706 */,
-/* 707 */,
-/* 708 */,
+/* 706 */
+/***/ function(module, exports, __webpack_require__) {
+
+	var baseFlatten = __webpack_require__(707);
+
+	/**
+	 * Flattens `array` a single level deep.
+	 *
+	 * @static
+	 * @memberOf _
+	 * @since 0.1.0
+	 * @category Array
+	 * @param {Array} array The array to flatten.
+	 * @returns {Array} Returns the new flattened array.
+	 * @example
+	 *
+	 * _.flatten([1, [2, [3, [4]], 5]]);
+	 * // => [1, 2, [3, [4]], 5]
+	 */
+	function flatten(array) {
+	  var length = array == null ? 0 : array.length;
+	  return length ? baseFlatten(array, 1) : [];
+	}
+
+	module.exports = flatten;
+
+
+/***/ },
+/* 707 */
+/***/ function(module, exports, __webpack_require__) {
+
+	var arrayPush = __webpack_require__(287),
+	    isFlattenable = __webpack_require__(708);
+
+	/**
+	 * The base implementation of `_.flatten` with support for restricting flattening.
+	 *
+	 * @private
+	 * @param {Array} array The array to flatten.
+	 * @param {number} depth The maximum recursion depth.
+	 * @param {boolean} [predicate=isFlattenable] The function invoked per iteration.
+	 * @param {boolean} [isStrict] Restrict to values that pass `predicate` checks.
+	 * @param {Array} [result=[]] The initial result value.
+	 * @returns {Array} Returns the new flattened array.
+	 */
+	function baseFlatten(array, depth, predicate, isStrict, result) {
+	  var index = -1,
+	      length = array.length;
+
+	  predicate || (predicate = isFlattenable);
+	  result || (result = []);
+
+	  while (++index < length) {
+	    var value = array[index];
+	    if (depth > 0 && predicate(value)) {
+	      if (depth > 1) {
+	        // Recursively flatten arrays (susceptible to call stack limits).
+	        baseFlatten(value, depth - 1, predicate, isStrict, result);
+	      } else {
+	        arrayPush(result, value);
+	      }
+	    } else if (!isStrict) {
+	      result[result.length] = value;
+	    }
+	  }
+	  return result;
+	}
+
+	module.exports = baseFlatten;
+
+
+/***/ },
+/* 708 */
+/***/ function(module, exports, __webpack_require__) {
+
+	var Symbol = __webpack_require__(7),
+	    isArguments = __webpack_require__(208),
+	    isArray = __webpack_require__(70);
+
+	/** Built-in value references. */
+	var spreadableSymbol = Symbol ? Symbol.isConcatSpreadable : undefined;
+
+	/**
+	 * Checks if `value` is a flattenable `arguments` object or array.
+	 *
+	 * @private
+	 * @param {*} value The value to check.
+	 * @returns {boolean} Returns `true` if `value` is flattenable, else `false`.
+	 */
+	function isFlattenable(value) {
+	  return isArray(value) || isArguments(value) ||
+	    !!(spreadableSymbol && value && value[spreadableSymbol]);
+	}
+
+	module.exports = isFlattenable;
+
+
+/***/ },
 /* 709 */,
 /* 710 */,
 /* 711 */,
 /* 712 */,
 /* 713 */,
 /* 714 */,
 /* 715 */,
 /* 716 */,
@@ -28925,37 +30015,40 @@ return /******/ (function(modules) { // 
 /* 958 */,
 /* 959 */,
 /* 960 */,
 /* 961 */
 /***/ function(module, exports, __webpack_require__) {
 
 	"use strict";
 
+	var _closest = __webpack_require__(1055);
+
 	var _scopes = __webpack_require__(1049);
 
 	var _getSymbols = __webpack_require__(1050);
 
 	var _getSymbols2 = _interopRequireDefault(_getSymbols);
 
-	var _resolveToken = __webpack_require__(1054);
-
-	var _resolveToken2 = _interopRequireDefault(_resolveToken);
+	var _getOutOfScopeLocations = __webpack_require__(1072);
+
+	var _getOutOfScopeLocations2 = _interopRequireDefault(_getOutOfScopeLocations);
 
 	var _devtoolsUtils = __webpack_require__(900);
 
 	function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
 
 	var workerHandler = _devtoolsUtils.workerUtils.workerHandler;
 
 
 	self.onmessage = workerHandler({
+	  getClosestExpression: _closest.getClosestExpression,
+	  getOutOfScopeLocations: _getOutOfScopeLocations2.default,
 	  getSymbols: _getSymbols2.default,
-	  getVariablesInScope: _scopes.getVariablesInScope,
-	  resolveToken: _resolveToken2.default
+	  getVariablesInScope: _scopes.getVariablesInScope
 	});
 
 /***/ },
 /* 962 */,
 /* 963 */
 /***/ function(module, exports, __webpack_require__) {
 
 	var baseKeys = __webpack_require__(217),
@@ -29237,171 +30330,17 @@ return /******/ (function(modules) { // 
 	exports.extractScriptTags = extractScriptTags;
 	exports.generateWhitespace = generateWhitespace;
 	exports.getCandidateScriptLocations = getCandidateScriptLocations;
 	exports.parseScript = parseScript;
 	exports.parseScripts = parseScripts;
 	exports.parseScriptTags = parseScriptTags;
 
 /***/ },
-/* 1024 */
-/***/ function(module, exports) {
-
-	/**
-	 * Helpers.
-	 */
-
-	var s = 1000
-	var m = s * 60
-	var h = m * 60
-	var d = h * 24
-	var y = d * 365.25
-
-	/**
-	 * Parse or format the given `val`.
-	 *
-	 * Options:
-	 *
-	 *  - `long` verbose formatting [false]